Italy - Silent Signals (network)

6KB
Open

Inspect all DNS responses, and realise that there is a character from the flag encoded at offset 0x55 of each of the dns response packets. Let's look at the first two:

I then got GPT to generate me the solve script:

#!/usr/bin/env python3
"""
extract_flag.py

Usage:
    python extract_flag.py capture.pcap
    python extract_flag.py capture.pcap --base payload
    python extract_flag.py capture.pcap --base packet   # default
    python extract_flag.py capture.pcap --base udp
    python extract_flag.py capture.pcap --offset 0x55

Description:
    Reads the pcap and extracts the byte at offset (default 0x55)
    for each DNS/UDP packet. By default the offset is applied to the
    entire raw packet (base='packet'). Use base='payload' to apply the
    offset relative to the UDP payload start (useful for application-layer data).
"""

import sys
import argparse
from scapy.all import rdpcap, UDP, DNS, Raw

def is_dns_packet(pkt):
    # Consider packets with DNS layer or UDP port 53
    return (DNS in pkt) or (UDP in pkt and (pkt[UDP].sport == 53 or pkt[UDP].dport == 53))

def main():
    p = argparse.ArgumentParser(description="Extract byte at offset 0x55 from DNS/UDP packets")
    p.add_argument("pcap", help="pcap file to read")
    p.add_argument("--offset", default="0x55", help="offset (hex like 0x55 or decimal like 85)")
    p.add_argument("--base", choices=["packet", "udp", "payload"], default="packet",
                   help=("where to apply offset:\n"
                         "  packet  - offset from start of entire packet (default)\n"
                         "  udp     - offset from start of UDP header\n"
                         "  payload - offset from start of UDP payload (application layer)"))
    args = p.parse_args()

    # parse offset
    if isinstance(args.offset, str) and args.offset.startswith("0x"):
        offset = int(args.offset, 16)
    else:
        offset = int(args.offset)

    try:
        packets = rdpcap(args.pcap)
    except FileNotFoundError:
        print(f"File not found: {args.pcap}", file=sys.stderr)
        sys.exit(2)
    except Exception as e:
        print("Error reading pcap:", e, file=sys.stderr)
        sys.exit(2)

    extracted = bytearray()

    for i, pkt in enumerate(packets, start=1):
        if not is_dns_packet(pkt):
            continue

        raw = bytes(pkt)  # full packet bytes
        base_off = 0

        if args.base == "udp":
            if UDP in pkt:
                # find start of UDP header in raw bytes:
                # locate the UDP header by searching for the UDP source and dest ports in bytes
                # fallback: try to compute via IP header length if present
                try:
                    ip_layer = pkt.payload  # could be IP, IPv6, etc.
                    # Scapy gives access to raw bytes for the UDP header via pkt[UDP].build() not safe here.
                    # Simpler and robust approach: locate pkt[UDP].sport and dport in the raw stream
                    sport = pkt[UDP].sport
                    dport = pkt[UDP].dport
                    # 2-byte numbers in network order
                    sport_bytes = sport.to_bytes(2, "big")
                    dport_bytes = dport.to_bytes(2, "big")
                    # search for sport+dport or dport+sport sequence near each other
                    candidate = raw.find(sport_bytes + dport_bytes)
                    if candidate != -1:
                        base_off = candidate
                    else:
                        # try dport+sport
                        candidate = raw.find(dport_bytes + sport_bytes)
                        base_off = candidate if candidate != -1 else 0
                except Exception:
                    base_off = 0
            else:
                base_off = 0

        elif args.base == "payload":
            # find UDP payload start (Raw.load) if present
            if Raw in pkt:
                try:
                    payload = pkt[Raw].load
                    pos = raw.find(payload)
                    base_off = pos if pos != -1 else 0
                except Exception:
                    base_off = 0
            else:
                base_off = 0

        target_index = base_off + offset
        if target_index < len(raw):
            extracted.append(raw[target_index])
        else:
            # skip if packet too short
            # optionally you could pad or raise a warning
            # print(f"Packet #{i} too short for offset {hex(target_index)} (len {len(raw)})", file=sys.stderr)
            pass

    # Try to decode as ASCII printable
    try:
        s = extracted.decode("ascii")
        # if it contains unprintable bytes, show hex as well
        if all(32 <= b < 127 for b in extracted):
            print("Flag (ASCII):", s)
        else:
            print("Extracted bytes (ASCII with non-printables present):")
            print(s)
            print("Hex:", extracted.hex())
    except Exception:
        print("Extracted bytes (hex):", extracted.hex())
        # also try best-effort printable representation
        printable = ''.join(chr(b) if 32 <= b < 127 else '.' for b in extracted)
        print("Printable rep:", printable)

if __name__ == "__main__":
    main()

# CSG_FLAG{Y0u_N33d_T0_Parse_DNS_TTL}

Last updated