当前位置:首页>编程日记>正文

NASL脚本语言

1.简介

1.1.什么是NASL?

  NASL是一个为网络安全扫描工具Nessus开发的脚本语言。通过它,任何人都可以方便快速地针对新出现的漏洞编写出测试插件,也便于不同操作系统的用户分享测试脚本。除此之外,NASL还可以保证编写的脚本只能用于针对目的主机的测试,使编写者难以使用编写的脚本用于恶意用途。

  使用NASL,你可以很容易地打造IP报文,或者发送通常的报文。NASL中还有一些专门的函数用于向FTP和WEB服务器发送数据。另外,NASL还可以保证:

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

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

1.2.What NASL is not

  NASL不是一种功能很强大的脚本语言。它的目的只是用于安全测试。因此,别指望使用这种脚本语言写出第三代的WEB服务器或者文件转换工具,要编写此类软件还是使用Perl、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可以暂时用于排错。

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解释器将不解释特殊字符(例如:)。例如:

a = 'Hello I'm Renaud'; #a等于Hello I'm Renaud',没有特殊含义
a = string('Hello I'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

202.108.66.229在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,' ');

# 显示从1到9以及它们是奇数还是偶数
for(j=1;j<=10;j=j+1){
if(j&1)display(j,' is odd ');
else display(j,' is even ');
}
# 使用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),' );

  另外,用户自己定义的函数不能调用其它的用户定义函数(实际上是可以的但是遇到这种情况,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,' ');
}

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
{
soc=open_sock_tcp(i);
if(soc){
display('Port ',i,' is open ');
close(soc);
}
}

3.1.2.关闭一个端口

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

3.1.3.读写套接字

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

  recn(socket:,length: [,timeout:])

  从套接字读取个字节,这个函数可以用于TCP和UDP。超时(timeout)参数是可选的,以秒为单位。

  recv_line(socket:,length: [,timeout:])

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

  send(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 : ',data,' ');
}
else
{
display('The remote FTP server seems to be tcp-wrapper ');
}
close(soc);
}

 

3.1.4.高层操作

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

ftp_log_in(socket:,user:,pass:)
  尝试通过套接字登录到远程FP主机。如果用户名和密码都正确,就返回TRUE,否则返回FALSE。

ftp_get_pasv_port(socket:)
  向远程FTP服务器发出一个PASV命令,获得连接的端口。NASL脚本可以通过这个端口从FTP服务器下载数据。如果发生错误函数将返回FALSE。

is_cgi_installed()
  测试远程WEB服务器是否安装了名为的CGI程序。这个函数向远程WEB服务器发出GET请求实现这个目的。如果不是以斜杠(/)开头,就认为它是相对于/cgi-bin/。这个函数也可以用于确定某个文件是否存在。

  示例脚本:

#
# 针对WWW服务器的测试
#
if(is_cgi_installed('/robots.txt')){
display('The file /robots.txt is present ');
}
if(is_cgi_installed('php.cgi')){
display('The CGI php.cgi is installed in /cgi-bin/ ');
}
if(!is_cgi_installed('/php.cgi')){
display('There is no php.cgi in the remote web root ');
}

#
# 针对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 ');
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报文;使用get_ip_element()函数获得报文某个域的值;使用set_ip_element()函数改变现有IP跋文某个域的值。forge_ip_packet函数的原形如下:

=forge_ip_packet(
ip_hl :,
ip_v :,
ip_tos :,
ip_len :,
ip_id :,
ip_off :,
ip_ttl :,
ip_p :,
ip_src :,
ip_dst :,
[ip_sum :]);

  其中,ip_sum参数是可选的,如果没有使用,NASL会自动计算报文的校验和。ip_p参数可以是一个整数值,或者是IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP、IPPROTO_IGMP或者IPPROTO_IP等常量中的某个值。

  get_ip_element()函数的原型如下:

=get_ip_element(
ip :,
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_hl :,]
[ip_v :,]
[ip_tos :,]
[ip_len :,]
[ip_id :,]
[ip_off :,]
[ip_ttl :,]
[ip_p :,]
[ip_src :,]
[ip_dst :,]
[ip_sum :]
);

  这个函数可以改变IP报文的值,如果你没有修改ip_sum域的值,它会自动重新计算。这个函数没有构造报文的能力,因此需要把它放在forge_ip_packet()函数之后。

  最后,还有一个函数dump_ip_packet()需要说明一下,这个函数能够以可读的方式把IP报文的内容输出到屏幕。这个函数应该只用于调试目的。

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()
  如果TCP端口打开或者其状态是未知,就返回TRUE。

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

soc = open_sock_tcp(23);
buffer = telnet_init(soc);
display('The remote telnet banner is ',buffer,' ');

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

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

result = getrpcport(program :<PROGRAM_NUMBER),
protocol :<IPPROTO_TCP|IPPROTO_UDP,
[version :]);

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

4.字符串处理函数

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

a = 'version 1.2.3';
b = 'version 1.4.1';

