이번 분석 자료는 unsafe_unlink 자료이다.

그 전까지 하던 자료와는 다르게 이해하기 매우 어려웠다.
heap영역의 chunk 구조를 정확하게 이해하고 있지 않았기 때문에
이해하기 어려웠었다.

이번 자료를 이해한다면 heap 영역의 chunk구조를 파악할 수 있다.

unlink의 취약점을 보여주는 자료다. 예전에 unlink 동작을 구현한 함수가 있는 워게임 문제를 푼 경험이 있는데, 이번 자료를 분석하면서 unlink 취약점을 위해 unlink 검증과정을 우회하는 경우도 확인 할 수 있었다.

목적
unlink취약점을 이용해 원하는 메모리 영역에 원하는 값을 적을 수 있다는 것.!

조건
1. fastbin 크기가 아닌 chunk 할당필요
2. global 변수 처럼 직접적으로 그 chunk에 접근할 수 있는 변수의 주소를 알고 있어야한다.
3. free 함수를 호출할 수 있어야한다.

먼저 글로벌 변수 chunk0_ptr을 선언해준다.



(글로벌 변수)


이제 이 포인터를 우리가 원하는 메모리 주소로 옮기고, 이 포인터를 이용하여 값을 적는것이 목표이다.

malloc_size = 0x80



(malloc)



2개의 heap chunk를 할당한다. 크기는 0x80으로, 실제 chunk 사이즈는(헤더포함) 0x90이 된다.

그 후 가짜 chunk를 첫번째 chunk안에서 만들어준다.
이 부분에서 이해하기 힘들었다.
우리가 가짜 chunk를 만드는 이유를 이해하고 넘어가야한다.

우리는 unlink 취약점을 이용할 것이다.
unlink 매크로를 호출할 수 있어야한다. unlink 매크로는 free가 일어날 때, 연속적으로 있는 chunk인지 확인 한 후 연속적이라면 인접 chunk와 병합을 하기 위해 호출되는 매크로이다.
그렇다면 unlink를 호출하려면 chunk 병합을 일으켜야한다.
또, unlink 과정에서 취약점을 이용하기 위해, 우리는 fd와 bk를 조작할 수 있어야한다.

우선 가짜 chunk를 첫번째 청크주소+0x10 (chunk0_ptr[0]) 에 만든다. ( 이 자료에서 )

chunk0_ptr[0]부터 chunk라고 생각을 해본다면
구조는
chunk0_ptr[0] -> 이전 청크가 free됬다면, PREV_CHUNK_SIZE
                         -> 사용중이라면, 사용자 data
chunk0_ptr[1] -> chunk_size
chunk0_ptr[2] -> free됬다면 fd ,아니라면 user data
chunk0_ptr[3] -> free됬다면 bk, 아니라면 user data

우리는 free된 것처럼 보이는 가짜 청크를 만들고 있으므로

fd와 bk를 설정해준다.



(fd 설정)



(bk 설정)


* fastbin에 관리되지 않는 사이즈므로 fd와 bk가 존재한다.

그렇다면 fd와 bk를 왜 저 값으로 설정하는 걸까?
바로 unlink 검증을 우회하기 위해서이다.

검증 조건 중 하나는
P->fd->bk와 P->bk->fd의 값이 P와 같은지 확인한다.
다르다면 corrupted double-linked list 에러 메세지를 출력한다.

이 때 넘겨주는 P는 unlink로 넘어가는 주소 인자이다.
unlink로 넘어가기 전 P는 병합 설정이 된 주소이다. 즉, 우리는 unlink 매크로 인자로 가짜 chunk의 주소가 넘어가게 만들 것이므로 우리가 적는 fd와 bk 자리가 P->fd, P->bk이다.
우리는 글로벌 변수 주소를 이용할 것이므로 &chunk0_ptr을 사용한다.
fd는 chunk주소에서 0x10을 더한 값
bk는 chunk주소에서 0x18을 더한 값
그렇기에 fd에는 글로벌 변수 주소 -0x18 값을 넣게 되면
P->fd 이 값이 글로벌 변수 주소 - 0x18이 되게 되고, 
P->fd->bk는 (글로벌 변수 주소 - 0x18) + 0x18 즉 글로벌 변수가 된다.
그런데 글로벌 변수에는 우리가 malloc으로 할당 받은 chunk의 +0x10(헤더) 주소이고 이 주소는 P의 값이다. 그러므로 P = P->fd->bk를 만족시킬 수 있고
같은 방법으로 P = P->bk->fd를 만족시킬 수 있다.

