2015/5/15 16:04:49
前面我们简单介绍了Hacker的编程基础,然后讲解了Linux下缓冲区漏洞的基本原理,从中我们了解到一个典型的缓冲区漏洞***总是由下面三个部分组成:
具体的实现可能会有变化,但是“万变不离其宗”,基本原理还是不变的。我们注意到***功能的实现由shellcode来体现,可以说shellcode也是一个缓冲区***的核心部分。今天我们来了解下shellcode的知识,首先是一个入门,来介绍下shellcode的使用策略。本节要点如下:
什么是shellcode?
用户空间的shellcode
基本shellcode
端口绑定shellcode
反向shellcode
查找套接字shellcode
shellcode的变化
一、什么是shellcode?
shellcode就是上图缓冲区溢出***中的实际***代码,由于最早的时候Hacker***的结果是控制EIP执行***指令返回一个目标系统的交互shell,因此这段***代码就被称作shellcode。随着时代的演变,shellcode的功能也逐渐丰富,最早的时候shellcode用于从一个没有***者账户的计算机上返回一个登录shell,或者已有一个账户,则用来提升shell的操作权限,最理想的情况就是获得root特权或者管理员权限;现在还有一些shellcode加入了内存进程操作、网络通信等功能,但是其名称却一直沿用下来。shellcode的名称也提醒着人们***最初的形式。
二、用户空间的shellcode
这个部分我们会分别介绍下实际使用中shellcode的使用策略,即shellcode一般实现什么样的功能。我们会着重介绍下shellcode的基本模式、端口绑定模式以及套架子查找模式,然后对剩下的使用方式简要地说明。
1. 基本shellcode
shellcode用于执行一段***者希望的代码,一般的功能如建立账户、文件操作、建立新进程等,而无论是哪个操作,都需要系统底层内核的配合。
这里我们来简单介绍下操作系统的结构。出于安全性和稳定性的考虑,一般操作系统会将内存空间分成用户态和内核态,也可以称作用户空间和内核空间。用户态主要负责数据的数据存储、运行程序的跟功能,而这些操作真的落实到计算机的实际资源(如内存、硬盘)则需要内核态的代码来代理执行。这样操作系统向用户提供了一个封装的操作核心函数接口,用户只需要使用就可以,即使用户程序崩溃,也不会影响系统的整体稳定。
不同的系统有不同的机制进入内核态。类Unix中一般使用软中断来实现,即发送给CPU一个0x80命令,然后系统会陷入内核空间。而Windows中则必须使用标准的API来实现。
在了解了用户态与内核态的区别后,我们来看看基本的shellcode模式,一般来说,shellcode会在目标机器上建立一个新的shell进程,然后同***者进行通信,接受***者的指令,返回结果。这里有两个关键的问题:
如何建立新的shell进程?这个问题是比较容易理解的,类Unix下使用execve命令,Windows下可以使用CreateProcess API;
如何实现shell与***者的通信?这个没有那么好理解了,首先我们要知道的是,无论什么操作系统,每建立一个进程就会为其绑定三个文件描述符作为输入输出的指定资源,即stdin(标准输入)、stdout(标准输出)、stderr(标准错误提示) 三个文件描述符,一般默认都是屏幕或终端。这里我们需要在新建shell进程前设置好这三个文件描述符,从而可以建立shell进程同***者的通信。
大家可以将基本的shellcode模式理解成本地漏洞***,此时返回shell后可以直接在终端中进行通信。
2. 端口绑定shellcode
在上面的模式中,迁移到一个网络远程环境下,那么现在需要建立***者和shell进程间一个网络连接,我们的想法是shell进程建立一个TCP套接字,然后绑定一个预先指定的端口(如6666),然后开启监听模式;同时***者发起连接到目标的6666端口,建立与shell进程的通信。关键步骤如下:
创建一个TCP套接字;
将套接字绑定到***者指定的端口(硬编码到shellcode中);
开启该套接字的监听模式;
接受新连接;
将新接受的套接字复制到stdin、stdout、stderr上;
创建一个新的shell进程,通过这个新套接字收发数据;
具体如图:
一般此时***者可以使用Netcat之类的工具来实现通信。但是这种模式存在缺点,比如如果网络不允许外部进入的连接,则shell无法建立通信;并且细心的管理员会发现可疑的shell进程和监听模式的套接字。
3. 反向shellcode
为了解决上面提到的第一个问题,即外部连接禁止的情况,我们可以转换思路,使得shellcode进程主动发起连接,由网络内部来连接我们,故而称作“反向shellcode”。 关键步骤是:
创建一个TCP套接字;
配置该套接字,连接到***者预先指定的IP和Port;
连接到指定的IP和Port;
将建立连接的套接字复制到stdin、stdout、stderr;
创建新的shell进程进行通信;
具体如图:
这里的问题同样是如果不允许内部80端口以外的端口发起连接,则shell通信也无法建立。另外,可疑的shell进程和套接字依旧容易引起细心管理员的怀疑,有一定的暴露风险。
4. 查找套接字shellcode
我们的第三种建立shell的方法就是复用已有的连接,既然已经有了80端口的网络连接,何苦自己再建立一条呢?如果可以复用80端口的连接,不仅节省了代码开支,而且还具有很好的隐蔽性。这里的关键问题是获取当前连接的套接字,这也是“查找套接字shellcode”的来由,一般的方法是遍历0-255的描述符,来确定到底当前使用的是哪一个。这里的关键步骤是:
对于256个可能的文件描述符判断每个描述符是否表示一个有效的网络连接,如果是,判断远程端口是否是我们已经在用的端口(这个远程端口提前硬编码到shellcode中);
一旦找到期望的套接字描述符后,将其复制到stdin、stdout、stderr;
创建一个新的shell进程通过原始套接字来收发数据;
具体如图:
5. shellcode的变化
除了以上几种最初的shellcode模式外,现在还存在多种功能的shellcode,比如:
命令执行代码:shellcode用于执行特定的命令,如将SSH的公钥复制到目标计算机上便于日后SSH连接访问;比如修改配置文件允许后门shell进行访问等;
文件传输代码:这种代码用于在目标机器上下载一个文件,功能类似于下载器,或者wget;
多级shellcode:为了突破缓冲区的一些限制(比如大小、权限),采用多层shellcode,第一级shellcode的唯一作用是读取更多的shellcode,然后将控制权传给第二级shellcode,以此类推;
系统调用代理shellcode:这种模式不需要在目标机器上下载代码,只需要在进程的上下文中运行着shellcode,然后***者输入一个单纯的系统调用指令,shellcode执行后返回结果,可以开发出方便的代理库同shellcode通信;
进程注入shellcode:这类的典型应用是MSF的meterpreter,在一个进程的独立线程下面运行整个代码库的加载任务。这部分可以参考http://www.metasploit.cn/thread-736-1-1.html
三、内核空间的shellcode
内核空间也存在漏洞利用的可能,但是由于公开信息很少,因此研究并不多,但是现在越来越多的人正在研究内核中的漏洞利用。这里编写shellcode特别需要注意的就是稳定性,在用户态中,shellcode崩溃仅仅导致用户进程中止,但是内核态中却会引起系统崩溃。所以要特别小心的调试。
另外,内核态中没有了系统调用的概念,只能使用由内核导出的函数,而这些函数的资料和说明也并不丰富。
因此,更为稳妥的方法是,将内核态的shellcode运行后加载到一个用户态中较高进程的空间中运行。
Refer: Gray Hat Hacking: The Ethical Hacker's Handbook, Third Edition