tmxklab

[heap exploit] - Unsafe Unlink 본문

Security/01 System Hacking

[heap exploit] - Unsafe Unlink

tmxk4221 2020. 7. 31. 20:14

1. Unsafe Unlink

Unsafe Unlink는 헤더 값이 조작된 fake chunk와 다른 인접한 chunk간에 병합이 일어나면서 비정상적으로 unlink되어 발생하는 취약점이다. 이를 통해 원하는 공간에 값을 작성하거나 Leak할 수 있다.

 

사용 조건 :

  • 힙 영역을 전역 변수에서 관리

  • 2개의 Allocated Chunk가 필요하며 한 개는 Fake Chunk를 생성할 수 있어야 함

  • 첫 번째 Chunk를 통해 두 번째 Chunk의 헤더를 조작할 수 있어야 함

 

Unlink가 발생하는 이유

이전에 glibc malloc bin을 공부하면서 잠깐 Unlink가 한 번 언급되었던 적이 있었다.

 

heap(4) - glibc malloc(3) (feat. bin)

참고 : GNU C Library의 Memory Allocator인 ptmalloc2(glibc 2.23)를 대상으로 설명 이번에는 Free된 Chunk들을 다시 재활용하기 위해서 Free Chunk들을 어떻게 관리하는지 자세하게 다뤄보겠다. 0. Boundary Tag..

rninche01.tistory.com

fast bin을 제외한 나머지 bin들은 인접한 Free Chunk가 존재하면 병합할 수 있으며 double linked list구조를 가지고 있다.

 

그럼 기존에 bin list에 등록된 Chunk가 존재하는데 인접한 Chunk가 Free되면 병합이 이루어져야 하지만 위에서 말했듯이 bin list에 등록된 Chunk는 double liked list구조를 가지고 있다. 따라서, 병합이 일어날 경우 bin list에 제거하기 위해 포인터 fd, bk를 정리해주는 작업이 진행된다. → Unlink

 

, Unlink란 Chunk합병이 일어날 경우 bin list에서 제거할 때 포인터 fd, bk를 정리해주는 작업이다.

(아마 자료구조를 배우신 분들은 Double Linked List구조에서 노드를 제거하는 과정을 알고 계시니 이해하는데 수월하실 듯 하다.)

 

bin list에서 제거되면서 fd, bk포인터 정리하는 작업

  • FD → bk = P → bk

  • BK → fd = P → fd

출처 : https://bpsecblog.wordpress.com/2016/10/06/heap_vuln/

 

Unlink macro sorce code

/* 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;                 
              }                                                               
          }                                                                   
      }                                                                       
}

위 코드에서 보면 Unlink가 수행되기 전에 2번의 if문을 통해 double-linked-list가 손상되었는지 검증을 하게 되고 검증이 통과하면 FD→bk = BK, BK→fd =FD를 수행한다.

 

첫 번째 if문)

if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) 
  • chunksize(P)와 prev_size(next_chunk(P))값이 같지 않으면 참이 아님

    • chunksize(P) : Free chunk→size

    • prev_size(next_chunk(P)) : Free chunk → prev_size

  • Free된 Chunk는 다음 인접한 Chunk의 Prev_size값에 자신의(Free된 Chunk) size값이 들어있으므로 정상적인 경우 참이 된다.(Boundary Tag)

  • 결국, chunksize(P)와 prev_size(next_chunk(P))가 동일하면 참이 된다.

 

두 번째 if문)

FD = P->fd;                                                               
BK = P->bk; 
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
  • FD→bk와 P가 같지 않거나 BK→fd와 P가 같지 않으면 참이 아님

    • FD→bk (p→fd→bk) : 다음 Chunk의 이전 Chunk, 결국 본인(P)

    • BK→fd (p→bk→fd) : 이전 Chunk의 다음 Chunk, 결국 본인(P)

  • 결국, FD→bk와 BK→fd 모두 P와 동일하면 참이 된다.

 


2. 공격 시나리오

이제, 우리는 저 두 개의 if문을 우회하기 위해 다음과 같은 과정을 거쳐야 한다.

 

1) 2개의 Allocated Chunk 생성

  • 먼저, 2개의 Chunk를 할당해준다.

 

2) Fake Chunk 생성

  • Chunk (A)에서 Fake Chunk를 만들어준다. 이 때, if문 2개를 우회하기 위해 다음과 같이 변경한다.

    • prev_size, size : 0x0

    • fd : [원하는 주소 값] - 24 , bk : [원하는 주소 값] - 16

    • 다음 Chunk의 prev size : Fake Chunk size

    • 다음 Chunk의 PREV_INUSE [P] bit : 0x0

 

3) 2번째 Chunk (Chunk B)해제

  • Chunk B를 free하게 되면서 P비트를 확인했을 때 0x0인 것을 확인하고 인접한 Chunk인 Fake Chunk가 free된 Chunk임을 알 수 있다.

  • 따라서, 인접한 Chunk인 Fake Chunk와 Chunk B가 병합을 시도하기 전에 fake chunk를 bin list에서 해제하기 위해 unlink가 호출될 것이다.

  • unlink가 호출되면서 chunk의 크기와 double linked list가 손상되었는지 다음 if문 2개를 통해 검증하게 된다. → 이를 우회하기 위해 fake chunk작성

 

3-1) 첫 번째 if문(chunk size 검증)

  • chunk size(fake) : 0x0

  • prev_size(next_chunk(fake)) : 0x0

    • fake chunk을 다음 chunk는 size가 0x0이므로 다시 자신을 가리키게 된다. 결국 fake chunk의 prev size는 0x0이 된다.

  • 위 2개의 결과 값이 동일하므로 첫 번쨰 if문은 통과된다.

 

3-2) 두 번째 if문(double linked list검증)

  • FD→bk(p→fd→bk) : 0x1967030

  • Bk→fd(p→bk→fd) : 0x1967030

  • 위 2개의 결과 값이 동일하므로 두 번쨰 if문도 통과된다.

 

4) if문 통과 이후

위 2개의 if문이 통과되면서 다음과 같이 unlink가 진행된다.

 

4-1) FD→bk = BK (p→fd→bk = p→bk)

 

4-2) BK→fd = FD (p→bk→fd = p→fd)

 

결론)

전역 변수에서 heap영역을 관리하게 되는데 처음에 0x6020c8주소에 Chunk(A)의 주소 값이 존재했으나 위 과정을 통해 마지막 사진에서 0x6020c8주소에 0x6020b0을 가리키게 된다.

이후에, 0x6020b0을 통해 overflow를 일으켜 0x6020c8주소에 전역 변수 주소가 아닌 다른 주소 값을 넣을 수 있다.

 

Unsafe Unlink 문제풀이)

 

hitcon training [LAB 11]

UAF(Use After Free)이후로 이번 문제를 풀면서 새로운 Heap Exploit기법 두 가지(House Of Force, Unsafe Unlink)를 배웠는데 따로 자세하게 정리하도록 하겠다. 1. 문제 1) mitigation확인 2) 문제 확인 3) 코..

rninche01.tistory.com

 


3. References

Comments