← Back to Writeups
CUSTOMMediumNetwork

security-breach-ruin

XESXOR9/10/20262 min read
#network#custom#medium#arp_spoofing#median_based_bruteforce#mitm_packet_capture#tcp_length_oracle

security-breach-ruin

Platform: CUSTOM | Category: Network | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: arp_spoofing, median_based_bruteforce, mitm_packet_capture, tcp_length_oracle

Summary

Walkthrough of the CUSTOM Network security-breach-ruin. Task: source code and shell access exposed an internal HTTPS dashboard where attacker-controlled filter text and the secret flag were gzip-compressed into the same JSON response. Solution: become on-p

Description

Recover the flag from the internal HTTPS dashboard using the provided source code and shell access on 10.0.0.3.

The challenge gave server.py, admin.py, and a shell on 10.0.0.3. A local helper exploit script also exists at tasks/umdctf/security-breach-ruin/solve.py.

Analysis

Reading the source showed a classic BREACH-style compression side channel. The server returned gzip-compressed JSON from /api/dashboard:

json
{"filter":"<attacker>","flag":"<secret>"}

The attacker controlled latest_suggestion through POST /api/suggestions, and the admin polled /api/dashboard over HTTPS about every 50 ms with Accept-Encoding including gzip. That meant a correct flag prefix inside filter should compress better against flag, making the encrypted response slightly shorter.

The first obstacle was visibility. Shell access was only on 10.0.0.3, and direct sniffing failed because unicast traffic between 10.0.0.1 (admin) and 10.0.0.2 (server) was not naturally visible from our host.

Exploit Summary

I used bidirectional ARP spoofing to become the man in the middle:

bash
arpspoof -t 10.0.0.1 10.0.0.2
arpspoof -t 10.0.0.2 10.0.0.1

With IP forwarding already enabled, tcpdump could then capture the inbound HTTPS packets. The oracle was built by summing server -> admin TCP payload lengths between consecutive admin -> server request packets. When the guessed prefix was correct, gzip achieved a better match and the encrypted response became smaller.

To recover the flag reliably, I brute-forced characters from [a-z0-9_}] starting from the known prefix UMDCTF{. Because packet sizes were noisy, each candidate was measured across repeated admin polls and ranked by median response length.

Solution

  1. Read server.py and admin.py and identify the gzip compression oracle in /api/dashboard.
  2. Notice that plain packet sniffing on 10.0.0.3 does not reveal the 10.0.0.1 <-> 10.0.0.2 HTTPS flow.
  3. Launch ARP spoofing in both directions so the admin and server route traffic through the player host.
  4. Capture inbound packets with tcpdump while repeatedly submitting candidate prefixes through POST /api/suggestions.
  5. For each guess, sum the server -> admin TCP payload sizes until the next admin poll begins.
  6. Choose the candidate with the smallest median size, append it to the known prefix, and repeat until } is found.
python
#!/usr/bin/env python3
# See tasks/umdctf/security-breach-ruin/solve.py

Key Takeaways / Lessons

  • Mastering arp_spoofing, median_based_bruteforce, mitm_packet_capture, tcp_length_oracle was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-04-25 • CUSTOM • security-breach-ruin • Difficulty: Medium