Windows Programing 중 가장 기초인 창만들어서 띄우는 것을 할 것이다.
창 이름은 Hellow World로 만들것이다.

먼저, 함수 선언과 전역변수, 상수 선언이다.



(선언)



윈도우 프로그래밍에서는 보통 windows.h 헤더를 include한다.
우리는 MFC를 사용하지 않을 것이므로, WIN32_LEAN_AND_MEAN을 define해준다.

윈도우 프로그래밍에서의 시작점은 WinMain 함수이다.



(WinMain)



기존 C코딩에서 main 함수와 같다.
보면 우리는 CreateMainWindow 함수를 만들어 두어 이 함수로 창을 만들것이고
그 아래에는 메인 메시지 루프가 있다. 메시지 루프는 윈도우 프로그래밍에서 중요한 부분이다.
윈도우 프로그램은 윈도우와의 메시지를 주고 받으면서 동작을 한다. 그렇기에 오고 가는 윈도우 메시지를 확인하는 부분이라고 생각하면 된다.

그렇다면 CreateMainWindow 함수를 보자.
윈도우를 표시하기 전에 Window 클래스를 만들고 등록해야하는데 그 작업을 하는 함수이다.

윈도우의 초기 설정을 하는 클래스가 바로 WNDCLASSEX 구조체이고 이 안의 멤버값들을 채워넣어 윈도우를 설정한다.



(WNDCLASSEX 설정)



설정이 끝나면 클래스를 등록하는 부분이 이어진다.




(클래스 등록)



설정한 wcx 구조체를 넣어주어 클래스를 등록한다.

그 후 창을 생성하는 CreateWindow 함수가 나온다. (각 인자에 대한 설명은 따로 포스팅할 것이다.)



(창 생성)



창을 생성하고 ShowWindow 함수를 이용하여 윈도우를 표시하고
UpdateWindow를 통해 윈도우 프로시저에게 자기 자신을 그리라고 메시지를 보낸다.

* WinProc 함수
중요한 함수이다.
위에서 윈도우 프로그램은 윈도우와 메시지를 주고 받으면서 동작한다고 했다. 그 메시지를 처리하는데 사용되는 함수이다.
위에서 설정한 WNDCLASSEX 구조체 안에 명시된 이름과 동일해야한다. 여기서는 WinProc

무시하는 메시지들은 윈도우가 알아서 처리한다. 여기서는 윈도우가 종료될 때의 메시지만 작성할 것이다.



(WinProc)



메시지가 WM_DESTORY라면 (윈도우가 종료되는 메시지)
PostQuitMessage(0)을 통해 프로그램이 종료를 요청한다고 윈도우에게 신호를 보내는 것이다.
그러면 메시지큐에 WM_QUIT이 추가되고 WinMain에서 이 메시지를 보게 되면 루프를 빠져나가 프로그램이 종료되게 되는 것이다.

컴파일 해서 프로그램을 실행해본다.



(윈도우 창 생성)



우리가 원하는 Hello World 창이 생성되었다.

(*참조 - 2D 게임 프로그래밍, 찰스 켈리)

이번에 분석한 자료는 house_of_spirit이다.

house_of_spirit 공격의 목적은 malloc의 return 결과를 우리가 원하는 청크의 주소로 만드는 것이다.
즉, freelist에 우리가 원하는 위치의 주소가 들어갈 수 있게 하는 것이다.

이 공격을 사용할 수 있는 조건은
우리가 메모리 영역에 가짜 청크를 만들 수 있어야한다는 것이다.
가짜 청크를 2개 만들어주는데 연속적으로 있어야한다.
또, 그 가짜 청크 주소를 free 함수에 넘겨 줄 수 있어야한다.
* free하기 전 malloc 1번은 꼭 필요하다.

먼저 malloc을 해준다.



(malloc)


 이 이유에 대해서 이해가 잘 가지 않았었다.
하지만 주석처리하고 실행해보고 이해할 수 있었다. 뒤에 우리가 가짜 청크를 만들고 그 주소를 free 시킬 것인데, malloc 할당을 한번도 하지 않고 free를 했기 때문에 에러가 난다.
이것을 막기 위해 malloc을 한번 해준다. 1로 해준 것은 그 값이 어떤 값이어도 상관없고 다만, 이 자료에서는 fastbin의 freelist를 이용할 것이기 때문에 작은 값으로 설정한 것이다.

