Frame Pointer Overwirte는 하위 함수의 SFP 값 1바이트를 변조했을 때
2번의 에필로그 과정으로 RIP 레지스터를 컨트롤 할 수 있는 공격 기법이다.
공격의 특징은 다음과 같다.
메인 함수에서 호출된 하위 함수에서 SFP 값 중 1바이트를 변조할 수 있어야 한다.
변조한 SFP 값이 쉘 코드가 저장된 스택의 주소 - 4가 되어야 한다.
변조되기 전 SFP 값과 스택의 주소를 비교했을 때 하위 1.5바이트 이상 다르면 공격이 불가능하다.
익스플로잇 흐름은 다음과 같다.
스택의 주소를 구한 뒤 하위 함수의 SFP 값을 쉘 코드가 저장된 주소 - 4로 변조한다.
이후 에필로그 과정을 2번 거치면서 쉘 코드가 저장된 주소가 RIP 레지스터로 들어가 쉘 코드가 실행된다.
함수의 에필로그
먼저 함수의 에필로그 부분을 살펴보자.
함수의 에필로그 과정은 leave; ret;으로 구성되어 있는데 leave는 mov rsp, rbp; pop rbp;를 수행한다.
mov rsp, rbp;를 수행하면 지역 변수 영역을 정리하게 된다.
pop rbp;로 저장했던 sfp 값이 rbp 레지스터로 들어가 이전 함수의 rbp로 돌아가게 된다.
ret(리턴)은 pop rip; jmp rip;를 수행한다.
RET 영역에 저장된 이전 함수의 백업 주소를 rip 레지스터에 저장시켜 이전 함수로 완전히 복귀하게 된다.
Frame Pointer Overwrite
프로그램에서 메인 함수의 실행 중에 호출한 서브 함수에서 BOF 취약점으로 SFP 값을 1바이트 변조시켜
쉘 코드가 저장된 주소 - 4로 만든 상황을 가정해보자.
파란색으로 칠해진 부분은 사용자가 입력한 부분이다.
지역 변수 영역에 쉘 코드가 들어갔고 SFP의 값이 1바이트 변조된 상태이며
변조된 SFP 값은 쉘 코드가 저장된 주소 - 4를 가리키고 있는 상황이다.
먼저 첫 번째 에필로그 과정인 하위 함수의 에필로그 과정이다.
leave를 수행하면 mov rsp,rbp; pop rbp를 수행한다.
mov rsp,rbp;를 수행해서 rsp는 rbp로 내려간 뒤 pop rbp;를 통해 rbp 값이 변하며
rsp의 값은 pop을 수행했으므로 +4가 되면서 리턴 어드레스가 저장된 주소를 가리키게 된다.
원래라면 pop rbp;로 rbp 레지스터 값은 원래 메인 함수의 sfp가 저장된 주소가 되어야 하지만
sfp 값이 변조되었기 때문에 쉘 코드가 저장된 주소 -4가 되면서 의도하지 않은 주소가 rbp 레지스터의 값으로 저장된다.
ret;를 수행하며 메인 함수로 복귀하게 된다.
이제 두 번째 에필로그 과정인 메인 함수의 에필로그 과정을 살펴보자.
원래라면 mov rsp, rbp;로 메인 함수의 지역 변수 영역을 정리하면서 SFP가 저장된 주소로 내려와야 하지만
rbp가 저장된 곳은 쉘 코드가 저장된 주소-4이므로 rsp의 값은 쉘 코드가 저장된 주소-4로 올라가게 된다.
이후 pop rbp를 통해 rsp 레지스터 값이 +4 되면서 정확히 쉘 코드가 저장된 주소를 가리키게 된다.
이제 ret를 수행하게 되면 pop rip를 통해 rip 레지스터의 값은 쉘 코드가 저장된 주소가 되고
jmp rip를 통해 해당 주소로 점프하면 쉘 코드가 실행되어 쉘을 딸 수 있게 된다.
예제로 확인해보는 FPO
아래와 같은 예제 파일을 만들었다.
위와 같은 보호 기법을 가지도록 컴파일 하였으며 32비트 아키텍처로 구성된 예제이다.
메인 함수에서 main_buf 주소를 알려주므로 스택의 주소를 알 수 있다.
fpo 함수에서 main_buf에 입력한 내용을 fpo_buf로 복사하는 strncpy 함수를 수행할 때 bof 취약점이 발생해
sfp에 1바이트 변조가 가능하다.
또한 익스플로잇의 편의를 위해 get_shell 함수를 마련하였다.
gdb로 메인 함수와 fpo 함수를 디스어셈블 해보면 이렇게 나온다.
각 함수의 에필로그 부분과 bof 취약점이 발생하는 strncpy 함수 부분을 빨간색 박스로 표시했다.
노란색 화살표로 표시한 fpo+3, fpo+24, main+52에 브레이크 포인트를 걸고 ebp 레지스터와 esp 레지스터의 변화를 살펴보자.
main_buf에는 'a' 40개와 'b' 1개를 입력한다.
현재는 fpo+3에 설정한 첫 번째 브레이크 포인트가 히트 된 상태이다.
변조되기 전 fpo 함수의 sfp 값을 확인해보기 위해 ebp 레지스터를 확인해주면 sfp 값은 0xffffd0e8임을 알 수 있다.
strncpy 함수를 수행하기 전과 후의 스택의 변화를 확인하기 위해 ebp-0x30부터 sfp까지의 값을 확인해준다.
맨 마지막에 sfp 값이 있는 것을 확인해볼 수 있다.
이후 strncpy 함수를 수행하면 sfp의 1바이트가 변조 될 것이다.
이제 fpo+24에 설정한 두 번째 브레이크 포인트에 히트 된 상태이다.
스택의 상태를 확인해보면 ebp-0x28부터 'a' 40개가 채워지고 'b' 1개가 sfp의 1바이트에 오버라이트되어
sfp 값이 0xffffd062(e8->62)로 변조됨을 확인할 수 있다.
두 주소를 비교해보면 1바이트만 다르다.
(FPO 공격 수행 조건 만족)
마지막으로 브레이크 포인트인 main+52가 히트 된 상태이다.
현재 ebp는 아까 변조했던 sfp 값인 0xffffd062이고, esp는 0xffffd0bc이다.
이 상태에서 leave를 수행하면 mov esp,ebp; pop ebp;를 수행하게 된다.
mov esp,ebp;로 esp 레지스터의 값이 ebp의 값으로 바뀌고 pop ebp;로 esp 레지스터는 +4가 된다.
즉, esp 레지스터가 0xffffd066이 될 것이다.
leave를 수행시켜 보면
이렇게 esp 레지스터는 0xffffd066이 되었다.
이제 ret을 수행하는 단계인데 main+53 ret를 보면
esp의 주소에 저장된 0x0ff40000이 eip 레지스터에 들어간 뒤 해당 주소로 점프할 것임을 확인할 수 있다.
이렇게 one-byte overflow로 인해 eip 레지스터를 컨트롤 할 수 있음을 확인해보았다.
레퍼런스
https://hackstoryadmin.tistory.com/entry/FPO-Frame-Pointer-Overflow
'background > linux' 카테고리의 다른 글
[how2heap] Tcache House of Spirit (glibc 2.35) (0) | 2023.05.23 |
---|---|
[how2heap] House of Force (glibc 2.27) (0) | 2023.03.26 |
return-to-csu x64 (RTC, JIT ROP) (0) | 2023.01.12 |
[glibc-2.27] fwrite & fputs (1) | 2023.01.11 |
[glibc-2.27] fread & fgets (0) | 2023.01.09 |