Writeup HTB Cereal

Table of Contents
Cereal⌗
NMAP⌗
IIS httpd 10.0 running(80:http, 443:https)
OpenSSH for Windows 7.7 running (22)
FOOTHOLD⌗
gobuster⌗
Nikto: https://cereal.htb⌗
They seem to have configure the powered by to say Sugar, good one.
There is a rate limit on the server,
meaning we can most likely get timed out if we don’t throttle our scans.
Nikto: http://source.cereal.htb⌗
Nikto found a .git folder on this vhost, juicy.
Let’s keep that in mind and try to extract the source from it.
Manual Enumeration⌗
Analyzing how the frontend works⌗
While enumerating the site manually,
I read the react.js for the frontend that is accessible via firefox inspector (among other tools).
It gives a lot of interesting information on how the frontend supplies the authentication.
The application saves “username”, “password” and “token” in localStorage which lets us control it from the javascript console.
The password is used once to obtain a JWT, then only the token is used to stay authenticated.
Obtaining the Source Code⌗
After identifying the http://source.cereal.htb virtualhost,
I found out that there is a .git directory at the webserver’s root.
I know that the source code at different stages can be reconstructed from this,
but I rather not do it by hand.
So I found this set of tools called GitTools.
I used these tools in order to reconstruct the .git folder locally and then extract the files contained for different commits.
Finding the .git folder in source.cereal.htb:

Dumping the .git folder locally:

Extracting the source code:

Now that we have the code, let’s start analyzing it.
Forging JWT tokens⌗
From that source code, I managed to retrieve the private key used to sign the JWTs:

From this key I used this useful online utility to create my JWT.
Using this JWT with the right Authorization Header, allows us to create a new cereal request.
Here is the response:
Useful JWT RCF for reference on the protocol: JWT RFC
Reading the source code, I found an endpoint that is vulnerable to json deserialization.
Unfortunately, I noticed that every endpoint except the POST one is protected with a Policy.
The policy is that the remote address has to be within specified whitelist.
If we aren’t then we are not alowed to interact with it, and the request drops.
The only two permitted adresses are the loopback ones: [127.0.0.1, ::1].
Here is the API that is vulnerable to deserialization and restricted (RequestsController.cs):
Here is the whitelist used in the “RestrictIP” Policy (appsettings.json):
This means that we have to find a way for the server to request its own pages and get the value. We can achieve this through either some sort of SSRF or CSRF vulnerability.
After reading the source code more carefully, I noticed the use of a particuliar frontend library for REACT.js.
It is used on the AdminPage.
This library is known to have an XSS vulnerability.

Looking up known vulnerabilities for this particular library gives us the following instructive articles:
- https://snyk.io/vuln/npm:react-marked-markdown:20180517
- https://github.com/advisories/GHSA-m7qm-r2r5-f77q
- https://hackerone.com/reports/344069
Using the information within these reports, we can gather that the following section in AdminPage.jsx is vulnerable to XSS:
This library is used in the AdminPage while displaying the recent Cereal request made to his API. This means that we can target the admin with an XSS.
Let’s see what we can achieve with this by exploiting it locally.
Testing the XSS locally⌗
In order to test the XSS locally and understand exactly how this works,
I took the client portion of the source code and installed it locally.
Since a REACT app is completely separated from its backend in most cases,
I can try and feed it malicious payloads to test from a local python server.
I also modified the code a bit to change the type of the route from PrivateRoute to just Route for the AdminPage so I don’t need to authenticate to see the XSS trigger. I changed the URL used to fetch the requests to http://localhost:80/requests in the request.service.js file.

This makes it so that my local webserver can serve a JSON file in format that the backend would provide to the frontend.
Letting me test my payloads locally without switching to a Windows machine in order to run the whole backend.
Here is an exemple of the JSON file format:
Here is the result after gettting http://localhost:3000/admin:

Now, let’s manipulate that to create an XSS locally, let’s start by getting an alert.
For this section, I installed the “CORS everywhere” extension in order to turn off the browser’s (chromium) restrictions on CORS.
Using the payload below, I was able to inject javascript and show an alert when the link is clicked.


The only thing left to confirm is if the administrator is clicking on the links in the AdminPage.
Luckily for us we can easily figure that out by going a bit further and trying to get a request back to us, proving that the XSS is effective.
If we do get a request back, there is a very high chance that the user will be local to the machine the server is running on, otherwise he himself would not have access to the AdminPage because of the ip whitelist.
Let’s get crafting.
Since we want more than just an alert, we will switch to using a base64 encoded payload into an eval call.
Which will allow us to have a more complex attack.
The thing here is, we cannot close the opened parentheses.
If we do so, The react-marked-markdown library will prevent the rest of our XSS from being included, because closing the parentheses means closing the link in markdown syntax.
I decided initially to use the “)” html encoding, in order to create a parenthesis in the html, it worked fine.
I then found out about another neat trick from other XSS payloads.
I can URL encode the closing parentheses and since the XSS is a link, when it is clicked these %XX will be converted into actual symbols which will complete my XSS.
Here is the final container for our payload:
I wrote this simple python script in order to test my attack in a more streamlined fashion:
Launching our test:

Receiving an answer:

With this, we have proof that the request is coming directly from the hosting server, and we have proof that XSS is happening on the target.
Deserializing our way to victory⌗
While analyzing the source code, I noticed a bad practice that allows deserialization of .net object through JSON. The developers of the applications even left a github link for us.
Let’s take a look back at the source code to analyze the code handling the deserialization.
With those gadgets filtered out, let’s keep looking at the source to see what we can use to leverage our object injection to RCE or another stage that could then give us RCE.
Reading some more, we find the Cereal.DownloadHelper class.
This class automatically downloads a file using a URL and FilePath property when constructing itself.
This is perfect, all we have to do is to inject that object with the wanted URL and FilePath and it should automagically execute the download function.
Here is the json payload that should instanciate the desired object.
The reason we used C:/inetpub/source/uploads/ is because I found that directory while enumerating the https://source.cereal.htb virtual host.
It is an educated guess that the user running the service might have write access to this directory.
To use this, we now have to modify our JS payload.
We need to create a cereal request by posting the above JSON file to /requests,
and then we can call the GET method on /requests using the CSRF which means that we are on 127.0.0.1 (bypassing the whitelist from the backend).
Calling the GET method on /requests?id=<id>, will trigger the deserialization of our object, which will cause it to download a reverse shell from our webserver.
Let’s generate a aspx reverse shell with msfvenom:
Here is the complete python script for the final exploit that I used:
I added a try catch that gets me the error if the javascript crashes and the error/success message if the object is deserialized correctly or incorrectly.
Of course, this was not done in one iteration and took a lot of research and reading, the try catch was very useful in the debugging parts of this process.


After running this and getting our reverse shell uploaded, we can start the listener and execute said reverse shell:
This gives us the user HASH !
We now have a shell as the “sonny” user.
Privesc to SYSTEM⌗
While enumerating the compromised system as “sonny”.
I noticed a service listening on port 8080 that was not shown in my earlier scan with NNMAP.
8080 is normally a port associated with a webserver so let’s try to cURL it from the machine:
Here is the result:
We see here that this seems to be a webpage that gets fed by a GraphQL running behind it at the /api/graphql endpoint.
That might be interesting to enumerate.
Let’s port forward this to our local host to make our lives easier.
I created an msfvenom meterpreter shell binary that will run on our windows x64 victim.


Now let’s port forward the service back to us:


Let’s enumerate the GraphQL Service with this script:
Using this we can enumerate with different queries:
The result without default information:
From this we see that there is a mutation which is like a function, it does actions on the backend.
As we can see, this mutation seems to take in a sourceURL, we could probably execute web requests using this.
We also know that this process is also running as system by doing a ‘netstat’ in our meterpreter shell:

Here is our call to test it out:


So we can send request as the System process to the destination of our choosing.
We also know from our basic enum on the system, that we have the SeImpersonatePrivilege.
THIS IS SCREAMING POTATO EXPLOIT.
Normally Potato exploits leverage different kind of services, could it be possible with an IIS ?
The answer is YES and there is an amazingly written article about it here: https://micahvandeusen.com/the-power-of-seimpersonation/
After reading this article, I went to the author’s github to grab his homegrown potato:
https://github.com/micahvandeusen/GenericPotato
I then hopped on my commando VM (Windows Offsec distribution) to compile this, for some reason my Kali machine was completely unwilling to compile it.
After compiling it I transferred it back to Kali using a python webserver and continued.
Now that we have GenericPotato, we need a way to receive our elevated process.
I ended up using a nc.exe binary compiler for windows amd64 and sending ourselves a reverse shell:
So GenericPotato can be started to listen for incoming priviledged HTTP connections, it will then ask for authorization, which will prompt the webserver to give its token for authentication.
GenericPotato will then steal that token and start an elevated thread with it, which will be our reverse shell.
Here’s what we have to do:
-
[Attacker] Start our listener for the chosen port (1337 in our case)
-
[Victim] Execute GenericPotato with the right arguments
-
[Attacker] Launch SSRF to our target listener (127.0.0.1:8888 is the default for GenericPotato)
And we have our System shell and we finally owned this machine !

And we have the root.txt !
This box took me an actual 2 weeks to root but it happened.
This was the hardest box i’ve owned by far, it was also very fun and rewarding.
If you made it here, thank you for reading !
Feel free to contact me for questions or improvements I could make on the writeup.
Ressources⌗
https://github.com/micahvandeusen/GenericPotato https://graphql.org/learn/schema/ https://micahvandeusen.com/the-power-of-seimpersonation/ https://github.com/jpillora/chisel https://0xdf.gitlab.io/2020/08/10/tunneling-with-chisel-and-ssf-update.html