overlapping chunk 두번째 자료이다.

이 자료 또한 어떠한 문서를 참조했다고 나와있었다.



(참조 문서)


그렇기에 나 또한 이 참조 문서 먼저 공부하였다.
이 문서에는 Heap overlapping에 관해서 3가지 정도의 방법을 제시했다.
Heap Overlapping에 관해 조금씩 다른 방법들을 제시했는데,
다들 비슷비슷한 느낌이었다. 그래도 많은 방법들을 봐두는 것이 좋은 것 같다.

지금까지 Heap Overlapping에 관련된 공격들을 정리하면서 깨달은 점이 있다.
그건
전부 Chunk 구조체의 SIZE 필드를 조작하므로써 공격을 한다는 것이었다.
PREV_SIZE라 던가, SIZE 필드라던가 chunk의 사이즈를 Allocator에게 솎여서 잘못된 할당과 해제를 유발하므로써 기존에 있던 chunk에 중첩되는 chunk를 만들게 하는 방식이었다.

그렇기에 최신 glibc에서는 PREV_SIZE 체크 라던가 SIZE 체크 부분이 추가되었고
이 부분을 우회하는 것을 최근 how2heap에서 본 기억이 난다.

무튼,

heap overlapping 2번째 분석을 시작하자!



(5개의 힙)


5개의 힙 영역을 할당한다.
malloc 사이즈는 1000으로 16진수로는 3E8이다.

뒷자리가 8인 것을 보면 눈치 채겠지만, off-by-one에 취약할 것이다.



(각 힙영역의 범위)


각 힙영역을 표시하기 위해 해당 영역에 문자들을 넣어주었다.



(문자 입력)


그 후 p4 영역을 free해준다.
왜 굳이 p4일까?
-> p4를 free해주면 top청크와 병합되지 않는다. 이유는 top청크와 인접한 p5가 존재하기 때문에 병합되지 않는다. 그리고 공격의 개략적인 시나리오를 말하자면 p1 청크에서 off-by-one으로 p2의 청크 사이즈를 조작할 것이고, p3의 영역을 포함하고 있는 중첩된 heap영역을 할당받는 것이 우리의 목적이다.

: 그렇기에 p4를 free해준 것이다.



(free)


p4를 free해준 후

p1을 이용해 p2의 청크 사이즈를 조작한다.



(청크 사이즈 조작)


조작한 사이즈는
p2 청크의 사이즈 + p3 청크의 사이즈 + PREV_IN_USE + sizeof(size_t)
조작할 사이즈는 p2와 p3를 합한 청크 사이즈로 만들 것이다.
뒤에 prev_in_use 값은 0x1 로써, 이전 청크가 사용중이라는 것을 알려주기 위해 설정한다. 그렇지 않으면 이전 청크와의 병합도 시도할 것이다.
sizeof 값이 더해지는 이유는 p2, p3의 헤더 크기를 추가해주는 것이다. 




(p2 free)


그 후 p2를 free해준다.
그렇게 되면 다음과 같은 일이 일어난다.
1. allocator는 p2를 free하려고 한다.
2. p2의 PREV_IN_USE 비트를 보니 이전 청크를 사용하는 군!
3. next chunk를 검사한다. (p2 주소 + p2의 사이즈 = p4) = next chunk는 p4
=> 왜 다음 청크가 p3이 아닌지? 방금 우리는 p2의 사이즈를 p2,p3을 합한 덩어리로 조작했다. 그렇기에 p2 주소 + (조작된)p2의 사이즈 = p4의 주소 이고 p4가 next chunk가 된다.
4. p4(next chunk)를 확인해보니, free 되어있네???
5. p4랑 병합해야지~, p2는 결론적으로 p2,p3,p4를 포함한 커다란 free chunk가 되버린다.

그 후 malloc을 통해 p3을 포함할만큼 커다란 영역을 할당받는다면?



(큰 영역 할당)


그렇게 되면 p3영역이 포함된 영역을 할당해준다.



(할당된 영역)


할당 된 영역을 보면 p6 사이에 p3영역이 들어있는 것을 확인 할 수 있다.

현재 p3에 들어있는 데이터



(p3의 데이터)


그 후 p6을 F로 1500 글자 채워준다.



(F로 채우기)


