'angr 사용법'에 해당되는 글 2건

  1. 2017.02.19 [Codegate CTF_2017] EasyCrack 101(reversing)
  2. 2017.02.19 [Codegate CTF_2017] angrybird(reversing)
2017. 2. 19. 00:58

## Codegate CTF_2017(EasyCrack 101, rev)


[Summary]

1. 키 값을 찾아야 하는 간단한 바이너리가 101개 주어지고 각각의 바이너리의 키 값을 웹 페이지에 인증해야함

2. 바이너리들이 작고 101개가 전부 동일한 구조이므로 angr를 이용하여 자동화하여 문제를 풀 수 있음


[Analysis] 

  EasyCrack 101 문제는 Binary.zip 파일을 하나 던져주고 전부 다 크랙해서 패스워드를 찾아내 101개의 바이너리 

모든 패스워드를 웹을 통해 인증하는 문제였다. 우선 다운로드 받은 binary 파일들 중 prob1과 prob2를 아래 

[그림 1]과 같이 IDA를 이용해 분석해 보았다. prob1과 prob2 바이너리는 완전 동일한 구조로 이루어져 있었고

주소 값만 약간씩 다른 것을 확인할 수 있었다. 또한 이 문제도 angrybird 문제와 비슷하게 input값이 주어지면 

자체 연산을 거쳐 조건 분기문으로 가서 비교를 한 후 키 값이 맞으면  "Good job"이라는 문자열을 틀리면 

"No No!"라는 문자열을 출력하고 프로그램을 종료했다.


 따라서 이 문제도 angr를 이용해서 풀면 쉬울거라고 생각했다. 하지만 문제점이 하나 있는데 여기서 각 101개의

바이너리들의 모든 find 주소와 avoid 주소를 수동으로 직접 IDA를 켜서 눈으로 확인하고 angr로 만든 파이썬 

코드에 주소 값을 수정하면서 하나하나 키 값을 찾아내어 웹 상에 인증하는 것은 너무 비효율적이라고 생각이 

되었다.


[그림 1] prob1 & prob2 분석


 이 문제점은 아래 [그림 2]와 같이 avoid 주소의 어셈블리 코드(OPCODE)와 find 주소의 어셈블리코드(OPCODE)의 

특징을 이용하여 해결하였다.


[그림 2] find & avoid OPCODE 특징


 따라서 해당 OPCODE를 이용하여 find와 avoid 리스트를 자동으로 구한 후 angr를 이용하여 키 값을 자동으로 찾게 

파이썬 스크립트를 짠 후 실행하면 아래 [그림 3]과 같이 101개의 바이너리에 대한 각각의 키 값을 얻을 수 있게 된다.


[그림 3] 키 값 자동화


 각각의 키 값을 웹에 다 입력하게 되면 아래 [그림 4]와 같이 flag가 있는 웹페이지가 로드된다.


[Exploit Code] - easycrack101_exploit.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import angr
import hexdump
from pwn import *
 
def solve(binary_name, find_addr, avoid_addr):
    proj = angr.Project(binary_name, load_options={'auto_load_libs':False})
    argv1 = angr.claripy.BVS("argv1"100*8)
    initial_state = proj.factory.entry_state(args=[binary_name, argv1])
 
    pg = proj.factory.path_group(initial_state)
 
    pg.explore(find=find_addr, avoid=avoid_addr)
    found = pg.found[0]
    solution = found.state.se.any_str(argv1)
 
    solution = solution[:solution.find("\x00")]
    return solution
 
if __name__ == '__main__':
    for i in xrange(0,101):
        binary_name = "prob"
        binary_name += str(i+1)
    
        f = open(binary_name, 'rb')
        binary_content = f.read()
        f.close()
        total = ''
        for j in xrange(len(binary_content)):
            total += '%02X' % int(ord(binary_content[j]))
 
        find_list_tmp = [j for j in range(len(total)) if total.startswith('EB0ABF',j)]
        avoid_list_tmp = [j for j in range(len(total)) if total.startswith('FFFFC9C3',j)]
        
        find_list = []; avoid_list = []
        for j in xrange(len(find_list_tmp)):
            find_list.append(0x400000 + (find_list_tmp[j]+1)/2 - 0xa)
        for j in xrange(len(avoid_list_tmp)):
            avoid_list.append(0x400000 + (avoid_list_tmp[j]+1)/2 - 0x8)
 
        #print avoid_list
        #print find_list
    
        #hexdump.hexdump(solve(binary_name, find_list[0], avoid_list[0]))
        #print find_list[0], avoid_list[0]
        print '['+str(i+1)+']',
        print(repr(solve(binary_name,find_list[0],avoid_list[0])))
 
