이번에는 함수로 argv[1] 인자를 전달해서 포인터 변수 src가 가리키게 한다. Strncpy 함수로 buffer에 src을 41 길이만큼 복사한다.
strncpy 함수가 strcpy 함수와 다른 점은, strcpy 함수가 null을 만날 때까지 복사했다면, strncpy 함수는 딱 주어진 n 바이트의 크기만 복사한다. 복사한 n 바이트에 null이 없으면, 복사한 것이 저장되는 buffer에는 null로 끝나지 않는 문자열인 것이다.
그렇다면 strncpy에서 41바이트를 복사하는 것은 buffer 크기에 1바이트를 더 복사한다는 것인데 1바이트만큼만 overflow를 발생시킬 수 있다.
buffer 40byte 뒤의 1byte라면 sfp(프레임 포인터) 4byte 중 처음 1byte가 해당할 것이다. sfp에는 이전 ebp값이 들어있다.
Main 함수에서 problem_child 함수로 넘어가면 problem_child 함수의 ebp를 새로 설정해야 한다. 하지만 이 함수가 끝난 후 main 함수의 ebp가 다시 필요하기 때문에 main 함수의 ebp를 sfp에 저장해놓는다.
Leave 명령 실행 후 ebp 값이 이전 함수의 ebp 값을 돌려받는다. Leave 명령은 move ebp, esp pop ebp 명령을 수행한다. Esp 값을 현재의 ebp, 즉 sfp가 있는 곳으로 이동시키고, sfp값을 pop해서 ebp에 저장한다. 따라서 main 함수의 ebp 값은 현재의 ebp값에 저장되고, esp 값은 sfp를 가리키고 있다가 pop ebp에 의해 4가 증가하여 ret을 가리킨다.
Main 호출
Problem_child 호출
Overflow가 일어나서 problem_child()의 sfp부분만 값이 변경되었다고 하면,
Problem_child()가 종료되면서 leave 수행
이 때 esp는 problem_child()의 ebp, 즉 sfp를 가리키게 되고, pop ebp, 즉 main()의 ebp를 돌려 받는 과정에서 변경된 sfp값을 가져오게 되므로 main()의 ebp는 main()의 sfp를 가리키는 값이 아닌 다른 값을 갖게 된다.
그리고 main()의 leave 실행 시 mov ebp, esp에 의해 변경된 ebp로 esp가 이동하고,
Pop ebp에 의해 4바이트의 어떤 값이 꺼내진 후(이때 pop 명령에 의해 esp는 4 증가)
그곳에서 ret 명령에 의해 eip값을 얻어 다음 명령을 수행한다.
따라서 변경되어버린 ebp+4에 shellcode의 주소가 있으면 ret 명령에 의해 이 쉘코드 명령이 수행될 수 있다.
그렇다면 ebp+4 위치를 알아내서 쉘코드의 주소만 넣으면 된다는 것이다.
FPO(Frame Pointer Overwrite)
함수가 종료하기 전인 leave에서 breakpoint를 잡아서 실행시켜 인자를 주고, ebp를 출력해보면 현재 ebp는 0xbffffb0c를 가리키고 있다.
buffer 40byte에 A(41)로 채워져 있고, sfp, 즉 현재의 ebp가 가리키는 곳의 1byte가 41로 overwrite된 것을 확인할 수 있다.
Buffer 40byte에 “\xff”로 채워졌고, sfp의 1byte가 “\xff”로 덮어진 것을 확인할 수 있다.
아래로 내려가면 argv[1]이 있다. 따라서 여기의 “\xff”*40+”\xdd”는 신경쓰지 않아도 된다. (buffer 다음의 sfp의 1byte가 덮어씌어진 것이기 때문에…main()에서는 인자만 함수에 전달한 것)
Argv[2]에는 NOP+shellcode가 있다.
Ret에 넣어야 할 주소는 NOP의 주소이므로 0xbffffc25를 주소로 선택하자.
Buffer를 NOP의 주소 0xbffffc25로 채우고, sfp는 0xbffffadd로 1byte를 “\xdd”로 변경한 것처럼 1byte를 변경해서 buffer 부분으로 이동하게 하면, buffer에는 모두 NOP의 주소로 채워져 있어서 ebp+4를 해도 NOP의 주소이므로 ret에는 NOP의 주소가 들어가게 되므로 shellcode가 실행될 수 있다.
저 dd 부분을 24로 overwrite하면, buffer의 시작주소 0xbffffa24가 sfp가 되어 main의 ebp가 0xbffffa24로 바뀌게 되고, 그곳은 NOP의 주소 0xbffffc25로 되어있다. Ret은 ebp+4에 위치해서 0xbffffc24에서 4byte 위치로 가게되는데, 그곳에도 0xbffffc25가 들어가 있어서 shellcode가 실행될 수 있다.
./dark `python -c ‘print “\x25\xfc\xff\xbf”*10+”\x24”’` `python -c ‘print “\x90”*100+” \x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80”’`
임시파일로는 성공했다. 이제 원본파일에서 exploit해보자.
원본파일에 대한 심볼릭 링크 파일을 만들어서
exploit하면 성공!
“new attacker”
[LOB] level 14: bugbear -> giant (0) | 2019.05.15 |
---|---|
[LOB] level 13: darkknight -> bugbear (0) | 2019.05.15 |
[LOB] level 11: skeleton -> golem (0) | 2019.05.15 |
[LOB] level10: vampire -> skeleton (0) | 2019.05.01 |
[LOB] level8: orge->troll (0) | 2019.04.28 |