Page cover

Week 10. Ouija

TL;DR

This is an insane Ubuntu 22.04.3 machine running a web server behind a balancing Haproxy v2.2.16. This is vulnerable to HTTP request smuggling (CVE-2023-25725), which can be abused to reach a subdomain and dump the code of another JS application running on port 3000. Analyzing this code we deduce how a superuser is formed and using this information we launch a hash extension attack, that allows us to forge a superuser token. Using this token we can query the API endpoint to dump the private key of the low-privileged user and get the user flag. For escalation, we find a parameter accepting user input is vulnerable to buffer overflow. This can be exploited to generate a malicious PHP file in the host, which in fact acts as a webshell run by root.

KEYWORDS

Haproxy 2.2.13, CVE-2023-25725, HTTP request smuggling, SHA-256, hash extension attack, API token, reversing, Ghidra, buffer overflow, PHP installation, gdb debugger.

REFERENCES

https://www.haproxy.com/blog/february-2023-header-parser-fixed

https://www.cvedetails.com/cve/CVE-2023-25725/

https://medium.com/nerd-for-tech/http-request-smuggling-part-1-concepts-b89bfe17b210

https://medium.com/nerd-for-tech/http-request-smuggling-part-2-tl-ce-exploit-ec1171a88459

https://www.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks

https://github.com/iagox86/hash_extender

https://github.com/hugsy/gef

ENUMERATION

Port scan.

Enumerate the open ports.

Continue fuzzing for subdomains.

There is a web server running on port 80, and a subdomain running on http://dev.ouija.htb

Inspecting the source code in Firefox, we find another subdomain on http://gitea.ouija.htb

Browse the Gitea server, register a new account and log in. List the instructions on how the ouija server has been set up. It uses an Haproxy application version 2.2.16.

Finally, enumerate the API running on http://gitea.ouija.htb/api/swagger

Most of the endpoints require an authentication token but the endpoint /users/search dumps the username leila without authentication.

Take note of this username and move forward.

USER

Looking for Haproxy 2.2.16 vulnerabilities, we find out it is vulnerable to HTTP request smuggling: https://www.haproxy.com/blog/february-2023-header-parser-fixed. CVE details available here: https://www.cvedetails.com/cve/CVE-2023-25725/

There are good articles to investigate what HTTP request smuggling is here: https://medium.com/nerd-for-tech/http-request-smuggling-part-1-concepts-b89bfe17b210 and here: https://medium.com/nerd-for-tech/http-request-smuggling-part-2-tl-ce-exploit-ec1171a88459

The Hhaproxy application is installed for traffic balancing purposes. We can abuse the HTTP CE.TE smuggling vulnerability to reach the dev.ouija.htb subdomain.

Payloads can be smuggled with Burpsuite, just deactivate the automatic update of the content length.

The following payload allows reaching the dev.ouija.htb subdomain exploiting HTTP smuggling.

In the response we find 2 interesting files:

Using the HTTP request smuggling again we dump the contents of both files. First, for the init.sh file we use the same payload, just update the content-length to match the size of the request.

The file init.sh contains a hash and a username.

This is not useful for the moment, so we take note and dump the other app.js file. The payload is the same, just remember to update the content-length accordingly in the request.

This file contains the source code of an application running on port 3000.

The app running on port 3000 runs several APIs, we just need to focus on /get/file and /users. These endpoints use token authentication which is enforced in function ensure_auth. An user authenticates sending 2 headers: ihash and identification

The identification header is formed taking an username and appending certain string, then it is base64-encoded. Finally, a SHA-256 hash is calculated on the resulting string using a secret key stored in env variable k, but we do not know this key or its length.

The ihash header is a signature hash that will be compared to the resulting identification to verify user's identity.