cs



[Get Flag~~!!!!]

[그림 4] flag 확인("Thank_U_4 s0lving_MY_Pr0b...u_@re_vEry_genius!!!")


끝~!





Posted by holinder4S
2017. 2. 19. 00:40

## Codegate CTF_2017(angrybird, reversing)


[Summary]

1. 바이너리가 동작하지 않는 몇가지 조건들이 있음 => 바이너리 패치를 해야함.

2. 바이너리가 원래 목적대로 정상 동작을 하면 angr을 이용하여 키 값을 찾아내야 함.


[Analysis] 

  angrybird 문제는 맨 처음 IDA를 이용해 분석하려고 하면 아래 [그림 1]과 같이 어셈블리로는 코드가 업청나게 

많은데 디컴파일을 하면 단순히 exit()만 하는 프로그램으로 인식하고 실제로 리눅스에서 실행시켜도 아무것도

하지 않고 종료하는 것을 확인할 수 있다.


[그림 1] angrybird 디컴파일


 따라서 제대로 인식하고 정상적으로 fgets()함수로 사용자 입력을 받고 돌게 하기 위해서 프로그램 코드 패치를 

할 필요가 있는데 [그림 1]에서 "jz _exit"명령을 nop으로 헥사에디터로 패치하면 아래 [그림 2]와 같이 정상적으로 

인식하게 된다.


[그림 2] 첫 번째 코드 패치


 하지만 [그림 2]에서 보이듯이 실행을 하면 의미심장한 문자열이 출력된 후 Segmentation fault가 뜨고 프로그램이

비정상적으로 종료하는 것을 확인할 수가 있다. 따라서 디컴파일 후의 소스코드를 보니 sub_4006F6(), 

sub_40070C(), sub_40072A()라는 3개의 함수가 있었다.


 이 각각의 코드를 한번 보면 우선 첫번째 sub_4006F6()함수는 아래 [그림 3]과 같이 return 값이 1이 아니라 21이

되어야 한다고 하고 실제로 dword_606060의 값을 return 하는데 dword_606060값은 1인 것을 확인할 수 있고

다음과 같이 패치를 할 수 있다.


[그림 3] 두 번재 패치 코드


 이렇게 패치 했음에도 바이너리를 실행하면 [그림 2]과 동일하게 Segmentation Fault가 뜨면서 프로그램이 죽는다.

따라서 두 번째 함수 sub_40070C()를 보았다. sub_40070C()를 보면 아래 [그림 4]와 같이 

"mov eax, [esp+var_s0]" 부분에서 64bit 환경에서 eax에 값을 집어넣으려다가 seg fault가 뜨게 되고 이걸 

해결한다고 하더라도 _exit()함수가 있어서 종료하게 되는데 이를 [그림 4]와 같이 패치하면 정상적으로 사용자 

입력을 받고 조건식을 거쳐 merong이 뜨는 것을 확인할 수 있다.


[그림 4] 세 번째 패치 코드


 그럼 이제 마지막 3번째 함수가 남게 되는데 3번째 함수인 sub_40072A()함수는 아래 [그림 5]와 같이 __libc_main

주소가 들어있는 off_606038과 "hello"라는 문자열과 strncmp()함수를 이용해 비교를 한다. 그래서 같으면

정상적으로 함수를 종료하고 사용자 입력 문자열을 여러가지 연산과 비교를 통해 검증하고 그렇지 않으면 exit()

함수를 이용해 바로 프로그램을 종료시켜버린다. 따라서 strncmp()함수를 nop로 패치를 하면 되는데 여기서 중요한

점이 있다.


[그림 5] 네 번째 패치 코드(1)


 이 함수에서 off_606038와 "hello"라는 문자열을 왜 비교할까? 라는 생각을 해야하는데 실제 main()함수에서 보면

아래 [그림 6]과 같이 [rbp+var_58]이라는 변수에 off_606038의 값을 집어 넣는데 나중에 fgets()함수로 받는

