Post

(UNDEAD) The House of Mind

The House of Mind

http://phrack.org/issues/66/10.html

https://gbmaster.wordpress.com/2015/06/15/x86-exploitation-101-house-of-mind-undead-and-loving-it/

unlink와 반대로, chunk가 free되면서 bins와 link하는 과정에서 발생하는 쓰기를 이용하는 방식이다.

[free] unsorted bin link

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bck = unsorted\_chunks(av); // == &av->bins[0]
fwd = bck->fd;
if (\_\_glibc\_unlikely (fwd->bk != bck))    // DEAD
{
errstr = "free(): corrupted unsorted chunks";
goto errout;
}
p->fd = fwd;
p->bk = bck;
if (!in\_smallbin\_range(size))
{
p->fd\_nextsize = NULL;
p->bk\_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;
fwd->bk = p

\*(&bins[0] + 8)->bk = p , \*(\*(&bins[0] + 8) + 12 ) = p 이므로 \*(&bins[0] + 8) + 12번지에 ` p`에 있는 값이 대입

따라서,&bins[0] + 8

번지에 Execution point addr - 12 주소를 넣는다.

\*(&bins[0] + 8) = Execution point addr - 12가 되므로, Execution point addr번지에 p를 대입하게 된다.

원래 House of Mind는 이 포인터를 이용하는데, 지금은 patch되어 사용할 수 없다.

bck->fd = p

\*(&bins[0] + 8) = p 이므로, &bins[0] + 8번지에 ` p에 있는 값이 대입 ( type(p) == mchunkptr 이므로 chunk의 주소가 대입된다.) \* &bins[0] +8 == &bins[2] 이기는 하다만 arena에도 chunk가 있는 것 처럼 간주하고 동작하기 때문에 이게 맞다. 아래 그림은 조금 잘못그렸다. 이 경우 다음과 같이 .dtors의 위치에 &bins[0] + 8가 위치하도록 해야 하므로 arena를 아예 .dtors` 주변에 잡아버려야한다.

| | | | — | — | | &bins[0] + 8 && Execution point | &p | 그래서 조건을 충족시키기가 꽤나 어렵다.

[UNDEAD] fastbin method 가 이와 동일한 방법으로 동작한다.

exploit
  1. overflow를 이용하는 방식으로는 main_arena bins[0]에 직접 값을 쓸 수는 없다. ⇒ fake bins[0] ( fake arena )가 필요하다.
  2. free(chunk) 시 main_arena가 아니라 fake arena를 사용하도록 해야 한다. ⇒ fake heap_info가 필요하다.
  3. free(chunk) 시 fake arena와 연결된 fake heap_info를 사용하도록 해야 한다. ⇒ chunk가 main_arena heap range 밖에 있어야 하며, NON_MAIN_ARENA가 set 되어야 한다.

Note) 그림을 조금 잘못 그렸는데, bins[0]에서 나가는 노란색 화살표가 가리키는 chunk는 실제로는 없고, arena 내부에 있는 것으로 간주한다. 따라서 bins[2] == bins[0]'s fd다. * consolidate를 막기 위해 PPREV_INUSE flag도 있어야 한다. * P를 비롯한 chunk들은 모두 heap에 정상적으로 할당된 chunk들 이며, P 이후에는 top chunk가 위치하기 때문에 free’s next size check는 따로 신경쓰지 않아도 된다. * 8 bytes A..A는 위쪽 chunk를 free 하면서 fd/bk가 채워지는 자리이므로, 이 부분 이후에 arena가 시작된다. 사실 위쪽 chunk를 굳이 free할 필요는 없는 듯. * 이후 위치하는 8 bytes도 free 하면서 0으로 초기화되므로 뒤 쪽 4 bytes 0을 mutex로 이용한다.

1
2
3
4
5
free(p+8);    // free(0x081002a0)
-> 0x08100298              : p's value
-> 0x08100000              : fake heap\_info AND ar\_ptr
-> 0x0804a010              : fake arena
-> 0x0804a010 + x + 8      : &ar->bins[0]

p가 가리키는 곳은 chunk의 prev_size(0x08100298)이므로 여기에 shellcode를 넘기면 되는데 그 아래 field가 망가지면 안된다.

따라서 p 가 가리키는 곳에는 jmp instruction을 넣고, field 아래에 shellcode를 넣어 그리로 jmp할 수 있도록 구성한다.

DEAD

check를 통과하기 위해서는 \*(&bins[0] + 8) + 12번지에 있는 값이, c &bins[0] 이어야만 한다. &bins[0] + 8번지의 값이 가리키는 B chunk의 ` bk의 값이 &bins[0]`이어야 한다.()

최종적으로 P가 대입되는 곳(B chunk의 bk)의 값이 &bins[0]인 경우에만 사용할 수 있기 때문에, 사실상 사용할 수 없다. * 최종적으로 P가 대입되는 곳을 &bins[0]으로 만들어 주는 것은, 이미 target의 값을 조작할 수 있다는 것을 의미하므로 모순이다.

[UNDEAD] fastbin method

https://github.com/umbum/pwn/blob/master/how2heap/mind.c

https://github.com/umbum/pwn/blob/master/how2heap/mind_exploit.c

1
2
p->fd = \*fb;
\*fb = p;

* 소스가 조금 변경되어 \*fp = p 부분을 매크로에서 처리하는 것 같지만, 별다른 check가 새로 생긴 것 같지는 않아 아직 동작한다. (2.24)

fwd->bk = p를 활용하는 House of Mind 방법과는 꽤나 다른 것이, 포인터를 한 번 덜 거치기 때문에 아예 arena를 Execution point를 포함하는 영역으로 잡아버려야 한다.

* fastbin 처리 부분은 \_int_free()에서 두 가지 기본적인 check 이후 바로 등장하기 때문에 fastbin size라면 바로 fastbin 처리 부분을 수행하게 된다. * ar\_ptr = arena\_for\_chunk(p);\_int_free() 이전에 이미 구해져서 넘어오기 때문에 fastbin도 다른 arena를 참조하도록 하는 것이 가능하다.

사실상 arena가 다음과 같이 구성되어 있을 때 fastbins[idx]에 값을 쓰게 되는 건데, mutex == 00000000이어야만 하며, system_mem도 너무 작으면 안된다. (원래는 max_fast도 신경 써주어야 하지만 버전이 올라가면서 flag로 변경되었다.)

   
mutex  
flag max_fast ( old version )
fastbinsY[0]  
  
system_mem ( av+1848 )  

따라서 Execution point 근처에서 두 가지 정도만 만족한다면 사용할 수 있다.

그러나 문제는 이 방법을 이용해 Execution point에 대입할 수 있는 값은 heap addr인 p(chunk’s addr) 뿐인데 , heap에 X 권한이 없다. ( 원하는 값을 아무거나 쓸 수는 없다. “arena에 p 만” 쓸 수 있다. / NX가 해제되어 있어도 보통 stack에만 X권한 있고 heap에는 없다. ) 따라서 Execution point의 값이 p로 변경된다고 해도 p위치에 있는(mchunkptr) instruction을 실행할 수 없다는게 문제.

그래서 instruction까지 가기 위해 포인터를 한 번 더 거치는 Execution point에만 사용할 수 있다. GOT에는 &func가 들어가기 때문에, p위치에 바로 instruction이 있어야 해서 사용 불가. func@plt + 2에 대입하는 방법도 생각해 볼 수 있겠지만, 여기는 쓰기 권한이 없다.

따라서 double function pointer 정도로 사용처가 제한 되기 때문에, 상당히 적용하기가 그렇다.

This post is licensed under CC BY 4.0 by the author.