分享
 
 
 

Nessus安全测试插件编写教程

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Nessus安全测试插件编写教程

作者:Renaud Deraison(Nessus最主要的编写者,法国人)

整理:翻译:nixe0n

日期:2002-07-17

发布日期: 2002-7-2

内容:

----------------------------------------------------------------------版本:1.0.0pre2

1.简介

1.1.什么是NASL?

NASL是一个为网络安全扫描工具Nessus开发的脚本语言。通过它,任何人都可以方便快

速地针对新出现的漏洞编写出测试插件,也便于不同操作系统的用户分享测试脚本。除

此之外,NASL还可以保证编写的脚本只能用于针对目的主机的测试,使编写者难以使用

编写的脚本用于恶意用途。

使用NASL,你可以很容易地打造IP报文,或者发送通常的报文。NASL中还有一些专门的

函数用于向FTP和WEB服务器发送数据。另外,NASL还可以保证:

除了目标主机之外,不向任何的主机发送报文。

不允许在本地系统执行任何命令。

1.2.What NASL is not

NASL不是一种功能很强大的脚本语言。它的目的只是用于安全测试。因此,别指望使用

这种脚本语言写出第三代的WEB服务器或者文件转换工具,要编写此类软件还是使用Per

l、Python或者其它的脚本语言吧。用它们编写要比使用NASL快100倍。

另外,由于NASL的设计有些仓促,在语法上还有一些需要改进的地方。

1.3.为什么不在Nessus中使用Perl、Python、tcl或者其它脚本语言

我知道有很多功能非常强大的脚本语言,和它们相比NASL功能要弱很多。不过,虽然这

些语言都非常强大,但是它们都不太安全。使用这些语言,你可以非常容易地编写出木

马检测插件,泄露你的信息,让第三者知道你是一个Nessus用户,甚至会把一些敏感信

息(例如:密码文件)发送到第三方主机。

使用这些语言还有另外一个问题,它们都会消耗大量的系统资源,尤其是内存。这非常

令人头疼。以Perl为例,Perl非常棒,并且非常优美。但是,如果要使用它编写Nessus

的测试插件,你需要消耗大量的时间安装必须的模块,Net::RawIP就是其中之一。

与此相反,NASL根本不会消耗大料的内存。因此,即使没有256M内存,你也可以同时启

动20个nessusd线程。而且,对于编写检测插件,NASL本身就足够了,你不必为了为了编

写新的安全检测插件而安装大量的软件包。

1.4.为什么你应该自己编写安全测试插件

你可能会盘算为了自己编写Nessus安全测试插件而在学习一种脚本语言是否值得?但是

,你要知道:

NASL为Nessus做过专门的优化,因此使用NASL编写的安全测试插件效率很高。

在很多方面,NASL和C非常类似,因此你没有必要担心很难掌握。

NASL非常适合编写安全测试插件。

NASL的移植性很好。在M$版本的Nessus发布之后,所有的安全测试插件根本勿需修改,

就可以使用。

1.5.这个教程会教你一些什么东西

这个教程的目的是教你如何使用NASL编写自己的Nessus安全测试插件。

1.6.NASL的局限

我在上面讲过,NASL不是一种强大的脚本语言。它最大的局限是:

结构(structure)。目前NASL还不支持结构,可能在不久的将来可以支持。

一个调试程序。NASL还没有一个合适的debug程序。不过,有一个单独的解释程序nasl可

以暂时用于排错。

1.7.感谢

下面这些人为NASL的设计提出了高贵的意见,作者在此致谢:

Denis Ducamp(denis@hsc.fr)

Fyodor(fyodor@dhp.com)

Noam Rathaus(nomr@securiteam.com)

2.NASL基础:语法

在语法上,NASL非常类似于C,只是去掉了一些烦人的东西。你勿需顾及对象的类型,也

不用为它们分配和释放内存;在使用变量之前不必事先声明。这样,你就可以只致力于

安全测试插件的的编写。

如果你以前不懂C语言,读这个教程可能要费点劲,如果你对C语言已经很精通,读本教

程将非常轻松。

2.1.注释

在NASL中,注释符是#。它只对当前行有效,例如:

有效的注释:

a = 1 ; #let a = 1

#set b to 2

b = 2;

无效的注释:

#

set a to 1

#

a = 1;

a = # set a to 1 # 1;

2.2.变量、变量类型、内存分配和包含(include)

与C语言不同,在使用变量之前,你不用事先声明,也不用关心它们的类型。如果你的操

作错误(例如:把一个IP报文和一个整数相加),NASL就会提醒你。你也不必关心C语言中

经常遇到的内存分配和包含(include)等问题,在NASL中没有include,而且内存是在需

要时自动分配。

2.3.数字和字符串

NASL中的数字可以使用三种进制:十进制、十六进制和二进制。例如:

a = 1204;

b = 0x0A;

c = 0b001010110110;

d = 123 + 0xFF;

数组必须使用引号。注意:和C语言不同,除非使用string()函数,否则NASL解释器将不

解释特殊字符(例如:\n)。例如:

a = "Hello\nI'm Renaud"; #a等于Hello\nI'm Renaud",\n没有特殊含义

a = string("Hello\nI'm Renaud");#b等于"Hello

