# 환경은 ubuntu 16.04 64bit 에서 진행했습니다.

# House of Einherjar 는 malloc() 에 의해 반환 된 포인터를 제어하기 위해 null byte 로 off-by-one 오버플로우를 사용한다.

# 이 기술은 Poison Null Byte 보다 더 강력한 원초적인 기술이지만, 추가적으로 heap leak 이 필요하다.

 

 

포인터 변수 a, b, c, d 선언한다. 그리고 우리는 'a' 에 0x38 bytes 만큼 할당한다. 

우리는 'a' 에 overflow 를 원하기 때문에, 0x38 을 반올림 한 후의 'a' 의 실제 크기가 필요하다. 

a : 0x603420

real_a_size : 0x38 

 

 

우리는 우리가 원하는 어느 곳이든 fake chunk 를 만든다. 이 글에서 우리는 stack 에 chunk 를 만들 것이다.

그러나, 너는 그 주소를 아는 한 heap 이든 bss 든 chunk 를 만들 수 있다.

우리는 unlink checks 를 우회하기 위해 fwd 와 bck 포인터들을 fake_chunk 를 가리키도록 설정한다.

그러나 우리는 일부의 시나리오에서 unsafe unlink 기술을 사용할 수 있다.

 

Our fake chunk at 0x7fffffffe4d0 looks like:

prev_size (not used): 0x41414141

size : 0x100

fwd : 0x7fffffffe4d0

bck : 0x7fffffffe4d0

 

 

우리는 'b' 에 0xf8 bytes 만큼 할당한다. 그리고 우리는 3rd chunk 를 'c' 에 0x60 bytes 만큼 할당한다.

 

b : 0x603460

real_b_size : 0xf8

c : 0x603560

 

 

우리는 'a' 에서 single null byte 를 'b' 의 메타 데이터에 overflow 한다. 

 

[ overflow 전 ]

b_size_ptr : 0x603458 -> 0x101

b.size : 0x101

 

[ overflow 후 ]

b.size : 0x100

 

이것은 b.size 가 0x100 의 배수라면 가장 쉽다. 너는 b 의 size 를 바꿀 필요 없이 오직 prev_inuse bit 만 바꾸면 된다.

만약 그것이 변경되었다면, 우리는 다음 chunk 를 병합하려고 하는 b 의 안에 fake chunk 가 필요할 것이다.

 

 

// a 의 끝에 fake prev_size 를 쓰자.

우리는 fake prev_size 를 a 의 마지막 8 byte 에 작성하여 그것이 우리의 fake chunk 와 병합시키도록 할 것이다.

우리의 fake prev_size 는 0x603450(b - prev_size -> 0x0) - 0x7fffffffe4d0(fake chunk - prev_size -> 0x41414141) = 0xffff800000604f80

 

fake size : 0x7fffffffe4c0 -> 0xffff800000604f80

&a[0x38] - 0x8 = 0xffff800000604f80    // 0x603450 -> 0xffff800000604f80

 

// 'b' 를 free 하고 그것은 fake chunk 와 병합할 것이다.

이제 우리는 b 를 free 하고, b 의 prev_inuse 가 unset 이기 때문에 이것은 fake chunk 와 병합할 것이다.

 

Our fake chunk size is now 0xffff800000605081 (b.size + fake_prev_size)

우리는 fake chunk 를 수정할 수 있고, 그것은 크기 검사를 통과할 수 있을 정도로 작다.

만약 fake chunk 가 top chunk 라면 ( 'c' 를 할당하지 않았다면 ) 필요하지 않다.

 

 

새로운 fake_chunk size : 0x1000

지금 우리는 malloc() 을 호출할 수 있고, 그것은 fake chunk 에서부터 시작할 것이다.

 

d = 0x7fffffffe4e0

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

x32, x64 에서의 fastbin 크기가 다르다.

E.g. on x64, 0x30-0x38 will all be rounded to 0x40.

'system' 카테고리의 다른 글

