## 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(0, len(s2)): if left == 0: left = 6 else: value1 = b64s.index(s2[i - 1]) & (2 ** left - 1) value2 = b64s.index(s2[i]) >> (left - 2) value = (value1 << (8 - 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) p = 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) p = 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 확인
끝~!
'CTF writeup' 카테고리의 다른 글
[ASIS CTF_2017] mrs. hudson(pwnable) (0) | 2017.09.13 |
---|---|
[Tokyo Westerns CTF_2017] swap(pwnable) (0) | 2017.09.13 |
[Samsung CTF_2017] Buildingblocks(Coding) (0) | 2017.07.23 |
[Samsung CTF_2017] dfa(Defense) (0) | 2017.07.23 |
[Samsung CTF_2017] Readflag(Attack) (0) | 2017.07.23 |