[1장][test01] Angr – Simulation_Manager’s Explore – 키를 찾아드립니다!

Angr는 simulation_manager 를 통해서 여러 분기 문에서 목적지까지 갈 수 있는 길을 찾을 수 있습니다. 앞으로 블로그에서는 Angr를 공부하면서 직접 만든 공격 대상 프로그램들을 이용하여 angr 사용법을 익혀보도록 하겠습니다.
 
먼저, 저만의 용어를 정리하겠습니다.
범용적인 말은 아니지만, 이 블로그에서는 다음과 같이 표현하겠습니다.
목적주소 : 우리가 목표로 하고 있는 주소
기피주소 : 우리가 가면 안되는 곳의 주소
 
다음은 오늘 포스팅할 자료가 올라와 있는 github입니다.
Contact Me : [email protected]
 

공격/분석 대상
먼저, 간단한 test01을 가지고 왔습니다.
이 test01 바이너리는 저의 생일인 “980414”를 입력해야만 Correct! 라고 뜹니다.
 
만약에 비밀번호를 풀고 싶다면, 해커는 Compile 된 파일을 분석하여 비밀번호를 알아내거나 브루트포싱을 통해서 알아내야 합니다.
 
소스로 실제로 if(input == 980414) 로 짜여있기 때문에 gdb 에서 cmp만 잘 찾아보시면 바로 알아내실 수 있습니다.
 
하지만 우리는 angr를 공부하기 위해서
일단, Debugger는 분기에 대한(성공/실패시 실행되는 주소 등..) 정보만 얻어가는 용도로만 합시다.

 


Angr Source Code ( solver.py )

코드에 대한 설명은 나중에 하도록 하겠습니다.

angr.Project 는 angr에서 분석할 바이너리를 지정하고 여러 설정들을 함으로써 프로젝트를 초기화 합니다.
 
project.factory
Angr에는 여러 클래스들이 정의되어있습니다.
Angr에서 주로 필요로 하는 class들을 간편하게 사용 할 수 있도록 angr.factory에 정의해 놓았습니니다.
 
project.factory.simulation_manager()
Angr에서 가장 중요한 컨트롤 인터페이스는 SimulationManager입니다.
SimulationManager는 Control Symbloic Execution을 가능하게 하며, 프로그램의 state space를 찾기 위해 전략들을 찾을 수 있습니다.
Simulation Manager는 부드럽게 여러 상태를 관리 할 수 있으며, states들은 stashes로 구성되어 있으며 원하는 대로 조작 할 수 있습니다.
(조작 = step forward, filter, merge, move)
예를 들면 각자 움직인 후 병합을 하는 등이 가능하다고 합니다.
해당 부분에서는 저도 공부 중에 있으니, 나중에 공부를 마친 이후 제대로 설명하도록 하겠습니다.
일단은 simulation_manager는 Angr의 핵심 요소이며, 해당 모듈을 통해 Angr를 제어 할 수 있다고만 알아둡시다.
project.factory.simulation_manager() 를 하면 active stash가 기본적으로 반환이 됩니다. 따라서 변수 sm에는 1개의 stash가 저장되어있다고 생각합시다.
 
sm.explorer
인자 값으로 find와 avoid를 받습니다.
find는 목적지로 하는 곳의 주소이며, avoid는 피해야 할 곳의 주소입니다.
ida로 봅시다.
분기는 총 1개입니다. ( -fno-stack-protector 옵션을 주고 컴파일을 했기 때문에, 따로 카나리를 체크하는 부분은 없습니다. )
해당 분기 문을 해석하자면 scanf로 받은 데이터가 eax값과 같은지 보고 jump를 하는 부분입니다.
우리의 목표는 “Collect”가 뜨는 겁니다.
단순히 ASM 코드에서 cmp만 보고 eax가 0xef5be와 같은지 확인하는 것만 보고 “아! 비밀번호는 980414구나!” 라고 할 수 있겠지만, 우리는 angr를 공부하는 것이기 때문에 angr를 이용해서 풀어봅시다.
 
이 사진 부분에서 Correct 라고 출력 하는 것을 볼 수 있습니다. (망했다.. 이미 답이 나와있네요..)
주소는 0X4005D9 인 것을 알 수 있습니다. 우리는 “Correct”를 화면에 출력하는 것을 목표로 합니다. 따라서 해당 주소를 우리가 가야 할 “도착주소”라고 하겠습니다.
 
이 사진은 우리가 가면 안되는 부분인 Incorrect를 출력하는 부분입니다.
주소는 0x4005ec임을 알 수 있습니다.
따라서 해당 주소를 우리가 가면 안되는 “기피주소”라고 하겠습니다.
 
이렇게 정의해주고
sm.explore(find=(FIND_ADDR,), avoid=(AVOID_ADDR,)) 이렇게 explore Method를 실행해줍니다.
여기서, find에는 목적주소가, avoid에는 기피주소가 들어가야 합니다.
우리가 피해야하는 주소가 많은 경우, 기피주소를 튜플/배열 형식을 통해서 여러 개를 넣어줄 수 있습니다.
이렇게 explore Method가 호출되고 나면, Angr가 알아서 무언가 처리를 합니다.
그리고 찾은 Symbol을 반환합니다.
 

return sm.found
sm.explore 을 통해서 얻은 정보중 found 를 넘겨줍니다.
found 는 자료형이 list입니다.
 
for found in founded :
    print found.posix.dumps(0)
 
여기서,
found.posix.dumps(0) 은 표준 입력을 통해 저장된 정보를 반환하며,
found.posix.dumps(1) 은 표준 출력을 통해 저장된 정보를 반환하고,
found.posix.dumps(2) 는 표준 에러를 통해 저장된 정보를 반환하며,
found.posix.dumps(3) 는 파일 관련하여 저장된 정보를 반환합니다.
우리는 표준 입력을 통해서 키를 입력하므로, found.posix.dumps(0) 을 print하면 됩니다.
 

RUN! 
 
-4293986882 라는 값이 나왔습니다.
Integer는 4byte로, 2^32 가지의 수를 표현 할 수 있습니다.
하지만 |-4293986882| > 2^32 이므로, 우리가 찾으려는 진정한 변수의 값은 아닙니다.
(물론 -4293986882를 입력해줘도 Correct가 뜹니다.)
따라서 4byte integer로 표현하기 위하여
-4293986882 에다가 2^32 를 더해줍니다.
 
자!
제가 의도했던 값인 980414 가 나왔습니다.

Submit
 
-4293986882 와 980414 모두 다 correct가 뜨는 것을 볼 수 있습니다.
 

감사합니다.

공부하면서 작성한 post입니다. 오류나 지적하실 부분이 있다면 부담없이 댓글로 알려주세요!

Jangtaejin < [email protected] >

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다