Objective: On The Wire Difficulty Level: 4
Help Evan next to city hall hack this gnome and retrieve the temperature value reported by the I²C device at address 0x3C. The temperature data is XOR-encrypted, so you’ll need to work through each communication stage to uncover the necessary keys. Start with the unencrypted data being transmitted over the 1-wire protocol. Location: City Hall

Solution Overview

Using browser developer tools, the data for each signal is captured. The data is analyzed using powershell and python scripts and decoded, leveraging the signal descriptions contained in the hints. Decoding each signal enables the decoding of the next signal. The protocols are dq, SPI, and I2C, which are board-level or device-level buses.

Activity Primary Tactic MITRE ATT&CK Technique ID MITRE ATT&CK Technique Name
Decode hidden payload Defense Evasion T1140 Deobfuscate/Decode Files or Information

Detailed Solution

Click to expand

Part One: 1-Wire

The following python script connects to the web socket and collects the data into a csv file:


import websocket
import csv
# Open CSV file once at the start
f = open("onthewire_1wire_data.csv", "w", newline="", encoding="utf-8")
writer = csv.writer(f)
def on_message(ws, message):
    print("Received:", message)  # See messages in console
    writer.writerow([message])
    f.flush()  # Ensure data is written immediately
def on_error(ws, error):
    print("Error:", error)
def on_close(ws, close_status_code, close_msg):
    print("Connection closed")
    f.close()
def on_open(ws):
    print("Connection opened")
url = "wss://signals.holidayhackchallenge.com/wire/dq"
ws = websocket.WebSocketApp(url,
                            on_open=on_open,
                            on_message=on_message,
                            on_error=on_error,
                            on_close=on_close)
ws.run_forever()

The data file collected: Download 1-Wire Data

The data contains the following markers:

In 1-Wire, data is encoded using pulse width modulation:

Decode the signal after the presence pulse by measuring the low-pulse widths:

Time slot analysis (from t=701 onwards):

701→941: 240µs LOW → 0

1001→1011: 10µs LOW → 1

1071→1081: 10µs LOW → 1

1087→1151: 64µs LOW → 0

1157→1221: 64µs LOW → 0

1281→1291: 10µs LOW → 1

1351→1361: 10µs LOW → 1

1367→1431: 64µs LOW → 0

Continuing this analysis through the entire sequence and assembling bits LSB-first (1-Wire standard):

Decoded bits (grouped by byte, LSB first):

  1. 01100011 → 0x63 → 'c'
  2. 01101000 → 0x68 → 'h'
  3. 01110010 → 0x72 → 'r'
  4. 01101001 → 0x69 → 'i
  5. 01110011 → 0x73 → 's'
  6. 01110100 → 0x74 → 't'
  7. 01101101 → 0x6D → 'm'
  8. 01100001 → 0x61 → 'a'
  9. 01110011 → 0x73 → 's'

Decoded Message

XOR key: christmas

The data is not encoded with the key Christmas, the bites translate to the following message:

read and decrypt the SPI bus data using the XOR key: icy

Part 2: Get the SPI bus data stream and decode.

SPI has the following characteristics:

Using Firefox developer tools, the SPI data signal is captrued and exported to the following json file: Download SPI Data

The following decoder was written in Powershell: SPI Decoder

Running the decoder:


Found 14019 WebSocket messages
Extracted 14016 signal frames
Clock frames: 7110, Data frames: 3234
Decoded 800 bits
Assembled 100 bytes
Raw bytes (hex): 1b 06 18 0d 43 18 07 07 59 0d 06 1a 1b 1a 09 1d 43 0d 01 06 59 20 51 3a 49 01 0c 1a 43 1d 08 17 18 49 16 0a 00 0d 1e 49 17 11 0c 43 21 26 31 59 02 06 00 53 43 1b 08 0d 18 07 19 18 47 43 0d 01 06 59 1d 06 14 19 06 0b 08 17 0c 1b 06 59 1a 06 17 1a 0c 0b 49 02 1d 0d 11 1c 1a 10 59 00 10 59 59 1b 4a 2a
=== DECRYPTED DATA ===
read and decrypt the I2C bus data using the XOR key: bananza. the temperature sensor address is 0x3C

read and decrypt the I2C bus data using the XOR key: bananza. the temperature sensor address is 0x3C

Part 3: I2C Decoding

The following data file was collected using Edge's Developer Tools: Download I2C Data

The data file contains markers, but due to the volume of data, a script was used to identify the unique markers in the data structure.

I2C Unique Markers Script

These are the unique markers:


=== UNIQUE MARKERS ===
SCL markers: bus-idle, clock-low, address-sample, address-hold, ack-sample, ack-hold, data-sample, data-hold, stop-setup, gap-start
SDA markers: bus-idle, start, address-bit, ack-bit, ack-release, data-bit, stop, gap-start

The following decoder was written in Powershell: I2C Decoder

Script Workflow

Load and parse the capture file

Use markers instead of raw edge detection

Group transactions

Decode transaction contents

Report transactions

Iteration analysis

Separate READ vs WRITE

The raw data repeats every 5 bytes, so the XOR key needs to be shortened to 5 characters “banan”

