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