HTB: Low Logic Challenge
Low Logic - HackTheBox Challenge Writeup
Challenge Information
| Field | Value |
|---|---|
| Name | Low Logic |
| Category | Misc / Hardware |
| Difficulty | Very Easy |
| Author | d3vn0mi |
Description
I have this simple chip, I want you to understand how it’s works and then give me the output.
The challenge ships a schematic image of a small transistor-level circuit and a CSV of input vectors. The goal is to reverse-engineer the boolean function the circuit implements and evaluate it against the provided inputs to recover the flag.
Solution
The challenge archive contained two files: chip.jpg, a hand-drawn RTL (resistor-transistor logic) schematic built from NPN transistors and 1kΩ resistors, and input.csv, 192 rows of 4-bit input vectors (in0–in3).
The circuit has four inputs and one output, built from three transistor-pair “blocks” that at a glance all look identical. The trick is that they aren’t: two of the blocks stack their transistors in series (an AND gate), while the third wires its transistors in parallel (an OR gate). Reading all three as OR — the intuitive read at normal zoom — produces garbage output. Only a pixel-level inspection of how the emitters and collectors are actually wired reveals the correct logic function:
OUT = (IN0 AND IN1) OR (IN2 AND IN3)With the correct boolean function in hand, each of the 192 CSV rows produces one output bit. Concatenated MSB-first, 192 bits packs cleanly into 24 ASCII bytes — the flag.
Key Steps
1. Recover the archive contents
The staged chip.jpg initially showed as a 0-byte artifact. The real files were inside the challenge zip and needed extraction with the standard HTB archive password:
# List the zip contents to confirm what's actually insideunzip -l /out/<challenge-id>.zip# => chip.jpg (47KB) + input.csv (hidden, not staged separately)
# Extract with the standard HTB zip passwordunzip -o -P hackthebox /out/<challenge-id>.zip2. Inspect the input data shape
head -20 input.csvwc -l input.csv192 data rows, 4 boolean input columns (in0..in3) → 192 output bits total, which divides evenly into 24 bytes (192 / 8) — a strong hint the output is a 24-character ASCII flag.
3. Trace the schematic at pixel-level zoom
The full schematic looked like three copies of the same paired-transistor block. Cropping and enlarging each block individually was necessary to tell them apart:
from PIL import Image
im = Image.open('chip.jpg')
# Crop and heavily upscale each transistor block for pixel-level tracingim.crop((390, 190, 460, 330)).resize((70*8, 140*8), Image.NEAREST).save('z_top.png')im.crop((390, 455, 460, 630)).resize((70*8, 175*8), Image.NEAREST).save('z_bot.png')im.crop((90, 20, 480, 320)).resize((390*2, 300*2)).save('z_right.png')Findings from the zoomed crops:
- Left block (IN0, IN1): upper transistor’s emitter feeds directly into the lower transistor’s collector — a series stack. Top collector to +6V, bottom emitter to a 1kΩ pull-down. Series NPN pairs behave as an AND gate (both must conduct to complete the path to ground).
- Middle block (IN2, IN3): identical series-stack topology → also an AND gate.
- Right block: the two transistors sit side-by-side, both collectors tied to +6V, both emitters joined at a shared 1kΩ pull-down — a parallel emitter-follower arrangement. This behaves as an OR gate (either transistor conducting pulls the shared node high).
Net function: OUT = (IN0 & IN1) | (IN2 & IN3)
4. Evaluate the function against all 192 input rows and decode
import csv
rows = list(csv.DictReader(open('input.csv')))print('rows:', len(rows)) # 192
# Evaluate OUT = (IN0 AND IN1) OR (IN2 AND IN3) for every rowbits = ''.join( str((int(r['in0']) & int(r['in1'])) | (int(r['in2']) & int(r['in3']))) for r in rows)
# 192 bits -> 24 bytes, MSB-firstflag = ''.join( chr(int(bits[i:i+8], 2)) for i in range(0, len(bits), 8))print(flag)This decoded cleanly on the first try into the flag string.
HTB{REDACTED}Tools Used
unzip— password-protected archive extraction- Python 3 +
Pillow(PIL) — image cropping/upscaling for schematic pixel-tracing - Python 3
csvmodule — parsing the 192-row input vector table - Manual circuit tracing (transistor topology analysis)
Key Learnings
- Series vs. parallel NPN transistor pairs are the whole challenge. Two stacked (series) NPNs form an AND gate — both must conduct to complete the current path to the pull-down resistor. Two side-by-side NPNs sharing a single pull-down (parallel) form an OR gate — either conducting is enough to pull the shared node high. At normal zoom, both layouts can look deceptively similar; the distinction only becomes clear when tracing exactly where each transistor’s collector and emitter terminate.
- A single misread gate silently poisons the entire output. Assuming all three blocks were OR gates produced 192 bits of plausible-looking but ultimately garbage output — there was no error or crash to signal the mistake, only a non-ASCII/non-flag-shaped result. When decoded output doesn’t look right, re-verify the boolean model before trusting the bit count or encoding.
- Data shape hints the encoding. 192 rows of a single-bit-output function cleanly dividing into 24 bytes was a strong early signal that the answer was a flag-length ASCII string, which helped confirm MSB-first bit packing was the right decode strategy.
- Check for incompletely staged archives before assuming a file is corrupt. A 0-byte
chip.jpgwasn’t actually broken — it just hadn’t been extracted from the accompanying zip yet, alongside a second file (input.csv) that wasn’t staged separately at all.