arm in qemu  (0) 2018.02.10
2017년 KUCIS 영남권 세미나  (0) 2017.06.30
[how2heap translation] house_of_einherjar.c  (0) 2017.06.26
[how2heap translation] overlapping_chunk.c  (0) 2017.06.26
[how2heap translation] house_of_lore.c  (0) 2017.06.21
[how2heap translation] poison_null_byte.c  (0) 2017.06.19
Posted by woodonggyu

댓글을 달아 주세요

테스트 환경은 ubuntu 14.04 32bit 환경에서 진행했습니다.

 

우선 heap 에 3 chunks 를 할당하고, memset 을 통해 각각의 chunk 를 초기화한다.

p1 : 0x0804b008

p2 : 0x0804b108

p3 : 0x0804b208

 

 

p2 를 free 한다. p2 는 이제 동일한 크기의 새로운 malloc 할당을 대비해 unsorted bin 에 들어가게 된다.

 

 

이제 free 된 chunk 의 크기를 overwrite 할 수 있는 overflow 를 시뮬레이션 하자.

교육용 프로그램에서 size 의 마지막 3bit 는 중요하지 않지만 힙의 안정성을 유지하는 것이 좋다.

이 안정성을 유지하기 위해, 우리는 prev_inuse bit 를 설정함으로써 p1 chunk 가 free chunk 로 오인하지 않음을 보장한다.

우리는 chunk p2 크기를 385 로 셋팅하고, 우리에게 주어진 영역의 크기는 376(385 - chunk header) 이다.

 

*(p2-1) = evil_chunk_size;     // 우리는 chunk p2 의 size 필드를 0x181 의 값으로 overwrite 한다.

 

 

이제 우리가 chunk p2 의 size 필드를 overwrite 했던 0x181 과 같은 크기의 또 다른 chunk 를 할당한다.

이 malloc 은 크기가 변경된 unsorted bin 에 저장되어 있는 이전의 free chunk 로부터 제공받을 것이다.

 

p4 has been allocated at 0x0804b108 and ends at 0x0804b6e8

p3 starts at 0x0804b208 and ends at 0x0804b288

p4 는 p3 과 overlap 된다. 이 경우 p4 는 p3  을 포함한다.

 

 

이제 chunk p4 에 복사된 모든 내용이 chunk p3 의 데이터를 overwrite 할 수 있다.

그리고 마찬가지로, chunk p3 에 쓰여진 값은 chunk p4 에 저장된 값을 덮어쓸 수 있다.

 

예제를 통해 실행보자. 지금 우리는 다음과 같은 값을 가진다.

p4 : 2222222222222222222222222222222...

p3 : 3333333333333333333333333333333...

 

만약 memset(p4, '4', evil_region_size); 를 실행한다면, 우리는 다음과 같은 값을 가진다.

p4 : 4444444444444444444444444444444...

p3 : 4444444444444444444444444444444...

 

만약 memset(p3, '3', 80); 를 실행한다면, 우리는 다음과 같은 값을 가진다.

p4 : 4444444444444444333333333333333...

p3 : 3333333333333333333333333333333...

 

끝.

Posted by woodonggyu

댓글을 달아 주세요

House of Lore - Malloc Maleficarum 의 확장된 공격

이 PoC 는 glibc 의 smallbin corruption 에 대해서도 다룬다.

 

 

이것은 glibc malloc 에서 동작하는 체크를 우회하기 위한 업그레이드 버전이다.

ubuntu 14.04.4 - 32bit - glibc-2.23 환경에서 테스트 되었다.

 

가장 먼저 stack_buffer_1 과 stack_buffer_2 의 주소 값들을 초기화한다.

그리고, victim chunk 를 100byte 크기만큼 할당한다.

victim : 0x804c008

 

 

victim_chunk 포인터 변수에 victim - 2 의 주소를 넣는다.

stack_buffer_1 : 0xbffff6c0

stack_buffer_2 : 0xbffff6b4

 

