1.12. Important NASL FunctionsThis section presents the most basic string, plug-in maintenance, and reporting functions available in NASL. For an exhaustive list of all function calls available in the NASL library, read the NASL2 Reference Manual available at http://nessus.org/documentation/. 1.12.1. StringsNASL provides a rich library for string manipulation. When scanning for vulnerabilities, outgoing requests and incoming responses contain data presented to NASL plug-ins as strings, so it is important to learn how to best utilize the available string API. This section discusses NASL-provided functions for pattern matching, simple string manipulation and conversion, and other miscellaneous string-related functions. 1.12.1.1 Simple string manipulation functionsThe chomp() function takes in a string as a parameter and strips away any carriage returns, line feeds, tabs, or whitespace at the end of the string. For example: mystring='abcd \t\r\n'; display ('BEGIN',chomp(mystring),'END\n'); displays BEGINabcdEND on one line. The crap( ) function is used to fill a buffer with repeated occurrences of a specified string. The function takes in two parameters, length and data. The length parameter specifies the length of the string to be returned, while the data parameter specifies the string that should be used to fill the buffer. For example, crap(length:10,data:'a') returns aaaaaaaaaa. If data is not specified, a default value of X is used. To perform string concatenation, you can use the strcat( ) function. This function also converts given variables to strings when performing concatenation. The following example causes the value of mystring to be set to abcdefgh123: string1="abcd"; string2="efgh"; number1=123; mystring=strcat(string1,string2,number1); 1.12.1.2 Finding and replacing stringsMany functions in this section discuss regular expressions you can apply to search for string patterns. These regular expressions correspond to the POSIX standard. On any Unix or Linux system, you can obtain more information about the format of such regular expressions by typing: [notroot]$ man re_format The egrep( ) function analyzes a string for a given pattern and returns every line of the string that matches the pattern. For example: mystring="One dog two dog\nThree cat four cat\nFive mouse Six mouse"; display(egrep(pattern:'dog|mouse',string:mystring)); displays: One dog two dog Five mouse six mouse The pattern parameter specifies the pattern to match, while the string parameter specifies the actual string to perform the match against. Another parameter, icase, is optional, and its value is FALSE by default, which causes egrep( ) to be case-sensitive. When icase is set to TRUE, egrep( ) is case-insensitive. Sometimes it is necessary to perform matching on a string with respect to a given pattern. For this purpose, you can use the ereg( ) function. This function accepts the parameter string that specifies the string to match against, in addition to pattern, which specifies the regular expression to be used to perform the matching. The function returns trUE if a match is found and FALSE if no match is found. Here is an example of how ereg( ) can prove useful in determining if a URL is present in a given string: if(ereg(pattern:"^http://", string:mystring, icase:TRUE)) { //URL found at beginning of mystring } The icase parameter is optional, and when set to TRUE it causes ereg( ) to be case-insensitive. If icase is not specified, it is FALSE by default. Another optional parameter to ereg( ) is multiline, which is also FALSE by default. This causes ereg() to ignore the string contents after a newline character is found. When set to trUE, ereg() continues to search the string even after newline characters. Alternatively, you can use the match() function, which accepts simple patterns that consist of * or ? as wildcards. It accepts the same parameters as ereg( ). The ereg_replace( ) function searches for a given pattern in a string and replaces occurrences of the pattern with a given string. Here is an example of how you can use ereg_replace() to replace a string containing an assignment statement a=1; with just the left operand, a: example_string="a=1;"; newstring = ereg_replace(string:example_string,pattern: "(.*)=.*","\1"); The \1 string signifies the first pattern provided within parenthesesi.e., (.*). Similarly, it is legal to use \2, \3, and so on, if applicable. The ereg_replace( ) function also accepts the icase parameter which, if set to trUE, causes ereg_replace( ) to be case-insensitive. The eregmatch( ) function searches for a string within another given string, and returns the found patterns in the form of an array. Here is an example of how you can use eregmatch() to find an IP address within a given string: mystring = "The IP address is 192.168.1.111."; ip = eregmatch(pattern: "([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)", string: mystring); display (ip[0],"\n"); ip[1] contains the string 192, ip[2] contains 168, ip[3] contains 1, and ip[4] contains 111. Because ip[0] contains the entire string, the preceding example will print the string 192.168.1.111. The eregmatch( ) function also accepts an optional parameter, icase, which, if set to trUE, causes the function to be insensitive. It is FALSE by default. The insstr( ) function replaces a part of a given string with another string, starting from a given index and an optional end index. For example: newstring=insstr("I hate my cat. I love cats.","dog",10,12); display (newstring,"\n"); displays: I hate my dog. I love cats. Another function, strstr( ), accepts two strings as parameters, searches for the occurrence of the second string with the first given string, and returns the second string starting from where the occurrence was found. For example, the following returns http is 80: strstr("The default port for http is 80","http"); The stridx( ) function simply returns the index of a found substring. For example: stridx("A dog and a cat", "and",0) returns the value 6 because the string and occurs in "A dog and a cat" from the sixth position, starting from the beginning (i.e., from the index 0). You can split strings into parts by using the split( ) function. The split( ) function simply splits a given string into parts when given a particular separator. Take a look at the following example: the_string="root::0:root"; split_string=split(mystr,sep:":"); In the preceding example, the value of split_string[0] will be root:, the value of split_string[1] will be :, the value of split_string[2] will be 0:, and the value of split_string[3] will be root. The function substr( ) accepts one string as an argument along with a start index. The end index is optional. This function returns a substring of the given string, which contains the original string starting from the given start index up until the end index. If the end index is not provided, substr( ) returns the substring up until the end of the given string. For example: substr("Hi there! How are you?",10); returns How are you?. Another function, str_replace(), replaces a part of a given string with another string depending upon a pattern. Here is an example of how to use str_replace( ) to replace the first occurrence of cat with dog: newstring=str_replace(string: "I hate my cat. I love cats.",find: "cat", replace:"dog",count:1); The count parameter is optional. If it is not specified, str_replace( ) replaces all occurrences. 1.12.1.3 ConversionsTo convert a number into a string representation of its hexadecimal equivalent, use the hex( ) function. The following example returns the string 0x0f: hex(15); The hexstr( ) function accepts a string as a parameter and returns another string that contains the hexadecimal equivalent of each character's ASCII value. For example, the ASCII equivalent of "j" in hexadecimal is "6a," and "k" is "6b," so the following example returns the string 6a6b: hexstr("jk"); The int( ) function takes in a string as an argument and returns an integer. For example, the following causes the variable x to be assigned 25 as its value: x=int("25"); The ord( ) function accepts one string as an argument, and returns the ASCII equivalent of the first character in the string. The main purpose of the function is to calculate the ASCII code of a given character, so it is usually invoked with a string whose length is equal to 1. For example, the following returns 97, which is the decimal equivalent of the ASCII code for the character "a": ord("a"); It is possible to convert a set of variables into a string by using the raw_string( ) and string( ) functions. Arguments passed to the raw_string( ) function are interpreted, and a string is eventually returned. If you pass an integer to this function, it will use its ASCII character equivalent. For example, the following returns the string abcd because the ASCII equivalent of the decimal 100 is "d": raw_string("abc",100); The string( ) function, on the other hand, converts given integers into strings, so the following returns the string abc100: string("abc",100); Quite often, a given string will need to be converted to uppercase, and for this purpose, you can use the toupper( ) function. For example: caps_string=toupper('get / http/1.0\r\n'); returns the string GET / HTTP/1.0\r\n. Conversely, you can use the tolower() function to convert a string to lower case. 1.12.2. Plug-in DescriptionsThis section covers NASL functions that you can use to provide plug-in descriptions to the end user. When Nessus runs a script, the value of the variable description is set to TRUE. When you run a script using the NASL interpreter, description is not defined. Therefore, the functions presented in this section should be defined in an if (description) block. Here is an example: if (description) { script_id(99999); script_version ("$Revision: 1.2 $"); script_name(english:"Checks for /src/passwd.inc"); desc["english"]="/src/passwd.inc is usually installed by XYZ web application and contains username and password information in clear text. Solution: Configure your web-browser to not serve .inc files. Risk factor: High"; script_description(english:desc["english"]); script_summary(english:"Checks for the existence of /src/passwd.inc"); script_category(ACT_GATHER_INFO); script_copyright(english:"This script is Copyright (c)2004 Nitesh Dhanjani"); script_family(english:"CGI abuses"); script_require_ports("Services/www",80); exit(0); } The script_id( ) function sets a unique ID for the plug-in. Every plug-in's value must be unique. In this case, we use a high number, 99999, to ensure a distinct value. The script_version( ) function sets the version number of the plug-in. It is a good idea to update this number to reflect the latest version of the plug-in. The script_description() function sets the description of the plug-in. The Nessus client shows this description when the user queries a plug-in. Similarly, the script_summary() function produces a summary description of the plug-in. The script_category() function sets the plug-in's category as required by Nessus. (See the Section 1.11.3 earlier in this chapter for more information on applicable plug-in categories.) The script_copyright() function sets author copyright information. Nessus categorizes plug-ins into different families to help sort the vulnerability-check plug-ins. In our example, we set it to CGI abuses to indicate an abuse of a CGI-based web application. See http://cgi.nessus.org/plugins/dump.php3?viewby=family to view a list of already-available plug-ins that have been categorized by family. Nessus can optimize scans if you select the appropriate checkbox in the "Scan options" tab of the GUI client. When this option is enabled, Nessus scans for vulnerabilities related to the applications running on the open ports of the target host. We use the script_require_ports() function to set the port related to the vulnerability, which in our case is set to www, for HTTP traffic. Another function, namely script_require_udp_ports( ), is also available, and you can use it to set applicable lists of UDP ports that need to be open for the script to be executed by Nessus. You can use additional description functions when writing Nessus plug-ins. Take a look at the "NASL2 Reference Manual" available at http://nessus.org/documentation/ for an exhaustive list. The functions described so far set various description values for the plug-in. Click the appropriate plug-in name from the list of plug-ins displayed in the Plugins tab of the Nessus client to view them. 1.12.3. Knowledge BaseQuite often, plug-ins need to communicate with each other and with the Nessus engine. The two functions presented here allow for plug-ins to define items in a shared memory space that is referred to as the Knowledge Base. The set_kb_item( ) function expects two parameters as input: name and value. For example: set_kb_item(name:"SSL-Enabled",value:TRUE); The get_kb_item( ) function expects one parameter as input: name. For example: value = get_kb_item(name:"SSL-Enabled"); If set_kb_item( ) is called repeatedly with the same name, a list is created in the Knowledge Base memory. Note that if get_kb_item( ) is called to retrieve such a list, the plug-in process spawns a new child process for every item that is retrieved. The get_kb_item( ) function will return a single value to each spawned plug-in process. In this way, each plug-in process can deal with each element of the list in parallel. This behavior is by design and might change in the future. It is not possible to call get_kb_item( ) to retrieve an item set by set_kb_item( ) in the same plug-in process. This is because NASL forks a new process to set the item in the Knowledge Base. This behavior is by design and might change in the future. Plug-in authors should not be affected by this because if a plug-in sets a particular item in the Knowledge Base, it is assumed that the plug-in is already aware of the particular item. You can use the get_kb_list( ) function to retrieve multiple entries from the Knowledge Base. For example: tcp_ports = get_kb_list(" Ports/tcp/*"); 1.12.4. Reporting FunctionsOnce a specific vulnerability is found, a plug-in needs to report it to the Nessus engine. The security_note( ) function reports a miscellaneous issue to the user. For example, the popserver_detect.nasl plug-in calls security_note( ) if it detects that the remote server is running a POP3 server: security_note(port:port, data:report); The data parameter accepts a string that will be displayed to the user viewing the Nessus report after scanning is complete. In this case, the string is stored in the variable report, which contains text that lets the user know a POP3 server has been found on the target host. The function also accepts another parameter, proto, which should be set to tcp or udp. If proto is not specified, tcp is assumed. The security_warning( ) function is used to indicate a mild security flaw. It accepts the same parameters as security_note( ). For example, the ftp_anonymous.nasl plug-in invokes security_warning( ) if the target host is running an FTP server with the anonymous account enabled. The security_hole( ) function is used to indicate a severe security flaw. It also accepts the same parameters as security_note( ). As an example, test-cgi.nasl attempts to exploit a web server that has the test-cgi CGI script installed. The plug-in tests to see if it can exploit the test-cgi web script to view the host's root directory listing. It is obvious that such a vulnerability is a severe security flaw, so the plug-in invokes security_hole( ) to indicate a major flaw. |