2017. 7. 23. 19:08

## Samsung CTF_2017(Easyhaskell, rev)


[Summary]

1. Custom Base 64를 이용한 문제이다.

2. 파일의 이름인 argv[0]을 input으로 Custom Base64 인코딩한 값을 출력하는 프로그램이다.

3. 2가지 풀이법이 존재한다.

=> 파일명 브루트포싱

=> Base 64 테이블을 알아내어 바로 디코딩(하지만 이것도 브루트포싱하였다.)


[Analysis] 


[그림 1] Easyhaskell 문제

 이 문제는 간단하게 요약하면 argv[0]을 인자 값으로 커스텀 Base64를 해 결과 값으로 출력하는 프로그램이다.

이 문제를 처음에는 IDA로 열어서 분석하려고 했으나 haskell이라 그런지 조금 분석하기 모호했다. 그래서 우선

처음 IDA로 분석했을 때는 이 프로그램은 프로그램 인자 값을 받아서 무언가를 한다는 것 정도만 확인하였다.


 이제 이 프로그램을 알기 위해서 실행을 해보았는데 인자 값(argv[1])을 주고 프로그램을 실행하든 안주고 실행하든

아래 [그림 2]와 같이 똑같은 결과를 리턴했다.



[그림 2] argv[1]의 영향


 뭔가 이상해서 argv[0], 즉 파일 이름을 변경하고 실행해보았더니 아래 [그림 3]과 같이 프로그램의 결과 값이 

다른 것을 확인할 수 잇었다.



[그림 3] argv[0](파일이름)의 영향


 위 [그림 3]의 결과를 보고 딱 base64가 떠올랐다. 그래서 base64 인코딩 형태가 맞는지 아래 [그림 4]와 같이 

몇 가지 실험을 했다. 먼저 "1"의 결과가 "CH55"가 나왔고, "11"의 결과는 "C)Q5", "111"의 결과는 "C)Q]", "1111"의

결과는 "C)Q]CH55"가 나왔다.



[그림 4] Base64의 특성을 확인하는 실험


 우선 위 [그림 14]의 결과를 보면 패딩 값이 "5"이고 3글자를 주기로 해시 값에 4글자씩 Output으로 나오는 

것을 확인할 수 있다. base64의 원리에 대해 간단하게 그림으로 설명한 블로그가 있다. 해당 블로그를 확인하고

따라오면 된다. 말로 설명하면 3글자 각 8비트 씩을 쭉 나열하여 6비트씩 쪼개어 4글자로 만드는데 각 6비트의 

값은 base64테이블의 index를 가리킨다. 이 때 base64의 테이블이 통상적으로 사용하는 것이 흔히 말하는 

base64 이고, 이 테이블을 사용자가 원하는대로 바꾼 것이 custom base64이다.


 참고 블로그 주소 : http://bbolmin.tistory.com/46


 이제 이 문제가 Custom Base64 라는 것을 눈치챘고 테이블을 알아내야 하는데 테이블의 경우 바이너리에 

하드코딩되어 있을 수도 있고 어떠한 알고리즘으로 만들어낼 수도 있지만 그렇게 문제를 해결하지 않았다.


 이 테이블을 직접 000000~111111 까지 64개의 테이블을 직접 만들었는데 만든 방식은 아래와 같다.


1) 앞의 6 * 3 비트는 아무 값으로 채운다. (Plain Text가 printable 하도록) => 마지막 비트는 1로 

   세팅해서 2번의 결과ㅏ가 printable 한 문자가 나오도록 조작한다.


2) 마지막 6bit 를 000000 부터 111111 까지 만들어 프로그램을 실행한 후 마지막 바이트를 확인후

   테이블에 추가한다.


3) 2번의 방법에서 printable 하지 않은 경우 3번째 6비트 값을 조작하여 2번과 비슷한 방법으로 

   알아낸다.


 위 과정을 통해 테이블을 구해보면 아래 [그림 5]와 같은 테이블을 얻을 수 있다.



[그림 5] 테이블 구하기


  이제 이렇게 구해진 테이블을 이용하여 base64 디코딩하는 루틴을 파이썬으로 작성해서 돌리면 문제에서

주어진 Flag - "=ze=/<fQCGSNVzfDnlk$&?N3oxQp)K/CVzpznK?NeYPx0sz5"가 아래 [그림 6]과

같이 복호화되어 나타나게 된다.


[Exploit Code] - easyhaskell_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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
from pwn import *
import os
#import hexdump
 
context(arch='amd64',os='linux')
local=True
#local=False
 
###################################################################################
 
def frombase64(s):
    #b64s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    b64s = "|yt2QGYA;u_RCeD0H/c)=NWVo&6nPk9$~dOKa?:<w81T!f3ip]Bxzl@sJjMrXS%#" 
    b64p = "5"
    ret = ""
    s2 = s.replace(b64p, "")
    left = 0
    for i in range(0len(s2)):
        if left == 0:
                left = 6
            else:
                    value1 = b64s.index(s2[i - 1]) & (** left - 1)
                    value2 = b64s.index(s2[i]) >> (left - 2)
                    value = (value1 << (- left)) | value2
                    ret += chr(value)
                    left -= 2
        return ret
 
################################################################################### 
bf = "abcdefghijklmnopqrstuvwxyzABCEDFGHIJKLMNOPQRSTUVWXYZ1234567890"
 
exec_file_path = "/home/holinder4s/Desktop/SCTF_2017/Reversing/EasyHaskell/"
 
result = []
for i in xrange(64):
    result.append('')
 
for i in xrange(64):
    offset = i | 0x40
    if offset > 0x20 and offset < 0x7f and offset != 0x60 and offset != 0x7c:
        target = "AA"+chr(offset)
        print target
    
        os.system("cp "+exec_file_path+"easyhaskell "+exec_file_path+target)
        p = process(exec_file_path+target)
        data = p.recv(1024)
        print data
        #data[-2:-1]
        print data[-3:-2]
        result[i] = data[-3:-2]
            
        os.system("rm "+exec_file_path+target) 
        p.close()
    elif offset == 0x7f:
        offset = i
        target = "AA?"
        os.system("cp "+exec_file_path+"easyhaskell "+exec_file_path+target)
        p = process(exec_file_path+target)
        data = p.recv(1024)
        print data
        result[0x3f= data[-3:-2]
 
        os.system("rm "+exec_file_path+target)
                p.close()
 
target = "Ah0"
os.system("cp "+exec_file_path+"easyhaskell "+exec_file_path+target)
= process(exec_file_path+target)
data = p.recv(1024)
print data
result[0x20= data[-4:-3]
os.system("rm "+exec_file_path+target)
p.close()
 
target = "A?0"
os.system("cp "+exec_file_path+"easyhaskell "+exec_file_path+target)
= process(exec_file_path+target)
data = p.recv(1024)
print data
result[0x3c= data[-4:-3]        
os.system("rm "+exec_file_path+target)
p.close()
 
 
print "[*] table generated~~!!"
print "   - \""+"".join(result)+"\""
 
print frombase64("=ze=/<fQCGSNVzfDnlk$&?N3oxQp)K/CVzpznK?NeYPx0sz5")
 
# ref1) http://bbolmin.tistory.com/46
# ref2) https://blog.affien.com/archives/2004/12/27/base64-encodingdecoding-algorithm/
 
cs



[Get Flag~~!!!!]


[그림 6] flag 확인




끝~!








Posted by holinder4S