[Stack] Fake EBP에 대하여
두비니
·2020. 8. 31. 00:28
FEBP(Fake EBP)에 대하여
오늘은 Fake EBP에 대해서 이야기해봅시다.
FPO랑 비슷한 맥락입니다. FPO를 몰라도 충분히 이해할 수 있지만, 설명하는 과정에서 FPO와 비교를 하면서 많이 설명할 것이기 때문에 한번씩은 읽으면 좋을 것 같습니다.
https://dokhakdubini.tistory.com/228?category=809542
Fake EBP기법은 FPO와 같이 ip(instruction pointer)를 변경시켜서 원하는 값에 접근하는 공격기법인데요.
우선 이 기법을 사용하려면 sfp, 그리고 ret까지 overflow가 가능해야합니다.
0. 기본지식
우선 Fake EBP를 이해하기 위해서는 함수 에필로그인 leave, ret을 잘 알고 있어야 합니다. 잘 모르는 사람은 따로 설명해놓은 글이 있으니 참고합시다. 이 글에서는 함수 에필로그를 안다는 전제 하에 진행합니다.
https://dokhakdubini.tistory.com/227?category=809542
결론적으로 함수의 에필로그는 leave와 ret로 이루어져 있고, 각 명령어는 다음과 같은 어셈블리어로 이루어져있습니다.
Internal of "Leave" move esp, ebp pop ebp |
Internal of "Ret" pop eip jmp eip |
한 줄로 설명하자면, 현재 있는 함수 이전의 ebp값과 주소로 돌아가게 하는 과정입니다. 그 이전의 ebp주소를 sfp가 저장하고 있고, 이전의 주소는 ret의 값으로 덮어져있습니다. 이제 이를 이용하여 공격을 할 예정입니다.
1. 공격방법
공격기법을 설명할때 항상 그래왔듯, 우선 페이로드를 구성하고, 그렇게 공격할 때 어떤 방식으로 공격되는지 보여준 후 마지막으로 설명한 뒤 마무리하겠습니다.
예시 코드를 보여드리겠습니다. 코드는 LOB 16단계 문제코드입니다.
/*
The Lord of the BOF : The Fellowship of the BOF
- zombie_assassin
- FEBP
*/
#include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[])
{
char buffer[40];
if(argc < 2){
printf("argv error\n");
exit(0);
}
if(argv[1][47] == '\xbf')
{
printf("stack retbayed you!\n");
exit(0);
}
if(argv[1][47] == '\x40')
{
printf("library retbayed you, too!!\n");
exit(0);
}
// strncpy instead of strcpy!
strncpy(buffer, argv[1], 48);
printf("%s\n", buffer);
}
우선 이 문제의 스택 구조는 다음과 같습니다.
문제 자체는 굉장히 간단하기 때문에 크게 어려운 부분은 없습니다.
이 문제의 공격 시나리오는 다음과 같이 설계할 수 있습니다.
공격 payload는 다음과 같습니다.
shellcode의 주소[4] + shellcode+NOP[36] + buffer-4의 주소[4] + leave-ret 가젯의 주소[4]
shellcode는 저부분이 아니라 다른 곳에 넣어도 되지만, 일단 쉽게 설명하기 위해서 buffer에 삽입하였습니다. 이제 이 상태로 프로그램이 종료되면 어떤 일이 일어나는지 보겠습니다. 함수에서 leave-ret이 일어나는 부분부터 순서대로 그려보겠습니다.
먼저 leave입니다.
처음에는 mov esp, ebp가 일어나므로 esp의 값이 ebp와 같아집니다. 오른쪽에 buffer의 부분이 회색으로 변하는데, 값이 초기화되거나 그러지 않습니다. 다만 크게 볼 필요가 없다는것을 나타내고 싶었습니다.
그리고 0xbffffaa4처럼 주소를 적어놓은 부분이 있는데, 이건 제가 임의로 설정한 값이기 때문에 문제마다 값은 다를 수 있습니다.
다음은 pop ebp입니다.
원래는 이전 함수에서의 ebp값으로 pop을 했겠지만, 우리가 0xbffffaa0이라는 buffer-4의 주소값으로 덮어주었기 때문에 ebp는 buffer-4의 주소를 가리키고 있고(매우중요!), esp는 pop했기 때문에 sfp밑에있는 ret를 가리키고 있게 됩니다. 실제로 쓰이지 않을것이기때문에 0xbffffaa0에는 쓰레기값이 있다고 써놓았지만, 굳이 따지고 실제로 확인해본다면 buffer의 주소(0xbffffaa4)가 들어가있을겁니다. 아무튼 이건 중요하지 않으니 pass
다음은 ret입니다.
우선 ret안에 0x80484df라는 값이 들어가있는 것을 볼 수 있습니다. 이건 그냥 실제 함수를 disassemble해서 찾아내었습니다.
(gdb) disas main
Dump of assembler code for function main:
0x8048440 <main>: push %ebp
0x8048441 <main+1>: mov %esp,%ebp
...
0x80484dc <main+156>: add $0x8,%esp
0x80484df <main+159>: leave
0x80484e0 <main+160>: ret
0x80484e1 <main+161>: nop
...
End of assembler dump.
(gdb)
다음과 같이 leave와 ret은 붙어있기때문에 leave가 실행된 후에는 ret가 자동적으로 실행될 수 있겠죠?
아무튼, eip안에는 0x80484df라는 값이 들어가게 되고, 이 주소로 jump하게 됩니다.(jmp eip에 의해)
근데 이 eip안에는 leave의 instruction의 주소가 있었죠? 이것때문에 leave-ret이 한번 더 실행됩니다.
이 부분에서 FPO와 매우 비슷한데요, FPO는 sub함수가 있었기 때문에 sub함수가 종료되면서 1차적으로 leave-ret이 실행되고, main함수의 leave-ret이 2차적으로 실행되면서 shellcode를 실행시켰다면, Fake EBP기법의 경우에는 overflow를 이용해 leave-ret 가젯을 넣어줌으로써 억지로 leave-ret을 한번 더 실행시키는 방법입니다.
사실 이 두번째 leave-ret을 통해 shellcode가 실행된다고 이정도로도 설명을 끝낼 수 있지만, 이해가 안되는 사람들을 위해 두번째 leave-ret도 진행해 보도록 하겠습니다.
두 번째 leave입니다.
mov esp, ebp가 실행되면서 esp도 이전에 우리가 수정해놓은 ebp값인 0xbffffaa0를 가리키게 됩니다.
다음은 pop ebp입니다.
pop ebp를 통해 ebp안에는 0xbffffaa0안에 있던 값이 들어가게 되고, pop했기 때문에 esp는 0xbffffaa4를 가리키게 됩니다. 이 0xbffffaa4안에는 shellcode의 주소값이 들어가있구요.
다음은 ret입니다.
pop eip를 하게 되면 eip안에는 0xbffffaa8이 들어가게 됩니다. 그런데 0xbffffaa8안에는 어떤 값이 있죠?
그렇죠. shellcode가 안에 들어가 있기 때문에 마지막으로 jmp eip가 실행되면서 우리가 원했던 shellcode가 실행되면서 shell이 따이게 됩니다.
저는 개인적으로 공부하면서 왜 shellcode자체가 아니라 shellcode의 주소가 들어가는지, 왜 buffer의 주소가 아니라 buffer-4의 주소가 들어가는지 많이 헷갈려했습니다.
이걸 이렇게 헷갈리는 이유는 하나하나 파헤쳐보지 않아서 그렇습니다. 조금이나마 도움이 되었으면 좋겠습니다.
이 문제를 푸는데 이용되었던 LOB 16번의 풀이는 여기 있습니다.
https://dokhakdubini.tistory.com/248
끝!
'SYSTEM HACKING > PWNABLE&REVERSING' 카테고리의 다른 글
[Stack] Stack Frame 공부하기 - 1 (0) | 2021.03.01 |
---|---|
함수의 got 구하기 (0) | 2020.10.09 |
[Stack] RET sled에 대하여 (0) | 2020.08.08 |
[Stack] Frame Pointer Overflow, FPO에 대하여 (0) | 2020.07.30 |
[stack] 함수의 에필로그(epilogue) (0) | 2020.07.30 |