Save Frame Pointer Overwrite (SFO)
개념
Buffer Over Flow (이하 BOF, bof)가 일어나는 프로그램 취약점을 이용하여 Save Frame Pointer를 덮어씌워 원하는 주소로 이동하는 기법.
설명
앞서 BOF에 대해서 공부를 하셨다면 SFP Overwrite도 쉽게 이해 할 수 있을 것입니다.
SFP는 Save Frame Pointer로 이전 함수의 ebp를 저장해 두어 함수의 실행이 끝나고 이전 함수로 돌아가기 위해 저장해두는 역할을 하게 됩니다.
위 C 코드를 보면 main 함수에서는 출력과, sub_func()을 호출합니다.
sub_func에서는 buf가 8개의 공간이 할당 되어 있고, scanf를 이용하여 buf에 값을 저장합니다.
앞서 BOF의 포스팅 내용에 단지 sub_func만 추가된 코드입니다.
위 어셈블리 코드를 보면 puts와 sub_func의 호출을 볼 수 있습니다.
C 코드에서는 printf를 이용하여 작성하였는데, 막상 어셈블리 코드로 보니 puts로 변경된 것을 알 수 있는데, 이것은 컴파일러가 컴파일 하면서 바꾸게 됩니다.
main 함수에서 printf에는 단지 문자와 \n만 있어서 puts로 바꿔버린 것입니다. 이게 중요한게 아니고...
마찬가지로 puts() 또한 "Main Func \n"이라는 인자를 필요로 하므로 esp에 0x8048540이라는 값을 넣어 스택에 저장해 둔 것을 볼 수 있습니다.
자 그러면 0x804847f에 브레이크 포인트를 걸고 해당 주소까지 실행시켜 보도록 하겠습니다.
해당 위치에서의 esp 위치부터 10개의 값을 가져왔습니다.
puts에서 사용하기 위해 0x8048540을 스택에 쌓아둔 값이 보이네요.
자 이제 여기서 sub_func 함수 안으로 들어가 보도록 하겠습니다.
sub_func 함수 내로 들어온 후 esp 값을 확인 해 봤습니다.
스택에 쌓여진 값을 확인하면 0x8048484라는 값을 확인 할 수 있는데 이 값은 main함수에서 sub_func가 있는 곳의 다음 주소를 나타냅니다.
다음 주소를 쌓아두는 이유는 sub_func이 끝나면 main에서 다음 소스코드를 실행해야 하기 때문에 돌아가기 위해서 쌓아둔 것입니다.
0x804849f에 브레이크 포인트를 걸고 해당 주소까지 실행 후 스택을 확인해 보도록 하겠습니다.
마찬가지로 scanf가 사용할 인자 값 2개는 0x804854b와 0xbffff144 두가지 입니다.
0x804854b는 "%s"를 나타낼 것이고 0xbffff144는 buf의 주소를 나타낼 것입니다.
0xbffff144의 위치를 확인 해 보면 아직 값을 넣지 않았기 때문에 0으로 되어있는 것을 알 수 있습니다. scanf의 다음 주소에 브레이크 포인트를 걸고 값을 입력 후 다시 확인 해 보겠습니다.
12345678을 입력한 후의 buf의 값을 확인한 결과 입니다.
1234 | 5678 | SFP | RET 순으로 저장되어 있는 것을 알 수 있습니다.
SFP의 값은 이전 함수의 ebp를 저장한다고 했습니다.
main 함수에서 sub_func가 끝나고 난 뒤의 주소인 0x8048484에 브레이크 포인트를 걸고 해당 주소까지 실행을 시킵니다.
이렇게 0x8048484의 위치에 멈춘 상태에서 ebp를 확인해 보면 0xbffff100인 것을 알 수 있습니다.
바로 SFP가 이러한 역할을 담당하고 있습니다.
그러면 이제 SFP Overwrite를 해서 다른 주소로 이동하는 것을 해보도록 하겠습니다.
그전에 python 스크립트를 이용하여 input을 만들어 줍니다.
python -c 'print "1234" + "5678" + "\x4c\xf1\xff\xbf"' > input
그리고 해당 프로그램을 다시 실행하는데, r < input 과 같이 실행하면 scanf에 입력이 들어가게 됩니다.
SFP의 값이 0xbffff14c로 변경된 것을 알 수 있습니다.
0xbffff14c는 현재 sub_proc의 ebp를 나타냅니다.
이렇게 한 후 실행 시켜 보도록 하겠습니다.
어디 이상한 곳으로 이동했지만 ebp를 확인해 보니 SFP Overwrite 를 이용해서 만든 0xbffff14c인 것을 알 수 있습니다.
이렇게 SFP를 Overwrite를 시켜서 ebp의 주소를 이용할 수 있게 됩니다.
이해
LOB의 golem의 소스코드를 가지고 분석하면서 이해를 해보도록 하겠습니다. LOB를 다시 만들어두기 귀찮...
값을 main에서 인자 값으로 받아오고 해당 값을 src에서 받아서 strncpy를 이용해 41만큼 buffer에 복사합니다.
여기서 중요한 부분은 strncpy가 41만큼 복사를 한다는 것이죠.
버퍼가 40이니 1을 더 복사하는 것은 SFP에 침범 할 수 있다는 말이 됩니다.
그러면 41만큼 넣어 보도록 하겠습니다.
일단 쓰잘떼기 없는 부분은 넘어가고 problem_child 위치에 브레이크 포인트를 걸고 `python -c 'print "A*40 + "B"'` 를 이용해 argv[1]에 값을 넣습니다. 그리고 problem_child에 들어갑니다.
strncpy의 위의 어셈블리 코드를 보면 ebp-0x28을 볼 수 있습니다. 해당 주소가 buffer의 주소인 것을 알 수 있습니다.
0x804849d에 브레이크 포인트를 걸고 buffer의 값을 확인해 보도록 하겠습니다.
40개의 A와 B를 넣었더니 위와 같이 buffer에 채워졌습니다.
여기서 중요한 것은 SFP의 값이 0xbffff142 이라는 것입니다. 40개를 채우고 마지막 B를 채워 SFP의 1바이트가 42로 채워진 것입니다.
이제 이 변조된 SFP를 어떻게 이용할까요?
leave와 ret가 중요합니다.
main에서의 leave와 ret가 실행 되면서 조작되었던 SFP가 이용되는 것입니다.
그러면 leave와 ret에 대해서 알아야 하는데, 앞서 bof에 대한 포스팅 내용에서 에필로그라는 말을 했었습니다.
leave는
mov esp, ebp
pop ebp
ret는
pop eip
jmp eip
입니다.
일단 main의 leave까지 이동해 보겠습니다.
ebp를 확인 해본 결과 조작되었던 0xbffff142가 되었습니다.
leave를 수행하게 되면, mov esp, ebp pop ebp 이므로 ebp의 값을 esp로 옮기고 pop하여 ebp에 저장합니다.
변조 된 ebp의 값이 mov esp, ebp를 통해서 esp에 저장되었습니다.
pop ebp를 하면서 pop을 하게 되어서 +4 만큼 증가하게 되었습니다.
그래서 esp의 위치가 0xbffff146이 됩니다. (0xbffff142 + 4)
이제 ret 를 수행하게 되면, pop eip jmp eip가 됩니다.
즉, 스택에 쌓여있는 값을 eip에 넣고 해당 eip로 이동하게 되는 것입니다.
스택에 쌓여있는 값인 0xf164bfff이 eip에 저장이 되고, 해당 주소로 이동하게 됩니다.
이러한 과정을 통해서 값을 넣어서 SFP Overwrite를 시켜 조작 할 수 있습니다.
간략히 말하자면
1. 처음 넣은 4바이트 값은 leave에서 pop된다.
2. leave에서 pop이 되면서 조작된 SFP의 + 4를 이용하게 된다.
3. SFP + 4의 값에 쉘 코드를 넣으면 실행이 된다.
'High Level Technique > System Hacking' 카테고리의 다른 글
Return To Library (RTL) (2) | 2016.04.20 |
---|---|
Plt 와 Got (0) | 2016.04.20 |
Fake EBP (0) | 2016.04.19 |
main의 SFP는 왜 0이 되는가? (1) | 2016.04.19 |
Buffer Over Flow (BOF) (5) | 2016.04.15 |