Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
ForensicsMedium

Silicon Data Sleuthing

CTF writeup for Silicon Data Sleuthing from HTB CTF TRY OUT

//Silicon Data Sleuthing

This writeup documents a full forensic analysis of the firmware image provided in the challenge "Silicon Data Sleuthing" and reproduces the steps used to recover the final HTB flag. It includes commands, artifacts, code used during interaction with the remote challenge server, and key observations.

Contents

  • Summary
  • Environment & files
  • Extraction steps
  • Key findings (configs, credentials)
  • Firewall/NAT analysis and WAN ports
  • Interaction script (full source)
  • Final flag
  • Reproducibility notes & next steps

>Summary

We analyzed a firmware dump (chal_router_dump.bin) extracted from the provided archive, located an embedded SquashFS root filesystem and a JFFS2 overlay. The overlay contained a sysupgrade.tgz which held the runtime /etc/ configuration. Using the discovered artifacts (OpenWrt release, kernel version, root password hash, PPPoE credentials, WiFi SSID/PSK, and NAT redirect ports), we answered a remote interactive questionnaire at 94.237.120.99:55066 which returned the final flag.

Final flag (as returned by the remote service):

HTB{Y0u'v3_m4st3r3d_0p3nWRT_d4t4_3xtr4ct10n!!_98d4717bd7bbc2c69ad223c9ea5f904a}

>Environment & files

Workspace root: /home/noigel/HTB/Forensic/Silicon Data Sleuthing

Important files produced during analysis:

  • 001.forensics_silicon_data_sleuthing/chal_router_dump.bin — original firmware image

  • 001.forensics_silicon_data_sleuthing/_chal_router_dump.bin.extracted/ — extraction artifacts

  - squashfs-root/ — extracted SquashFS root filesystem

  - 7C0000.jffs2 — embedded JFFS2 overlay

  • 002.jffs2_extracted/ — result of extracting JFFS2 overlay

  • 003.sysupgrade_contents/ — extracted sysupgrade.tgz (overlay etc/)

  • 004.interact.py — Python script used to drive the remote challenge

  • 005.writeup.md — this writeup

>Extraction steps (commands used)

Note: run these in the workspace root. Use a Python venv for tools if desired.

  1. Unpack provided archive (already done in the workspace):
bash

unzip forensics_silicon_data_sleuthing.zip -d 001.forensics_silicon_data_sleuthing
  1. Inspect the firmware for known headers and offsets (example quick checks):
bash

file 001.forensics_silicon_data_sleuthing/chal_router_dump.bin

hexdump -C -n 512 001.forensics_silicon_data_sleuthing/chal_router_dump.bin | head

strings -a 001.forensics_silicon_data_sleuthing/chal_router_dump.bin | egrep -i "U-Boot|OpenWrt|Squashfs|JFFS2|uImage" | head
  1. Extract SquashFS and JFFS2 components (binwalk or manual offsets). Example (binwalk recommended):
bash

# Example using binwalk (if installed)

binwalk -e 001.forensics_silicon_data_sleuthing/chal_router_dump.bin

# This creates _chal_router_dump.bin.extracted/ with squashfs and jffs2
  1. Unsquash the SquashFS image (if a .squashfs file appears):
bash

unsquashfs -d squashfs-root _chal_router_dump.bin.extracted/42C2C8.squashfs

# or if extracted already: unsquashfs -d squashfs-root _chal_router_dump.bin.extracted/squashfs.img
  1. Extract the JFFS2 overlay to recover runtime upper/sysupgrade.tgz.

There are different JFFS2 tooling options; one approach used in this workspace was a Python CLI installed into a 002.venv and used to dump the overlay. After extracting the JFFS2 image you should see upper/sysupgrade.tgz which can be unpacked:

bash

tar -xzf 002.jffs2_extracted/upper/sysupgrade.tgz -C 003.sysupgrade_contents

>Key findings (extracted artifacts)

From 003.sysupgrade_contents/etc/ and squashfs-root/etc/ the following items were discovered and used to answer the remote challenge:

  • OpenWrt release (/etc/openwrt_release):

  - DISTRIB_RELEASE='23.05.0'

  • Kernel (uImage header and modules folder):

  - Linux: 5.15.134

  • Root shadow line (/etc/shadow inside overlay):

  - root:$1$YfuRJudo$cXCiIJXn9fWLIt8WY2Okp1:19804:0:99999:7:::

  • PPPoE credentials (from etc/config/network in overlay):

  - username: yohZ5ah

  - password: ae-h+i$i^Ngohroorie!bieng6kee7oh

  • WiFi configuration (from etc/config/wireless):

  - SSID: VLT-AP01

  - Password/Key: french-halves-vehicular-favorable

>Firewall / NAT analysis — WAN->LAN redirect ports

The NAT/port-forward configuration was found in 003.sysupgrade_contents/etc/config/firewall. Relevant redirect sections were:

config redirect         option dest 'lan'         option target 'DNAT'         option name 'DB'         option src 'wan'         option src_dport '1778'         option dest_ip '192.168.1.184'         option dest_port '5881' config redirect         option dest 'lan'         option target 'DNAT'         option name 'WEB'         option src 'wan'         option src_dport '2289'         option dest_ip '192.168.1.119'         option dest_port '9889' config redirect         option dest 'lan'         option target 'DNAT'         option name 'NAS'         option src 'wan'         option src_dport '8088'         option dest_ip '192.168.1.166'         option dest_port '4431'

From this we extracted the external WAN ports (src_dport) that redirect inbound WAN traffic to LAN hosts: 1778, 2289, 8088.

The remote challenge expects the three WAN ports numerically sorted as a comma-separated list: 1778,2289,8088.

>Interaction script (full source)

The script used to automate the remote interaction is 004.interact.py. Full contents (as used) below:

python

#!/usr/bin/env python3

import socket

import sys

import time

  

HOST = '94.237.120.99'

PORT = 55066

RECV_TIMEOUT = 2.0

  

answers = [

    '23.05.0',

    '5.15.134',

    "root:$1$YfuRJudo$cXCiIJXn9fWLIt8WY2Okp1:19804:0:99999:7:::"

]

  

prompts = [

    'What version of OpenWRT runs on the router',

    'What is the Linux kernel version',

    "What's the hash of the root account's password"

]

  

# PPPoE credentials discovered in overlay

answers.extend(['yohZ5ah', "ae-h+i$i^Ngohroorie!bieng6kee7oh"])

prompts.extend(['What is the PPPoE username', 'What is the PPPoE password'])

  

# Wireless credentials discovered in overlay

answers.extend(['VLT-AP01', 'french-halves-vehicular-favorable'])

prompts.extend(['What is the wireless SSID', 'What is the WiFi password'])

  

# WAN->LAN redirect ports (numerically sorted, comma separated)

# Found in etc/config/firewall as src_dport entries: 1778, 2289, 8088

answers.append('1778,2289,8088')

prompts.append('What are the 3 WAN ports that redirect traffic from WAN -> LAN')

  

s = socket.create_connection((HOST, PORT), timeout=10)

s.settimeout(RECV_TIMEOUT)

  

def recv_all(timeout=RECV_TIMEOUT):

    out = b''

    start = time.time()

    while True:

        try:

            data = s.recv(4096)

            if not data:

                break

            out += data

            # continue reading for a short burst

            start = time.time()

        except socket.timeout:

            break

        except Exception:

            break

    return out

  

try:

    # initial banner

    data = recv_all()

    sys.stdout.buffer.write(data)

    sys.stdout.flush()

  

    for i, prompt in enumerate(prompts):

        # wait for the prompt text to appear

        buf = b''

        deadline = time.time() + 15

        while time.time() < deadline:

            chunk = recv_all()

            if chunk:

                sys.stdout.buffer.write(chunk)

                sys.stdout.flush()

                buf += chunk

                if prompt.encode() in buf:

                    break

            time.sleep(0.2)

        # send answer

        ans = answers[i] + "\n"

        sys.stdout.write(f"\n>>> Sending answer #{i+1}: {answers[i]}\n")

        sys.stdout.flush()

        s.sendall(ans.encode())

        time.sleep(0.2)

        # print any immediate response

        resp = recv_all()

        if resp:

            sys.stdout.buffer.write(resp)

            sys.stdout.flush()

  

    # after final answer, read until socket closes or timeout

    final = b''

    # give some time to produce final output

    for _ in range(30):

        chunk = recv_all()

        if chunk:

            final += chunk

            sys.stdout.buffer.write(chunk)

            sys.stdout.flush()

        time.sleep(0.2)

  

except KeyboardInterrupt:

    pass

finally:

    try:

        s.close()

    except Exception:

        pass

  

print('\n[client finished]')

>Run transcript (important excerpts)

When the script was run it sent the answers in this order and the server responded with confirmation for each answer and finally the flag.

Key prompt-answer pairs:

  • Q: What version of OpenWRT runs on the router -> A: 23.05.0  -> [+] Correct!

  • Q: What is the Linux kernel version -> A: 5.15.134 -> [+] Correct!

  • Q: What's the hash of the root account's password -> A: root:$1$YfuRJudo$cXCiIJXn9fWLIt8WY2Okp1:19804:0:99999:7::: -> [+] Correct!

  • Q: PPPoE username -> A: yohZ5ah -> [+] Correct!

  • Q: PPPoE password -> A: ae-h+i$i^Ngohroorie!bieng6kee7oh -> [+] Correct!

  • Q: WiFi SSID -> A: VLT-AP01 -> [+] Correct!

  • Q: WiFi password -> A: french-halves-vehicular-favorable -> [+] Correct!

  • Q: What are the 3 WAN ports that redirect traffic from WAN -> LAN -> A: 1778,2289,8088 -> [+] Correct!

Server returned the final flag:

HTB{Y0u'v3_m4st3r3d_0p3nWRT_d4t4_3xtr4ct10n!!_98d4717bd7bbc2c69ad223c9ea5f904a}

>Reproducibility notes

  • binwalk, unsquashfs (from squashfs-tools), and a JFFS2 extraction tool are useful. If you don't have them installed, use your package manager (e.g., apt, pacman) or use Python-based alternatives.

  • The interaction script 004.interact.py is intentionally simple — it waits for prompt substrings then submits the corresponding answers. It depends only on Python 3's stdlib.

  • To reproduce:

  - Ensure the extracted directories exist (squashfs-root, 002.jffs2_extracted, 003.sysupgrade_contents)

  - Inspect 003.sysupgrade_contents/etc/config/firewall for the redirect sections and confirm src_dport values.

  - Run python3 004.interact.py from the workspace to recreate the remote interaction and obtain the flag.