if(a<B){
#因为version 1.2.3比version1.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:)

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

if(ereg(pattern:'.*',string:'test'))
{
display('Always execute ');
}

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()函数返回一个多行文本中,匹配的第一行。如果对单行文本使用这个函数,它就相当于ereg()。如果没有匹配的行,它就返回FALSE。其原型如下:

str=egrep(pattern:,string:)

  示例:

soc=open_soc_tcp(80);
str=string('HEAD / HTTP/1.0 ');
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()
  获得一个以'X'填充的长度为的字符串。

crap(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(,[,...,]);

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

name='Renand';
a=string('Hello,I am ',name,' '_; #a等于'Hello,I am Renaud'
#末尾回行
b=string(1,' and 'm',2,' make ',1+2); #b等于'1 and 2 make3'
c=string('MKD ',crap(4096),' '); #c等于'MKDXXX...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:,size:);

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

4.8.tolower()函数

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

string2=tolower();

a='Hello,World'

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

5.环境搭建

执行命令:apt-get install openvas-nasl

利用openvas调试nasl脚本

5.1 不使用证书与签名:

1、编写nasl脚本,保存为script.nasl。

2、# openvas-nasl -X script.nasl。


5.2 使用证书与签名(强烈建议):

# vim  /etc/openvas/openvassd.conf

nasl_no_signature_check = no

1、生成密钥:

# gpg --homedir=/etc/openvas/gnupg --gen-key
或直接使用官网的OpenVAS_TI.asc key文件:
# wget http://www.openvas.org/OpenVAS_TI.asc
双击OpenVAS_TI.asc文件将密钥注册到系统中
2、设置信任
# gpg --homedir=/etc/openvas/gnupg --list-keys
/etc/openvas/gnupg//pubring.gpg
-------------------------------
pub   2048R/CA9633422013-09-24
uid                  telnet (no) 
sub   2048R/EFBBA6A92013-09-24
找到public key id: CA963342
# gpg --homedir=/etc/openvas/gnupg --lsign-key CA963342
3、生成签名文件signature

# gpg --homedir=/etc/openvas/gnupg/ --detach-sign -a -o script.nasl.asc script.nasl
4、添加证书

# gpg --homedir=/etc/openvas/gnupg --import script.nasl.asc
5、验证签名
# openvas-nasl -p script.nasl
6、执行脚本
# openvas-nasl -t 192.168.2.105 script.nasl
 
四、将自己写的plugin加入插件库
1、将自己写的插件复制到openvas插件库目录:
 /var/lib/openvas/plugins
2、加载插件
# openvassd
3、重建插件库
# openvasmd –rebuild

 
 


http://www.coolblog.cn/news/3f1b6a6805333f01.html

相关文章:

  • asp多表查询并显示_SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询
  • s7day2学习记录
  • 【求锤得锤的故事】Redis锁从面试连环炮聊到神仙打架。
  • 矿Spring入门Demo
  • 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才
  • Linux 实时流量监测(iptraf中文图解)
  • Win10 + Python + GPU版MXNet + VS2015 + RTools + R配置
  • 美颜
  • shell访问php文件夹,Shell获取某目录下所有文件夹的名称
  • 如何优雅的实现 Spring Boot 接口参数加密解密?
  • LeCun亲授的深度学习入门课:从飞行器的发明到卷积神经网络
  • Mac原生Terminal快速登录ssh
  • java受保护的数据与_Javascript类定义语法,私有成员、受保护成员、静态成员等介绍...
  • mysql commit 机制_1024MySQL事物提交机制
  • 支撑微博千亿调用的轻量级RPC框架:Motan
  • jquery 使用小技巧
  • 2019-9
  • 法拉利虚拟学院2010 服务器,法拉利虚拟学院2010
  • vscode pylint 错误_将实际未错误的py库添加到pylint白名单
  • 科学计算工具NumPy(3):ndarray的元素处理
  • 工程师在工作电脑存 64G 不雅文件,被公司开除后索赔 41 万,结果…
  • linux批量创建用户和密码
  • newinsets用法java_Java XYPlot.setInsets方法代碼示例
  • js常用阻止冒泡事件
  • 气泡图在开源监控工具中的应用效果
  • 各类型土地利用图例_划重点!国土空间总体规划——土地利用
  • php 启动服务器监听
  • dubbo简单示例
  • 【设计模式】 模式PK:策略模式VS状态模式
  • [iptables]Redhat 7.2下使用iptables实现NAT
  • Ubuntu13.10:[3]如何开启SSH SERVER服务
  • CSS小技巧——CSS滚动条美化
  • JS实现-页面数据无限加载
  • 阿里巴巴分布式服务框架 Dubbo
  • 最新DOS大全
  • Django View(视图系统)
  • 阿里大鱼.net core 发送短信
  • 程序员入错行怎么办?
  • 两张超级大表join优化
  • 第九天函数
  • Linux软件安装-----apache安装
  • HDU 5988 最小费用流
  • Sorenson Capital:值得投资的 5 种 AI 技术
  • 《看透springmvc源码分析与实践》读书笔记一
  • 正式开课!如何学习相机模型与标定?(单目+双目+鱼眼+深度相机)
  • Arm芯片的新革命在缓缓上演
  • nagios自写插件—check_file
  • python3 错误 Max retries exceeded with url 解决方法
  • 行为模式之Template Method模式
  • 通过Spark进行ALS离线和Stream实时推荐