그렇게 되면 500자가 p3영역을 침범하게 되어 p3 데이터 앞부분이 F로 채워지게 된다.



(확인)


확인을 통해 p3가 중첩된 영역을 할당받은 것을 확인 할 수 있다.

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

(how2heap) - overlapping_chunks  (0) 2018.02.22
(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
  오늘 분석한 자료는 overlapping chunks이다.
이 방법은 The Forgotten Chunks 라는 문서에서 가져왔다고 하여

해당 문서를 먼저 읽어보았다.



(참고 문서)


저 페이퍼는 정말 재밌었다.
작명센스도.! 잊혀진 청크..!

전략을 간략히 말하자면
heap overflow를 통하여 청크의 SIZE 필드를 조작한다.
그리하여 malloc 혹은 free를 통하여 중첩된 영역을 얻는다.

* 중첩된 영역을 얻는다 라는 것에 대해서
-> 예를들어 A라는 malloc 영역이 주소 4~8 이라고 하자.
그리고 B에 malloc을 받는데 0~11의 주소로 할당을 받는다고 한다면?
B 주소 영역에 A의 주소 영역이 포함되어있다. 그렇기에 B에 작성하므로 A영역의 데이터를 건들일 수 도 있고, A에 작성하므로써 B영역의 데이터를 건들일 수 도 있는 상황이 된다.

우리는 중첩된 영역을 얻는 것을 목표로 할 것이다.

먼저 3개의 malloc 주소를 할당받는다.


(malloc)


이 때 보면 malloc의 사이즈가 조금 의도된 듯한 느낌이 든다.
바로 -8 을 해준 이유는 사용 가능한 영역까지 꽉 할당받기 위해서이다.

포스트 글 malloc의 사용가능 영역
에 정리해 둔것을 참고하면 좋다.
https://blog.naver.com/rlagkstn1426/221212549437

실제의 상황에 위와같이 할당을 받고 문자열을 입력받는다면
쉽게 다음 chunk의 사이즈 영역을 건들일 수 있다.

그 후 각각의 heap 영역을 표시하기 위해 아래와 같은 작업을 한다.



(영역 표시)


p1 에는 1로 가득채우고 , p2는 2로, p3은 3으로

그리고 p2를 free해준다.



(free)


p2 를 free 해준다.

그렇게 되면 가운데가 구멍이 뚫린 상황이 된다.
p1 - p2(free됨) - p3

실제 같으면 p1에 heap overflow를 이용하여 p2의 사이즈 영역을 덮어썻을 것이지만
테스트 코드이므로 직접 사이즈 영역에 값을 작성할 것이다.

(아직 우리가 해당 주소를 가지고 있으므로 p2에!)



(적어줄 사이즈)


evil_chunk_size는 우리가 가짜로 적을 사이즈이고,
evil_region_size는 우리가 추후에 malloc을 할 때 사용할 값이다.

우리는 지금 free 된 p2의 영역의 SIZE 필드에 0x181이라고 적을 것이다.
맨 마지막 비트가 1인 이유는 이전 청크가 사용중이기 때문에 1이라고 섬세하게 맞춰준다.



(SIZE 조작)


그렇게 되면 p2의 영역은 free된 상황인데,
free 된 청크의 크기가 0x180 이라고 설정되어있는 상황이다.

변화 과정을 메모리에서 살펴보면


(덮어쓰기 전)



(덮어쓴 후)


덮어쓴 후 p2의 메모리 영역에 SIZE를 보면 0x181이라고 덮어쓰여진 것을 확인 할 수 있다.

그렇기에 지금 freelist에 p2가 있을 것이고,
p2의 청크 크기는 0x180으로 설정되어있는 셈이다.

그렇다면 여기서 malloc(0x180)을 한다면?



(malloc(0x180))


그러면 p2의 영역을 주는데 0x180 만큼의 크기를 할당해줄 것이다.
그러면 p3는?
-> 잊혀진 것이다. 알 길이 없다. (The Forgotten Chunks 의 작명센스!)

return 된 주소를 확인해보자.


(return 된 주소)



(return 영역)


return 된 영역을 보면 원래 p2의 시작점인데
사이즈는 0x180만큼으로 그 안에 p3 영역이 있는 것을 볼 수 있다.

덮어쓰여진 것을 한번 테스트해보자!



(4로 덮어쓰기)


p4안에 p3이 들어있다.

그러면 p4의 영역을 4로 가득채우면?



(결과)


p3의 영역 또한 4로 덮어쓰여지게 된다.

그러면 p3을 3으로 다시 덮어쓰면?



(3으로 덮어쓰기)



(결과)


이번엔 p4의 영역 중간에 3으로 덮어쓰여지게 된다.

how2heap의 이 자료에서는 
해제된 free의 SIZE를 조작하고 malloc 하는 방법을 택했다.

반대로
malloc된 청크의 사이즈를 조작한 후, 다시 free하고
malloc 하는 방식으로 똑같은 결과를 만들 수 있다.

이 방법은 The Forgotten Chunks 문서에 나와있다.

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

(how2heap) - overlapping_chunks_2  (0) 2018.02.27
(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
  우리는 malloc을 이용해 heap영역의 메모리 주소를 할당 받는다.
우리가 할당받은 주소는 heap영역의 chunk(덩어리)이다. 실제 덩어리에서 우리가 사용할 수 있는 공간은 어느정도 일까?

간단하게 malloc으로 0x10과 0x18 사이즈로 두개를 할당받는 코드를 만들었다.


(테스트코드)


그리고 malloc_usable_size 함수로 사용가능한 크기를 확인해보았다.



(확인 결과)


확인 결과 두개의 주소 모두 0x18로 나왔다. 어떻게 된것일까?

gdb를 통해 메모리 모습을 확인해보았다.



(메모리 모습)


우리는 분명 0x10과 0x18의 크기로 메모리 할당을 요청했음에도 불구하고 실제로 chunk 사이즈를 보면 0x21로 두개의 chunk가 할당된 것을 볼 수 있다.
여기 나와있는 사이즈는 chunk의 실제 크기이다.

0x10으로 할당을 하면 위의 예시에서 6010 주소가 반환된다. 이 chunk의 헤더는 0x10크기를 가진다.
그렇기에 실제 chunk의 크기는 0x10 큰 사이즈이다. 그렇다면 0x20이 아닌 0x21인 이유는 마지막 3비트는 flag비트로 사용되기 때문이다.

다시 글의 주제로 돌아가서 실제 사용가능한 크기가 전부 0x18이라니.
이 이유는 우리가 0x10을 요청했을 경우 실제로 0x10크기의 영역이 잡힌다. 그 후에 다음 청크가 할당되었는데 이 때 다음 청크의 헤더 앞부분까지 실제로는 사용가능하다. 위 사진에서 빨간색 테두리만큼 실제로 데이터를 쓰기가 가능하다는 것이다.

이해하기 쉽게 정리하면 청크의 헤더는 0x10크기이다.
그 중 0x8 크기는 자신의 chunk 사이즈를 담고있고 그 앞의 0x8바이트는 
PREV_SIZE 영역으로 이전 chunk의 크기를 가지고 있는데, 
-> 이전 chunk가 사용중이라면 이 영역은 이전 chunk가 사용한다.
-> 이전 chunk가 사용중이지 않다면(free됬다면) 이 영역은 이전 chunk의 size를 가지고 있게 된다.
그렇기에 실제로 사용가능한 크기는 다음 chunk의 헤더 앞부분 0x8크기만큼을 더 쓸 수 있다는 이야기이다.

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

(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
(how2heap) - unsafe_unlink  (0) 2018.01.13
(how2heap) - fastbin_dup_into_stack  (0) 2018.01.11
이번에 분석한 자료는 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


오늘 분석한 자료는 이전 포스팅에서 다루었던 fastbin_dup을 이용하여
malloc으로 할당받는 주소를 우리가 원하는 주소로 할당받는 것이다.

이번 자료에서는 그 주소를 stack 영역의 주소로 할당받는 것이 목적이다.

fastbin_dup과 같이, free list가 a -> b -> a 이러한 상황을 만들어야한다.
같은 chunck를 free 2번 시킨 후 하나를 다시 malloc으로 받은 후 그 주소에 접근할 수 있으므로 다음 chunk를 가리키는 포인터를 조작하는 방법이다.

이 기술을 직접 사용하기 위해서는
free 3번(같은 chunk로 2번), malloc으로 double free한 chunk를 받아오거나 double free한 chunk에 접근 할 수 있는 상황과 그 조작한 chunk를 malloc 시킬 수 있는 상황이어야한다.
그렇다면 stack 주소뿐 아니라 다른 주소로도 malloc을 이용해 받을 수 있다.
(단, chunk의 구조체중 size 멤버가 적절해야한다.)

fastbin_dup_into_stack 자료를 분석해보자.

먼저 주소로 받을 위치는 stack_var의 주소이다.



(우리의 목적 주소)


여기서 +8을 해준 주소를 목적이라고 한 것은 heap의 chunk 구조를 보면 앞의 8바이트가 chunk size정보가 들어있는 위치이기 때문이다. 

먼저, fastbin_dup과 같이 3개의 malloc을 해준다.



(3개의 malloc)


fastbin을 이용할 것이기에 8바이트를 할당한다.
최소 16바이트이기 때문에 16바이트의 user data 영역이 생기고 8바이트의 size, 8바이트의 prev_size영역으로 총 32바이트, 총 chunk의 크기는 0x20일 것이다.

첫번째 free 해준다.



(첫번째 free)


그 후 바로 a를 free를 하면 free list 맨 위에 있는 a가 같으면 에러가 나므로

b를 free해준 뒤 a를 free해준다.



(free)


지금 까지의 상황은
free list의 top에는 a 주소가 들어있고,
a는 b를 가리키고 b는 a를 가리키는 a->b->a 의 상황이다.

여기서 malloc을 해주어 a의 주소를 d에 넣고, 다시 malloc을 해준다.




(malloc)


위의 코드까지 진행이 된다면
d의 주소에는 기존 a의 주소가 들어있고, 현재 free list의 top에는 이전 a의 주소(현재 d의 주소)가 들어있을 것이다.

free chunk list에 있는 chunk에 접근할 수 있다.
-> 이전 a의 주소(현재 d의 주소)가 free chunk list에 있고, d로 접근할 수 있기 때문에!

그렇다면 이제 우리가 할당받을 주소는 stack주소이기 때문에
stack 영역에 chunk라고 속일 수 있게 흉내를 내야한다.
우리가 malloc(8)을 사용하여 받을 것이기 때문에 총 청크 사이즈는 0x20이다.(위에 설명)

그렇기에 stack_var주소 위치에 0x20을 넣는다.




(chunk 총 크기)


그 후 chunk size영역의 크기는 8바이트 이전의 주소를 적어줘야한다.

그렇기에 d의 sizeof의 값이 8바이트이기에 sizeof(d)를 해주었다.



(fd 포인터 조작)


위의 과정까지 진행이 되면

메모리의 상황은 다음과 같다.



(메모리 상황)



위와 같이 fd 포인터 위치에 stack 주소가 들어가게 된다.

그 후 malloc을 해주게 되면 a의 주소가 할당 되고,
fd 포인터에 적혀있는 stack 주소로 넘어가 chunk size를 확인해보고 우리가 0x20으로 적어주었으므로 fastbin list에 들어가게 된다.

그러므로 free list의 top에는 이제 우리가 정한 stack 주소가 들어가게 되고

이번에 다시 malloc을 하게 되면 stack 주소가 반환되는 것이다.



(stack 주소 malloc)



(stack 주소 반환)


(* 위의 캡쳐 사진은 따로따로 찍은 것으로 gdb메모리 상황과 stack 주소 반환 결과가 같지 않다.)

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

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

how2heap의 첫번째 분석 자료는 first_fit 이다.
first fit이란 메모리 할당 전략 중 하나이다.

대표적으로 3가지가 있는데
first fit, best fit, worst fit 이 있다.

기본적으로 리눅스에서 heap 할당 관련해서는 first fit전략을 취하고 있다.

first fit이란 할당 가능한 메모리 영역을 발견하면 바로 할당하는 방식이다.
best fit은 internal fragment가 최대한 작게  딱 맞는 크기의 영역을 할당하는 방식이고
worst fit은 정반대로 최대한 널널한 영역을 할당하는 방식이다.

first fit 프로그램은 취약점을 보여주는 프로그램은 아니고
리눅스의 메모리 할당방식을 직접 보여주는 프로그램이다.

과정을 따라가며 분석해보겠다.



(메모리 할당)


 처음에 malloc을 통해 heap영역을 2개 할당한다.

a와 b에 할당을 해준다.



(메모리 할당 확인)



프로그램 실행시 확인해 볼 수 있다.
첫번째로 할당한 영역의 주소는 46010 이고 (뒷자리만 부를것이다.)

두번째로 할당한 영역의 주소는 46220 이었다.



(할당된 메모리 사용)


그리고 할당된 주소를 사용한다. a라는 주소에 문자열을 입력해준다.



(메모리 해제)



그리고, 메모리 할당을 free를 이용해 해제해준다.
그렇게 되면 현재 a에 할당된 주소인 46010 주소는 free된 상태이며 사용가능한 주소로
bin 구조에 들어가게 된다. 
리눅스에서는 haep영역의 메로리 할당과 해제를 bin을 통해 관리한다. 
나중에 heap영역 메모리 요청이 들어오면 이 bin 구조를 탐색한 후 메모리 할당을 해줄 것이다.

이 bin 구조는 사이즈에 따라 fast bin, small bin, unsorted bin 여러가지 종류로 구분되고
구조도 조금씩 다르다. 이에 대한 설명과 분석은 해당 취약점 분석때 자세히 하겠다.
이번 자료에서는 first fit에 초점을 맞춰보겠다.

다시 이어서 malloc을 해준다.



(malloc)


malloc을 통해 얻은 주소를 c에 넣어준다.

자 그렇게 되면 아까 해제(free) 되었던 주소가 c에 들어갈 것이다.
bin 구조에서 탐색하다가 사용가능한 영역을 발견하고 바로 아까 해제해주었던 영역을 할당해 주는 것이다.

확인을 위해 이번엔 방금 할당된 주소를 사용해본다.



(메모리 사용)



방금 할당 받은 주소에 C라는 문자열을 적는다.

그리고 a변수 처음에 할당되었던 주소가 담긴 변수와

c변수 방금 할당했던 주소가 담긴 변수를 주소값과 안에 담긴 문자열을 출력해본다.



(같은 주소)


결과는 처음 할당했던 주소가 46010이었는데 해제(free)해준 뒤 다시 malloc을 통해 받은 주소가 똑같은 46010이다. 그리고 그 안에 있는 문자열은 thist is C! 로 정확히 같은 주소라는 것을 보여준다.


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

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


정말 오래(?) 걸렸던 문제이다.

하루종일 문제푼것은 아니지만 조금씩 풀었는데 그래서그런지 오래걸린 문제이다.



(dragon 문제)




(문제화면)


문제 화면을 보면 게임을 준다.

게임을 다운로드해서 실행해본다.




(dragon 게임 실행)



직업을 고르고 용과 싸우는 게임이다.
이길 수 없다...

gdb로 분석을 시작했다.
main 함수에는 입출력 버퍼 설정하고 PlayGame 함수로 넘어간다.

그러므로 PlayGame 함수부터 분석해보겠다.



(PlayGame 함수)



함수를 보니 SecretLevel 함수?가 있다.
직업을 고를 때 3을 입력하면 이 함수로 넘어갈 수 있다.

그러면 SecretLevel에서는 뭘 할까?


(SecretLevel 함수)


비밀번호?를 입력 받고 문자열과 비교해서 일치하면 쉘을 던져준다...!

그래서 입력 문자열포멧과 비밀번호를 확인해보니



(입력 포멧, 입력해야할 문자열)



scanf 로 10글자를 받는데,,,
입력해야할 글자는 10글자가 훨씬 넘는다

즉 불가능.! 이다.
(그러면 저 문자열 10번째 다음 바이트를 00으로 바꿔서 해보자라는 생각이 들었는데, 저 문자열은 코드영역에 있기 때문에 쓰기 권한이 없어 불가능했다..)

그러므로 우리가 쓸수있는 방법은 바로 이 system("/bin/sh") 이 실행되도록 0x08048dbf 이 주소로 실행 흐름을 돌리면 된다.



(우리가 사용할 쉘)



여기서 이제 함수 하나하나 분석을 하는데 오래걸렸다...

FightDragon함수를 보면 malloc과 free를 2번씩 사용하는 모습을 확인하고 uaf 취약점이 아닐까 생각이 들었다.

그런데 uaf 취약점이든 bof든 무슨 취약점이든 입력할 곳이 있어야하는거 아닌가?...
전부 scanf  %d 로 입력을 받고 우리가 뭐 어떤 값을 입력할 곳이 없는 것이다

그러던 중 FightDragon 함수 아래쪽에서 문제 해결의 핵심을 찾았다.



(문제의 핵심)



이 부분의 scanf는 조금 특별했다.

위에서 malloc을 해주고 그 곳에 입력을 받고 게다가!



(16글자)



더구나 16글자를 입력 받는다!!!
이 부분에서 우리의 payload를 넣을 수 있다는 생각이 들었다.

그러면 어떻게 해야하나 위로 올라가보니



(공격함수)



공격함수 끝난 후 eax 값을 ebp-0x18을 넣고



(비교)



ebp-0x18 값이 0이 아니어야 입력할 기회가 주어진다.

즉, 용을 잡아야 기회가 주어진다.

또 여기서 시간을 많이 썼다.
용을 어떻게 잡는담... 인터넷에서 힌트를 좀 받았다. dragon 구조체를 잘 보면 된다고 나왔다.

dragon 구조체를 몇일을 들여다 보다 무릎을 탁 쳤다.




(dragon 구조체)



dragon 구조체를 보면
처음에는 몬스터 정보를 출력하는 함수 주소.
두번째는 몬스터 체력과 그 옆에 5가 체력회복량이다.
여기서 잘 보면 몬스터 체력이 바이트 단위로 설정되어있다. C언어로 표현하면 char자료형 처럼 1바이트 짜리이다.
범위로는 -127~127 이다.

엄마용이 80체력에 공격력이 10이니깐 계속 공격안하고 버티면 127을 넘길수 있을거 같다.
그러면 음수 값이 되고 용이 죽는다?

테스트!


(테스트 성공)



정말 127이 넘으니 용이 죽었다.
그리고 16바이트 입력을 넣을 수 있는 기회가 주어졌다.

그러면 취약점은?
scanf 로 입력받고 아래 코드를 보니 call eax 가 보인다.

보니 dragon 구조체가 free 된걸 까먹은건지 dragon 구조체의 첫번째 인자인 몬스터정보 출력 함수를 호출하고 있다.



(취약점)



내가 16바이트 입력한 곳이 방금 malloc 으로 받은 곳이니 이 주소가 전에 free 되었던 힙영역 주소이다.
같은 크기의 메모리 할당이니 전에 사용했던 그 주소가 재사용 된다.
확인해보자.! 
이름을 AAAA로 입력하였다. 그러면 처음 4바이트가 0x41414141 가 될것이고 아래 call eax에서

이 주소를 몬스터정보 출력 함수인줄 알고 이 주소를 호출하려 할 것이다.


(테스트)



AAAA 입력!




(확인)


테스트가 확인되었다.

그렇다면? 입력을 해줄 때 아까 우리가 봤던 SecretLevel에서의 쉘을 실행시키는 부분 주소를 입력해주면 된다.

pwn 모듈을 이용해 작성했다.



(exploit)



그리고 공격!



(쉘획득)



쉘을 획득하였다.!

'WarGame > 500 Project' 카테고리의 다른 글

(61/500) NOE.systems - double_input  (0) 2017.09.17
(60/500) pwnable.kr - crypto1  (0) 2017.08.27
(58/500) pwnable.kr - fsb  (0) 2017.08.12
(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09
(56/500) pwnable.kr - asm  (0) 2017.07.14


UAF!!
새로 배운 취약점이다.
Use After Free
검색해보니 유명한 취약점이었다.

(몰랐다니...)

검색해서 공부한 후 문제를 풀어보았다. 

uaf 문제이다.


(uaf)



문제 화면은 아래와 같다.


(uaf 문제 화면)



Use After Free 버그를 아냐고 물어본다.

Use After Free 버그란 힙영역에서 일어나는 버그이다.
운영체제의 최적화를 위해 운영체제는 힙에 할당하고 해제한 영역을 flag 비트만 설정해둔 뒤 그 영역의 메모리 데이터를 그대로 남겨둔다.
해제한 힙 영역을 재사용 함으로써 문제가 발생한다.
특히 객체지향 언어인 JAVA, C++ 등 에서 더 강력히 발생한다.

이유는 동적으로 힙영역에 객체를 생성하고 해제하게 되면 그 객체 데이터가 힙 영역 메모리에 그대로 남아있기 때문이다.  객체안에 함수가 있다면 그 영역에 함수 포인터가 존재하는데 만약 객체 해제 후 그 객체의 함수를 재사용하는 코드가 있다면 그 영역의 객체 함수 포인터를 내가 원하는 값으로 덮어 씌워서 프로그램의 실행을 움직 일 수 있다.

소스코드를 보면서 전략을 다시 정리해보자!



(소스코드)



체크할 것은 Use After Free 함수의 취약점 조건은
1. 동적으로 메모리를 할당한다. ( 위 코드는 new라는 함수를 이용해 힙영역에 객체를 만든다. )
2. 메모리를 해제한다. ( free, 여기서는 delete로 메모리 해제한다. )
3. 힙 영역에 다시 메모리를 할당한다. ( 2를 입력하면 new를 이용해 메모리 할당한다. )

조건은 충족됬다.
이 프로그램은 실행하자마자 남자, 여자라는 객체를 생성하므로 메모리 해제 후 우리의 데이터를 힙 영역에 넣는다. 넣으면서 함수 주소를 덮어쓸 것이다. (아직 메모리 확인을 안했으므로 여기까지 계획을 세우고 조금 이따가 구체적으로 생각해보자)



(우리가 불러낼 함수)



남자, 여자 객체는 Human에게서 상속받는다. 즉 남자, 여자 객체 안에는 우리가 불러낼 give_shell 함수를 가지고 있는 것이다.



(덮어쓸 함수)



우리가 덮어쓴 후
객체의 함수를 호출해야하는데 위의 introduce를 불러낼 수 있으므로(1번을 누르면)
introduce 함수 주소에 give_shell 함수 주소를 넣을 것이다.

gdb를 이용해 메모리를 확인해보자.
우선 분기문 case 1: 모양이 보인다.



(case 1)



case 1 이므로
introduce 함수를 불러내는 코드이다.
브레이크를 걸고 실행해보면서 introduce 함수를 어떻게 부르는지 알아보자.


(함수 호출)



rbp-0x38 에서 값을 가져와 rax에 넣는다.
그 후 rax주소에 있는 값을 가져와 8을 더하고 함수를 호출한다.

여기서 rax는 0x401570 이었다.



(0x401570 주소)



0x401570 이 주소에서 8을 더한 위치의 값을 빨간색으로 표시했다.
0x004012d2에 뭐가있나 보면


(0x004012d2)



introduce 함수가 보인다.

그렇다면 -8 위치인 0x0040117a 주소를 확인해보자



(0x0040117a)



우리가 원하던 give_shell 함수 주소이다.

정리를 하면
rbp-0x38 주소는 0x00614c50 이고
여기서 가져온 값이 0x401570이다. 만약 이 값이 0x401570-8이라면 
가져와서 8더한 값이 0x401570일테고 우리의 give_shell 함수가 실행될 수 있다.

즉! 0x00614c50 위치에 0x401570-8가 되면 된다.


(덮어쓸 위치)



내가 A 4개를 입력한 파일을 이용하여 데이터를 2번 쓰니 저 위치를 덮어쓰게 되었다.

이제 공격을 해보자.
 0x401570-8 를 파일에 넣어둔다.



(파일 생성)


그 후 free 해준 뒤 데이터를 2번 써준다. (그래야 우리가 원했던 위치에 값이 올라가므로 -> 이 사실은 내가 gdb를 이용해 실험을 해봐서 나온 결과이다.)


(공격 성공)



해당 쉘을 획득 할 수 있었다.

'WarGame > 500 Project' 카테고리의 다른 글

(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09
(56/500) pwnable.kr - asm  (0) 2017.07.14
(54/500) pwnable.kr - cmd1  (0) 2017.07.12
(53/500) pwnable.kr - blackjack  (0) 2017.07.12
(52/500) pwnable.kr - coin1  (0) 2017.07.12

+ Recent posts