The Nessus Attack Scripting Language Reference Guide

Renaud Deraison < [email protected] > Version 1.0.0pre2

1 Introduction 1.1 What is NASL ? 1.2 What NASL is not ? 1.3 Why not using Perl/Python/tcl/ whatever you like for Nessus ? 1.4 Why should you write your tests in NASL ? 1.5 What this guide will teach you 1.6 NASL limitations : what to not expect 1.7 Thanks 2 The basics : NASL syntax 2.1 Comments 2.2 Variables, variables types, memory allocation, includes 2.3 Numbers and strings 2.4 Anonymous / Non Anonymous arguments 2.4.1 Non Anonymous functions 2.4.2 Anonymous functions 2.5 For and while 2.6 User-defined functions 2.7 Operators 2.7.1 The 'x' operator 2.7.2 The ' > < ' operator 3 The NASL Network related functions 3.1 Sockets manipulation 3.1.1 How to open a socket 3.1.2 Closing a socket 3.1.3 Writing to a socket, and reading from it 3.1.4 Higher level operations 3.2 Raw packets manipulation 3.2.1 Forging an IP packet 3.2.2 Forging a TCP packet 3.2.3 Forging a UDP packet 3.2.4 Forging an ICMP packet 3.2.5 Forging an IGMP packet 3.2.6 Sending a raw packet 3.2.7 Reading raw packets 3.3 Utilities 4 String manipulation functions 4.1 The ereg() function for regular expressions 4.2 The egrep() function 4.3 The crap() function 4.4 The string() function 4.5 The strlen() function 4.6 The raw_string() function 4.7 The strtoint() function 4.8 The tolower() function 5 Writing a Nessus Security test 5.1 How to write an efficient Nessus test 5.1.1 Determining whether a port is open 5.1.2 The Knowledge Base (KB) 5.2 NASL script structure 5.2.1 The register section 5.2.2 The attack section 5.2.3 CVE compatibility 5.2.4 An example 5.3 Tuning your script 5.3.1 Asking nessusd to execute the script only if it is necessary 5.3.2 Be smart enough to use the result of the other scripts 5.4 So you want to share your new script ? 6 Conclusion A The knowledge base B The 'nasl' utility

1 Introduction

1.1 What is NASL ? NASL is a scripting language designed for the Nessus security scanner. Its aim is to allow anyone to write a test for a given security hole in a few minutes, to allow people to share their tests without having to worry about their operating system, and to garantee everyone that a NASL script can not do anything nasty except performing a given security test against a given target. Thus, NASL allows you to easily forge IP packets, or to send regular packets. It provides you some convenient functions that will make the test of web and ftp server more easy to write. NASL garantees you that a NASL script : will not send any packet to a host other than the target host will not execute any commands on your local system

1.2 What NASL is not ? NASL is not a powerful scripting language. Its purpose is to make scripts that are security tests. So, do not expect to write a third generation web server in this language, nor a file conversion utility. Use perl, python or whatever scripting language to do this - they are 100 times faster

NASL was designed rather quickly, so you may spot some inconstencies in its syntax. Please, let me know if you find some.

1.3 Why not using Perl/Python/tcl/whatever you like for Nessus ? I know that there is a lot of very good scripting languages around here, and that NASL is really weak compared to them. But none of these languages is secure, in the sense that you can easily write a test that will be a trojan and will indeed open a connection to a third party host - letting it know that you are a Nessus user, and even eventually send the name of your targets to this evil third party host. Or worse, it could send your passwd file, or whatever.

Another problem with many of these scripting language : a lot of them are memory hungry. It can also be an headache if you want to configure them for Nessus. Just think about Perl. Perl is good. Perl is beautiful (according to some). But how much time will you have to spend to install all the modules that may be necessary for writing efficient Nessus tests ? Net::RawIP is only one of them.

NASL, on the other hand, does not take a huge amount of memory. This way, you can launch 20 threads of nessusd at the same time, without the need of having 256Mb of RAM. NASL is also self-sufficient. That is, you will not have to install a dozen of packages for each new security test. 1.4 Why should you write your tests in NASL ? You may already wonder whether it is worth or not to learn yet another scripting language to write your tests, rather than coding them in C or Perl, or whatever. What you must know is that :

NASL is optimized for Nessus. Writing a Nessus test in this language is fast NASL has a lot of things in common with C, so you should not be afraid of it NASL produces secure and easily sharable security tests NASL produces portable and easily modifiable security tests. When the Windows NT version of Nessus will be released, you will use the same functions to do the same things (such as sending raw IP packets)