# I'm Renaud"

c = string(a); #c等于b

string()函数将在“字符串处理”中详细讨论。

2.4.匿名/非匿名参数

非匿名函数(Non-anonymous Function)

NASL对函数参数的处理方式也C语言也不相同。在C语言中,程序员必须只参数的位置。

如果一个函数的参数超过10个,就非常让人头疼。例如,一个构造IP报文的函数就可能

有很多参数。如果你需要使用这个函数,就得记住参数的确切次序,这非常浪费时间。

在NASL中尽量避免出现这种情况。

在NASL中,当函数的参数次序比较重要,并且当这个函数不同的参数是不同的类型,这

个函数就是一个非匿名函数。也就是,你必须给出元素名。如果你忘记了某些元素,在

运行时NASL会给你错误提示。例如:

forge_ip_packet()函数有很多参数。以下两种调用方式都有效并且执行相同的操作:

forge_ip_packet(ip_hl:5,ip_v:4,ip_p:IPPROTO_TCP);

forge_in_packet(ip_p:IPPROTO_TCP,ip_v:4,ip_hl:5);

在运行时,用户会被提示缺少参数(ip_len等)。

匿名函数(Anonymous function)

如果函数只有一个参数,或者所有参数的类型是相同的,这种函数就叫做匿名函数。例

如:

send_packet(my_packet);

send_packet(packet1,packet2,packet3);

这些函数可以有选项。例如:在使用send_packet()函数时,你可以决定是否等待回应。

如果你感觉没有必要接收目标的回应,你可以使用如下调用形式来加速安全测试速度:

send_packet(packet,use_pcap:FALSE);

2.5.for和while

在NASL中也存在for和while两种循环控制,和C语言的几乎完全相同,其语法格式如下:

for(instruct_start;condition;end_loop_instruction)

{

#

#需要执行的代码

#

}

或者

for(instruction_start;condition;end_loop_instruction)fuction();

While的格式:

while(condition)

{

#

#执行的代码

#

}

或者:

while(condition)function();

例如:

# 显示从1到0

for(i=1;i<=10;i=i+1)display("i : ",i,"\n");

# 显示从1到9以及它们是奇数还是偶数

for(j=1;j<=10;j=j+1){

if(j&1)display(j," is odd\n");

else display(j," is even\n");

}

# 使用while

i = 0;

while(i<10)

{

i=i+1;

}

2.6.用户定义的函数

NASL允许用户定义自己的函数。用户可以使用如下的语法定义自己的函数:

function my_func(argument1,argment2,....)

用户定义的函数必须使用非匿名(non-anonymous)参数,NASL能够处理递归调用。例如:

function fact()

{

if((n==0)││(n==1))

return(n);

else

return(n*fact(n:n-1));

}