사용자 입력 값을 엄청나게 많은 연산고 조건으로 검증할 때 [rbp+var_58]변수가 7번 정도 개입하게 된다. (IDA 기능

중 cross reference 기능을 이용) 따라서 위 [그림 5]처럼 단순 nop처리만 할 것이 아니라 main()함수에서 var_58

변수에 offset off_aHello의 값을 넣어주도록 패치를 한번 더 해야 한다.


[그림 6] 네 번째 코드 패치(2)


 이제 바이너리 패치는 모두 끝났다. 이제 그럼 본격적으로 문제를 풀어야 하는데 단순히 사용자 입력을 받아 여러가지 

연산들과 엄청나게 많은 수의 분기문을 거쳐 마지막으로 아래 [그림 7]과 같은 최종 목적지로 가는 것이 문제이다.

이러한 유형의 CTF 문제는 python 모듈 중 angr이라는 Symbolic Execution을 자동화 해주는 도구로 해결할 수 

있는데 angr을 간단하게 소개하자면 최종 목적지 주소(find 주소)를 설정해주고 가면 안되는 주소(avoid 주소)들을 쭉

설정해두면 프로그램이 알아서 symbolic execution을 실행하면서 조건 분기문에 맞는 알맞은 입력값을 찾아내준다.


[그림 7] 최종 목적지 주소


 그런데 한 가지 문제가 있다. 조건 분기문이 너무 많아 avoid 리스트들의 주소를 하나하나 입력하기에는 너무 많은 

양이기 때문에 자동화하여 avoid 주소의 리스트를 뽑아내야 한다. 이 문제점은 avoid 주소가 아래 [그림 8]과 같이

단순한 똑같은 어셈블리어로 이루어져 있어서 해당 OPCODE를 파일에서 찾아 offset을 계산하여 주소 값을 리스트로

뽑아주는 파이썬 코드를 간단하게 짜서 해결할 수 있다.


[그림 8] avoid 리스트 자동화


 이렇게 찾아낸 avoid 리스트들을 적용해 angr을 써서 키 값을 찾아내면 아래 [그림 9]와 같이 모든 조건 분기문들을 

만족하는 문자열이 나타난다.


[Exploit Code] - angrybird_avoid_list.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
= open("angrybird_patched4",'rb')
binary = f.read()
total = ''
for i in xrange(len(binary)):
    total += '%02X' % int(ord(binary[i]))
 
avoid_list_tmp = [i for i in range(len(total)) if total.startswith('BF94504000', i)]
avoid_list = []
for i in xrange(len(avoid_list_tmp)):
    avoid_list.append(hex(0x400000 + (avoid_list_tmp[i]+1)/2))
 
print avoid_list
f.close()
 
cs


[Exploit Code] - angrybird_exploit.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import angr
import hexdump
 
