CSAW CTF – RE200 – Hacking Time

For this challenge we got a NES ROM to reverse engineer.

HackingTime_03e852ace386388eb88c39a02f88c773.nes: iNES ROM dump, 2x16k PRG, 1x8k CHR, [Vert.]
I used FCEUX emulator to run the ROM file. The game asks for a password after few messages



The ROM memory could be viewed using Hex viewer, provided as part of FCEUX debug tools. I supplied a password ABCDABCDABCDABCDABCDABCD and found this is memory



Then set read memory access breakpoint for address 0x5 i.e. first byte of string. Now on continuing the game, the breakpoint is hit



The program counter is at 0x82F7, so we know what part of code to analyze. Then I found the Nintendo Entertainment System (NES) ROM loader module for IDA Pro, for analyzing the challenge ROM.

ROM:82F1 algorithm: ; CODE XREF: main_function+1C7
ROM:82F1 LDY #0
ROM:82F3 LDA #0
ROM:82F5 STA VAL
ROM:82F7
ROM:82F7 process_loop: ; CODE XREF: algorithm+44
ROM:82F7 LDA 5,Y ; LDA INPUTKEY,A
ROM:82FA TAX ; X = A
ROM:82FB ROL A ; ROL A,1
ROM:82FC TXA ; A = X
ROM:82FD ROL A ; ROL A, 1
ROM:82FE TAX ; X = A
ROM:82FF ROL A ; ROL A, 1
ROM:8300 TXA ; A = X
ROM:8301 ROL A ; ROL A,1
ROM:8302 TAX ; X = A
The PC 0x82F7, takes us to the actual validation algorithm used. The algorithm processes one byte at a time using multiple rotate operations and couple of XOR's with hardcoded arrays values. Good reference to instruction set is found here. The algorithm was rewritten in python and bruteforce gave the solution

import string

CHECKA = [0x70, 0x30, 0x53, 0xa1, 0xd3, 0x70, 0x3f, 0x64, 0xb3, 0x16,
0xe4, 0x04, 0x5f, 0x3a, 0xee, 0x42, 0xb1, 0xa1, 0x37, 0x15,
0x6e, 0x88, 0x2a, 0xab]
CHECKB = [0x20, 0xac, 0x7a, 0x25, 0xd7, 0x9c, 0xc2, 0x1d, 0x58, 0xd0,
0x13, 0x25, 0x96, 0x6a, 0xdc, 0x7e, 0x2e, 0xb4, 0xb4, 0x10,
0xcb, 0x1d, 0xc2, 0x66, 0x3b]

CF = 0
def ROR(REG):
global CF
if CF: REG |= 0x100
CF = REG & 1
REG >>= 1
return REG

def ROL(REG):
global CF
REG <<= 1
if CF: REG |= 1
CF = 1 if (REG > 0xFF) else 0
return REG & 0xFF

STACK = 0
ADDR_3B = 0
KEY = ''

for Y in range(24):
for A in string.uppercase+string.digits:
C = A
VAL_3B = ADDR_3B
A = ord(A) # ROM:82F7 LDA $5,Y
X = A # ROM:82FA TAX
A = ROL(A) # ROM:82FB ROL A
A = X # ROM:82FC TXA
A = ROL(A) # ROM:82FD ROL A
X = A # ROM:82FE TAX
A = ROL(A) # ROM:82FF ROL A
A = X # ROM:8300 TXA
A = ROL(A) # ROM:8301 ROL A
X = A # ROM:8302 TAX
A = ROL(A) # ROM:8303 ROL A
A = X # ROM:8304 TXA
A = ROL(A) # ROM:8305 ROL A
STACK = A # ROM:8306 PHA
A = VAL_3B # ROM:8307 LDA ROLL
X = A # ROM:8309 TAX
A = ROR(A) # ROM:830A ROR A
A = X # ROM:830B TXA
A = ROR(A) # ROM:830C ROR A
X = A # ROM:830D TAX
A = ROR(A) # ROM:830E ROR A
A = X # ROM:830F TXA
A = ROR(A) # ROM:8310 ROR A
VAL_3B = A # ROM:8311 STA ROLL
A = STACK # ROM:8313 PLA
CF = 0 # ROM:8314 CLC
A = (A + VAL_3B) & 0xFF # ROM:8315 ADC ROLL
A = A ^ CHECKA[Y] # ROM:8317 EOR $955E,Y
VAL_3B = A # ROM:831A STA ROLL
X = A # ROM:831C TAX
A = ROL(A) # ROM:831D ROL A
A = X # ROM:831E TXA
A = ROL(A) # ROM:831F ROL A
X = A # ROM:8320 TAX
A = ROL(A) # ROM:8321 ROL A
A = X # ROM:8322 TXA
A = ROL(A) # ROM:8323 ROL A
X = A # ROM:8324 TAX
A = ROL(A) # ROM:8325 ROL A
A = X # ROM:8326 TXA
A = ROL(A) # ROM:8327 ROL A
X = A # ROM:8328 TAX
A = ROL(A) # ROM:8329 ROL A
A = X # ROM:832A TXA
A = ROL(A) # ROM:832B ROL A
A = A ^ CHECKB[Y] # ROM:832C EOR $9576,Y
if A == 0:
KEY += C
ADDR_3B = VAL_3B
break
else:
VAL_3B = 0
print KEY
The flag is NOHACK4UXWRATHOFKFUHRERX