display("b! is ",fact(n:5),"\n);

另外,用户自己定义的函数不能调用其它的用户定义函数(实际上是可以的但是遇到这种

情况,NASL解释器会向你发出警告)。

注意:如果你需要让自己的函数返回一个值,需要使用return()函数。因为return()是

一个函数,因此需要有括号,下面这种写法就是错误的:

function func()

{

return 1; #这种写法在C语言中是可以的,但是在NASL中不性

}

2.7.操作符

一些标准的C语言操作符也可以用于NASL,包括:+、-、*、/和%。目前,NASL还不支持

操作符的优先级,但是以后版本将会支持操作符的优先级。另外,NASL也支持C语言的二

进制操作符│和&。

除此之外,NASL还有两个独有的操作符:

x操作符

对于某些简单的循环使用for或者while非常不便,而且每次循环还需要对条件进行检查

,造成效率的下降。因此NASL引入了一个x操作符来简化某些循环代码。例如:如果你需

要发出10次UDP报文,使用x操作符,只要下面一行代码就可以了:

send_packet(udp)x10;

><操作符

><操作符是一个布尔型操作符,表示如果一个字符串A包含在另一个字符串B中,就返回

真,例如:

a = "Nessus"'

b = "I like Nessus";

if(a><b){

#结果为真

display(a " is contained in ",b,"\n");

}

3.网络相关函数

3.1.套接字处理

套接字是使用TCP或者UDP协议和其它主机通讯的途径。在NASL中不允许你直接打开一个

和测试目标通讯的套接字,因此你只能使用NASL提供的函数打开套接字。

3.1.1.如何打开一个套接字

在NASL中,函数open_sock_tcp()和open_sock_udp()分别用于打开一个TCP或者UDP套接

字。这两个函数使用匿名(anonymous)参数。当前,你每次智能打开一个端口,将来的版

本将解决这个问题。例如:你可以使用如下代码分别打开一个TCP和UDP套接字:

#在80端口打开一个TCP套接字

soc1=open_sock_tcp(80);

#在123端口打开一个UDP套接字

soc2=open_sock_udp(123);

如果无法和远程主机建立连接,这两个函数会返回0。不过,通常open_sock_udp()不会

失败,因为没有办法确定远程主机的UDP端口是否开放,对于open_sock_tcp(),如果远

程主机的端口是关闭的,它就会返回0。

open_sock_tcp()可以用于对TCP端口的简单扫描,例如:

start = prompt("First port to scan?"); #输入开始的端口

end = prompt("Last port to scan?"); #输入结束的端口

for(i=start;i<end;i=i+1)

{

soc=open_sock_tcp(i);

if(soc){

display("Port ",i," is open\n");

close(soc);

}

}

3.1.2.关闭一个端口

关闭一个端口使用close()函数,在close()内部,关闭端口之前,它首先会调用shutdo

wn()函数。

3.1.3.读写套接字

根据被读写的套接字类型,可以选择使用如下函数完成这两项操作:

recn(socket:<socketname>,length:<length> [,timeout:<timeout>])

从套接字<socketname>读取<length>个字节,这个函数可以用于TCP和UDP。超时(timeo

ut)参数是可选的,以秒为单位。

recv_line(socket:<socketname>,length:<length> [,timeout:<timeout>])

这个函数和recv()函数类似,只是如果遇到换行(\n)操作终止。这个函数只能用于TCP套

接字。

send(socket:<socket>,data:<data> [,length:<length>])

从套接字<socket>发送数据<data>。可选参数length告诉函数发送<length>字节。如果

没有设置length,发送操作就在遇到NULL时终止。

如果没有设置超时参数,读函数(recv()和recv_line())就使用默认的超时时间5秒。如

果时间到,它们就返回FALSE。例如:

# 以下代码用于显示远程主机的banner信息

soc = open_sock_tcp(21);

if(soc)

{

data = recv_line(socket:soc,length:1024);

if(data)

{

display("The remote FTP banner is : \n",data,"\n");

}

else

{

display("The remote FTP server seems to be tcp-wrapper\n");

}

close(soc);

}

3.1.4.高层操作

NASL有一些针对FTP和WWW协议的函数,用于简化对这两个应用层协议的某些操作。

ftp_log_in(socket:<soc>,user:<login>,pass:<pass>)

尝试通过<soc>套接字登录到远程FP主机。如果用户名<login>和密码<pass>都正确,就

返回TRUE,否则返回FALSE。

ftp_get_pasv_port(socket:<soc>)

向远程FTP服务器发出一个PASV命令,获得连接的端口。NASL脚本可以通过这个端口从F

TP服务器下载数据。如果发生错误函数将返回FALSE。

is_cgi_installed(<name>)

测试远程WEB服务器是否安装了名为<name>的CGI程序。这个函数向远程WEB服务器发出G

ET请求实现这个目的。如果<name>不是以斜杠(/)开头,就认为它是相对于/cgi-bin/。

这个函数也可以用于确定某个文件是否存在。

示例脚本:

#

# 针对WWW服务器的测试

#

if(is_cgi_installed("/robots.txt")){

display("The file /robots.txt is present\n");

}

if(is_cgi_installed("php.cgi")){

display("The CGI php.cgi is installed in /cgi-bin/\n");

}

if(!is_cgi_installed("/php.cgi")){

display("There is no php.cgi in the remote web root\n");

}

#

# 针对FTP服务器的测试

#

# 打开一个连接

soc = open_sock_tcp(21);

# 匿名登录到远程FTP主机

if(ftp_log_in(socket:soc,user:"anonymous",pass:"joe@"))

{

# 打开一个被动传输模式的端口

port = ftp_get_pasv_port(socket:soc);

if(port)

{

soc2 = open_sock_tcp(port);

#尝试获得远程系统的/etc/passwd文件

data = string("RETR /etc/passwd\r\n");

send(socket:soc,data:data);

password_file = recv(socket:soc2,length:10000);

display(password_file);

close(soc2);

}

close(soc);

}

3.2.原始报文处理

NASL允许用户构造自己的IP报文,而且报文的定制是以一种智能的方式进行的。例如,

如果你改变了一个TCP报文的某个参数,就会造成其TCP校验和发生改变,但是你不必为

此费心,NASL会自动完成。

所有的原始报文构造函数都使用非匿名(non-anonymous)参数。参数的名字都是来自BSD

的包含文件。因此一个IP报文的长度域叫做ip_len而不是length。

3.2.1.构造IP报文

在NASL中,你可以使用forge_ip_packet()函数构造一个新的IP报文;使用set_ip_elem

ent()函数获得报文某个域的值;使用set_ip_element()函数改变现有IP跋文某个域的值

。forge_ip_packet函数的原形如下:

<return_value>=forge_ip_packet(

ip_hl :<ip_hl>,

ip_v :<ip_v>,

ip_tos :<ip_tos>,

ip_len :<ip_len>,

ip_id :<ip_id>,

ip_off :<ip_off>,

ip_ttl :<ip_ttl>,

ip_p :<ip_p>,

ip_src :<ip_src>,

ip_dst :<ip_dst>,

[ip_sum :<ip_sum>]);

其中,ip_sum参数是可选的,如果没有使用,NASL会自动计算报文的校验和。ip_p参数

可以是一个整数值,或者是IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP、IPPROTO_IGMP

或者IPPROTO_IP等常量中的某个值。

get_ip_element()函数的原型如下:

<element>=get_ip_element(

ip :<ip_varible>,

element :"ip_hl"│"ip_v"│ip_tos"│"ip_len"│

"ip_id"│"ip_off"│"ip_ttl"│"ip_p"│

"ip_sum"│"ip_src"│"ip_dst");

get_ip_element()将返回报文中的某个域的值。element参数必须是"ip_hl"、"ip_v"、

ip_tos"、"ip_len"、"ip_id"、"ip_off"、"ip_ttl"、"ip_p"、"ip_sum"、"ip_src"、

"ip_dst"中的一个,而且引号是必不可少的。

set_ip_elements()函数的原型如下:

set_ip_elements(

ip :<ip_variable>,

[ip_hl :<ip_hl>,]

[ip_v :<ip_v>,]

[ip_tos :<ip_tos>,]

[ip_len :<ip_len>,]

[ip_id :<ip_id>,]

[ip_off :<ip_off>,]

[ip_ttl :<ip_ttl>,]

[ip_p :<ip_p>,]

[ip_src :<ip_src>,]

[ip_dst :<ip_dst>,]

[ip_sum :<ip_sum>]

);

这个函数可以改变IP报文<ip_varible>的值,如果你没有修改ip_sum域的值,它会自动

重新计算。这个函数没有构造报文的能力,因此需要把它放在forge_ip_packet()函数之

后。

最后,还有一个函数dump_ip_packet()需要说明一下,这个函数能够以可读的方式把IP

报文的内容输出到屏幕。这个函数应该只用于调试目的。

3.2.2.构造一个TCP报文

forge_tcp_packet()用来构造TCP报文。函数原型如下:

tcppacket = forge_tcp_packet(

ip :<ip_packet>,

th_sport :<source_port>,

th_dport :<destination_port>,

th_flags :<tcp_flags>,

th_seq :<sequence_number>,

[th_x2 :<unused>],

th_off :<offset>,

th_win :<window>,

th_urp :<urgent_pointer>,

th_sum :<checksum>,

[data :<data>]);

其中,标志参数th_flags必须是TH_SYN、TH_ACK、TH_FIN、TH_PUSH或者TH_RST,这些标

志可以使用│操作符结合到一块。th_flags还可以使用一个整数值。ip_packet必须首先

由forge_ip_packet()函数产生或者使用send_packet()、pcap_next()函数得到的返回值

函数set_tcp_elements()能够修改TCP报文的内容,其原型如下:

set_tcp_elements(

tcp :<tcp_packet>,

[th_sport :<source_port>,]

[th_dport :<destination_port>,]

[th_flags :<tcp_flags>,]

[th_seq :<sequence_number>,]

[th_ack :<acknowledgement_number>,]

[th_x2 :<unused>,]

[th_off :<offset>,]

[th_win :<window>,]

[th_urp :<urgent_pointer>,]

[th_sum :<checksum>,]

[data :<data>]);

除非你自己设置th_sum参数,否则函数会自动计算报文的校验和。

函数get_tcp_element()用来设置TCP报文的内容,其原型如下:

element = get_tcp_elements(

tcp :<tcp_packet>,

element :<element_name>);

element_name必须是"tcp_sport"、"th_dport"、"th_flags"、"th_seq"、"th_ack"、"

th_x2"、"th_off"、"th_win"、"th_urp"、"th_sum"其中之一。注意:引号是不可缺少

的。

3.2.3.构造UDP报文

UDP报文构造函数forge_udp_packet()和TCP构造函数极为类似,其原型如下:

udp = forge_udp_packet(

ip :<ip_packet>,

uh_sport :<source_port>,

uh_dport :<destination_port>,

uh_ulen :<length>,

[uh_sum :<checksum>,]

[data :<data>]);

而set_udp_elements()和get_udp_elements()函数和TCP报文对应的处理函数用法也相同

3.2.4.构造ICMP报文

3.2.5.构造IGMP报文

3.2.6.发送报文

构造报文的操作完成之后,你可以使用send_packet()函数将它们发送出去,其原型如下

reply = send_packet(packet1,packet2,....,packetN,

pcap_active:<TRUE│FALSE>,

pcap_filter:<pcap_filter>);

如果pcap_active参数为TRUE,这个函数就会等待目标的回应。pcap_filter用来设置你

需要得到的报文类型,详情请参考pcap或者tcpdump的手册页。

3.2.7.读取报文

你可以使用pcap_next()函数读取一个报文,其原型如下:

reply = pcap_next();

这个函数将从你使用的最后一个接口读取一个报文,报文的类型取决于最后设置的pcap

类型。

3.3.工具函数

NASL还提供了一些工具函数以简化你的编程。

this_host()

获得运行脚本的主机IP地址,没有参数。

get_host_name()

返回当前被测试主机的主机名,没有参数。

get_host_ip()

返回当前被测试主机的IP地址,没有参数。

get_host_open_port()

获得远程主机打开的第一个端口号,没有参数。这个函数对于某些脚本(例如:land)非

常有用,有些TCP序列号分析程序需要通过这个函数获得远程主机一个打开的端口。

get_port_stat(<portnum>)

如果TCP端口<portnum>打开或者其状态是未知,就返回TRUE。

telnet_init(<soc>)

在一个打开的套接字上初始化一个telnet会话,并且返回telnet数据的第一行。例如:

soc = open_sock_tcp(23);

buffer = telnet_init(soc);

display("The remote telnet banner is ",buffer,"\n");

tcp_ping()

如果远程主机应答TCP ping请求(发送一个设置ACK标志的TCP报文),本函数就返回TRUE

,没有参数。

getrpcport()

获得远程主机的RPC端口号,原型为:

result = getrpcport(program :<program_number),

protocol :<IPPROTO_TCP│IPPROTO_UDP,

[version :<version>]);

