HTB: Mission Pinpossible Challenge

Mission Pinpossible - HackTheBox Challenge Writeup

Challenge Information

PropertyValue
NameMission Pinpossible
CategoryHardware (Misc/Forensics-labeled)
DifficultyEasy
Authord3vn0mi

Description

Our field agent cannot access the enemy base due to the password-protected internal gates, but observed that the password seemed to be partially displayed as it was typed into the security keypad. Thanks to an audacious mission, we were able to implant an embedded device into the wiring for the keypad’s monitor, and intercepted some data. The mission is to recover the password from the collected data.

Solution

The shipped artifact, op_pinpossible.logicdata, was a 0-byte file — a Saleae Logic analyzer capture file that failed to be included correctly in the challenge package. Rather than being unsolvable, the file’s session UUID (a12c7342-5328-425d-be48-c2b6e440111e) matched the exact filename HTB uses for its challenge download ZIPs, which meant the real artifact could be recovered directly from the HTB API.

Once the real 2 MB .logicdata file was retrieved (password-protected with hackthebox), the archive also contained a photo of the physical keypad setup. That photo showed a QAPASS 1602 LCD screen driven by a PCF8574T I2C backpack — meaning the two captured logic channels were I2C SDA/SCL lines running the LCD at I2C address 0x4E.

From there, the I2C traffic was decoded down through the PCF8574 GPIO expander’s bit mapping into HD44780 LCD 4-bit-mode nibble writes, and the nibbles were reassembled into the literal text drawn to the screen over time. The keypad displayed each typed password character briefly before masking it with * — so replaying the sequence of screen redraws and pulling out the newly revealed (non-masked) trailing character from each one spelled out the full password, which was the flag.

Key Steps

1. Recognize the 0-byte artifact and recover the real file via the HTB API

Terminal window
# op_pinpossible.logicdata was 0 bytes as shipped.
# Its filename's UUID matched HTB's own challenge zip naming convention,
# so the actual capture could be pulled straight from the platform.
import sys
sys.path.insert(0, 'lib')
from htb_api import HTBClient
c = HTBClient()
# Category metadata was mislabeled ("forensics") but the challenge
# actually lives under Hardware.
for cat in ['Hardware', 'Forensics']:
items = c.list_challenges(category=cat)
# ... search results for "Pinpossible" -> cid=132
info = c.challenge_info(132)
fn, data = c.download_challenge(132)
open('/tmp/pin.zip', 'wb').write(data)
# fn == "a12c7342-5328-425d-be48-c2b6e440111e.zip"
# -> exact match to the session UUID embedded in the 0-byte artifact's name,
# confirming this is the correct source file.

2. Extract the archive

Terminal window
# The zip is password-protected with the challenge platform's default password
unzip -P "hackthebox" pin.zip -d pinout
# Contents:
# op_pinpossible.logicdata (2 MB Saleae Logic 1.x capture)
# security_keypad.jpeg (photo of the physical rig)

3. Identify the hardware from the keypad photo

Photo reveals:
- QAPASS 1602 LCD (16x2 character display)
- Driven via a PCF8574T I2C "backpack" board
- I2C slave address: 0x4E
Conclusion: the two captured logic channels are I2C SDA and SCL,
not raw keypad GPIO lines.

4. Decode the I2C bus into PCF8574 GPIO writes

import csv
# I2C bytes -> PCF8574 output register writes
# PCF8574 bit layout driving the HD44780 in 4-bit mode:
# bit0 = RS (register select: 0=command, 1=data)
# bit2 = EN (enable strobe)
# bits4-7 = 4-bit data nibble
rows = []
with open('i2c-hex.csv') as f:
r = csv.DictReader(f)
for row in r:
d = int(row['Data'], 16)
rows.append(d)
# Latch a nibble each time EN transitions high->low (falling edge),
# since HD44780 reads the data lines on that strobe.

5. Reassemble nibbles into HD44780 bytes and simulate the DDRAM

# Each LCD byte write arrives as two 4-bit nibble writes (high nibble, then low).
# Pair consecutive latched nibbles -> full byte -> command or character write
# (depending on RS), then simulate cursor position/DDRAM writes to reconstruct
# exactly what text appeared on the 16x2 display over time.

6. Recover the flag from the masked password redraws

# The "Enter Password" screen redraws once per keystroke:
# each new character is shown briefly in the clear, then the whole
# password field is redrawn with '*' masks once the next key is pressed.
#
# Extracting the single revealed (non-'*') trailing character from
# each successive redraw, in order, spells the full password.
password_chars = []
for frame in reconstructed_screen_frames:
revealed = extract_unmasked_trailing_char(frame)
if revealed:
password_chars.append(revealed)
flag = ''.join(password_chars)
print(flag)

7. Submit and verify

print(c.submit_challenge(132, 'HTB{REDACTED}', 5))
# -> "Congratulations!"

Tools Used

  • HTB Challenge API (custom htb_api.py client) — recovering the real artifact from a corrupted 0-byte download
  • unzip — password-protected archive extraction
  • Saleae Logic capture format (.logicdata) — raw I2C bus capture
  • Custom Python I2C/PCF8574/HD44780 decoder — bit-level protocol reconstruction
  • CSV-based I2C transaction log (community-sourced reference decode) for cross-validation

Key Learnings

  • A 0-byte challenge artifact isn’t necessarily broken beyond recovery — filenames can carry a session UUID that maps directly back to the platform’s own challenge storage, giving a legitimate path to the intact file.
  • Hardware fingerprinting from a single photo can fully determine a decode strategy. Recognizing the PCF8574T I2C backpack + HD44780 LCD combo immediately explained the two-channel capture as I2C SDA/SCL rather than raw parallel GPIO, which dictated the entire decoding pipeline.
  • I2C-to-LCD decoding is a layered protocol stack: I2C byte transactions → PCF8574 GPIO bit mapping → HD44780 4-bit-mode nibble pairing → DDRAM/character-display simulation. Each layer must be unwrapped correctly before the underlying data (in this case, on-screen text) becomes visible.
  • “Partially displayed while masked” passwords leak character-by-character through UI redraw timing — the flag was reconstructed purely from the sequence of transient unmasked characters visible before each * masking redraw, illustrating a classic UX/security tradeoff (the challenge itself notes it “reads as bad design can lead to leaks”).