1.5 What this guide will teach you This guide teaches you how to write your own Nessus tests in NASL. This is my first attempt to write a comprehensive document, so I may have written complicated things.

1.6 NASL limitations : what to not expect As I stated before, NASL is not a powerful language. The biggest limitations as of now are :

Structures. Structures are not supported. They may be in a not-so-far-away future, but today they are not

A correct debugger. NASL has no correct debugger. However, there is a standalone interpretor 'nasl'

1.7 Thanks I would like to thank the following persons for their advices regarding the design of NASL. Without them, NASL would be more akward than it is already :

Denis DUCAMP ([email protected]) FYODOR ([email protected]) Noam RATHAUS ([email protected]) I always appreciate suggestions and complaints about the language. Do not hesitate to share your opinion (be it good or bad) with me.

2 The basics : NASL syntax NASL syntax is very similar to C, except that a lot of boring stuff has been removed. You do not have to care about the type of your objects, nor do you have to allow memory for them or free it. You do not need to declare your variables before you use them. You just have to focus on the security test you want to perform.

If you do not know C, then you will have a hard time reading this manual has it is currently intended for C programmers. Just complain and this guide will be made more readable in the future.

2.1 Comments The comment char is '#'. It only comments out the current line.

Examples :

Valid comments are :

a = 1; # let a = 1

# Set b to 2 : b = 2; Invalid comments would be :

# Set a to 1 : #

a = 1;

a = # set a to 1 # 1;

2.2 Variables, variables types, memory allocation, includes You do not need to declare variables before you use them. You do not have to care about the variable types. The NASL interpretor will yell at you if you try to do bogus things, such as adding an IP packet to a number. And you do not have to care about memory allocation nor do you have to care about the includes. There is no include. Memory is allocated when needed.

2.3 Numbers and strings Numbers can be entered in three bases : decimal, hexadecimal, or binary.

All these lines are correct :

a = 1204; b = 0x0A; c = 0b001010110110; d = 123 + 0xFF;

The strings must be quoted. Note that, unlike C, the characters are not interpolated unless you explicitly ask to interpolate them using the string() function.

a = "Hello\nI'm Renaud"; # a equals to "Hello\nI'm Renaud" b = string("Hello\nI'm Renaud"); # b equals to "Hello # I'm renaud"

c = string(a); # c equals to b

The string() function will be dealt with in the ``String Manipulation'' section.

2.4 Anonymous / Non Anonymous arguments

2.4.1 Non Anonymous functions One thing which is different with C is the way NASL handles the arguments of a function. In C, you must know by heart which argument must be at which place. And this quickly becomes an headache when a function you call has more than 10 arguments. For instance, imagine a C function which will forge an IP packet for you. This function requires a dozen of arguments. If you want to use it, then you will have to remember their exact order or read the the documentation of this function. This is a waste of time, and this is what NASL attempts to avoid.

So, when the order of the arguments of a function is important, and when the different arguments of the function have different types, then the function is a non anonymous function. That is, you have to give the name of the elements. If you forget some elements, then you will be prompted for them at runtime.

Example : The function forge_ip_packet() has a lot of elements. These two calls are valid and perform the exact same thing :

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

forge_ip_packet(ip_p : IPPROTO_TCP, ip_v : 4, ip_hl : 5); The user will be prompted at runtime for the missing arguments (ip_len, and so on...). Of course, a security test must not directly interact with the user, but this is handy for debugging and quick coding.

2.4.2 Anonymous functions The anonymous functions are functions that take only one argument, or arguments of the same type.

Examples :

send_packet(my_packet); send_packet(packet1, packet2, packet3); These functions may have options. For instance, the send_packet() function waits for an answer. If you feel there is no need to read the host's answer, then you can deactivate the pcap, and speed up the test : send_packet(packet, use_pcap:FALSE);

2.5 For and while The for and while work like in C :

For :