如果远程主机的<program_number>程序没有在RPC portmap监控进程中注册就返回0。

4.字符串处理函数

NASL允许你象处理数字一样处理字符串。因此,你能够安全地使用==、<和>等操作符。

例如:

a = "version 1.2.3";

b = "version 1.4.1";

if(a<b){

#因为version 1.2.3比version 1.4.1低

#因此,开始执行这里的代码

}

c = "version 1.2.3";

if(a == c){

#两个字符串相等

#因此执行这里的代码

}

在NASL中,也可以获得一个字符串的某个字符,和C语言完全相同,例如:

a = "test";

b = a[1]; #b等于"e"

你也可以在一个字符串中加、减一个字符串,例如:

a = "version 1.2.3";

b = a - "version"; #b等于"1.2.3"

a = "this is a test";

b = "is a ";

c = a - b; #c等于"this test"

a = "test";

c = " is a ";

c = a - b; #a等于"testtest"

除此之外,><也可以用于字符串的处理。NASL有很多函数来构造或者修改字符串:

4.1.处理正则表达式的ereg()函数

在NASL中,模式匹配是由ereg()函数完成的。原型如下:

result = ereg(pattern:<pattern,string:<string>)

正则表达式的语法是egrep风格的。细节请参考egrep的手册页。例如:

if(ereg(pattern:".*",string:"test"))

{

display("Always execute\n");

}

mystring=recv(socket:soc,length:1024);

if(ereg(pattern:"SSH-.*-1\..*",string:mysting))

{

display("SSH 1.x is running on this host");

}

4.2.egrep()函数

egrep()函数返回一个多行文本中,匹配<pattern>的第一行。如果对单行文本使用这个

函数,它就相当于ereg()。如果没有匹配的行,它就返回FALSE。其原型如下:

str=egrep(pattern:<pattern>,string:<string>)

示例:

soc=open_soc_tcp(80);

str=string("HEAD / HTTP/1.0\r\n\r\n");

sen(socket:soc,data:str);

r=recv(socket:soc,length:1024);

server=egrep(pattern:"^Server.*",string:r);

if(server)display(server);

4.3.crap()函数

crap()函数非常便于测试缓冲区溢出,有两种原型:

crap(<length>)

获得一个以'X'填充的长度为<length>的字符串。

crap(length:<length>,data:<data>)

高一个长度为<length>的字符串,并使用<data>填充字符串。例如:

a=crap(5); #a="XXXXX"

b=crap(4096); #b="XXX...XXX"(4096个X)

c=crap(length:12,data:"hello"); #c="hellohellohe"

4.4.string()函数

这个函数用来定制字符串。其原型为:

string(<string1>,[<string2>,...,<stringN>]);

它能够解释字符串中\n、\t等特殊字符。例如:

name="Renand";

