상세 컨텐츠

본문 제목

[EKOPARTY CTF 2016] Fuckzing reverse (ft. angr)

SYSTEM HACKING/CTF, etc

by koharin 2022. 7. 16. 17:39

본문

728x90
반응형

Binary 정보

$ file FUck_binary 
FUck_binary: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=583997ea22be0b90670780cb91b36de37e1100d2, not stripped

Setting

shared library를 로드할 수 없다고 오류가 뜬다.

임의로 shared library를 만들어서 오류를 해결한다.

 

shared library 파일 내용 (fake_getflag.c)

#include <stdio.h>
#include <string.h>

char *flag = "EKO{THIS_IS_FLAG_FOR_LOCAL}";
void get_flag(char *str){
    strcpy(str, flag);
}

 

shared library로 컴파일

gcc -fpic -shared fakse_getflag.c -o libget_flag.so

 

바이너리가 라이브러리 로드하는 경로에 복사

ldd FUck_binary 로 바이너리가 로드하는 라이브러리 경로 확인하고 적당한 위치에 임의로 만든 라이브러리를 복사해놓으면 오류가 해결된다.


Static Analysis

.. 분석해보자.

 

main() 함수 초기에는 team name을 입력하라는 문구를 출력하고, v418에 0x12C 바이트(300 바이트)만큼 입력받는다.

v22=&v18로 v22에 v18 주소를 저장하는데 v22는 rsi 레지스터, 즉 read 함수의 두 번째 인자임을 알 수 있다.

이후 사용자 입력을 가리키는 v22를 가지고 많은 검증을 한다. 이 모든 constraint를 manual하게 구할 수는 없을 것이다.

 

string view를 보면 “Your flag is %s”로 flag를 출력하는 string이 있다.

 

main 함수 내 해당 코드를 확인해보면, get_flag 함수 호출 후 v417 값을 출력해준다. get_flag 함수는 libget_flag.so shared 라이브러리 내 함수이다.

 

마지막으로 “Goodbye!”를 출력하고 종료된다.


Constraint Solving

AFL을 사용해서 만족하는 입력을 fuzz할 수도 있지만, angr를 사용하면 더 명확하고 정확하게 constraint를 해결할 수 있다.

angr의 symbolic execution engine을 이용하여 프로그램 내 특정 부분을 도달하는데 필요한 입력을 계산할 수 있다. 


Exploit

1) start, find, avoid 주소 구하기

프로그램 내 어느 지점에서 시작할 것인지(start), 찾는 경로(find), 피해야 할 경로(avoid)의 주소를 알아야 한다.

avoid는 constraint solving에 실패 시 실행이 이동하는 지점으로, jl loc_403A7E 에서 v410 < 76인 경우 0x403A7E로 이동하고, v410 ≥ 76인 경우 flag 출력하는 경로로 실행 흐름이 이동한다.

따라서 avoid를 0x403A7E로 find를 0x403A3B로, start 주소는 main 함수 시작 주소인 0x400B30으로 설정한다.

 

2) 초기 state 설정 & shared library 로드

p = angr.Project('./FUck_binary', auto_load_libs=False)

# set initial state
state = p.factory.blank_state(addr=START)

angr.Project로 바이너리를 로드한 후, p.factory.blank_state(START) 를 통해 시작 지점을 start 주소로 설정한다.

 

3) stdin input에 constraint 추가

# set initial state
state = p.factory.blank_state(addr=START)

# set initial flag
flag = state.solver.BVS('flag', BUF_LEN*8)
def char(state, c):
    return state.solver.And(c <= '~', c >= ' ')

# add constraint to stdin
for c in flag.chop(8):
    state.solver.add(char(state, c))

입력값 flag 초기값을 설장한다.

flag의 경우 127개 문자 중에서 값을 가지므로 stdin에 constraint를 추가한다.

 

4) constraint solving

# create simulation manager
sm = p.factory.simulation_manager(state)

# create state
sm.use_technique(angr.exploration_techniques.Explorer(find=FIND, avoid=AVOID))

# run Explorer
sm.run()

이후 smulation_manager의 Explorer를 사용하여 find, avoid 주소를 지정하고 constraint를 해결한다. 이후 found stash에 find 주소로 갈 수 있는 state가 저장되고, 이를 해결하기 위해 구한 flag 값을 구할 수 있다.


Exploit Code

#!/usr/bin/python 
import angr

START=0x400B30
FIND=0x403A3B
AVOID=[0x403A7E + i*60 for i in range(100)]
BUF_LEN=100

def char(state, c):
    return state.solver.And(c <= '~', c >= ' ')

p = angr.Project('./FUck_binary', auto_load_libs=False)

# set initial state
state = p.factory.blank_state(addr=START)

# set initial flag
flag = state.solver.BVS('flag', BUF_LEN*8)

# add constraint to stdin
for c in flag.chop(8):
    state.solver.add(char(state, c))

# create simulation manager
sm = p.factory.simulation_manager(state)

# create state
sm.use_technique(angr.exploration_techniques.Explorer(find=FIND, avoid=AVOID))

# run Explorer
sm.run()

# get solution
flag_input = sm.one_found.posix.dumps(0)
print(repr(flag_input))

 

728x90
반응형

'SYSTEM HACKING > CTF, etc' 카테고리의 다른 글

[angr_ctf] 08_angr_constraints  (0) 2022.05.15
[angr_ctf] 07_angr_symbolic_file  (0) 2022.05.14
[angr_ctf] 06_angr_symbolic_dynamic_memory  (0) 2022.05.14
[angr_ctf] 05_angr_symbolic_memory  (0) 2022.05.14
[angr_ctf] 04_angr_symbolic_stack  (0) 2022.05.14

관련글 더보기