Stack Pivoting은 스택 포인터를 원리 스택이 아닌 다른 메모리 영역으로 옮기는 기법이다.
보통 프로그램의 취약점을 이용해 RIP를 덮을 수는 있는데 스택 공간이 너무 좁아서 원하는 ROP 체인을 충분히 올리지 못할 때 사용된다.
공격자가 제어 가능한 더 넓은 공간 bss, heap, mmap 영역 등에 미리 ROP 체인을 만들어두고 이후 rsp를 그 위치로 바꿔서 그 공간을 스택처럼 사용하는 것이다.
일반적인 버퍼 오버플로우에서는 다음과 같이 생각할 수 있다.
하지만 다음과 같은 상황이 발생할 수 있다.
이럴 때 쓰는 것이 Stack Pivoting이다.
x86-64에서 함수 리턴은 보통 rsp를 기준으로 동작한다.
따라서 ret은 다음 값을 꺼내 RIP로 사용한다.
만약 공격자가 rsp를 준비한 fake stack으로 옮길 수 있다면 ROP Chain을 배치해놓고 CPU가 체인을 정상적인 스택처럼 일게 만들 수 있다.
Fake Stack은 실제 운영체제가 관리하는 스택이 아니라 공격자가 스택처럼 보이게 꾸며 놓은 메모리 영역이다.
예를 들어 bss 영역에 ROP 체인을 써두면 rsp를 bss 영역으로 옮겨 ROP 체인을 실행시킬 수 있다.
공격자는 먼저 .bss, heap 등의 쓰기 가능한 영역에 원하는 데이터를 넣는다.
이후 취약점을 통해 rsp를 제어 가능한 영역으로 바꾼다.
이제부터 CPU는 기존 스택이 아니라 공격자가 준비한 fake stack의 데이터를 읽는다.
leave 명령어는 다음과 비슷하게 동작한다.
mov rsp, rbp
pop rbp
이후 ret이 실행되면
pop rip
가 되어 다음과 같은 의미가 된다.
rsp = rbprsp 값을 새로운 rbp로 가져간다.rip로 가져간다.saved rbp를 덮을 수 있다면 그 값을 fake stack의 시작 주소로 바꿔 놓고 leave ; ret을 실행해서 스택 전체를 새 위치로 옮길 수 있다.
stack
buffer
saved rbp -> fake_stack_addr
saved rip -> leave_ret
fake stack
rbp
pop rdi ; ret
puts@got
puts@plt
main
pop rsp
ret
이 경우 다음 값 하나만 준비하면 바로 rsp를 원하는 위치로 바꿀 수 있다.
xchg rax, rsp
ret
xchg rdi, rsp
ret
mov rsp, rdi
ret