Return to Shellcode란 Stack 영역에 있는 RET 주소를 Shellcode가 저장된 주소로 변경해, Shellcode를 실행하는 방식이다.
CALL & RET instruction
우선 Return to Shellcode를 이해하기 위해 어셈블리어인 CALL, RET 명령어에 대한 이해가 필요하다.
Instruction | Processing |
CALL <Operation> | PUSH ReturnAddress |
RET | POP RIP JMP RIP |
- CALL : Return Address를 Stack에 저장하고, 피연산자 주소로 이동
- RET : POP 명령어를 이용해 RSP(ESP, 32bit)가 가리키는 Stack 영역에 저장된 값을 RIP(EIP, 32bit)에 저장 후, 해당 주소로 이동
위의 명령어를 이용해 RET 주소를 Shellcode가 저장된 주소로 변경한다면, Shellcode가 실행되고 Shell을 획득할 수 있다.
Proof of concept
다음 코드를 이용해 분석해 보겠다. main() 함수에서 vuln()을 호출하는 단순한 코드이다.
#include <stdio.h>
#include <unistd.h>
void vuln(){
}
void main(){
vuln();
}
다음과 같이 2개의 Break point를 설정하고 분석을 진행한다.
lazenca0x0@ubuntu:~/Exploit$ gcc -fno-stack-protector -o test test.c
lazenca0x0@ubuntu:~/Exploit$ gdb -q ./test
Reading symbols from ./test...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x00000000004004dd <+0>: push rbp
0x00000000004004de <+1>: mov rbp,rsp
0x00000000004004e1 <+4>: mov eax,0x0
0x00000000004004e6 <+9>: call 0x4004d6 <vuln>
0x00000000004004eb <+14>: nop
0x00000000004004ec <+15>: pop rbp
0x00000000004004ed <+16>: ret
End of assembler dump.
gdb-peda$ b *0x00000000004004e6
Breakpoint 1 at 0x4004e6
gdb-peda$ disassemble vuln
Dump of assembler code for function vuln:
0x00000000004004d6 <+0>: push rbp
0x00000000004004d7 <+1>: mov rbp,rsp
0x00000000004004da <+4>: nop
0x00000000004004db <+5>: pop rbp
0x00000000004004dc <+6>: ret
End of assembler dump.
gdb-peda$ b *0x00000000004004d6
Breakpoint 2 at 0x4004d6
gdb-peda$ b *0x00000000004004dc
Breakpoint 3 at 0x4004dc
gdb-peda$
프로그램을 실행하면 다음과 같이 CALL 명령어의 동작을 확인할 수 있다.
- vuln() 함수 호출 전, RSP(ESP)가 가리키는 Stack의 위치는 0x7fffffffe4b0이며, 해당 영역에 저장되어 있는 값은 0x4004f0이다.
- vuln() 함수 호출 후, 해당 RSP(ESP)가 가리키는 Stack의 위치는 0x7fffffffe4a8이며, 해당 영역에 저장되어 있는 값은 0x4004eb이다.
- 즉, CALL 명령어에 의해 Stack에 호출된 함수가 종료된 후에 이동할 주소 값을 저장하고 이는 해당 함수의 RET가 된다.
gdb-peda$ r
Starting program: /home/lazenca0x0/Exploit/test
Breakpoint 1, 0x00000000004004e6 in main ()
gdb-peda$ i r rsp
rsp 0x7fffffffe4b0 0x7fffffffe4b0
gdb-peda$ x/gx 0x7fffffffe4b0
0x7fffffffe4b0: 0x00000000004004f0
gdb-peda$ c
Continuing.
Breakpoint 2, 0x00000000004004d6 in vuln ()
gdb-peda$ i r rsp
rsp 0x7fffffffe4a8 0x7fffffffe4a8
gdb-peda$ x/gx 0x7fffffffe4a8
0x7fffffffe4a8: 0x00000000004004eb
gdb-peda$
다음과 같이 RET 명령어의 동작을 확인할 수 있다.
- RET 명령어가 실행 전 RSP(ESP)가 가리키는 Stack의 위치는 0x7fffffffe4a8이며, 해당 영역에 저장되어 있는 값은 0x4004eb이다.
- RET 명령어가 실행 후 RSP(ESP)가 가리키는 Stack의 위치는 0x7fffffffe4b0이며, 해당 영역에 저장되어 있는 값은 0x4004f0이다.
- RET 명령어가 실행되면서 POP RIP 명령어가 실행되기 때문에 RSP(ESP)가 변경되었다.
Breakpoint 3, 0x00000000004004dc in vuln ()
gdb-peda$ i r rsp
rsp 0x7fffffffe4a8 0x7fffffffe4a8
gdb-peda$ x/gx 0x7fffffffe4a8
0x7fffffffe4a8: 0x00000000004004eb
gdb-peda$ ni
0x00000000004004eb in main ()
gdb-peda$ i r rip
rip 0x4004eb 0x4004eb <main+14>
gdb-peda$ i r rsp
rsp 0x7fffffffe4b0 0x7fffffffe4b0
gdb-peda$ x/gx 0x7fffffffe4b0
0x7fffffffe4b0: 0x00000000004004f0
gdb-peda$
이를 이용한다면 RET Overwrite를 통해 Shellcode를 실행할 수 있게 된다.
Protection
만약, 바이너리에 NX bit가 적용되어 있다면 Stack에서 명령어 실행이 불가능하다. 따라서, Shellcode도 실행이 되지 않기 때문에 해당 방법은 사용할 수 없다.
참고 사이트
02.Return to Shellcode - TechNote - Lazenca.0x0
Excuse the ads! We need some help to keep our site up. List Return to Shellcode Return to Shellcode란 Return address 영역에 Shellcode가 저장된 주소로 변경해, Shellcode를 호출하는 방식입니다. CALL & RET instruction Return to Shellco
www.lazenca.net
'hacking > pwnable' 카테고리의 다른 글
ROP(Return Oriented Programming) (0) | 2023.05.27 |
---|---|
RTL(Return to Libc) (0) | 2023.05.27 |
hook overwrite (0) | 2023.03.11 |
patchelf (0) | 2023.01.06 |
x64 stack alignment (0) | 2022.12.06 |