$ cat writeup.md…
$ cat writeup.md…
DawgCTF SP26
Task: analyze a PCAP containing a router web administration session and firmware upgrade. Solution: extract HTTP objects, recover the router UI pages and multipart upload, then combine the old Linksys WRT610N v1 firmware version with the uploaded OpenWrt image name.
Provided artifact:
morph.pcapFlag format:
DawgCTF{Manufacturer_Model_OldFirmwareVersion_NewFirmwareName_NewFirmwareVersion}
English summary: the challenge gives a packet capture of a router administration session. The goal is to identify the device, determine the old firmware version, recover the uploaded replacement firmware, and assemble the flag fields in the required order.
The first step was to confirm what protocols were present:
tshark -r morph.pcap -q -z io,phs
Relevant result:
http frames:250 bytes:67977 mime_multipart frames:1 bytes:375
That immediately suggests a web-based workflow, and the single mime_multipart entry is a strong hint that a file upload happened inside HTTP.
Listing HTTP requests showed a browser talking directly to 192.168.1.1 and visiting typical embedded router pages such as /Wireless_Basic.asp, /Management.asp, and /Upgrade.asp.
tshark -r morph.pcap -Y "http.request" -T fields \ -e frame.number -e http.request.method -e http.host -e http.request.uri
Important requests included:
2748 GET 192.168.1.1 /Management.asp 3291 GET 192.168.1.1 /Upgrade.asp 13300 POST 192.168.1.1 /upgrade.cgi
The capture also contains repeated Basic Authentication headers:
tshark -r morph.pcap -Y "http.authorization" -T fields \ -e frame.number -e ip.src -e ip.dst -e http.authorization
Example output:
69 192.168.1.101 192.168.1.1 Basic OmFkbWlu
Decoding the Base64 portion confirms the credentials:
python3 - <<'PY' import base64 print(base64.b64decode('OmFkbWlu').decode()) PY
Output:
:admin
The observed header decodes to :admin, showing that the capture uses the default admin password during HTTP Basic Auth. That was enough to explain why the browser could access the router interface in the capture.
To inspect the router web interface offline, I exported all HTTP objects:
mkdir http_objects tshark -r morph.pcap --export-objects http,http_objects
This recovered the router pages and the upload transaction artifacts, including:
http_objects/Upgrade.asphttp_objects/Management.asphttp_objects/upgrade.cgihttp_objects/upgrade(1).cgiAt this point the challenge becomes a straightforward reconstruction of what the administrator saw and uploaded.
Searching the recovered ASP pages for model and version strings:
rg -n 'WRT610N|1\.00\.00|B18|WRT610NV1' http_objects/*.asp
The key evidence is in Upgrade.asp:
style="FONT-SIZE: 7pt" ... : 1.00.00 B18 style="FONT-SIZE: 8pt"><B>WRT610N</B>
That gives the visible router model WRT610N and old firmware version 1.00.00 B18.
Management.asp provides an additional detail that matters for the flag:
onclick=window.location.href="WRT610NV1_v1.00.00.cfg"
This backup filename is the strongest evidence for the hardware revision being v1. The main UI only displays WRT610N, but the configuration backup naming convention includes WRT610NV1, so the correct model field for the flag is WRT610N_v1.
The upload is the POST to /upgrade.cgi seen at frame 13300. After exporting objects, the multipart body is preserved in http_objects/upgrade.cgi.
The easiest way to recover the uploaded filename is:
strings http_objects/upgrade.cgi | rg 'filename=|openwrt|Content-Disposition'
Output:
Content-Disposition: form-data; name="file"; filename="openwrt-24.10.0-bcm47xx-generic-REDACTED-v1-squashfs.bin"
This single filename gives almost everything needed for the new firmware portion:
LinksysWRT610N v1OpenWrt24.10.0I also verified that the extracted firmware image itself matches the same naming pattern:
strings openwrt-24.10.0-bcm47xx-generic-REDACTED-v1-squashfs.bin | \ rg 'OpenWrt|24\.10\.0|wrt610n|linksys'
The important point is that the router upload is not ambiguous: the multipart POST explicitly contains the OpenWrt image filename.
Assemble the flag fields in the required order:
LinksysWRT610N_v1
WRT610N is shown in the UIv1 is justified by WRT610NV1_v1.00.00.cfg in Management.asp1.00.00_B18
Upgrade.aspOpenWrt
24.10.0
Final flag:
DawgCTF{REDACTED}
Minimal reconstruction commands:
#!/usr/bin/env python3 import base64 from pathlib import Path import re upgrade = Path("http_objects/Upgrade.asp").read_text(errors="ignore") management = Path("http_objects/Management.asp").read_text(errors="ignore") multipart = Path("http_objects/upgrade.cgi").read_text(errors="ignore") old_fw = re.search(r'1\.00\.00 B18', upgrade).group(0) model = re.search(r'WRT610N', upgrade).group(0) rev = 'v1' if 'WRT610NV1_v1.00.00.cfg' in management else 'unknown' filename = re.search(r'filename="([^"]+)"', multipart).group(1) assert base64.b64decode('OmFkbWlu').decode() == ':admin' assert filename == 'openwrt-24.10.0-bcm47xx-generic-REDACTED-v1-squashfs.bin' flag = f'DawgCTF{{Linksys_{model}_{rev}_{old_fw.replace(" ", "_")}REDACTED}}' print(flag)
$ cat /etc/motd
Liked this one?
Pro unlocks every complete writeup and expanded API access. $9/mo.
$ cat pricing.md$ grep --similar