'codegate 2017 reversing'에 해당되는 글 3건

  1. 2017.02.19 [Codegate CTF_2017] EasyCrack 101(reversing)
  2. 2017.02.19 [Codegate CTF_2017] angrybird(reversing)
  3. 2017.02.19 [Codegate CTF_2017] RamG-thunder(reversing) 2
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
2017. 2. 19. 00:17

## Codegate CTF_2017(RamG-thunder, rev)


[Summary]

1. Step by Step으로 하나하나 조건을 만족시키면 된다.

2. 만족시킨 값들로 특정 헥사 바이너리들을 xor연산하면 png파일이 생성된다.


[Analysis] 

  RamG-thunder 문제는 윈도우 PE 바이너리이고 실행시키면 아래 [그림 1]과 같이 간단한 4가지의 단순한 메뉴와

1개의 히든 메뉴를 가지고 있는 프로그램이다. 여기서 중요한 메뉴는 4번을 누르면 나타나는 히든 메뉴이다.

(히든 메뉴는 String 검색을 통해 찾을 수도 있고 스위치문을 통해 찾을 수도 있음)


[그림 1] RamG 문제 실행


 이 프로그램을 OllyDBG와 IDA를 통해 동적/정적 분석해보도록 할 것이다. 우선 먼저 올리디버거로 해당 히든 메뉴

부분을 스트링 기반으로 Search하여 아래 [그림 2]와 같이 시작점에 bp를 걸고 분석을 진행할 것이다. 1번 메뉴

help는 step by step이라는 힌트를 준다.


[그림 2] 히든 메뉴


 이제 F8을 누르면서 진행하다 보면 2번 메뉴를 들어가면 0x4015f0함수를 호출하는 것을 확인할 수 있고 IDA를 통해

해당 함수의 첫 부분을 보면 아래 [그림 3]과 같이 스택에 알 수 없는 값들을 쓰는 것을 확인할 수 있다.


[그림 3] sub_4015f0 함수 첫 부분


 그리고 더 진행하다 보면 [그림 4]와 같이 stage1에서 키 입력을 받아 메모리에 저장되어 있는 특정 값과 xor하여

메모리에 저장되어 있는 값과 비교를 한다.


[그림 4] stage1 풀이


 이렇게 Stage1 부분은 위 그림과 같이 xor_answer와 실제 사용자 입력 값과 47459와 xor한 값을 strncmp함수 

기능을 하는 sub_405c20()함수로 5바이트 비교하기 때문에 "yamya"라는 입력 값을 집어넣으면 stage1을

통과하게 된다. 그리고 Stage2로 넘어가기 전 아래 [그림 5]와 같이 IsDebuggerPresent()함수와 프로세스 명을

기반으로 디버거를 탐지하는 안티디버깅 루틴이 존재하는데 이는 올리 디버거 상에서 간단히 NOP처리를 하거나

레지스터를 수정하면서 우회가 가능하다.


[그림 5] 안티 디버깅 루틴


 그 다음 Stage2는 아래 [그림 6]과 같이 Local MAC address의 앞 3자리를 특정 MAC address 앞 3자리

(C8-59-78-??-??-??)와 비교하는데 이 값만 올리디버거 상에서 똑같이 맞춰 주고 진행하면 Stage2도

클리어 할 수 있다.


[그림 6] Stage2 풀이


 그 다음 Stage3은 아래 [그림 7]과 같이 윈도우 레지스트리 "HKCU\Hellow"를 RegOpenKeyExW()함수로 오픈 

한 후에 "hellow_FishWorld"라는 값을 RegQueryValueExW()라는 함수를 이용하여 read한 다음 값이 다 제대로 

있으면 "hel"이라는 문자열을 그렇지 않으면 "fis"라는 문자열을 Stage3의 answer로 저장한다.


[그림 7] Stage3 풀이


 그 다음 Stage4는 아래 [그림 8]과 같이 Local MAC address의 앞 3자리를 특정 MAC address 앞 3자리

(00-0C-29-??-??-??)와 비교하는데 이 값만 올리디버거 상에서 똑같이 맞춰 주고 진행하면 Stage4도 클리어 할 

수 있다. 그리고 Stage5로 넘어가기 전에 이전에 [그림 5]에서 설명한 안티디버깅 루틴이 또 등장하는데 이것도 

아까와 똑같이 우회해주면 된다.


[그림 8] Stage4 풀이


 그 다음 Stage5는 이전의 Stage1과 아주 비슷하게 아래 [그림 9]와 같이 xor_answer와 실제 사용자 입력 값과

36742와 xor한 값을 strncmp함수 기능을 하는 sub_405c20()함수로 5바이트 비교하기 때문에 "hello"라는 입력 값을

집어넣으면 stage5를 통과하게 된다.


[그림 9] Stage5 풀이


 이제 모든 스테이지를 다 클리어 했고 마지막 분석만 남았는데 아래 [그림 10]과 같이 지금까지 클리어 했던 

Stage들의 정답들을 특정 메모리에다가 strncat함수 역할을 하는 sub_40AD50()함수를 이용하여 쓴다.


[그림 10] 각 Stage들의 정답 이어 붙이기


 이렇게 이어 붙인 정답은 [그림 3]에서 봤던 이상한 값들과 아래 [그림 11]에서 보듯이 xor연산을 하여 새로운 

메모리 영역에 값을 쓴 후 fopen()역할을 하는 sub_404620함수와 fwrite()역할을 하는 sub_405ED0함수를 

호출하여 c라는 png파일을 생성한다.


[그림 11] stage 최종 키 값과 xor연산 => c(png 파일) 생성


 이렇게 나온 c파일을 c.png로 바꾸고 열면 아래 [그림 12]와 같이 flag를 확인할 수 있다.



[Get Flag~~!!!!]

[그림 12] flag 확인("ThANk_yOu_my_PeOP1E")


끝~!







Posted by holinder4S