a=string("Hello,I am ",name,"\n"_; #a等于"Hello,I am Renaud"

#末尾回行

b=string(1," and "m",2," make ",1+2); #b等于"1 and 2 make 3"

c=string("MKD ",crap(4096),"\r\n"); #c等于"MKD XXX...XXXXX"(4096个X)

#后面是一个回车和一个回行

4.5.strlen()函数

strlen()函数返回一个字符串的长度,例如:

a==strlen("abcd"); #a等于4

4.6.raw_string()函数

这个函数能够把数字转换为对应的字符,例如:

a=raw_string(80,81,82); #80、81、82分别对应ASCII字符的PQR

4.7.strtoint()函数

这个函数把一个NASL整数转换为一个二进制整数。原型为:

value=strtolen(number:<nasl_integer>,size:<number_of_byte>);

这个函数比较适合于和raw_string()函数一块使用。size参数是NASL整数的字节数,可

以是:1、2、4。

4.8.tolower()函数

这个函数能够把一个字符串中的所有大写字符转换为小写字符。原型为:

string2=tolower(<string>);

例如:

a="Hello,World"

b=tolower(a); #b等于"hello,world"

5.总结

这是NASL Reference Guide的第一部分,主要介绍了NASL的各种函数。在下一部分我们

将系统地介绍如何编写Nessus安全测试插件。'

Nessus安全测试插件编写教程2

作者:Renaud Deraison

翻译:nixe0n

1.怎样编写一个高效的Nessus安全测试插件

在Nessus安全测试系统中,所有的安全测试都是由nessusd进程发动的。在测试期间,一

个好的测试插件必须能够有效地利用其它测试插件的测试结果。例如:一个测试插件需

要打开一个到FTP服务器的连接,而在这之前它应该首先检查端口扫描测试插件的结果,

确定FTP端口是否打开。在一般情况下,这样只会节约一点点时间,但是如果被测试主机

位于防火墙之后,这样做会节省由于防火墙丢弃到21端口的TCP报文造成的漫长等待时间

1.1.确定端口是否打开

get_port_state(<portnum>)函数用于获得端口的状态。如果端口为开,这个函数就返回

TRUE;反之,则返回FALSE;如果这个端口没有被扫描过,也就是其状态为未知(unknow

n),函数也将返回TRUE。

这个函数只消耗很少的CPU资源,因此你可以尽可能地使用它,来提高测试插件的效率。

1.2.基础信息(Knowledge Base,KB)

在测试过程中,Nessus会为每个主机维护一份由扫描测试插件获得的基本信息(Knowled

ge Base,这个词本来应该是基础知识的意思,但是这里似乎翻译作基本信息更为恰当^

_^)。各种其它的测试插件应该尽可能地利用这些信息,以提高测试效率。实际上,端口

的状态就保存在这里。

KB被分为好几类。Service类包含每个已知的服务和为其分配的端口号。例如,在大多数

情况下,Server/smtp的值为25。但是,如果远程主机的SMTP服务被隐藏于2500端口,这

个值就改为2500。

有关基本信息各个元素的细节请参考附录B。

在NASL中,有两个有关节本信息(KB)的函数。使用get_kb_item(<name>)函数可以获得基

本信息的<name>项的值,这个函数是匿名函数;而函数set_kb_item(name:<name>,valu

e:<value>)能够把<name>项的值设置为<value>。

注意:你不能获得刚刚加入的基本信息条目的值。例如,以下代码将无法象你所期待的

那样执行:

set_kb_item(name:"attack",value:TRUE);

if(get_kb_item("attack"))

{

#这里的代码不可能执行

#因为attack基本信息项并没有更新

}

之所以会这样,是出于安全和代码稳定性的考虑。在安全测试期间,Nessus服务器会为

每个安全测试插件维护一份基本信息(KB)拷贝,安全测试插件只是从自己的基本信息(K

B)拷贝中获得信息。而set_kb_item()函数只更新原始的基本信息(KB)拷贝,不对当前安

全测试插件使用的拷贝进行更新操作。

2.NASL脚本结构

每个安全测试插件需要向Nessus服务器进行注册后,才能使用。注册信息包括名字、描

述、作者等。每个NASL脚本都需要有以下结构:

#

#NASL基本基本结构

#

if(description)

{

#这里是注册信息

#

#这里可以叫做注册部分(register section)

#

exit(0);

}

#

#这里是脚本代码。我们可以称为攻击部分(attack section)

#

description是一个全局变量,值可以是TRUE或者FALSE,取决于脚本是否需要注册。

2.1.注册部分

在脚本的注册部分,必须调用以下函数:

script_name(language1:<name>,[...])

设置在Nessus客户程序窗口中显示的名称。

script_description(language1:<desc>,[...])

设置在Nessus客户程序中显示的描述信息。

script_summary(language1:<summary>,[...])

设置总结信息,必须在一行之内总结描述信息的内容。

script_category(<category>)

设置脚本的类别。必须是ACT_ATTACK、ACT_GATHER_INFO、ACT_DENIAL和ACT_SCANNER之

一。

ACT_GATHER_INFO

信息采集类脚本。这种脚本率先启动,不会对远程主机造成伤害。

ACT_ATTACK

这类脚本会尝试获得远程主机的某些权限,可能会危害远程主机(例如,如果运行缓冲区

溢出测试插件)

ACT_DENIAL

这种脚本会发起拒绝服务攻击,试图造成远程主机宕机。

ACT_SCANNER

端口扫描脚本。

script_copyright(language1:<copyright>,[...])

设置脚本的版权信息。

script_family(language1:<family>,[...])

设置脚本所属的族(family)。NASL对此没有明确的规定,你可以任意定义脚本所属的族

,例如:nixe0n's PowerTools",不过我不建议这样做。当前使用的族名有:

Backdoors

CGI abuses

Denial of Service

FTP

Finger abuses

Firewalls

Gain a shell remotely

Gain root remotely

Misc

NIS

RPC

Remote file access

SMTP problems

Useless services

你可能注意到了,以上所有的函数都有一个叫做language1的参数。这个参数用于提供多

语言支持。使用NASL编写的脚本都需要支持英语,因此这些函数的确切语法是:

script_fuction(english:english_text,[francais:french_text,

deutsch:german_text,...]);

除了以上函数,还有一个用于解决安全测试插件依赖关系的函数script)dependencies(

)。它告诉nessusd服务器在某些脚本之后启动当前脚本。如果当前脚本需要其它脚本获

得的结果,就需要使用这个函数。其原型为:

script_dependencies(filename1 [,filename2,...,filenameN]);

filename参数是脚本文件名。

2.2.攻击部分

脚本的攻击部分可以包括所有用于攻击测试的代码。一旦攻击完成,你可以使用securi

ty_warning()和security_hole()函数报告是否存在此类安全问题。这两个函数的用途基

本相同,security_warning()用于攻击成功,但是问题不大的情况。它们的原型如下:

security_warning(<port> [,protocol:<proto>]);

security_hole(<port> [,protocol:<proto>]);

security_warning(port:<port>,data:<data> [,protocol:<proto>]);

security_hole(port:<port>,data:<data> [,protocol:<proto>]);

在上面的第一种情况下,客户程序显示的内容是脚本注册时script_description()函数

提供的。由于能够支持多语言,因此非常方便。

在第二种情况下,客户程序将显示data参数的内容。如果你需要显示动态获得的数据,

就必须使用这种形式。

2.3.CVE兼容性

CVE是麻省理工学院维护的一个数据库,主要是对安全相关的问题提供一个一般的描述。

详情请参考http://cve.mitre.org。

Nessus和CVE完全兼容,如果你要测试一个CVE定义过的安全问题,就可以在插件脚本的

描述部分调用script_cve_id()函数。其原型如下:

script_cve_id(string);

例如:

script_cve_id("CVE-1999-0991");

如果使用了这个函数,Nessus客户程序在生成报告时,会自动引用相关的CVE记录。

2.4.示例

除了安全测试外,NASL也可以用来编写一些用于维护的脚本。下面就是一个例子,用户

可以使用这个脚本检查那些主机正在提供SSH服务。

#

#检查SSH

#

if(description)

{

script_name(english:"Ensure the presence of ssh");

script_description(english:"This script makes sure that ssh is running");

script_summary(english:"connects ont remote tcp port 22");

script_category(ACT_GATHER_INFO);

script_family(english:"Admiminstration toolbox");

script_copyright(english:"This script was Writtern by Joe U.");

exit(0);

}

#

#SSH服务可能隐藏在别的端口

#因此我们需要依赖于find_service插件获得的结果

#

port=get_kb_item("Services/ssh");

if(!port)port=22;

#首先声明SSH没有安装

ok=0;

if(get_port_state(port))

{

soc=open_sock_tcp(port);

if(soc)

{

#检查端口是否是由TCP_Wrapper封装的。

data=recv(socket:soc,length:200);

if("SSH"><data)ok=1;

}

close(soc);

}

#

#报告不提供SSH服务的主机

#

if(!ok)

{

report="SSH is not running on this host!";

security_warning(port:22,data:report);

}

3.脚本优化

在安全测试期间,nessusd服务器将启动200多个脚本。如果所有脚本编写的都不好,这

个测试就会浪费大量的时间。因此,你必须尽量提高脚本的效率。

3.1.只在必要时运行

对于优化脚本,最有效的方法是告诉nessusd服务器什么时候不要启动它。例如,假设你

的脚本需要建立到远程主机123/TCP端口的连接,如果nessusd知道这个端口已经被关闭

,就没有必要启动你的脚本了。script_require_ports()、script_require_keys()和s

cript_exclude_keys()就是用来实现上述目的。这些脚本需要在描述部分调用:

script_require_ports(<port1>,<port2>,...)

参数中的至少一个端口开放才启动脚本。参数可以是数字,也可以是基本信息(KB)中定

义的符号,例如:"Services/www"。注意:如果端口的状态是未知的(例如:还没有进行

过端口扫描),这个脚本也会执行。

script_require_keys(<key1>,<key2>,...)

只有参数中的关键词在基本信息(KB)都有定义时,才执行脚本。例如:

script_require_keys("ftp/anonymous","ftp/writeable_dir");

表示只有远程FTP主机支持匿名用户以及存在可以写的目录时,才启动当前脚本。

script_exclude_keys(<key1>,<key2>,...)

参数表示的关键词至少有一个在基本信息(KB)中有定义,才执行当前脚本。

3.2.充分利用其它脚本的结果

充分利用基本信息拷贝中的信息,可以使脚本更高效。例如,如果在调用open_sock_tc

p()函数之前,先调用get_port_state()函数,就可以避免由于目标端口是关闭的带来的

时间上的浪费。

4.如何分享你的新脚本

如果你想让别人分享自己的成果,在编写测试脚本时要遵循以下原则:

你的脚本不能存在任何与用户交互的操作

NASL安全测试脚本是在服务器端运行的,因此用户看不到任何的输出信息。

一个脚本只能测试一个漏洞

如果你知道如何测试好几个漏洞,那就为每个漏洞都编写自己的测试脚本。

你的脚本最好归入现有的种类

如果你计划分享自己的成果,最好避免建立Joe's Power Tools这样的新插件种类,尽量

把插件划入已有的插件种类。

查询CVE中是否有相关漏洞的定义

如果你能够注意脚本的兼容性,可以节省Nessus维护者的很多时间。

把成果发给Nessus维护者

Nessus的维护者就是本文的作者。如果你不象独享自己的成果,就把它发给Nessus维护

者。如果你的脚本被采用,它就会被分配一个唯一的ID。

5.结论

希望你能够喜欢这个教程。学习这个语言不会占用你太多的时间,你需要多多练习。在

使用过程中,你会发现NASL解释器的一些BUGS,希望你能够及时把这些BUGS报告给我。

附录A.基本信息(Knowledge base)

所谓的基本信息是一些关键词,里面包含其它测试插件获得的信息。使用script_depen

dencies()、get_kb_item()和set_kb_item()函数,可以帮助你避免没有必要的重复测试

。附录A中将罗列出这些关键词。

在基本信息(KB)中,每个项可以有几个值。例如,远程主机运行两个FTP服务:一个在端

口21,另一个在端口2100。这样关键词Services/ftp就等于21和2100两个端口。在这种

情况下,测试脚本将执行两次:第一次,get_kb_item("Services/ftp")函数将返回21,

第二次这个函数将返回2100。不过,这是自动进行的,无须人工干预。对于脚本编写者

来说,相当于每个基本信息关键词只有一个值。

有些关键词目前没有多大用处,好多我就没有用过,但是有备无患。

Host/OS

定义文件:queso.nasl、nmap_wrapper.nasl

类型:字符串

含义:远程操作系统的类型

Host/dead

定义文件:ping_host.nasl和所有的DoS插件

类型:boolean

含义:远程主机关闭。如果这个项被设置,nessusd将终止针对这个主机的所有测试。

Services/www

定义文件:find_service.nes

类型:端口号

含义:目标主机WEB服务器监听的端口号。如果没有发现WEB服务器,就返回0。

Services/auth

定义文件:find_service.nes

类型:端口号

含义:identd服务使用的端口。如果没有这个服务,就返回0。

Services/echo

定义文件:find_service.nes

类型:端口号

含义:echo服务使用的端口。如果没有这个服务,就返回0。

Services/finger

定义文件:find_service.nes

类型:端口号

含义:finger服务使用的端口。如果没有这个服务,就返回0。

Services/ftp

定义文件:find_service.nes

类型:端口号

含义:ftp服务使用的端口。如果没有这个服务,就返回0。

Services/smtp

定义文件:find_service.nes

类型:端口号

含义:smtp服务使用的端口。如果没有这个服务,就返回0。

Services/ssh

定义文件:find_service.nes

类型:端口号

含义:ssh服务使用的端口。如果没有这个服务,就返回0。

Services/http_proxy

定义文件:find_service.nes

类型:端口号

含义:HTTP代理服务使用的端口。如果没有这个服务,就返回0。

Services/imap

定义文件:find_service.nes

类型:端口号

含义:imap服务使用的端口。如果没有这个服务,就返回0。

Services/pop1

定义文件:find_service.nes

类型:端口号

含义:pop1服务使用的端口。如果没有这个服务,就返回0。

Services/pop2

定义文件:find_service.nes

类型:端口号

含义:pop2服务使用的端口。如果没有这个服务,就返回0。

Services/pop3

定义文件:find_service.nes

类型:端口号

含义:pop3服务使用的端口。如果没有这个服务,就返回0。

Services/nntp

定义文件:find_service.nes

类型:端口号

含义:nntp服务使用的端口。如果没有这个服务,就返回0。

Services/linuxconf

定义文件:find_service.nes

类型:端口号

含义:linuxconf服务使用的端口。如果没有这个服务,就返回0。

Services/swat

定义文件:find_service.nes

类型:端口号

含义:SWAT服务使用的端口。如果没有这个服务,就返回0。

Services/wild_shell

定义文件:find_service.nes

类型:端口号

含义:shell服务使用的端口。如果没有这个服务,就返回0。

Services/telnet

定义文件:find_service.nes

类型:端口号

含义:telnet服务使用的端口。如果没有这个服务,就返回0。

Services/server

定义文件:find_service.nes

类型:端口号

含义:realserver服务使用的端口。如果没有这个服务,就返回0。

Services/netbus

定义文件:find_service.nes

类型:端口号

含义:NetBus服务使用的端口。如果没有这个服务,就返回0。

bind/version

定义文件:bind_version.nasl

类型:字符串

含义:远程BIND监控程序的版本

rpc/bootparamd

定义文件:bootparamd.nasl

类型:字符串

含义:bootparam RPC服务正在运行

Windows compatible

定义文件:ca_unicenter_file_transfer_service.nasl、ca_unicenter_transport_se

rvice.nasl、mssqlserver_detect.nasl和windows_detect.nasl

类型:boolean

含义:远程主机好象运行一种Windows兼容操作系统(只有相关的端口开放才进行这个测

试)

finger/search.**@host

定义文件:cfinger_search.nasl

类型:boolean

含义:使用**进行finger查询能够得到用户列表。

finger/0@host

定义文件:finger_0.nasl

类型:boolean

含义:使用0进行finger查询能够得到用户列表

finger/.@host

定义文件:finger_dot.nasl

类型:boolean

含义:使用.进行finger查询能够获得用户列表

finger/user@host1@host2

定义文件:finger_0.nasl

类型:boolean

含义:finger监控程序能够用于重定向攻击

www/frontpage

定义文件:frontpage.nasl

类型:boolean

含义:远程WEB服务器使用frontpage扩展

ftp/anonymous

定义文件:ftp_anonymous.nasl

类型:boolean

含义:远程FTP服务器可以匿名登录

ftp/root_via_cmd

定义文件:ftp_cwd_root.nasl

类型:boolean

含义:使用CWD命令可以获得远程FTP服务器的root权限。参看CVE-1999-0082

ftp/microsoft

定义文件:ftp_overflow.nasl

类型:boolean

含义:远程主机是Micro$oft FTP服务器,不能处理太长的参数

ftp/false_ftp

定义文件:ftp_overflow.nasl

类型:boolean

含义:远程主机经过TCP封装,或者FTP端口开放而连接关闭。

附录B.nasl工具

libnasl软件包中有一个单独的解释器nasl,可以用于脚本的调试。更多细节可以参考m

an nasl。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有