for(instruction_start;condition;end_loop_instruction) { # # Some instructions here # } or for(instruction_start;condition;end_loop_instruction)function(); While :

while(condition) { # # Some instructions here # } or while(condition)function(); Examples :

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

# Count from 1 to 9, and say the type # of each number (even or odd) for(j=1;j<10;j=j+1){ if(j & 1)display(j, " is odd\n"); else display(j, " is even\n"); }

# Do something completely useless :

i = 0; while(i < 10) { i = i+1; }

2.6 User-defined functions NASL now supports user-defined functions. A user-defined function is defined like this :

function my_function(argument1, argument2, ....)

User-defined functions must use non-anonymous arguments. Recursion is handled.

Example : function fact(n) { if((n == 0)||(n == 1)) return(n); else return(n*fact(n:n-1)); } display("5! is ", fact(n:5), "\n");

User-defined function may not contain other user-defined functions (actually, they can but the NASL interpretor will yell at you if you call the function that defines its subfunction more than once)

Note that if you want your function to return a value (that's the purpose of a function after all), then you have to use the function return(). Since return() is a function, you must use parenthesis, that is, the following is incorrect : function func() { return 1; # parenthesis are missing here ! }

2.7 Operators The standard C operators work in NASL. That is, +,-, *, / and % work. At this time, the operators priority is not taken in account, but this will change. In addition to this operators, the binary operators | and & are implemented.

In addition to this, there are two operators that do not exist in C :

2.7.1 The 'x' operator for and while are great and handy. But because the condition has to be evaluated at each iteration, then there is a loss of performance, which can be of some trouble if you want to send a SYN storm or whatever. The 'x' operator will repeat the same function N times, and will go really fast (at native C speed actually).

Example :

send_packet(udp) x 10; Will send the same udp packet ten times.

2.7.2 The ' > < ' operator The >< operator is a boolean operator which returns true if a string of chars A is contained in a string B.

Example :

a = "Nessus"; b = "I use Nessus";

if(a >< b){ # This will be executed since # a is in B display(a, " is contained in ", b, "\n"); }

3 The NASL Network related functions NASL will not let you open a socket to another host that the host that nessusd wants to test.

3.1 Sockets manipulation A socket is a way to communicate with another host using TCP or UDP. It is like a pipe, designed to send data on a given port of a given protocol.

3.1.1 How to open a socket The functions open_sock_tcp() and open_sock_udp() will open a TCP or UDP socket. These two functions are using anonymous arguments. You can currently open a socket on only one port at once, but this will eventually change in the future. Example : # Open a socket on TCP port 80 : soc1 = open_sock_tcp(80); # Open a socket on UDP port 123 : soc2 = open_sock_udp(123); The open_sock functions will return 0 if the connection could not be established on the remote host. Usually, open_sock_udp() will never fail, since there is no way to determine whether the remote UDP port is open or not, whereas the open_sock_tcp() function will return 0 if the remote port is closed. A trivial TCP port scanner would be like this : start = prompt("First port to scan ? "); end = prompt("Last port to scan ? "); for(i=start;i

3.1.2 Closing a socket The function close() is used to close a socket. It will internally perform a shutdown() before actually closing the socket.

3.1.3 Writing to a socket, and reading from it Reading and writing to a socket is done using one of these functions : recv(socket:, length: [,timeout : )Reads bytes from the socket . This function can be used for TCP and UDP. The timeout option is in seconds. recv_line(socket:, length: [, timeout: ])This function works the same way as recv(), except that it will stop reading data as soon as the \n character is read. This function only works with TCP sockets. send(socket:, data: [, length:]) : send the data on the socket . The optional argument length tells the function to only send bytes on the socket. If it is not set, then the data will be sent until a NULL character is met.

The functions that are used to read data from a socket have an internal timeout value of five seconds. If the timeout is reached, then they will return FALSE. Example :

# This Example displays the FTP banner of the remote host : 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-wrapped\n"); } close(soc); }

3.1.4 Higher level operations NASL has a set of high level functions, regarding FTP and WWW. ftp_log_in(socket:, user:, pass:) will attempt to log into the FTP server connected to the freshly open socket . This function returns TRUE if it was possible to log in as with password . It returns FALSE if an error occured. ftp_get_pasv_port(socket:) issues a PASV command on the FTP server, and returns the port to open a connection onto. This allows NASL scripts to retrieve data via FTP. This function returns FALSE if an error occurred. is_cgi_installed() returns TRUE if the cgi is installed on the remote web server. This function performs a GET request on the remote web server. If does not start by a slash (/), then /cgi-bin/ is appended in the front of it. This function can also be used to determine the existence of a given file. Examples :

# # 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 # # open a connection to the remote host soc = open_sock_tcp(21);

# Log in as the anonymous user if(ftp_log_in(socket:soc, user:"ftp", pass:"joe@")) { # Get a passive port port = ftp_get_pasv_port(socket:soc); if(port) { soc2 = open_sock_tcp(port); 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 Raw packets manipulation NASL allows you to forge your own IP packets, and will attempt to behave in an intelligent way with the packet forged. For instance, if you change a parameter in a TCP packet, then the TCP checksum will be recomputed silently. If you append a layer to an IP packet, then the ip_len element of the IP packet will be updated - unless you deliberately say to not do it.

All the raw packets functions use non-anonymous arguments. Their names comes straight from the BSD include files. So, the 'length' element of an ip packet is called ip_len and not 'length'.

3.2.1 Forging an IP packet The function forge_ip_packet() will forge a new IP packet. The function get_ip_element() will return an element of a packet, whereas the function set_ip_elements() will change the elements of an existing 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 : ] );

The ip_sum argument of this function is optional. If it is not set, it will be automatically computed. The field ip_p may be a numeric value, or one of the constants IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP, IPPROTO_IGMP or IPPROTO_IP.

= 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"); The function get_ip_element() will return one element of a packet. The element must be one of "ip_hl", "ip_v", "ip_tos", "ip_len", "ip_id", "ip_off", "ip_ttl", "ip_p", "ip_sum", "ip_src" or "ip_dst". Note that the quotes have their importance.

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 : ] );

The function set_ip_elements() change the value of the IP packet and recomputes the checksum if the element ip_sum is not altered. Since this function will not create a new packet in memory, you should prefer it to forge_ip_packet() when you have to send multiple, nearly similar, IP packets.

Last but not least, there is a function dump_ip_packet() which will print the IP packet in human readable form on screen. You should only use this for debugging purpose.

3.2.2 Forging a TCP packet The function forge_tcp_packet() is used to forge a TCP packet. Its syntax is :

tcppacket = forge_tcp_packet(ip : , th_sport : , th_dport : , th_flags : , th_seq : , th_ack : , [th_x2 : ], th_off : , th_win : , th_urp : , [th_sum : ], [data : ]);

The option th_flags must be one of TH_SYN, TH_ACK, TH_FIN, TH_PUSH or TH_RST. Flags can be combined using the | operator. th_flags may also be a numeric value. ip_packet must have been generated with forge_ip_packet() or must have be a packet read using send_packet() or pcap_next().

The function used to change TCP elements is set_tcp_elements(). It's syntax is similar to forge_tcp_packet() :

set_tcp_elements(tcp : , [th_sport : ,] [th_dport : ,] [th_flags : ,] [th_seq : ,] [th_ack : ,] [th_x2 : ,] [th_off : ,] [th_win : ,] [th_urp : ,] [th_sum : ], [data : ] );

This function will automatically recompute the checksum of the packet, unless you explicitly set the th_sum element. The function used to get one element of a TCP packet is get_tcp_element(). Its syntax is : element = get_tcp_elements(tcp: , element: ); element_name must be one of "tcp_sport", ""th_dport", "th_flags", "th_seq", "th_ack", "th_x2", "th_off", "th_win", "th_urp", "th_sum". Note the quotes !

3.2.3 Forging a UDP packet The UDP functions are nearly the same as for TCP functions :

udp = forge_udp_packet(ip:, uh_sport : , uh_dport : , uh_ulen : , [uh_sum : ], [data : ]); The functions set_udp_elements() and get_udp_elements() work the same way as for the TCP functions.

3.2.4 Forging an ICMP packet 3.2.5 Forging an IGMP packet 3.2.6 Sending a raw packet Once you have set up a packet using forge_*_packet(), you can send it using the send_packet() function.

This function syntax is :

reply = send_packet(packet1, packet2, ...., packetN, pcap_active: , pcap_filter: ); If the argument pcap_active is set to TRUE (the default), then this function will wait for a reply from the host the packet was sent to. You can set up the argument pcap_filter to define what kind of packet you want. See the pcap (or tcpdump) manual to learn more from pcap filters.

3.2.7 Reading raw packets You can read a packet using the pcap_next() function, the syntax of which is :

reply = pcap_next(); This function will read a packet from the last interface you used, with the last pcap filter you used on this interface.

3.3 Utilities NASL provides several handy functions that usually makes your coding easier.

The function this_host() takes no argument an returns the IP address of the host the script is running on.

The function get_host_name() takes no argument and returns the name of the currently tested host.

The function get_host_ip() takes no argument and returns the IP adress of the currently tested host.

The function get_host_open_port() takes no argument and returns the number of the first open TCP port of the remote host. This is useful for some scripts such as land or a TCP sequence analyzing program which need to work against an open port. The function get_port_state() returns TRUE if the TCP port is open, or if its state is unknown (for instance, if it was not scanned, or if it is outside the scanned range).

The function telnet_init() initialize a telnet session on the freshly opened socket and returns the first line of telnet data. Example :

soc = open_sock_tcp(23); buffer = telnet_init(soc); display("The remote telnet banner is : ", buffer, "\n");

The function tcp_ping() takes no argument and returns TRUE if the remote host answered to a TCP ping request (send a TCP packet with the ACK flag set).

The function getrpcport() is the same as the standard function of the same name. Its syntax is : result = getrpcport(program : , protocol: IPPROTO_TCP|IPPROTO_UDP, [version: ]); This function returns 0 if an error occured (if the program is not registered in the remote rpc portmapper for instance).

4 String manipulation functions NASL handles strings as numbers. So, you can play with the ==, <, and > operators safely.

Example :

a = "version 1.2.3"; b = "version 1.4.1";

if(a < b){ # # Will be executed, since version 1.2.3 is lower # than version 1.4.1 }

c = "version 1.2.3";

if(a==c) { # Will also be evaluated } It is also possible to get the n-th character of a string, the same way as in C :

a = "test"; b = a[1]; # b equals to "e" You can also add and substract strings :

a = "version 1.2.3"; b = a - "version "; # b equals "1.2.3"

a = "this is a test"; b = " is a "; c = a - b; # c equals to "this test"

a = "test"; a = a+a; # a equals to "testtest" In addition to this and to the >< operator defined above, NASL has a set of functions dedicated to forge or modify strings :

4.1 The ereg() function for regular expressions Pattern-matching operations are done through the ereg() function. Its syntax is :

result = ereg(pattern:, string:) The pattern syntax is egrep-style. Please refer to man 1 egrep for more details about it.

Example :

if(ereg(pattern:".*", string:"test")) { display("Always executed\n"); }

mystring = recv(socket:soc, length:1024); if(ereg(pattern: "SSH-.*-1\..*", string : mystring )) { display("SSH 1.x is running on this host"); }

4.2 The egrep() function egrep() returns the first line that matches the pattern in a multi-lined text. When it is used against a one-line text, then it is similar to ereg(). If no line in the text matches, then it returns FALSE. Syntax :

str = egrep(pattern : , string: ) Example :

soc = open_soc_tcp(80); str = string("HEAD / HTTP/1.0\r\n\r\n"); send(socket:soc, data:str);

r = recv(socket:soc, length:1024); server = egrep(pattern:"^Server.*", string : r);

if(server)display(server);

4.3 The crap() function The function crap() is very convenient to test for buffer overflows. It has two syntaxes : crap() : Will return a string of length containing the character 'X' crap(length:, data:) : Will return a string of length , containing the data Example :

a = crap(5); # a = "XXXXX"; b = crap(4096); # b = "XXXX...XXXX" (4096 X's) c = crap(length:12, # c = "hellohellohe" (length: 12); data:"hello");

4.4 The string() function This function is used to make strings of chars or of other strings. It syntax is : string(, [, ..., ])

This function will interpolate the blackslashed characters such as \n or \t.

Example :

name = "Renaud";

a = string("Hello, I am ", name, "\n"); # a equals to "Hello, I am Renaud" # (with a new line at the end) b = string(1, " and ", 2, " makes ", 1+2); # b equals to "1 and 2 makes 3" c = string("MKD ", crap(4096), "\r\n"); # c equals to "MKD XXXXX.....XXXX" # (4096 X's) followed by a carriage # return and a new line

4.5 The strlen() function strlen() returns the length of a string : a = strlen("abcd"); # a is equal to 4

4.6 The raw_string() function Example :

a = raw_string(80, 81, 82); # a equals to 'PQR'

4.7 The strtoint() function This function converts a NASL integer into a binary integer. Its syntax is :

value = strtoint(number:, size:); This function is suitable to use with raw_string(). The size argument is the number of bytes the nasl integer must be written to. It can be, 1, 2 or 4.

4.8 The tolower() function This function is used to convert a string to lower case. Its syntax is tolower(). This function will actually return the string in lowered letters.

Example :

a = "Hello"; b = tolower(a); # b equals to "hello"

5 Writing a Nessus Security test 5.1 How to write an efficient Nessus test All the security test are launched by nessusd, in a very short period of time, so a well written test must use the results of the other security test. For instance, a test which wants to open a connection to a FTP server should first check that the remote port is open, before opening a connection on port 21. This saves little time and bandwidth against a given host, but this dramatically speeds up the test against a firewalled host which would silently drop TCP packets going to port 21.

5.1.1 Determining whether a port is open The function get_port_state() returns TRUE if the port is open, and FALSE if it is not. This function will return true if the port has not been scanned, that is, if its status is unknown. This function uses very little CPU, so you should call it as much as you want.

5.1.2 The Knowledge Base (KB) Each host is associated to an internal knowledge base, which contains all the information gathered by the tests during the scan. The security tests are encouraged to read it and to contribute to it. The status of the ports, for instance, is in fact written somewhere in the knowledge base. The KB is divided into categories. The ``Services'' category contains the port numbers associated to each known service. For instance, the element Services/smtp is very likely to have the value 25. However, if the remote host has a hidden SMTP server on port 2500, and none on port 25, then this item will have the value 2500.

See Annex B for details about the knowledge base elements.

Basically, there are two functions regarding the knowledge base. The get_kb_item() function will return the value of the knowledge base item . This function is anonymous. The function set_kb_item(name:, value:) will mark the new item of value in the knowledge base.

Note : You can not read back an knowledge base item you have added. For instance, the following piece of code will not work and never execute what it should :

set_kb_item(name:"attack", value:TRUE); if(get_kb_item("attack")) { # Perform the attack - will not be executed # because our local KB has not been updated } This is due to the fact that for some security and code stability reason, the Nessus server will in fact start each new security test with a copy of the knowledge base, not the original one, and the function set_kb_item() will in fact add an element into the orginal knowledge base, within nessusd, but will not update the current security test knowledge base.

5.2 NASL script structure Each NASL script must register itself to the Nessus server. That is, it must tell nessusd its name, its description, the name of its author, and more. Thus, each NASL script that will be run with nessusd must have the following structure :

# # Nasl script to be used with nessusd # if(description) { # register information here...

# # I will call this section the 'register' # section #

exit(0); }

# # Script code here. I will call this section the # 'attack' section. #

The variable description is a global variable that will be set to TRUE or FALSE depending on whether the script must register or not.

5.2.1 The register section The register section must call the following functions : script_name(language1:, [...]) which sets the script name as it will appear in the Nessus client window. script_description(language1:, [...]) which sets the script description as it will appear in the client when the user clicks on the name. script_summary(language1:

, [...]) sets the script summary as it appears in the tooltips. It must be a sum up of the description that fits on one line. script_category() sets the script category. It must be one of ACT_ATTACK, ACT_GATHER_INFO, ACT_DENIAL or ACT_SCANNER.

ACT_GATHER_INFO : the script will be launched among the first. You know it will not harm the remote computer.

ACT_ATTACK : the script will attempt to gain some priviledges on the remote host. It may harm the remote system (if it tests a buffer overflow for instance)

ACT_DENIAL : the script will attempt to crash the remote host

ACT_SCANNER : the script is a port scanner script_copyright(language1:, [...]) sets the copyright of the script. It may be your name, a legal notice or whatever. script_family(language1:, [...]) sets the script family. There are no clearly defined families, so you may choose to register the script in the family ``Joe's PowerTools'', altough I do not recommand it. The currently used families are : 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

As you may have noticed, most of these functions take a language1 argument. In fact, this is not how they work. NASL provides Nessus multilingual support. Each script must support the english language, and the exact syntax for all these functions is in fact :

script_function(english:english_text, [francais:french_text, deutsch:german_text, ...]); In addition to these functions, the function script_dependencies() may be called. It tells nessusd to launch the current script after some other script. This is useful when you want to use the results that another script must store in the KB. The syntax is : script_dependencies(filename1 [,filename2, ..., filenameN]); where filename is the name of the script to be launched after, as it is stored on disk.

5.2.2 The attack section The attack section may contain anything you think is useful for an attack. Once your attack is done, you can report a problem using the security_warning() and security_hole() functions which work the same way. security_warning() must be used when the attack was a success but is not a great security problem. That is, it will not allow instant access to an attacker. These two functions have the following syntaxes : security_warning( [, protocol:]); security_hole( [, protocol:]); security_warning(port:, data: [, protocol:]); security_hole(port:, data: [, protocol:]); In the first case, the data displayed by the client is the script description, as entered with script_description(). It is handy, because of the multilingual support.

In the second case, then the client will display the data argument. This is handy if you must display information caught on the fly, such as a version number.

5.2.3 CVE compatibility CVE is an attempt to settle a common denominator to all the security-related products. See http://cve.mitre.org for more details.

Nessus is fully CVE-compatible. If you write a script that tests for a CVE-defined security problem, then call the script_cve_id() function in the description section of your plugin. script_cve_id() is defined as : script_cve_id(string); Example :

script_cve_id("CVE-1999-0991"); It is important to make a separate call to this function, rather than just writing the CVE id in the report, so that the Nessus clients may make an active use of it.

5.2.4 An example In addition to security tests, NASL can be used to do some maintenance. Here is a script example that will ensure that each host is running ssh, and tell the user which hosts are not running it :

# # Check for 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 on remote tcp port 22"); script_category(ACT_GATHER_INFO); script_family(english:"Administration toolbox"); script_copyright(english:"This script was written by Joe U."); script_dependencies("find_service.nes"); exit(0); }

# # First, ssh may run on another port. # That's why we rely on the plugin 'find_service' # port = get_kb_item("Services/ssh"); if(!port)port = 22;

# declare that ssh is not installed yet ok = 0; if(get_port_state(port)) { soc = open_sock_tcp(port); if(soc) { # Check that ssh is not tcpwrapped. And that it's really # SSH data = recv(socket:soc, length:200); if("SSH" >< data)ok = 1; } close(soc); }

# # Only warn the user that SSH is NOT installed # if(!ok) { report = "SSH is not running on this host !"; security_warning(port:22, data:report); }

5.3 Tuning your script During a test, nessusd will launch more than 200 scripts. If all of them were badly written, then a test would take even more time than it currently does. That's why you must absolutely make whatever you can to make your script go as fast as possible.

5.3.1 Asking nessusd to execute the script only if it is necessary The best way to optimize your script is to tell nessusd when to not launch it. For instance, let's imagine that your script attempts to connect to the remote TCP port 123. If nessusd knows that this port is closed, then it's no use to start your script, since it will not do anything. The functions script_require_ports(), script_require_keys() and script_exclude_keys() are designed for this purpose. They must be called in the description section of the script. script_require_ports(, , ...) : will make nessusd execute your script if and only if at least one of the ports is open. can be either a numeric value (ie: 80) or a symbolic value, as defined in the knowledge base (ie: "Services/www").

Example : script_require_ports(80, "Services/www") Note that if the state of a port is unknown (if, for instance, no portscan was made), then the script will be executed. script_require_keys(, , ...) : will make nessusd execute your script if and only if all the keys given in argument are defined in the knowledge base. Example : script_require_keys("ftp/anonymous", "ftp/writeable_dir") will only execute the script if the remote FTP server offers an anonymous access and if there is a writeable directory in it. script_exclude_keys(, , ...) : will make nessusd not execute your script if at least one of the keys given in argument is set in the knowledge base.

5.3.2 Be smart enough to use the result of the other scripts Be sure to read the appendix regarding the knowledge base to make sure that your script is as lazy as possible - that is, it must not do something that another script has already done. For instance, rather that directly opening a socket on a given tcp port (using open_sock_tcp()), make sure that this port is open using get_port_state(). The less your script will do, the faster things will go on.

5.4 So you want to share your new script ? If you plan to share your script then you should obey to these rules :

Your script must never interact with the user. NASL scripts are executed on the server side. Therefore, all the output will not be seen by the user.

Your script must test one vulnerability. If you know how to test multiple vulnerabilities, then write several scripts. So that you stay consistent with all the Nessus scripts

Your script should belong to an existing family. If you plan to share your script, then avoid to create a family like Joe's Power Tools but try to stay consistent

Look up in CVE to see if there is a definition of your script. If you take care of CVE compatibility, then the Nessus maintainer will not have to do it by himself, and this will save his time Send it to the Nessus maintainer. That is, me :) If you plan to share your script, then make it available to everyone, not only your friends or a newsgroup you hang on. Send it and see it being included in the Nessus distribution. Once your script has been included in the distribution, it will be given a unique ID.

6 Conclusion I hope you enjoyed this overview of NASL. Basically, the language should not evolve for a while, so it's safe to learn how to use it and to practice it.

You will see bugs in the NASL interpretor. That is for sure. I do not know how you program, so it is very likely that you will manage to make it crash. Please, do not keep the bugs for you. Share them, and send them to me.

I hope you enjoyed reading this guide.

-- Renaud Deraison

A The knowledge base The knowledge base is a set of keys which contains the results of the other plugins. Using the functions script_dependencies(), get_kb_item() and set_kb_item(), then you can make your scripts and upcoming scripts avoid to do something that has already been done.

Here is a sum up of the keys that are set by the plugins :

KB items may have several values. For instance, imagine that the remote host is running two FTP servers : one on port 21 and one on port 2100. Then, the key Services/ftp, which is the symbolic name of the FTP server port is equal to 21 and 2100. If that is the case, then the script will be executed twice : the first time, get_kb_item("Services/ftp") will return 21, the second time it will return 2100. This behavior is automatic and your script should not take care of this - that is, it should consider that a given key always has only one value. Even if that is not the case in real life, because nessusd is in charge of this.

Not all these keys are useful. I have never used several of them. But putting too much elements in the KB is better than the opposite...

Host/OS Defined in : queso.nasl and nmap_wrapper.nasl Type : string Meaning : Remote operating system type

Host/dead Defined in : ping_host.nasl and all the DoS plugins Type : boolean Meaning : The remote host is dead. If you set this item, then nessusd will interrupt the test of the host.

Services/www Defined in : find_service.nes Type : port number Meaning : port on which a web server is running. Returns 0 if no web server has been found.

Services/auth Defined in : find_service.nes Type : port number Meaning : port on which an identd server is running. Returns 0 if no such server has been found Services/echo Defined in : find_service.nes Type : port number Meaning : port on which 'echo' is running. Returns 0 if no such service has been found

Services/finger Defined in : find_service.nes Type : port number Meaning : port on which a finger server is running. Returns 0 if no such server has been found

Services/ftp Defined in : find_service.nes Type : port number Meaning : port on which an ftp server is running. Returns 0 if no such server has been found

Services/smtp Defined in : find_service.nes Type : port number Meaning : port on which an SMTP server is running. Returns 0 if no such server has been found

Services/ssh Defined in : find_service.nes Type : port number Meaning : port on which an SSH server is running. Returns 0 if no such server has been found

Services/http_proxy Defined in : find_service.nes Type : port number Meaning : port on which an HTTP proxy is running. Returns 0 if no such server has been found

Services/imap Defined in : find_service.nes Type : port number Meaning : port on which an imap server is running. Returns 0 if no such server has been found

Services/pop1 Defined in : find_service.nes Type : port number Meaning : port on which a POP-1 server is running. Returns 0 if no such server has been found

Services/pop2 Defined in : find_service.nes Type : port number Meaning : port on which a POP-2 server is running. Returns 0 if no such server has been found

Services/pop3 Defined in : find_service.nes Type : port number Meaning : port on which a POP-3 server is running. Returns 0 if no such server has been found

Services/nntp Defined in : find_service.nes Type : port number Meaning : port on which an NNTP server is running. Returns 0 if no such server has been found

Services/linuxconf Defined in : find_service.nes Type : port number Meaning : port on which a linuxconf server is running. Returns 0 if no such server has been found

Services/swat Defined in : find_service.nes Type : port number Meaning : port on which a SWAT server is running. Returns 0 if no such server has been found

Services/wild_shell Defined in : find_service.nes Type : port number Meaning : port on which a shell is open to the world (usually a bad thing). Returns 0 if no such server has been found Services/telnet Defined in : find_service.nes Type : port number Meaning : port on which a telnet server is running. Returns 0 if no such server has been found

Services/realserver Defined in : find_service.nes Type : port number Meaning : port on which a RealServer server is running. Returns 0 if no such server has been found

Services/netbus Defined in : find_service.nes Type : port number Meaning : port on which a NetBus server is running (usually not a good thing). Returns 0 if no such server has been found bind/version Defined in : bind_version.nasl Type : string Meaning : version of the remote BIND daemon rpc/bootparamd Defined in : bootparamd.nasl Type : string Meaning : The bootparam RPC service is running

Windows compatible Defined in : ca_unicenter_file_transfer_service.nasl, ca_unicenter_transport_service.nasl, mssqlserver_detect.nasl and windows_detect.nasl Type : boolean value Meaning : The remote host appears to be running a Windows-compatible operating system (this test is only done regarding the number of the opened-ports) finger/search.**@host Defined in : cfinger_search.nasl Type : boolean value Meaning : The finger daemon dumps the list of users if the query .** is made finger/0@host Defined in : finger_0.nasl Type : boolean value Meaning : The finger daemon dumps a list of users if the query 0 is made finger/.@host Defined in : finger_dot.nasl Type : boolean value Meaning : The finger daemon dumps a list of users if the query . is made finger/user@host1@host2 Defined in : finger_0.nasl Type : boolean value Meaning : The finger daemon is vulnerable to a redirection attack www/frontpage Defined in : frontpage.nasl Type : boolean value Meaning : The remote web server is running frontpage extensions ftp/anonymous Defined in : ftp_anonymous.nasl Type : boolean value Meaning : The remote FTP server accepts anonymous logins ftp/root_via_cwd Defined in : ftp_cwd_root.nasl Type : boolean value Meaning : It is possible to gain root on the remote FTP server using the CWD ~ bug (see CVE-1999-0082) ftp/microsoft Defined in : ftp_overflow.nasl Type : boolean value Meaning : The remote server is a Microsoft FTP server, which closes the connection whenever a too long argument is issued. ftp/false_ftp Defined in : ftp_overflow.nasl Type : boolean value Meaning : the remote FTP server is either protected by tcp wrappers or the FTP port is open but closes the connection

B The 'nasl' utility The libnasl package now comes with its own standalone interpretor nasl. Do 'man nasl' for more details

------File translated from TEX by TTH, version 2.34. On 16 Apr 2000, 16:20.