티스토리 뷰

Pwnable/Heap

[Protostar] Heap3

D41JUNG0D 2018. 3. 15. 23:20

1. Introduce

heap을 얼마나 이해하고 있는지를 알기위해서 protostar의 heap시리즈의

          write-up을 작성해보겠습니다. 제가 이해하기 위해서 쓰는 write-up이어서

설명이 길 수 있습니다.


2. Analysis


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

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include <sys/types.h>

#include <stdio.h>

void winner()

{

 printf("that wasn't too bad now, was it? @ %d\n", time(NULL));

}

int main(int argc, char **argv)

{

 char *a, *b, *c;

 a = malloc(32);

 b = malloc(32);

 c = malloc(32);

 strcpy(a, argv[1]);

 strcpy(b, argv[2]);

 strcpy(c, argv[3]);

 free(c);

 free(b);

 free(a);

 printf("dynamite failed?\n");

}

Colored by Color Scripter

cs


순서대로 a, b, c 에 32만큼 malloc을 해주고 역순서로 free를 해줍니다.



힙 구조는 위와같이 [C-B-A] 가 될 것입니다.



그리고 역순서로 free를 해줬으니 이런식으로 구조가 생길 것입니다.


구조가 이렇게 생겼다만 알고 바로 free 함수를 수행하면서 unlink 매크로를 설명하겠습니다.


1

2

3

4

5

6

#define unlink( P, BK, FD ) {

   BK = P->bk;

   FD = P->fd;

   FD->bk = BK;

   BK->fd = FD;

}

cs


이름만 들어도 무언가를 해제시키는 매크로 같죠? 맞습니다!


unlink 매크로를 그림으로 표현해 보겠습니다.



이런식으로 각 chunk들이 이중 연결 리스트로 연결되어 있을 것입니다.


그리고 P chunk를 free시킨다고 생각해 봅시다.


각 chunk들이 fd와 bk로 연결되어 있으니 그냥 막 free를 시킬 수는 없겠죠?


그래서 이렇게 P chunk의 다음 chunk와 이전 chunk의 fd와 bk를 이어주고 P chunk를 free합니다.


이 과정이 바로 unlink 매크로의 과정입니다!


unlink의 과정을 알았으니 어떤 경우에 unlink 매크로가 쓰이는지 알아보겠습니다.


1 . 현재 chunk의 이전  chunk나 다음 chunk가 free, 즉 사용중이지 않을 때 chunk를 합병할 때.


2. 중간에 있는 chunk를 free할 때.


저는 첫번째 경우의 unlink를 이용해서 exploit 해보겠습니다.


첫번째 경우에 chunk가 free되어있는지 아닌지는 size 필드의 PREV_INUSE 플래그를 이용해서 확인 한다고 했습니다. 이 부분은 제 블로그 포스팅을 보시면 자세히 설명되어 있습니다.


그렇다면 heap over flow를 이용해서 PREV_INUSE 플래그를 조작해주면 되겠지요?


그리고 unlink 매크로는 위와 같은 과정을 아래와 같은 식으로 변경할 수 있습니다.


FD = *P+8

BK = *P+12

FD + 12 = BK

BK + 8 = FD


FD+12 위치에는 BK가 들어가고 BK+8 위치에는 FD가 들어갑니다. 이를 이용해서 함수의 got를 바꾸거나, 소멸자를 바꾸면 프로그램의 흐름을 원하는대로 바꿀 수 있습니다.


3. Exploit



winner 함수를 실행시키는 것을 목표로 해야하니 주소를 구해줍시다.



그리고 puts_got를 조작할 것이니 got도 구해줍니다.


unlink 매크로를 이용해서 puts_got를 winner함수로 바꾸는 시나리오로 exploit 해보겠습니다.


unlink 매크로가 FD+12 = BK , BK+8 = FD 이런 식으로 계산을 하므로, puts_got-12 한 값을 FD에 넣어주면, puts_got-12 + 12 = BK 연산을 통해 puts_got = BK 가 들어가게 될 것입니다.


그리고 puts를 실행할 때 winner함수가 실행되겠지요.



payload에서 A를 32byte만큼 넣어주고 “\xfc\xff\xff\xff”를 넣어주는 것을 볼 수 있습니다. 이 부분은 다음 chunk의 prev_size와 size 필드인데요.


heap에서 chunk의 prev_size와 size 필드는 매우 중요한 부분인데요.


왜냐하면 이전 chunk의 주소와 다음 chunk의 주소를 prev_size와 size 필드를 이용해 계산하기 때문입니다.


예를 들자면, 현재 heap 구조가 [C - B - A] 라고 가정하고 free를 C, B, A 순서로 한다고 하면 free(c) 이후에 free(b)를 할 때 A chunk가 free 되었는지를 A chunk의 size필드의 플래그를 이용해서 확인하기 때문에 A chunk의 주소를 구해야합니다.

이전 chunk와 다음 chunk의 주소를 구하는 공식은 다음과 같습니다.


A chunk 의 주소 =  &B - prev_size

C chunk의 주소 = &B + size


이렇게 계산을 하므로 prev_size의 값을 -4로 바꾸어 주게되면 0xfffffffc로 마지막 플래그들의 값이 모두 0으로 바뀌고, &B-(-4) 로 &B+4를 A chunk로 인식하게 될 것이고, &B+4는 데이터 필드이므로 FD, BK를 변조할 수 있게됩니다.


그래서 puts-got -12 값을 FD에 넣어주고, winner 함수를 BK에 넣어주었는데, 익스가 성공적으로 되지 않았습니다.


왜인지 살펴보았더니, winner+8 주소는 write가 불가능한 code영역에 BK를 넣으려고해서 Segmentation fault 오류가 떴었던 것입니다.



그래서 스택영역을 이용하기로 했습니다. puts_got를 우리가 제어할 수 있는 A chunk의 데이터필드 주소로 조작해주고, A chunk 데이터필드에는 winner 함수를 실행하는 코드를 넣어줍니다.


“\x68\x64\x88\x04\x08\xc3”


\x68 = PUSH

\xc3 = RET


winner 함수의 주소를 스택에 PUSH해주고 RET을 실행하게 되면 winner 함수가 실행되게 됩니다.



또 다른 방법으로는 jmp code를 이용하는 방법입니다.


BK + 8 = FD


때문에 BK+8 ~ BK + 12에는 FD의 값이 들어가 있어서 실행하다가 흐름이 끊길 수도 있어서 \xeb(jmp)를 이용해서 \x0c 만큼 건너뛰게 되면 \x90을 타고 winner함수를 실행하게 될 것 입니다.


질문 있으시면 댓글 달아주세요. 저에게도 도움이 됩니다 ㅎ

'Pwnable > Heap' 카테고리의 다른 글

[Protostar] Heap2  (0) 2018.03.11
[Protostar] Heap1  (0) 2018.03.11
[Protostar] Heap0  (0) 2018.03.11
Heap 이해하기  (0) 2018.03.11
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함