[misc] DEFCON CTF Qualifier 2026 - rfc1149a Write-up
1. Overview
1.1 Challenge Description
## 1.2 Distributed Challenge Files
rfc1149a/
├── img01.jpg
├── img02.jpg
├── img03.jpg
├── img04.jpg
├── img05.jpg
├── img06.jpg
├── img07.jpg
├── img08.jpg
├── img09.jpg
├── img10.jpg
├── img11.jpg
├── img12.jpg
├── img13.jpg
└── img14.jpg
2. TL;DR
- The packet fragments contained in the provided image files were parts of IPv4/TCP packets. We ordered them according to their TCP sequence numbers, reconstructed the packet streams, and decompressed the chunked, gzip-encoded HTTP response data found in some of the packets.
- From the decompressed data, we discovered the challenge server domain,
supersecretflagserverdonttellanyoneaboutit.rfc1149.ctfwithbirds.com, as well as a 302 Found response.
- The latter part of the
302 Found response, including the timestamp and session_id, was missing. Using a reference 302 Found response obtained by logging in and the IPv4 Total Length field, we reconstructed the entire payload except for the Date and session_id values.
- The
Date could be estimated by comparing it with TSval, allowing us to reduce the number of possible session_id values using the TCP checksum.
3. Initial Analysis
3.1 Analysis of image files (img01.jpg~img14.jpg)
- There are several paper strips, and each strip has hex bytes in the form
45 00 ... printed on it.
(0x45 = IPv4 version 4 + IHL 5, 0x06 = TCP Packet) → IPv4/TCP Packet data
- Each image file was combined into a single file for analysis (with Kredsya, kimdy)
## 3.2 IPv4/TCP packet analysis
3.2.1 Common points
- Only server response direction packets exist
- 34.16.111.14:80 → 10.13.37.1:*
3.2.2 seq 010b94cf series (Home)
| Start seq |
Confirmed clues |
010b94cf |
200 OK, text/html, gzip, chunk 15d |
010b96fa |
200 OK, text/css, style.css |
010b9846 |
style.css gzip continuation, chunk 227 |
010b9992 |
style.css gzip/deflate continuation |
010b9a8e |
200 OK, text/css, theme-light.css |
010b9bda |
theme-light.css gzip body, chunk a9 |
3.2.3 seq 96670169 series
| Start seq |
Confirmed clues |
96670169 |
SYN,ACK, no payload |
9667016a |
200 OK, text/css |
966702b6 |
gzip body, chunk ab |
3.2.4 seq b132103e series (Login/Flag)
| Start seq |
Confirmed clues |
b132103e |
200 OK, text/html, gzip, chunk 1b6 |
b132118a |
HTML gzip/deflate continuation |
b13212c2 |
304 NOT MODIFIED, style.css |
b13214b3 |
304 NOT MODIFIED, theme-dark.css |
b13215ad |
200 OK, text/html, gzip, chunk 18d |
3.2.5 seq f4f56c96 series
| Start seq |
Confirmed clues |
f4f56c96 |
302 FOUND |
f4f56de2 |
redirect body tail, <a href="/">/</a> |
f4f56e40 |
200 OK, text/html, gzip, chunk 14e |
f4f56f8c |
HTML gzip/deflate continuation |
f4f5705c |
304 NOT MODIFIED, style.css |
f4f57152 |
304 NOT MODIFIED, theme-light.css |
f4f5724d |
304 NOT MODIFIED, theme-dark.css |
3.3 gzip decompress result
Confirmed the server domain http://supersecretflagserverdonttellanyoneaboutit.rfc1149.ctfwithbirds.com in style.css
@import url('http://supersecretflagserverdonttellanyoneaboutit.rfc1149.ctfwithbirds.com/static/theme-light.css') (prefers-color-scheme: light);
@import url('http://supersecretflagserverdonttellanyoneaboutit.rfc1149.ctfwithbirds.com/static/theme-dark.css') (prefers-color-scheme: dark);
Confirmed parts of the HTML responses (Home, Login, Flag)
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Home</title>
<link rel="icon" href="data:image
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Login</title>
<link rel="icon" href="data:image/svg+xml,<svg
xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🏁</text></svg>">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/login">Login</a>
<a href="/register">Register</a>
<a href="/flag">Flag</a>
</nav>
<h1>Login</h1>
<form method="POST" action="/login">
<label>Username <input name="username"></label>
<label>Password <input name="password" type="password"></label>
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="/register">Register</a></p>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Flag
In addition, theme-light.css and theme-dark.css were restored
4. Key Insight / Reconstruction Primitive
5. Solution Flow (Step-by-Step)
5.1 Restoring HTTP response structure
-
Align IPv4/TCP fragments based on sequence, inflate the gzip body as much as possible, and gzip decompress it.
-
Confirmed the server domain in style.css
-
When connecting to the server, /etc/hosts must be configured as follows:
34.16.111.14 supersecretflagserverdonttellanyoneaboutit.rfc1149.ctfwithbirds.com
-
Confirmed the /login, /register, and /flag links in the Login series pages.
-
Also confirmed <title>Flag in the Flag series HTML prefix.
-
Confirmed the beginning of HTTP/1.1 302 FOUND and the end of the body that redirects to /.
- When analyzing the web server response, the redirect to
/ was confirmed to occur after a successful login, and session_id was also returned in the response.
-
Why this is necessary: Because /flag exists within the actual site structure, it was necessary to first confirm that there was a target to access after obtaining an authenticated session.
5.2 Selection of restoration targets
- The
302 FOUND redirect response was selected as the core target for recovery.
- Assuming the possibility that this response contains authentication-related headers, the invisible header portion was modeled as unknown.
- Why this is necessary: Since the flag body was not recovered directly from the packet, it was necessary to find the session value that grants access to
/flag.
5.3 Calculation of HTTP Date Candidates
- Since the
Date: header is also included in the TCP payload, it affects the checksum calculation.
- Therefore,
session_id alone cannot be left as unknown, and the candidates for the Date string in the 302 Found response must also be narrowed down.
- Server TCP timestamp of the
302 Found response:
TSval of the 302 Found response: 0x00b34712 = 11,749,138
- The tick rate was calculated by comparing
Date and TSval in surrounding HTTP responses using the same timestamp clock.
23:16:01, TSval 12,991,738
23:44:56, TSval 13,636,919
00:44:09, TSval 14,958,892
03:01:41, TSval 18,028,619
- The growth rate of the surrounding interval converges to approximately
372 ticks/sec
645,181 / 1,735s ≈ 371.86
1,321,973 / 3,553s ≈ 372.07
3,069,727 / 8,252s ≈ 372.00
TSval difference between the 302 Found response and 23:16:01:
12,991,738 - 11,749,138 = 1,242,600 ticks
1,242,600 / 372 ≈ 3,340.3s
- Subtracting approximately
3,340s from 23:16:01 GMT gives a time near 22:20:21 GMT.
- Therefore, centering on
22:20:21 GMT and accounting for second-level uncertainty, 22:20:19-22:20:23 GMT was selected as Date candidates.
- Why is this necessary: Because even a difference of just one second in
Date changes the TCP checksum result, thereby altering the pool of session_id candidates.
5.4 Generating session_id candidates using TCP checksums
- Construct a
302 Found response header template for each Date candidate.
- Keep only candidates that do not conflict with the confirmed
302 Found data or the redirect body.
- Treat
session_id as an unknown 8-digit hex value, and keep only candidates where the TCP pseudo-header + TCP segment checksum matches.
- Reduce candidate search using a meet-in-the-middle method that splits the checksum contribution of the first 4 and last 4
session_id digits.
- Why this is necessary: Since directly verifying the full
16^8 space would take too long, the candidates had to be reduced using the packet's own checksum.
5.5 Candidate Session Verification
- Insert the generated candidate into
Cookie: session_id=... to verify access to /flag.
- Check the status using
HEAD /flag, and perform GET /flag only on candidates where status == 200 to check the body.
- Why this is necessary: Since the checksum only provides candidates compatible with the original packet, additional verification was required to confirm actual access to
/flag.
5.6 Flag acquired
- Checked the
GET /flag body with the session candidate where status == 200 appeared in HEAD /flag.
6. Flag
bbb{dear_bob_got_any_hot_goss_on_mallory_love_carol_ps_u_up?}
7. Pitfalls & Rabbit Holes
- Attempt 1
- After acquiring the server domain, attempted to hijack the session using web hacking techniques (SQL Injection, SSTI, XSS, etc.), but failed.
- Attempt 2
- Attempted to fully recover the invisible gzip body, but failed.
- Attempt 3
- Since the HTTP response header was visible for the 200 packet presumed to have arrived immediately after the 302 packet, an attempt was made using a
Date 0-5 seconds after that packet's Date; however, the actual difference was more than 20 minutes, resulting in failure.