The challenge description was: We're getting a transmission from someone in the past, find out what he wants.
A NES ROM with the name HackingTime_03e852ace386388eb88c39a02f88c773.nes was provided. My friend SciFi actually told me about this challenge during the CTF while I was working on another one, he thought I would get excited and he was absolutely right. That was a really funny and brilliant one thanks to CSAW organizers.
I never tried to debug or crack anything like that except when I attempted to solve Hack.lu GameBoy ROM challenge and failed miserably, so the way I solved this one was a bit painful and more about behavior analysis than reversing any encryption/encoding. With that said let's start shall we.
And this is where the fun starts, we have to find the password to disable lockdown. The first thing we need to do is to find out where and how our input is checked against the real password. I opened the Hex Editor in FCEUX after entering "AAAAAAA" in the game and on the very first row I could see my input in memory:
I right clicked on the first character of my input and set a Read Breakpoint which opened the Debugger window, I went back in the game and validated my input to see if the breakpoint would get caught:
00:82F1:A0 00 LDY #$00 00:82F3:A9 00 LDA #$00 00:82F5:85 3B STA $003B = #$00 00:82F7:B9 05 00 LDA $0005,Y @ $0005 = #$41 ; breakpoint here 00:82FA:AA TAX 00:82FB:2A ROL 00:82FC:8A TXA 00:82FD:2A ROL 00:82FE:AA TAX
The Debugger caught our breakpoint and we can see the value of our fist password character at 0x5 in memory (#$41 is A). Now even after checking a Quick 6502 Reference I thought it might be easier and much faster to keep on stepping until I see if this character is eventually compared to the proper one.
Further down in the same function we can see where the program is looping through each one of our character and go back to 82F7 where our breakpoint stopped the first time:
00:832F:99 1E 00 STA $001E,Y @ $0035 = #$87 ; store first input after calculation at 0x1E 00:8332:C8 INY 00:8333:C0 18 CPY #$18 ; 0x18 = 24 characters 00:8335:D0 C0 BNE $82F7 ; check next if not = 0x18
Now we know that our input is being read and some calculation is applied to each character and stored starting at 0x1E in memory. Full password must be 24 characters long.
Below this loop:
00:8337:A0 00 LDY #$00 ; set Y to 0x0 00:8339:B9 1E 00 LDA $001E,Y @ $001E = #$87 ; load calculated input at 0x1E in A 00:833C:D0 08 BNE $8346 ; if A not equal to Y jump to $8346 00:833E:C8 INY ; 00:833F:C0 18 CPY #$18 ; 0x18 = 24 characters 00:8341:D0 F6 BNE $8339 ; check next if not = 0x18
And this is where I wasn't sure if I should try getting the way it is generating the proper code or not. I knew that all our input must end up being equal to 0x0 in memory to succeed the password validation, I was aware it also had to be 24 characters and where it was stored after being calculated:
00000000: 4a91 0040 0041 4141 4141 4141 4141 4141 J..@.AAAAAAAAAAA 00000010: 4141 4141 4141 4141 4141 4141 4100 8725 AAAAAAAAAAAAA..% 00000020: 4c11 475f a062 13c2 781e 0c8e eb34 0604 L.G_.b..x....4.. 00000030: 0bc1 3808 85ae 4100 0100 968c 0017 231c ..8...A.......#. 00000040: 2c07 2701 8000 0000 0000 0000 0000 0000 ,.'.............
We see that our first input should be calculated to 0x0 at 0x1E however our input A (0x41) got calculated to 0x87. So I went the 'bruteforce' way and tried incrementing the current character until it matched the value 0x0, then I would go for the next one:
00000000: 4a91 0040 004e 4f48 4141 4141 4141 4141 J..@.NOHAAAAAAAA 00000010: 4141 4141 4141 4141 4141 4141 4100 0000 AAAAAAAAAAAAA... 00000020: 0000 034e 6411 c077 d7b4 6615 4212 8e66 ...Nd..w..f.B..f 00000030: 926b 5292 e4f6 4800 0100 9609 0002 231c .kR...H.......#. 00000040: 2c07 2701 8100 0000 0000 0000 0000 0000 ,.'.............
And I would change letters one by one until all my calculated input was equal to 0x0.
I ended up with the password: NOHACK4UXWRATHOFKFUHRERX
We got our flag: NOHACK4UXWRATHOFKFUHRERX