I2C Device 0x3C

33 32 2E 38 34 converts to ASCII 32.84

Answer: 32.84

Tools Reference

Tools Used Tool Version
claude.ai 4.5
Edge Developer Tools Version 142.0.3595.94
Firefox Developer Tools Version 145.0.2
Powershell 5.1.26100.6899
Python 3.12.8

Hints Reference

Provided By Hint
Santa Protocols
Key concept - Clock vs. Data signals:
-Some protocols have separate clock and data lines (like SPI and I2C)
-For clocked protocols, you need to sample the data line at specific moments defined by the clock
-The clock signal tells you when to read the data signal
For 1-Wire (no separate clock):
-Information is encoded in pulse widths (how long the signal stays low or high)
-Different pulse widths represent different bit values
-Look for patterns in the timing between transitions
For SPI and I2C:
-Identify which line is the clock (SCL for I2C, SCK for SPI)
-Data is typically valid/stable when the clock is in a specific state (high or low)
-You need to detect clock edges (transitions) and sample data at those moments
Technical approach:
-Sort frames by timestamp
-Detect rising edges (0→1) and falling edges (1→0) on the clock line
-Sample the data line's value at each clock edge
Santa Structure
What you're dealing with:
-You have access to WebSocket endpoints that stream digital signal data
-Each endpoint represents a physical wire in a hardware communication system
-The data comes as JSON frames with three properties: line (wire name), t (timestamp), and v (value: 0 or 1)
-The server continuously broadcasts signal data in a loop - you can connect at any time
-This is a multi-stage challenge where solving one stage reveals information needed for the next
Where to start:
-Connect to a WebSocket endpoint and observe the data format
-The server automatically sends data every few seconds - just wait and collect
-Look for documentation on the protocol types mentioned (1-Wire, SPI, I2C)
-Consider that hardware protocols encode information in the timing and sequence of signal transitions, not just the values themselves
-Consider capturing the WebSocket frames to a file so you can work offlineclock edge
Santa On Rails
Stage-by-stage approach
1. Connect to the captured wire files or endpoints for the relevant wires.
2. Collect all frames for the transmission (buffer until inactivity or loop boundary).
3. Identify protocol from wire names (e.g., dq → 1-Wire; mosi/sck → SPI; sda/scl → I²C).
4. Decode the raw signal:
- Pulse-width protocols: locate falling→rising transitions and measure low-pulse width.
- Clocked protocols: detect clock edges and sample the data line at the specified sampling phase.
5. Assemble bits into bytes taking the correct bit order (LSB vs MSB).
6. Convert bytes to text (printable ASCII or hex as appropriate).
7. Extract information from the decoded output - it contains the XOR key or other hints for the next stage.
1. Repeat Stage 1 decoding to recover raw bytes (they will appear random).
2. Apply XOR decryption using the key obtained from the previous stage.
3. Inspect decrypted output for next-stage keys or target device information.
- Multiple 7-bit device addresses share the same SDA/SCL lines.
- START condition: SDA falls while SCL is high. STOP: SDA rises while SCL is high.
- First byte of a transaction = (7-bit address << 1) | R/W. Extract address with address = first_byte >> 1.
- Identify and decode every device’s transactions; decrypt only the target device’s payload.
- Print bytes in hex and as ASCII (if printable) - hex patterns reveal structure.
- Check printable ASCII range (0x20-0x7E) to spot valid text.
- Verify endianness: swapping LSB/MSB will quickly break readable text.
- For XOR keys, test short candidate keys and look for common English words.
- If you connect mid-broadcast, wait for the next loop or detect a reset/loop marker before decoding.
- Buffering heuristic: treat the stream complete after a short inactivity window (e.g., 500 ms) or after a full broadcast loop.
- Sort frames by timestamp per wire and collapse consecutive identical levels before decoding to align with the physical waveform.
Santa Garbage
If your decoded data looks like gibberish:
- The data may be encrypted with XOR cipher
- XOR is a simple encryption: encrypted_byte XOR key_byte = plaintext_byte
- The same operation both encrypts and decrypts: plaintext XOR key = encrypted, encrypted XOR key = plaintext
How XOR cipher works:
function xorDecrypt(encrypted, key) {
let result = "";
for (let i = 0; i < encrypted.length; i++) {
const encryptedChar = encrypted.charCodeAt(i);
const keyChar = key.charCodeAt(i % key.length); // Key repeats
result += String.fromCharCode(encryptedChar ^ keyChar);
}
return result;
}
Key characteristics:
- The key is typically short and repeats for the length of the message
- You need the correct key to decrypt (look for keys in previous stage messages)
- If you see readable words mixed with garbage, you might have the wrong key or bit order
Testing your decryption:
- Encrypted data will have random-looking byte values
Evan So here's the deal - there are some seriously bizarre signals floating around this area. Not your typical radio chatter or WiFi noise, but something... different. I've been trying to make sense of the patterns, but it's like trying to build a robot hand out of a coffee maker - you need the right approach. Think you can help me decode whatever weirdness is being transmitted out there?

Acknowledgements

Provided By Notes
none none