이제 free의 인자로 넘길 포인터 변수를 만들어준다.



(포인터 변수 생성)


a 변수에 우리가 만든 가짜 청크의 주소를 쓸것이다.

이제 가짜 청크의 영역이다. 이 자료에서는 stack영역에 가짜 청크를 만들어주었다.

stack영역에는 비교적 우리가 값을 적기 쉽기 때문에 stack영역에 가짜 청크를 만들어 준 것 같다.



(가짜 청크를 위한 메모리 공간 확보)


위에 __attribute__는 컴파일러에게 특정한 방향으로 컴파일을 해달라고 부탁하는 명령이다.
aligned 16에 의해 16바이트로 정렬되게 컴파일 시킨다. 
64비트에서 unsigned long long은 8바이트이고 16바이트보다 작기 때문에 저 명령을 안써도 상관 없었다. (자료에서는 적혀있었다. 앞으로 64비트 이상의 컴퓨터가 나오게 되면 저 명령어가 필요할 수도 있을 것이다.)

그 다음으로 가짜 청크를 만들어주는 작업이다.



(가짜 청크 생성)


위 설명을 요약하자면
이 자료에서는 fastbin을 쓸것이기 대문에 PREV_INUSE는 무시해도 되지만, 다른 flag비트는 문제가 된다는 말로 모든 flag를 0으로 세팅하였다.
그리고 malloc 구현에 있어서 내부 internal size로 반올림 되기 때문에 0x30~0x38크기의 malloc 결과의 청크 사이즈는 0x40이 될것이기 때문에 우리는 0x40으로 만들어 줄 것이고
다음에 우리가 malloc할 때 0x30으로 할당 받을 것이다라는 이야기이다.

이제 다음 청크를 만들어 줘야한다.

free시 청크 뒤에 있는 청크의 사이즈를 검사한다. 그렇기에 정상적인 사이즈를 넣어야한다.



(검사 조건)


검사 조건은 이렇다.

SIZE_SZ란



(SIZE_SZ)


INTERNAL_SIZE_T의 크기가 SIZE_SZ 이고,
INTERNAL_SIZE_T는 size_t이다.
64비트에서는 8바이트 이므로 16바이트(0x10)보다 커야한다는 것이다.
당연한 것이, 청크의 헤더가 0x10이기 때문에 이거보다는 커야한다.

그 다음 조건은

av->system_mem 보다 작아야한다는 것




(system_mem)


av란 mstate로 정의 되어있고, mstate는 malloc_state 구조체이다.

이 구조체에 있는 system_mem은 할당된 메모리라고 나온다.
이 아레나가 할당된 크기를 말한다. 즉, 당연히 이 크기보다는 작아야한다는 것이다.

이런 아주 지극히 정상적인 크기의 값만 넣어주면 어떤 값이든 상관없다.

이 자료에서는 0x1234라는 값을 넣어주었다.



(두번째 청크 크기 설정)


여기서 배열 인덱스 9번에 적어주었는데, 그 이유는
우리가 첫번째 잡은 청크가 0x40의 크기였고 unsinged long long 크기가 8바이트 이기 때문에 총 배열 8개를 잡아먹고 그 다음 위치가 9이기 때문이다.

이제 우리가 만든 스택영역에 가짜 청크 두개를 만들었다.

이제 이 주소를 free 시키기 위해 포인터 변수에 이 주소를 먼저 넣어야한다.



(주소 입력)


인덱스가 2인 이유는 처음 0, 1은 헤더이다. free시킬 때는 user data영역의 주소를 가지고 free시키기 때문이다.

그 후 free 해준다.



(free)


그렇게 되면 지금 fastbin의 0x40의 리스트에 우리가 만든 가짜 청크의 주소 a의 주소가 들어있다.

이제 다시 malloc(0x30)을 하면 fastbin리스트에서 0x40의 주소를 찾아가 현재 있는 a의 주소를 return하게 되는 것이다.



(malloc)


공격 결과를 확인해보면 다음과 같다.



(공격 성공)

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

(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
(how2heap) - fastbin_dup  (0) 2018.01.04
이번 분석 자료는 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