CERTain Doom
Bob has since joined the CERT team and developed a nifty new site. Is there more than meets the eye? - by hydragyrum
The following post by 0xb0b is licensed under CC BY 4.0
Recon
We start with an Nmap scan and find four open ports. Two of them are web servers on port 80
and 8080
.
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.
Next, we enumerate the directories and pages of the endpoint on port 80
but do not find anything of interest.
Visiting the page gives us an ASCII art rickroll animation. The page redirects us later to a YouTube video.
In the source, we can find a domain and a subdomain. But further enumeration doesn't yield anything of interest.
We move on to the web server on port 8080
. Here we have a single site, enumerable: reports
.
The site itself does not have an index page.
On the report page, we can upload reports, which could be interesting.
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:
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
.
For our exploit, we use the following release of ysoserial:
As in the script, we create a payload that is then downloaded from our attacker machine, whose permissions are customized and then executed.
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:
Generate Payload to download our payload:
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.
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:
Execute script:
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.
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
.
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.
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.
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).
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.
Next, we download the latest release of ligolo-ng. The proxy and the agent are in the amd64 version.
On our attack machine, we start the proxy server.
Next on the target machine we start the agent to connect to our proxy.
We receive a message on our ligolo-ng proxy that an agent has joined. We select the session using session
and then start it.
We are now able to reach the machines in the networks 172.18.0.0/16
and 172.20.0.0/16
.
The machines of interest to us are 172.20.0.3
and 172.20.0.2
.
It is possible that the results of the nmap scan are different because the IP addresses could be swapped.
Recon
We have a web server running on 172.20.0.2
on port 80
.
And we have a web server running on 172.20.0.3
on port 8080
.
On the machine that has a web server running on port 80, we do not initially recognize anything conspicuous except a lot of JavaScript.
There seems nothing to detect on the machine running the web server on port 8080
.
Here, on 172.20.0.2
, we have a library for documents. We can search for them or upload them.
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.
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.
Changing the Origin to library-back header
gives us a 401
.
So we add the following entry to our /etc/hosts
file:
But still, we receive CORS errors.
With another addition to our /etc/hosts
file, we retry the request.
With a new filter request,...
... we are now reditected to a login page.
We capture the login request to see what is happening. It makes a post request to the backend.
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.
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.
In addition, we are able to download them via /documents/download/file.name
.
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.
But if we add the cookie credz
, we get insight.
When we search for documents with the author bob, we only find one entry.
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.
If we search for hidden files, we find two more. Only the ones of our user bob.
Furthermore, we can also query the information on files directly by specifying the ID.
Next, we try to download all the files we find.
Another rickroll.
An interesting todo list
And last but not least, the chat.log
. Here we find the second flag and a hint on how to get to the third.
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.
What is striking about the IDs is that they hardly differ except for the last digits. We can therefore fuzz these for further documents.
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
.
But unfortunately, we cannot open or download this.
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.
This could be an indicator of CVE-2022-21449
.
There is a proof of concept for this, using a manipulated signature that passes the checks on the jwt token.
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.
We submit a token with a valid signature. To do this, we use the Authorization header. After submitting, we receive a 401 response.
Next, we use the manipulatede one, which just appends an ECDSA signature with r=s=0
encoded in DER, MAYCAQACAQA=.
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.
With the following claim, we have our first success:
We don't get a 403
, but a 500
. It seems like it's still a bit broken, since we can't filter.
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.
And we still cannot download it.
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
.
Now we are able to find the hidden files of the user hydra
.
Furthermore we can now download the PDF.
And to avoid the last troll, we skip the last page and head to page 8, where we find the last flag hidden.
Last updated