Mission Pinpossible
hackthebox
Task: Recover password from I2C logic analyzer capture of LCD display. Solution: Decode PCF8574 I2C backpack protocol to HD44780 LCD in 4-bit mode, extract nibbles on EN pulse, group by sessions to recover each character as it was typed.
$ ls tags/ techniques/
Mission Pinpossible — HackTheBox
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. Your mission is to recover the password from the collected data.
Provided files:
op_pinpossible.logicdata— Saleae Logic 1.x logic analyzer capture (I2C bus)security_keypad.jpeg— photo of a QAPASS 16x2 LCD display (HD44780) with PCF8574 I2C backpack, showing "Enter Password"
Analysis
Hardware Identification
The photo shows a standard Arduino setup:
- LCD display: QAPASS 16x2 HD44780 — classic character display
- I2C backpack: PCF8574 — I2C-to-parallel converter (address 0x27), allowing control of the 8-bit HD44780 via a 2-wire I2C bus
- Capture format:
.logicdata— proprietary Saleae Logic 1.x format (NOT compatible with Logic 2 or sigrok)
PCF8574 → HD44780 Protocol
PCF8574 outputs 8 bits to a parallel port connected to the LCD in 4-bit mode:
Bit 0: RS (Register Select: 0=command, 1=data/character)
Bit 1: RW (Read/Write: 0=write)
Bit 2: EN (Enable: HIGH→LOW pulse to latch data)
Bit 3: BL (Backlight)
Bits 4-7: D4-D7 (high nibble of data in 4-bit mode)
Each nibble is transmitted via three I2C writes:
- Setup: data + EN=0 (preparation)
- Pulse: data + EN=1 (latch — capture nibble here)
- Latch: data + EN=0 (hold)
Two nibbles (high first, low second) form one byte. RS=0 → LCD command, RS=1 → character to display.
File Format Issue
.logicdata is a proprietary binary format for Saleae Logic version 1.x. It:
- Does NOT open in Saleae Logic 2 (different format)
- Is NOT parsed by sigrok-cli or PulseView
- Requires specifically Saleae Logic 1.x (legacy version 1.2.40, available at https://downloads.saleae.com/logic/1.2.40/)
Solution
Step 1: Export I2C Data
Opened op_pinpossible.logicdata in Saleae Logic 1.2.40:
- Added I2C analyzer (Channel 0 = SCL, Channel 1 = SDA)
- Exported analyzer results to CSV (
i2c.txt) - Got 8424 I2C write transactions to address 0x27 (PCF8574)
Step 2: Decode LCD Protocol
Python script processes the CSV:
- Extract nibbles: from all I2C writes, select only those where EN=1 (bit 2 is set) — this is the data latch moment
- Assemble bytes: combine every two consecutive nibbles (high << 4 | low)
- Separate: RS=0 → LCD commands (clear, cursor position), RS=1 → characters to display
- Group by sessions: time gaps >0.1s separate individual screen "redraws"
Step 3: Recover Password
Found 36 sessions (matching the number of characters in the password). Each session is a full LCD redraw after entering the next character:
Session 1: "Enter Password" + "H" ← first character
Session 2: "Enter Password" + "*T" ← H masked, T visible
Session 3: "Enter Password" + "**B" ← HT masked, B visible
Session 4: "Enter Password" + "***{" ← HTB masked, { visible
...
Session 36: "Enter Password" + "***...***}" ← last character }
Pattern: all previously entered characters are displayed as *, while the last entered character is visible. Collecting the last visible character from each session:
H, T, B, {, 8, 4, d, _, d, 3, 5, 1, 9, n, _, c, 4, n, _, 1, 3, 4, d, _, 7, 0, _, 1, 3, 4, k, 5, !, d, @, }
After full password entry, the LCD displayed: "ACCESS GRANDED" and "SYSTEM DISARMED".
Solve Script
#!/usr/bin/env python3 """ Solve script for HackTheBox Mission Pinpossible Decodes I2C data from PCF8574 -> HD44780 LCD to recover password. PCF8574 pin mapping (standard Arduino LiquidCrystal_I2C): Bit 0: RS (Register Select: 0=command, 1=data/character) Bit 1: RW (Read/Write: 0=write) Bit 2: EN (Enable: pulse HIGH->LOW to latch data) Bit 3: Backlight Bits 4-7: D4-D7 (4-bit mode data nibble) HD44780 4-bit mode: each byte sent as two nibbles (high first, then low). Each nibble requires 3 I2C writes: 1. data | EN=0 (setup) 2. data | EN=1 (enable pulse high) <-- we capture nibble here 3. data | EN=0 (enable pulse low - data latched) """ import csv import re def parse_i2c_csv(filename): """Parse I2C CSV export from Saleae Logic.""" writes = [] with open(filename, "r") as f: reader = csv.reader(f) header = next(reader) # skip header for row in reader: if len(row) >= 5: time_s = float(row[0]) addr = int(row[2], 16) data = int(row[3], 16) rw = row[4].strip() if addr == 0x27 and rw == "Write": writes.append((time_s, data)) return writes def decode_lcd(writes): """ Decode PCF8574 I2C writes into LCD commands and data. Each nibble is sent as 3 writes: setup(EN=0), pulse(EN=1), latch(EN=0). We capture the nibble when EN=1 (the middle write). Two consecutive nibbles form one byte (high nibble first). """ # Extract nibbles when EN is HIGH nibbles = [] for time_s, data in writes: en = (data >> 2) & 1 if en == 1: rs = data & 0x01 # RS: 0=command, 1=data rw = (data >> 1) & 0x01 # RW: should be 0 for writes d4_7 = (data >> 4) & 0x0F nibbles.append((time_s, rs, d4_7)) # Combine nibble pairs into bytes results = [] for i in range(0, len(nibbles) - 1, 2): time1, rs1, high_nibble = nibbles[i] time2, rs2, low_nibble = nibbles[i + 1] byte_val = (high_nibble << 4) | low_nibble results.append((time1, rs1, byte_val)) return results def main(): filename = "i2c.txt" writes = parse_i2c_csv(filename) print(f"Total I2C writes to 0x27: {len(writes)}") results = decode_lcd(writes) print(f"Decoded LCD bytes: {len(results)}") # Group by time gaps (sessions separated by ~0.6s) sessions = [] current_session = [] prev_time = 0 for t, rs, v in results: if t - prev_time > 0.1 and current_session: sessions.append(current_session) current_session = [] current_session.append((t, rs, v)) prev_time = t if current_session: sessions.append(current_session) print(f"Found {len(sessions)} sessions\n") # Decode each session all_data_text = [] for idx, session in enumerate(sessions): cmds = [(t, v) for t, rs, v in session if rs == 0] chars = [(t, v) for t, rs, v in session if rs == 1] text = "".join(chr(v) for t, v in chars if 0x20 <= v <= 0x7E) print(f"--- Session {idx + 1} (t={session[0][0]:.3f}s) ---") print(f" Commands: {' '.join(f'{v:02X}' for t, v in cmds)}") char_desc = [] for t, v in chars: if 0x20 <= v <= 0x7E: char_desc.append(f"'{chr(v)}'") else: char_desc.append(f"0x{v:02X}") print(f" Characters: {' '.join(char_desc)}") print(f" Text: '{text}'") all_data_text.append(text) print(f"\n{'=' * 60}") print("All decoded text:") for i, text in enumerate(all_data_text): print(f" Session {i + 1}: '{text}'") full_text = "".join(all_data_text) print(f"\nFull text: '{full_text}'") # Look for flag flags = re.findall(r"HTB\{[^}]+\}", full_text) if flags: print(f"\n*** FLAG: {flags[0]} ***") else: print(f"\nPassword/data on LCD: '{full_text}'") if __name__ == "__main__": main()
Key Indicators
Use this technique when:
.logicdatafile (proprietary Saleae Logic 1.x format)- I2C writes to address 0x27 (standard PCF8574 address)
- Mention of LCD display, keyboard, keypad
- Photo with QAPASS/HD44780 16x2 display and I2C backpack
- Pattern of 3 writes per nibble (EN=0, EN=1, EN=0) in I2C data
- HD44780 4-bit mode (data in upper 4 bits of PCF8574 byte)
Notes
- Saleae Logic 1.x vs 2:
.logicdataformat is ONLY for version 1.x. Logic 2 uses.salformat. Download legacy: https://downloads.saleae.com/logic/1.2.40/ - PCF8574 addresses: 0x20-0x27 (depends on A0-A2 jumpers). For LCD backpack usually 0x27 (all jumpers open)
- Password masking: typical pattern for security keypad — show the last entered character briefly, then replace with
*. Each LCD redraw is a separate "session" in I2C traffic - "ACCESS GRANDED": typo in the challenge (should be "GRANTED"), but this is part of the original challenge
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar
Similar writeups
- [hardware][free]Defusal— hackthebox
- [web][Pro]Пин код— duckerz
- [hardware][free]Trace— hackthebox
- [pwn][Pro]HackerLab: Simple BOF - Я хочу купить этот флаг!!!— hackerlab
- [hardware][free]Debug— hackthebox