Frame Faking
- Fake EBP로 더 잘 알려진 이 기법은 가짜 스택 프레임 포인터를 만들어 프로그램의 실행 흐름을 제어하는 기법이다.
- 일종의 스택 pivoting이라고 볼 수 있다.
- return 주소 뒤로 더이상 덮어쓰지 못하여 일반적인 ROP나 RTL이 불가능할 때 사용할 수 있다.
Leave & Ret Instruction
- Frame Faking을 공부하기 전에 먼저 알야할 것이 Reave, Ret 명령의 동작과정이다.
Leave
- Leave 명령은 2개의 명령으로 나누어져 동작한다고 볼 수 있다.
- 실제로 2개로 나누어진 명령은 아니고 내부적으로 이런 식으로 동작한다고 보면 된다.
1. 현재 RBP에 저장되어 있는 값을 RSP에 저장. 현재 함수에서 사용한 스택 프레임을 정리하는 과정이다.
- mov rsp, rbp
2. RSP레지스터가 가리키는 스택 영역의 값을 RBP에 저장. 현재 함수를 호출한 함수의 스택 프레임을 복구하는 과정이다.
- pop rbp
위는 mov rsp, rbp명령의 과정을 나타낸 그림이다.
위의 프레임은 func함수의 프레임, 밑의 프레임은 main함수의 프레임이라고 볼때, 현재 func함수가 끝나고 leave명령을 실행하는 과정이다.
현재 종료할 함수의 스택프레임을 정리하기 위해 rsp에 rbp값을 넣어 스택프레임을 정리하는 것을 볼 수 있다.
위는 pop rbp명령의 과정을 나타낸 그림이다.
현재 RSP가 가리키고 있는 스택의 값을 pop하여 RBP레지스터에 집어넣게 된다.
현재 RSP가 가리키고 있는 값은 호출한 함수의 SFP를 가리키고 있으므로 RBP가 호출한 함수의 스택 프레임으로 복구되게 된다.
RET
- Ret 명령 또한 2개의 명령으로 나뉘어 내부적으로 동작한다.
- 다시 말하지만 실제로 2개의 명령으로 나누어 동작하는 것이 아니라 우리가 관찰 했을때 그렇게 동작하는 것 처럼 보이는 것이다.
1. 현재 RSP가 가리키는 스택 값을 RIP에 저장. 호출한 함수의 다음 명령으로 돌아가기 위해 리턴 주소를 복구하는 과정이다.
- pop rip
2. Jmp 명령을 통해 현재 RIP에 저장된 값의 주소로 실행 흐름을 이동한다.
- jmp rip
위는 pop rip명령의 과정을 나타낸 그림이다. 주소의 경우 그냥 64비트라고 이해해주길 바란다. 8바이트 다쓰기 귀찮다.
현재 rip값이 0xdeadbeef라고 가정할 때 현재 ret주소에 저장되어 있는 0x41414141주소로 이동하기 위해 현재 rsp가 가리키고 있는 ret주소를 pop하여 rip로 집어 넣는다.
jmp rip의 경우 그냥 현재 rip에 저장되어 있는 주소로 점프하는 것이기 때문에 그림은 생략하겠다.
이 명령까지 수행하게 된다면 스택 프레임 복구, 코드 실행 흐름까지 모두 복구되어 정상적으로 호출한 함수로 되돌아가게 되는 것이다.
- 따라서 정리하자면 Leave & Ret 명령은 현재 함수가 종료되어 호출한 함수로 돌아가기 위해 현재 함수의 스택 프레임을 정리하고, 실행 흐름을 복구하는 명령어인 것이다.
Exploit
- 그렇다면 이러한 특성을 어떻게 공격에 이용할 수 있을까?
- BoF취약점이 존재하여 Return Address까지 덮을 수 있는 취약한 64비트 프로그램이 있다고 치자.
- overflow가 일어나 Frame Faking 공격을 수행하기 위해 현재 구성한 스택 상황이다.
- leave 명령의 mov rsp, rbp 까지는 수행된 상황을 가정한다.
- 현재 SFP의 경우 공격을 수행하기 위해 0x50000으로 조작되었고, ret의 경우 leave; ret가젯으로 조작되었다.
- SFP에 구성된 payload의 주소 - 8 을 넣는 이유는 설명을 보면 이해가 될 것이다.
- 다음으로 leave의 pop rbp가 수행되며 우리가 설정해 놓은 SFP으로 RBP가 이동하게 된다.
- 그후 ret명령을 통해 현재 return address에 저장된 leave; ret가젯이 실행되며 leave; ret 과정이 또 한번 실행된다.
- mov rsp, rbp가 다시 한번 수행되며 조작된 RBP로 RSP가 이동한다.
- pop rbp가 수행되며 rbp에 쓰레기 값이 들어가게 된다.
- 이때문에 payload - 8 의 값으로 SFP를 조작하는 것이다.
- 그 후 ret이 수행되며 pop rdi; ret을 통해 "/bin/sh"문자열 주소가 rdi레지스터에 들어가고, system()함수가 실행된다.
- 최종적으로 system("/bin/sh")가 실행되며 쉘을 획득 할 수 있다.
- 현재 시나리오의 경우 stack leak, libc leak이 모두 가능하다고 가정한 시나리오이다.
'Pwn > Stack-Based' 카테고리의 다른 글
[Pwn] Shellcode (0) | 2023.07.02 |
---|