def main():
    proj = angr.Project('angrybird_patched4', load_options={'auto_load_libs': False})
    ex = proj.surveyors.Explorer(find=(0x404fab,), avoid=(0x4007ef0x4008180x4008380x4008610x4008900x4008b90x4008e20x40090b0x4009340x4009770x4009a00x4009c90x4009f20x400a1b,0x400a470x400a6c0x400a950x400abb0x400ae00x400b090x400b3f0x400b680x400ba40x400bd60x400bff0x400c310x400c5d0x400c820x400cab0x400cd00x400cf50x400d1b0x400d400x400d690x400d920x400db20x400dd70x400e000x400e260x400e520x400e7b,0x400eb80x400edd0x400f060x400f2f0x400f580x400f780x400fa10x400fd00x400ff50x40101b0x40104b0x4010700x4010990x4010c20x4010e20x40110b0x4011340x40115d0x4011860x4011c20x4011eb0x40121b0x4012440x40126d0x4012960x4012bf0x4012e80x40131e0x401347,0x4013730x40139c0x4013c50x4013e50x40140e0x4014370x4014600x4014890x4014b20x4014db0x4015040x40152d0x4015560x40157f0x4015a80x4015d10x4016000x4016290x4016520x40167b0x4016a40x4016d30x4016fc0x4017250x40177b0x4017a40x4017cc0x4017f50x40181e0x4018540x40187d0x4018b30x4018dc0x4019050x40192e0x4019570x4019800x4019a90x4019d20x4019fb0x401a240x401a530x401a7c,0x401aac0x401ad50x401afe0x401b2d0x401b560x401b7f0x401ba40x401bcd0x401bf50x401c2b0x401c5a0x401c830x401cac0x401cd50x401cfe0x401d230x401d490x401d6e0x401d970x401dd30x401dfc0x401e250x401e4a0x401e700x401e950x401eba0x401ee30x401f120x401f3b0x401f640x401f8d0x401fb60x401fdf0x4020080x4020310x40205a0x4020830x4020a80x4020cd0x4020f30x4021180x4021410x40216a0x4021a00x4021c90x4021ff0x402224,0x40225d0x4022830x4022a80x4022d10x4022fa0x4023230x40234c0x4023750x40239e0x4023c30x4023e80x40240d0x4024330x4024580x40248e0x4024c00x4024fc0x4025250x40254a0x40256f0x4025950x4025ba0x4025e30x4026400x4026720x4026970x4026c60x4026ef,0x40271f0x4027480x40277e0x4027a70x4027cd0x4027f20x40281b0x4028440x40286d0x4028960x4028bf0x4028f10x40291a0x4029430x4029680x40298d0x4029b60x4029db,0x402a150x402a3a0x402a690x402a920x402abb0x402af10x402b1a0x402b430x402b790x402b9e0x402bc70x402bf00x402c150x402c3a0x402c690x402c920x402cb70x402cdd0x402d020x402d2b0x402d610x402d8a0x402db30x402dd80x402e010x402e2f0x402e580x402e8a0x402eaf0x402ede0x402f030x402f2c0x402f550x402f900x402fb60x402fe80x4030110x40303a0x40305a0x4030830x4030ac0x4030d50x4030fe0x40311e0x4031540x40317d0x40319d0x4031c60x4031ef0x4032180x4032410x40326a0x403293,0x4032da0x4033070x4033300x4033590x4033950x4033cb0x4033eb0x4034140x40343d0x4034660x40348f0x4034b80x4034e10x4035010x40352a0x4035590x4035820x4035ab0x4035d40x40360a0x40362a0x4036600x4036890x4036b20x4037020x40372b0x4037540x40378a0x4037c00x4037e90x4038120x40383b0x4038640x40388d0x4038c30x4038f90x40392f0x4039580x40397d0x4039a30x4039c80x4039f10x403a1a0x403a430x403a6c0x403a910x403ac00x403ae50x403b140x403b3d0x403b660x403b8f0x403bb80x403be10x403c060x403c2b0x403c5a0x403c830x403cac0x403ce20x403d0b0x403d340x403d5d0x403d820x403da70x403dd60x403dff0x403e280x403e510x403e900x403ed10x403f070x403f300x403f660x403f8b0x403fb00x403fd60x403ffb0x4040240x40405a0x4040830x4040ac0x4040d10x4040f60x40411c0x4041410x40416a0x4041930x4041c90x4041f20x4042180x40423d0x4042660x40428f0x4042b80x4042e10x40430a0x40432f0x4043580x40437d0x4043a20x4043d40x4043fa,0x40441f0x4044480x4044710x40449a0x4044c30x4044ec0x4045110x40453a0x40455f0x4045840x4045ad0x4045d20x4045f80x40461d0x4046460x40466f0x4046980x4046c10x4046ea0x40470f0x4047380x40475d0x4047820x4047ab0x4047d00x4047f50x40481a0x4048400x4048650x40488e0x4048b70x4048dc0x4049010x4049300x4049590x4049820x4049ab0x4049d40x4049fd0x404a1d0x404a460x404a6f0x404a980x404ac10x404aea0x404b0a0x404b330x404b5c0x404b850x404bae0x404bd70x404c000x404c290x404c6c0x404c950x404cbe0x404ce70x404d100x404d390x404d6f0x404d980x404dc10x404dea0x404e130x404e3c0x404e610x404e900x404eb90x404ee20x404f0b0x404f300x404f680x404f97,))
    ex.run()
    return ex.found[0].state.posix.dumps(0).strip('\0\n')
    #path_group = proj.factory.path_group(threads=4)
    #path_group.explore(find=0x400803, avoid=0x4007ef)
    #return path_group.found[0].state.posix.dumps(1)
 
if __name__ == '__main__':
    hexdump.hexdump(main())
    #print(repr(main()))
 
cs



[Get Flag~~!!!!]

[그림 9] flag 확인("Im_so_cute&pretty_:)")


끝~!








Posted by holinder4S