If we remember the init.sh file, we already know the value of the prepended data and the hash, so we we can launch a hash extension attack (https://www.skullsecurity.org/2012/everything-you-need-to-know-about-hash-length-extension-attacks).

Install the hash_extender tool from GitHub: https://github.com/iagox86/hash_extender

Several deprecation warnings prevent making the application, but this can be solved editing the Makefile and adding -Wno-deprecated-declarations in the CFLAGS line.

We do not know the size of the secret, so we will have to brute force it. First step is to generate a file called strings containing hashes from size 1 to 47. We use the username bot1:bo from the init.sh file, and the append string is ::admin:True. The signature hash is taken also from the init.sh file. Finally, the results are base64 encoded and written to the strings file.

We also need the New signature, we grab it launching a hash_extender with whatever length, (e.g. 20).

At this point we have the New signature field 14be2f4a24f876a07a5570cc2567e18671b15e0e005ed92f10089533c1830c0b, and the strings file containing the New string from size 1 to 47. We are ready to bruteforce the application. For this, a small bash script is proposed.

Running it we find the authentication token, and its length (note this is not the same as the secret length).

Finally, query the /file/get API on port 3000 with this token, and browse files in the application file system running on /proc/self/root to dump private key for user leila

Use this key to open an SSH sessions as leila and get the user flag.

ROOT

Start from the SSH session for user leilaand take the opportunity to enumerate the system and the user.

There is something listening on port 9999.

Forward port 9999 to Kali and enumerate it with Firefox.

Enumerate the file system, the source code of this site is located in the path /development/server-management_system_id_0/index.php.

The source code contains a call to a function say_lverifier() which checks username and password client inputs.

The library is located in the path /usr/lib/php/20220829/lverifier.so. Decompile with Ghidra and inspect the say_lverifier() function, this calls another function validating_userinput() for client input sanitization.

Navigate to the function validating_userinput(). Decompiling was done with IDA and generating pseudocode (F5).

This part is vulnerable to buffer overflow. The username input is stored in a buffer nu with an allocated size of 800; however, the user input is not limited. On the other hand, size_t buffer type is used to save the size of username, which maximum value is 65535 bytes. Finally, inspect the function event_recorder. This is used to write the log after the user input validation finishes.

To fuzz the buffer overflow we need to set up a test rig running PHP and gdb (C/C++ debugger). The rig machine will be an Ubuntu 22.04.2 host with PHP 8.2.

First, install PHP.

Verify PHP installation.

Verify gdb is installed (should be the case by default in Ubuntu), then install gef (gdb enhancer features https://github.com/hugsy/gef).

Once PHP is installed and running, we will load the lverifier.so file.

Finally, modify the php.ini and lverifier.ini files.

Run PHP under the gdb environment to debug the application.

Make the application crash by sending a large input in the username parameter.

The application crashes and dumps the register content.

Before restarting it, add a breakpoint in the event_recorder function, this will be useful in the next step.

In order to control the registers, we need to know the offset of the payloads. For this, create a pattern size 65535 with pwntools or msf-pattern_create, then feed it to the application and let it crash.

Inspect the content of the p and w parameters of the event_recorder function.

And find their position in the pattern.

So we have found that when we feed a large input in the username parameter, it overflows and overwrites parameters p (position 16 of the input payload) and w (position 128 of the input payload) in the event_recorder function. Also, we know the user input for username is assigned to a buffer size 800.

Abusing the buffer overflow we can manipulate the event_recorder function parameters, and therefore control the output log path and its content. In fact, manipulating the last part of the log path we can also manipulate the log content.

To get a root shell we will create a directory in the victim machine whose name is actual PHP code. Then we will use the overflow to write the PHP file to the web directory, so it can be exploited from a web browser. At the end, the final written log will also contain our directory name, which is the PHP code.

First, create a directory in leila SSH shell. The directory name itself is PHP code in fact.

To generate the payload, we will use a Python interpreter.

Back in Kali, capture a login attempt in http://localhost:9999 and use repeater to insert the payload in the username parameter.

Send the payload and verify an index.php file containing the malicious PHP code has been created in the victim machine in the path /development/server-management_system_id_0/index.php

Now we can send a reverse shell using the index.php file. Let's verify first in a browser we have RCE (i.e. a webshell).

The only thing that's left is to prepare a reverse shell payload and send it with Burpsuite.

A reverse shell is received on port 1919.

You are root.

Last updated