← Back to Writeups
HTBEasyForensics

I Love Bacon!

D3v0o0Nu119/2/20264 min read
#forensics#htb#easy#anomaly_hunting#base32_decoding#dns_traffic_triage#ttl_based_filtering

I Love Bacon!

Platform: Metactf | Category: Forensics | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-10 | Status: Solved Techniques: anomaly_hunting, base32_decoding, dns_traffic_triage, ttl_based_filtering

Summary

Task: analyze a DNS-only PCAP with suspicious request/response pairs that look like encoded C2 traffic. Solution: ignore the noisy base32-like data, isolate the three anomalous responses with TTL 1337 where TXT equals the query label, and base32-decode the recovered fragments into the flag.

Recon

Port scan

bash
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: metactf | ID: 20260410_metactf_i_love_bacon
  • Tags: pcap, base32, c2, dns, dns_tunneling, txt_record, ttl_anomaly
  • Indicators: a DNS-only PCAP with repeated TXT responses, query labels and TXT answers look like uppercase base32 data, most decoded values are high-entropy junk but a small subset has a distinct TTL, responses where TXT exactly matches the query label mark the real payload chunks
  • Source: 20260410_metactf_i_love_bacon.md

Foothold

Vulnerability / Misconfiguration

  1. anomaly_hunting
  2. base32_decoding
  3. dns_traffic_triage
  4. ttl_based_filtering
bash
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

bash
sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
bash
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • anomaly_hunting
  • base32_decoding
  • dns_traffic_triage
  • ttl_based_filtering
  • Tags: pcap, base32, c2, dns, dns_tunneling, txt_record, ttl_anomaly

Original Writeup

<details><summary>Click to expand original content</summary>

Description

PCAP containing suspicious DNS request activity to a C2.

English summary: the challenge provides a packet capture of DNS traffic that appears to carry encoded command-and-control data. The goal is to identify which packets contain the meaningful signal, reconstruct the hidden message, and recover the flag.

Analysis

The first pass over dns_c2.pcap shows a very regular pattern:

  • 2000 packets total
  • only UDP/DNS traffic
  • 1000 DNS A queries from 10.67.0.2 to 10.1.1.53
  • 1000 matching responses
  • each response carries one TXT answer
  • both the queried subdomain labels and TXT values look like uppercase RFC4648 base32 strings under *.dawg.cwa.sec

That strongly suggests DNS-based exfiltration or C2. However, decoding everything is a trap: most entries decode to high-entropy junk and do not produce useful plaintext.

The real clue is an anomaly in the DNS responses. Three packets stand out because their response TTL is 1337, while the rest use ordinary-looking values. Those three responses also have an exact equality between the TXT answer and the query label, which does not happen in the surrounding noise.

The key packets are:

  • frame 534
  • frame 910
  • frame 1824

Using TTL as the hunting pivot isolates the signal immediately. Extracting only those responses yields three base32 fragments:

  • IRQXOZ2DKRDHW4ZRPJ5GY2LO
  • L5ZXKY3DOVWDG3TU
  • L5RTEX3CGRRW63T5

Base32-decoding them gives readable ASCII chunks:

  • DawgCTF{s1zzlin
  • _succul3nt
  • _c2_b4con}

Concatenating the chunks reconstructs the full flag.

Solution

  1. Inspect the PCAP and confirm it contains only DNS over UDP.
  2. Notice the repetitive pattern of 1000 queries and 1000 TXT-bearing responses.
  3. Test the obvious hypothesis that all base32-looking labels contain data, then observe that most decode to junk.
  4. Hunt for anomalies instead of decoding everything blindly.
  5. Filter responses by TTL and isolate the three packets with dns.resp.ttl == 1337.
  6. Verify that in those packets the TXT answer exactly matches the query label.
  7. Base32-decode the three extracted labels and join the fragments.

Packet extraction command:

bash
tshark -r "dns_c2.pcap" -Y 'dns.flags.response==1 && dns.resp.ttl==1337' -T fields -e frame.number -e dns.qry.name -e dns.txt -e dns.resp.ttl

Expected output shape:

text
534   IRQXOZ2DKRDHW4ZRPJ5GY2LO.dawg.cwa.sec   IRQXOZ2DKRDHW4ZRPJ5GY2LO   1337
910   L5ZXKY3DOVWDG3TU.dawg.cwa.sec           L5ZXKY3DOVWDG3TU           1337
1824  L5RTEX3CGRRW63T5.dawg.cwa.sec          L5RTEX3CGRRW63T5          1337

Minimal decode snippet:

python
import base64

parts = [
    "IRQXOZ2DKRDHW4ZRPJ5GY2LO",
    "L5ZXKY3DOVWDG3TU",
    "L5RTEX3CGRRW63T5",
]

for s in parts:
    pad = '=' * ((8 - len(s) % 8) % 8)
    print(base64.b32decode(s + pad).decode())

print(''.join(base64.b32decode(s + '=' * ((8 - len(s) % 8) % 8)).decode() for s in parts))

Full working solver:

python
#!/usr/bin/env python3

import base64
import subprocess
import sys

def b32decode_nopad(s: str) -> str:
    pad = "=" * ((8 - len(s) % 8) % 8)
    return base64.b32decode(s + pad).decode()

def main() -> None:
    if len(sys.argv) != 2:
        print(f"usage: {sys.argv[0]} dns_c2.pcap")
        raise SystemExit(1)

    pcap = sys.argv[1]
    cmd = [
        "tshark",
        "-r",
        pcap,
        "-Y",
        "dns.flags.response==1 && dns.resp.ttl==1337",
        "-T",
        "fields",
        "-e",
        "frame.number",
        "-e",
        "dns.txt",
    ]

    out = subprocess.check_output(cmd, text=True)
    parts = []

    for line in out.splitlines():
        if not line.strip():
            continue
        frame, txt = line.split("\t", 1)
        parts.append(txt.strip())
        print(f"frame {frame}: {txt.strip()} -> {b32decode_nopad(txt.strip())}")

    flag = "".join(b32decode_nopad(part) for part in parts)
    print(f"flag = {flag}")

if __name__ == "__main__":
    main()

Example output:

text
frame 534: IRQXOZ2DKRDHW4ZRPJ5GY2LO -> DawgCTF{s1zzlin
frame 910: L5ZXKY3DOVWDG3TU -> _succul3nt
frame 1824: L5RTEX3CGRRW63T5 -> _c2_b4con}
flag = DawgCTF{REDACTED}
</details>

signed by XESXOR