stack 에 fake chunk 를 만들어준다. small bin corrupted 체크를 우회하기 위해 fwd pointer 를 victim_chunk 로 설정한다. 

bk pointer 를 stack_buffer_2 로 설정한다. 그리고 fwd pointer 는 stack_buffer_1 을 가리켜야 한다.

이렇게 설정하게 되면 bck = victim->bk 체크를 우회할 수 있다.

 

 

free 함수 진행 중에 top chunk 와의 병합을 피하기 위해서 large chunk 를 하나 할당한다.

large chunk : 0x804c070

 

victim(0x804c008) 을 free 하고 그것은 unsorted bin 에 삽입될 것이다.

 

 

unsorted bin 안의 victim's fwd 와 bk 포인터들은 nil..

victim->fwd : 0xb7fc0450

victim->bk : 0xb7fc0450

 

이제 unsorted bin 또는 small bin 에 영향을 받지 않는 청크를 malloc 으로 할당하자.

이는 victim(0x804c008) 은 smallbin 의 맨 앞쪽에 삽입될 것이다.

 

 

p2 : 0x804c460

victim->fwd : 0xb7fc04b0

victim->bk : 0xb7fc04b0

 

victim chunk 는 정리되어지고 fwd 와 bk 포인터들은 업데이트 되었다.

 

 

이제 victim->bk pointer 를 stack_buffer_1 의 주소로 덮어써서 취약점을 에뮬레이팅 한다.

 

 

이제 처음 free 되었던 chunk 의 size 만큼 할당한다. 이는 덮어써진 victim chunk 를 return 하고 bin->bk 에 victim->bk 포인터가 들어간다.

결과적으로 bk 포인터를 조작하여 한번 더 malloc 을 했을 때 glibc 가 bin->bk 에 있는 chunk 를 리턴하게 된다.

 

The fwd pointer of stack_buffer_2 has changed after the last malloc to 0xb7fc04b0

p4 is 0xbffff6c8 and should be on the stack!

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

문서를 보면서 따라했지만 조금 헷갈려서 내가 이해한 내용 정리.. (잘못 이해했다면 알려주시길..)

1. small bin 크기의 chunk 할당. -> top chunk 와의 병합을 피하기 위해 large chunk 하나 더 할당.

2. bck->fd != victim 을 우회하기 위해서 stack 에 fake chunk 설정해야함.

 

3. 이 fake chunk 가 어떻게 bck->fd != victim 을 우회할 수 있는지 그림을 통해 보자.

현재 p3 = malloc(100); 을 하기 바로 직전의 메모리 모습이다.

우리가 이전에 free 했던 chunk 와 같은 크기의 small bin chunk 를 할당할 때,

 

bck = victim->bk;

if (__glibc_unlikely(bck->fd != victim)) {

 

위와 같은 검증 과정을 거치게 된다. 이 때 victim : 0x0804c000 이고, victim->bk 는 우리가 overwrite 했던 stack_buffer_1(0xbffff6c0) 의 주소이다. 즉 bck = 0xbffff6c0 이 되고, bck->fd == 0xbffff6c0 + 0x8 과 동일하다.

위의 그림에서 0xbffff6c0 + 0x8 의 값은 0x0804c000 이고, bck->fd == victim 은 같으므로 검증을 우회한다.

즉, stack_buffer_1[2] = victim_chunk 를 넣은 이유는 이러한 검증 과정 우회를 위한 것이다.

 

4. 정상적으로 위의 검증을 우회하면

...

bin->bk = bck;

...

malloc 이 정상적으로 수행이 되고 이전에 free 했던 영역을 반환 받는다. 그리고 bin->bk 에 0xbffff6c0 값이 들어가게 되고, 마지막 malloc 은 glibc malloc 을 속여 bin->bk 에 삽입된 위치의 chunk 를 반환해야 한다.

즉, void *p4 = malloc(100); 을 수행하면서 p4 :0xbffff6c8 을 반환받는다. 

 

Posted by woodonggyu

댓글을 달아 주세요