이러한 방법으로 검증 과정 중 하나를 우회할 수 있다.



(fd, bk 설정)


fd와 bk를 설정한 모습이다.

또 검증을 우회해야하는 것이 하나 있다.
바로 size이다.
P의 size와 next_chunk에서의 prev_size가 같아야한다.
(chunksize(P) != prev_size (next_chunk(P))

우리가 만든 가짜 chunk에서 현재 chunk의 size를 가지고 있는 곳은 chunk0_ptr[1] 위치이다.
여기의 값과 next_chunk(현재 chunk주소+size)의 값이 같아야한다.

이 자료의 64비트에서는 아래와 같이 우회했다.




(우회방법)


사이즈를 8로 해두었다.
그렇게 되면 chunk주소+0x8의 위치는 chunk0_ptr[1]로 같은 주소이고
두 값은 같을 수 밖에 없으므로 우회가 가능하다.

모든 우회 준비는 끝났다.
이제 unlink를 작동시키기 위해 두번째 청크의 헤더를 바꾸어야한다.

두번째 청크의 헤더를 바꾸기 위해 포인터로 직접 주소에 접근한다.



(헤더 설정)


처음 chunk1_hdr[0] 이 위치는 prev_chunk 사이즈이고, 우리는 여기서 0x80으로 설정한다.
원래 우리가 할당한 사이즈는 0x80으로 실제 헤더포함한 청크사이즈는 0x90이다.
그런데 0x10보다 작은 크기로 헤더에 알려주었다.
그렇게 되면 병합할 때 이전 청크 주소를 찾기 위해 이 사이즈를 이용하여 계산한다.
현재 청크 주소 + 이전 청크 주소 => 이 값을 unlink 매크로의 주소 인자P로 전달하는 것이다.
그렇기에 우리는 첫번째 청크보다 0x10 큰 위치부터 가짜 청크를 만들었기에 사이즈를 0x80이라고 알려주는 것이다. 
두번째 청크 주소 + 0x80의 위치가 우리의 가짜 청크 위치 주소이기 때문이다.

그리고 PREV_INUSE 비트를 0으로 만들어줘야 이전 청크가 free됬다고 인식이 되기 때문에 비트 마스킹을 해준다.

이제 끝났다.!

free를 한다!



(free)


이 과정에서 두번재 청크를 free하는데 우리가 속인 정보를 보고 인접한 청크(가짜 청크)랑 병합을 하고 unlink 과정을 진행한다.

free전 chunk0_ptr에 있는 값이다.



(free 전)



free 후를 비교해보면




(free 후)



free 후 chunk0_ptr의 값이 바뀐 것을 확인 할 수 있다.

이유는 unlink 과정에서
FD->bk = BK;
BK->fd = FD;
의 과정이 존재하는데 여기서 FD->bk와 BK->fd는 우리의 글로벌 변수 주소인 chunk0_ptr이다.

그렇기에 chunk0_ptr에 우리가 아까 fd로 설정한 주소가 들어가 있는 것이다.

아까 fd가 글로벌 변수에서 +0x18 한 위치의 값으로 설정했으므로

글로벌변수[3] 으로 직접 이 값을 바꿀 수 있다.



(공격)



만약 우리가 victim_string의 주소에 어떠한 값을 적고 싶다고 가정을 해보자.

그렇게 되면 이제 글로벌변수[3]에 해당 victim_string의 주소를 넣어주면



(공격)



위의 상황처럼 된다.
이제 글로벌 변수를 통해 해당 주소 위치에 접근 할 수 있고,

값을 직접 쓸 수 있다.



(메모리 작성)




(공격 모습)

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
(how2heap) - fastbin_dup  (0) 2018.01.04
(how2heap) - first_fit  (0) 2017.12.14

+ Recent posts