Network Security Tools/Modifying and Hacking Security Tools/Extending Hydra and Nmap

From WikiContent

< Network Security Tools | Modifying and Hacking Security Tools
Revision as of 22:54, 11 March 2008 by Docbook2Wiki (Talk)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search
Network Security Tools

Many security tools do not support a plug-in architecture, making extending these tools somewhat challenging. However, if your security tool uses a modular architecture, or if it uses a configurable database for specifying its behavior, you can extend it more easily. In this chapter we demonstrate how to extend the popular open source security tool, Hydra, to support an additional protocol, as well as adding signatures to the service signature file for the popular port scanner, Nmap, to recognize additional services.

By extending existing tools to support additional protocols and services, you can test for security vulnerabilities in networks where nonstandard or proprietary protocols and services exist, without creating an entirely new tool from scratch.

Contents

Extending Hydra

Hydra is a popular tool written by Van Hauser (http://www.thc.org/) for testing networked services for weak username and password combinations. This technique, commonly known as brute-force testing, is valuable for ensuring that network services and systems are not vulnerable to password-guessing attacks due to weak username and password combinations.

Although Hydra supports a number of different protocols for testing, most likely you'll want to test services available on your network that Hydra doesn't support. In this section we will demonstrate how to add a module for testing Simple Mail Transport Protocol (SMTP) authentication. You could use this to determine if weak passwords exist in your email user base and close this potential exposure before a spammer takes advantage of it.

Hydra is freely available for noncommercial use and for commercial use with proper acknowledgment. You can download it from http://www.thc.org/thc-hydra/. The module described in this section is included in Hydra Version 4.2.

Overview of Hydra

Hydra is a very popular tool primarily because of the wide variety of protocols it supports and because its parallel nature divides password-testing tasks among a user-definable number of tasks.

As of Version 4.4, Hydra supports the following protocols:

telnet ftp http
https http proxy ldap
SMB SMBNT Microsoft SQL
mysql rexec socks5
VNC pop3 imap
nntp pcnfs icq
SAP/R3 Cisco auth Cisco enable
SMTP auth ssh2 snmp
cvs Cisco AAA


Hydra is primarily a command-line security-testing tool, and as such you can call it from within recent versions of Nessus to perform login (username) and password testing on services identified by Nessus. In addition to using the tool through Nessus, recent versions of Hydra also come with a graphical GTK user interface for platforms supporting the GTK toolkit.

Overview of SMTP Authentication

In this section we will demonstrate how to add SMTP authentication protocol support to Hydra. Mail servers commonly use SMTP authentication to identify a user as being valid prior to accepting email for delivery.

A number of different standards for SMTP authentication exist, many of which are not RFC standards. We are demonstrating an authentication method using the AUTH LOGIN method, as shown in Example 3-1.

Example 3-1. An SMTP AUTH session

220-mail.xxxxxxxx.com ESMTP Exim 4.34 #1 Wed, 23 Jun 2004 17:35:13 -0700 
EHLO mail.myserver.com
250-mail.xxxxxxxx.com Hello mail.myserver.com [192.168.0.156]
250-SIZE 52428800
250-PIPELINING
250-AUTH PLAIN LOGIN
250-STARTTLS
250 HELP
AUTH LOGIN
334 VXNlcm5hbWU6
bXl1c2VybmFtZQ==
334 UGFzc3dvcmQ6
bXlwYXNzd29yZA==
235 Authentication succeeded

The AUTH LOGIN authentication method is well supported by many common SMTP servers, and as such, it is a good protocol to use. The protocol is a simple process that uses unencrypted credentials. Even though the protocol is insecure, a number of mail servers support it in their default configurations as a lowest-common-denominator protocol for SMTP authentication.

The protocol can be demonstrated by using the telnet command to port 25 on an available mail server. The mail server then responds with a connection message:

220-mail.xxxxxxxx.com ESMTP Exim 4.34 #1 Wed, 23 Jun 2004 17:35:13 -0700

The mail server responds with a header containing the SMTP response code 220. Similar to the HTTP protocol, SMTP uses a numbered response code system, as shown in Table 3-1.

Table 3-1. SMTP response codes

Response code Description
2xx; e.g., 220 (service ready) Command accepted and processed
3xx; e.g., 354 (start mail input) Flow control message
4xx; e.g., 421 (service not available) Critical failure or transfer failure
5xx; e.g., 500 (syntax error) Errors with command


In this case, the mail server (or more accurately, the MTA, or Mail Transfer Agent) is running the open source Exim service. Then we need to start an email session with the mail server by using the EHLO command with our Internet hostname, as shown here:

250-mail.xxxxxxxx.com Hello mail.myserver.com [192.168.0.156]
250-SIZE 52428800
250-PIPELINING
250-AUTH PLAIN LOGIN
250-STARTTLS
250 HELP

The EHLO command informs the server that we want to use the Extended Simple Mail Transfer Protocol (ESMTP) and determines the SMTP extensions supported by the mail server, including the types of authentication (if any) supported by the server we are interrogating. The AUTH keyword is followed by two different types of authentication, indicating that this server supports both the PLAIN and LOGIN authentication methods. This command is important, as RFC-compliant mail servers should respond with an error message such as 503 AUTH command used when not advertised if the AUTH keyword is used without a preceding EHLO command.

Then we send the mail server an AUTH LOGIN command to start the authentication process with the server:

AUTH LOGIN
334 VXNlcm5hbWU6

The AUTH LOGIN command instructs the server that the client wants to begin SMTP authentication using the LOGIN method. The server has responded with the 334 status code, and a Base64-encoded representation of the string Username: to prompt the client to supply the username. The client supplies the username for authentication encoded using Base64 encoding. The username used here is myusername:

bXl1c2VybmFtZQ==
334 UGFzc3dvcmQ6

Then the server responds with a Base64-encoded representation of the string Password: to prompt the client to supply the password. The client supplies the password encoded using Base64 encoding. The password used in this example is mypassword:

bXlwYXNzd29yZA==
235 Authentication succeeded

Providing the username and password supplied are correct, the server responds with a 2xx status code. If the username and password combination is incorrect the server responds with a 5xx response code.

Adding Additional Protocols to Hydra

Hydra is structured in a very modular way, and therefore adding support for an additional protocol requires that Hydra support the defined module interface.

Each protocol is implemented in a file called hydra- <service name> .c containing a function prototype:

void service_<service name> (unsigned long int ip, int sp, unsigned char options,
            char *miscptr, FILE *fp, int port);

The options passed to the service function are outlined in Table 3-2.

Table 3-2. Service function parameters

Parameter Description
ip
ip is the IP address of the target host.
sp
sp is a socket used to read login (username) and password pairs for this task.
options
options is for user options. Currently this is 0, or OPTION_SSL if the user has specified to use Secure Sockets Layer (SSL).
miscptr
miscptr is a user-supplied additional parameter. This is for services that require more information than is supplied by default. Example modules using this parameter include the http, https, http-proxy, smbnt, ldap, cisco-enable, and SAP/R3 modules.
fp
fp is a socket used to report found login (username) and password pairs for this task.
port
If the user has defined a port to connect to, it is contained in port. This is used when services are run on nonstandard ports.


Once the service file has been written, integrating the modules into Hydra is simple:

  • Add the new hydra- <service> into the relevant areas within the Makefile.am file.
  • Edit the hydra.c file to add a reference to the new module. You can determine where to add this reference by searching for the string ADD NEW SERVICES HERE.
  • Add default service ports into hydra.h.

Note that this will not add the new module into the xhydra graphical interface. Also note that you will need to patch this to support the ability to call the new module.

Implementing SMTP-AUTH in Hydra

Every protocol Hydra supports needs to define the following variables and include files:

#include "hydra-mod.h"

extern char *HYDRA_EXIT;

char *buf;

The hydra-mod.h include file defines the functions the module accesses while running. The HYDRA_EXIT string is a value returned by some Hydra functions. The buf pointer is used in hydra-smtpauth.c as a temporary buffer for data received.

void
service_smtpauth(unsigned long int ip, int sp, unsigned char options,
        char *miscptr, FILE * fp, int port)
{
  int run = 1, next_run, sock = -1;
  int myport = PORT_SMTPAUTH, mysslport = PORT_SMTPAUTH_SSL;
  char *buffer = "EHLO hydra\r\n";

The run and next_run variables are used to control the state of the testing session. service_smtpauth follows a convention similar to many of the other text-based protocols supported in Hydra, whereby it is possible to connect and try multiple sets of credentials. The run values are specified in Table 3-3.

Table 3-3. run values

run values Description
1 Connect or reconnect to the service port.
2 Run the password-testing function on the established connection. You can run this multiple times for one connection for this protocol.
3 Close the connection and exit gracefully.


The sock variable is used to track the status of the connection to the service. The PORT_SMTPAUTH and PORT_SMTPAUTH_SSL values have been added to the hydra.h file, and they are the ports SMTP runs on normally and when run over SSL (ports 25 and 465, respectively). The string buffer is the SMTP EHLO command to be sent to the server.

  /* keep track of socket for login/password */
  hydra_register_socket(sp);

  /* get the next login/password pair to test */
  if (memcmp(hydra_get_next_pair( ), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
    return;

The hydra_register_socket() function is required to register the socket sp supplied to the module with the Hydra functions used to obtain the login (username) and password pairs for testing. Due to the parallelized structure of Hydra, each running task obtains separate login (username) and password combinations to optimize testing.

The hydra_get_next_pair( ) function is used to obtain the next pair of credentials for testing. This function returns HYDRA_EXIT on failure. These credentials are later obtained as strings using the functions hydra_get_next_login() and hydra_get_next_password().

  /* permanent loop keyed on the run variable */
  while (1) {
    switch (run) {
    case 1:                    /* connect and service init function */
      /* if we are already connected */
      if (sock >= 0)
        sock = hydra_disconnect(sock);
      usleep(300000);

The run variable is used here in a switch statement to control the state of the connection to the server. The values used for the run variable are shown in Table 3-3. This functionality ensures that if a connection to the server is already in place, it is disconnected with hydra_disconnect() . In this way, the module can ensure that a new connection is made if an error occurs by ensuring the run variable is set to 1.

      /* determine port to connect to */
      if ((options & OPTION_SSL) == 0) {
        if (port != 0)
          myport = port;
        sock = hydra_connect_tcp(ip, myport);
        port = myport;
      } else {
        if (port != 0)
          mysslport = port;
        sock = hydra_connect_ssl(ip, mysslport);
        port = myport;
      }

If the user has not specified the use of SSL, the module connects to the default port for the service, or it connects to the user-defined port if it has been supplied using hydra_connect_tcp() . If SSL has been specified, the default SSL port is used unless the user has specified a custom port, and the connection is made using hydra_connect_ssl() . For protocols using UDP, such as SNMP, Hydra also supports the hydra_connect_udp() function.

      /* see if connect succeeded */
      if (sock < 0) {
        hydra_report(stderr, "Error: Child with pid %d can't connect\n",
             (int) getpid( ));
        hydra_child_exit(1);
      }

If the connection did not succeed, Hydra will print an error to STDERR. The hydra_report( ) function is a synonym for fprintf. The hydra_child_exit() function reports the exit status of the child task, as in Table 3-4.

Table 3-4. hydra_child_exit( ) values

Value Description
0 Normal exit
1 Could not connect to the service
2 Application protocol error or service shutdown


Once the connection is made, many protocols send some form of data as a banner or to begin authentication.

      /* consume any data waiting in buffer */
      while (hydra_data_ready(sock)) {
        if((buf = hydra_receive_line(sock)) == NULL)
          exit(-1);
        free(buf);
      }

The hydra_data_ready() function returns regardless of whether data is to be read from the connected socket. If data is to be read, hydra_receive_line() reads the data in the receive buffer from the socket, and the data is thrown away. This is done to ensure that any banner messages are consumed from the buffer prior to any other actions. Note that we free the buffer that was read. It is important to perform this step on all data reads to avoid memory leaks.

In addition to the hydra_receive_line( ) function, Hydra also has the simpler hydra_recv( ) function that is useful if using a binary protocol.

      /* send EHLO command */
      if (hydra_send(sock, buffer, strlen(buffer), 0) < 0)
        exit(-1);

The hydra_send( ) function is used to send the EHLO command to the server.

      /* see if there was any response */
      if ((buf = hydra_receive_line(sock)) == NULL)
        exit(-1);

      /* see if the LOGIN keyword is in the response */
      if (strstr(buf, "LOGIN") == NULL) {       /* check AUTH LOGIN supported */
        hydra_report(stderr, "Error: SMTP AUTH LOGIN not supported: %s\n", buf);
        hydra_child_exit(2);
        exit(-1);
      }
      free(buf);
      next_run = 2;    /* run crack next */
      break;

The buf buffer received in response to the EHLO command is checked to see if it contains the word LOGIN . This is done to validate whether the server advertises the presence of the AUTH LOGIN command. If the command is present, the next_run value (and therefore the next value of the run variable) is set to 2, which initiates the testing process on the next cycle through the loop.

    case 2:                    /* run the cracking function */
      next_run = start_smtpauth(sock, ip, port, options, miscptr, fp);
      break;

Where the run variable is 2, the connection has been established and the testing function is started, as per Table 3-3.

    case 3:                    /* clean exit */
      /* if connected */
      if (sock >= 0)
        sock = hydra_disconnect(sock);
      hydra_child_exit(0);
      return;

Where the run variable is 3, the socket is disconnected and the task exits cleanly, as per Table 3-3.

    default:
      hydra_report(stderr, "Caught unknown return code, exiting!\n");
      hydra_child_exit(0);
      exit(-1);
    }
    run = next_run;   /* next step dependant on return from cracking function */
  }
}

The service_smtpauth() function exits if the start_smtpauth() function returns a value other than 1, 2, or 3. This ensures that the simple state machine controlled by the run variable is always in one of the three defined states—connecting/reconnecting, testing, or disconnecting.

Where the connection has been established successfully, and the run variable is set to 2, the service_smtpauth( ) function calls the start_smtpauth( ) function to perform a single testing instance.

int
start_smtpauth(int s, unsigned long int ip, int port, unsigned char options,
        char *miscptr, FILE *fp)

Here the start_smtpauth( ) function is passed the same values as those passed to the service_smtpauth( ) function. This function is not called from outside of this module; however, the naming and structure throughout the existing protocols supported in Hydra largely follow this convention.

  char *empty = "";
  char *login, *pass, buffer[300], buffer2[300];

  /* get login and password from the pair fetched */
  if (strlen(login = hydra_get_next_login( )) == 0)
    login = empty;
  if (strlen(pass = hydra_get_next_password( )) == 0)
    pass = empty;

The hydra_get_next_login() and hydra_get_next_password() functions are used to obtain the login (username) and password pair to be used for this instance of testing. These functions rely on the hydra_get_next_pair() function having been run to first read the login and password pair from the internal socket.

  /* consume any remaining data in the buffer */
  while (hydra_data_ready(s) > 0) {
    if ((buf = hydra_receive_line(s)) == NULL)
      return (1);
    free(buf);         /* make sure we free memory we use */
  }

Any data returned from the server remaining in the buffer is read and thrown away. If an error occurs while reading data, the function returns 1, which causes the service_smtpauth( ) function to attempt to reconnect to the server.

  /* send AUTH LOGIN command */
  sprintf(buffer, "AUTH LOGIN\r\n");
  if (hydra_send(s, buffer, strlen(buffer), 0) < 0) {
    return 1;
  }

The AUTH LOGIN command is sent to start an authentication attempt. If this fails, you should try to reconnect again.

  /* if no response received */
  if ((buf = hydra_receive_line(s)) == NULL)
    return 1;

  /* make sure we got a 334 response code (asking for username) */
  if (strstr(buf, "334") == NULL) {
    hydra_report(stderr, "Error: SMTP AUTH LOGIN error: %s\n", buf);
    free(buf);
    return 3;
  }
  free(buf);

If the response from the mail server is something other than 334 VXNlcm5hbWU6, you have experienced a protocol error, so you should exit. This might occur if the mail server does not support the authentication method you are attempting.

  /* base64 encode the username - also making sure string is < 250 */
  sprintf(buffer2, "%.250s", login);  
  hydra_tobase64((unsigned char *) buffer2);
  sprintf(buffer, "%.250s\r\n", buffer2);

  /* send the username */
  if (hydra_send(s, buffer, strlen(buffer), 0) < 0) {
    return 1;
  }

Send the login (username) obtained from hydra_get_next_login() . This is Base64-encoded using hydra_tobase64() . A hydra_conv64( ) function exists for Base64-encoding single characters, if required. Note that we are ensuring that the user-supplied data is cut off at 250 characters to avoid a potential buffer overflow issue.

  /* if no response received */
  if ((buf = hydra_receive_line(s)) == NULL)
    return (1);

  /* make sure we get a 334 - asking for password */
  if (strstr(buf, "334") == NULL) {
    hydra_report(stderr, "Error: SMTP AUTH LOGIN error: %s\n", buf);
    free(buf);
    return (3);
  }
  free(buf);

  /* base64 encode the password */
  sprintf(buffer2, "%.250s", pass);
  hydra_tobase64((unsigned char *) buffer2);
  sprintf(buffer, "%.250s\r\n", buffer2);

  /* send the password */
  if (hydra_send(s, buffer, strlen(buffer), 0) < 0) {
    return 1;
  }

  /* if no response received */
  if ((buf = hydra_receive_line(s)) == NULL)
    return (1);

The password received from hydra_get_next_password() is sent to the mail server the same way in which the username was sent.

  /* if authentication was successful */
  if (strstr(buf, "235") != NULL) {
    /* report the found credentials */
    hydra_report_found_host(port, ip, "smtpauth", fp);
    hydra_completed_pair_found( );
    free(buf);
    if (memcmp(hydra_get_next_pair( ), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
      return 3;
    return 1;
  }

If the 235 Authentication succeeded response is received from the mail server, the successful login (username) and password combination is reported using the hydra_report_found_host() function. The hydra_completed_pair_found() function is used to communicate on the internal socket that the current credentials were successful. Then the hydra_get_next_pair() function fetches the next pair of credentials for use and causes the module to exit cleanly if no credential pairs remain.

  free(buf);
  
  /* otherwise, we're finished with this pair anyway */
  hydra_completed_pair( );
  if (memcmp(hydra_get_next_pair( ), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
    return 3;

  return 2;
}

If the authentication attempt was not successful, the completed status of the pair is communicated using the hydra_completed_pair() function. Then the hydra_get_next_pair( ) function is used to fetch the next pair, and causes the module to exit cleanly if no credential pairs remain to be tested by this task.

Complete Source to hydra-smtpauth.c

The complete source to the SMTP authentication module as described earlier in Section 3.1.4 is contained in the src/hydra-smtpauth.c file in the Hydra distribution in Versions 4.2 and above.

Quick Reference to Hydra Functions

Although the SMTP authentication module highlighted most of the functionality Hydra supplies for use in modules, we have not yet covered all of Hydra's functionality. Because developer documentation of the functions is not available for Hydra modules, this section provides a quick reference to the Hydra functions available as of Version 4.4.

In addition to the functions described next, Hydra also contains files for supporting the MD4 and DES algorithms. These files are not part of the Hydra module structure, and as such are not covered here.

void hydra_child_exit(int code)

Exits the child task while signaling the exit status to Hydra

Synopsis

Valid values for code are shown in Table 3-4. Supply the value for code as 0 for normal exit, 1 for no connection possible, and 2 for protocol or service error.


void hydra_register_socket(int sock)

Registers the internal socket passed in by Hydra

Synopsis

hydra_register_socket( ) should be called with the sp variable passed into the module.


char *hydra_get_next_pair( )

Fetches the next pair of credentials for testing to an internal Hydra variable

Synopsis

The hydra_get_next_pair( ) function returns a pointer to the next credential pair with the pair formatted as login\0password. These can then be fetched cleanly using hydra_get_next_login( ) and hydra_get_next_password( ). The hydra_get_next_pair( ) function returns HYDRA_EXIT on failure, and HYDRA_EMPTY where no value was supplied (for example, when testing for blank passwords).


char *hydra_get_next_login( )

Fetches the next login (username) string

Synopsis

This function returns a pointer to the login value fetched by the hydra_get_next_pair( ) function.


char *hydra_get_next_password( )

Fetches the next password string

Synopsis

This function returns a pointer to the password value fetched by the hydra_get_next_pair( ) function.


void hydra_completed_pair( )

Updates the status of the current pair to Hydra as not valid to the internal socket

Synopsis

This is run when the current pair does not appear to be a valid login/password combination on the service being tested.


void hydra_completed_pair_found( )

Updates Hydra with the status that the current pair is valid to the internal socket

Synopsis

This is run when the current pair has been found to be a valid login/password combination on the service being tested.


void hydra_report_found(int port, char *svc, FILE *fp)

Used to supply the credentials found for display

Synopsis

This function is used to output the found credentials to the user. port is the port the service was tested on, svc is the name of the service (commonly a literal string such as smtpauth), and fp is the fp value Hydra supplied to the module.


void hydra_report_found_host (int port, unsigned int ip, char *svc, FILE *fp)

Used to supply the credentials found for display, including the host IP address

Synopsis

This function is similar to hydra_report_found( ), except the IP address of the server tested is displayed. It is used to output the found credentials to the user. port is the port the service was tested on, ip is the IP address, svc is the name of the service (commonly a literal string such as smtpauth), and fp is the fp value Hydra supplied to the module.


void hydra_report_found_host_msg (int port, unsigned int ip, char *svc, FILE *fp, char *msg)

Used to supply the credentials found for display, including the host IP address and a message to be displayed to the user

Synopsis

This function is similar to hydra_report_found_host(), with the addition of a message to be displayed. It is used to output the found credentials to the user. port is the port the service was tested on, ip is the IP address, svc is the name of the service (commonly a literal string such as smtpauth), fp is the fp value Hydra supplied to the module, and msg is a message to be displayed to the user.


int hydra_connect_tcp(unsigned long int host, int port)

Used to make a connection to a service using TCP

Synopsis

This function makes a connection to the host defined by the IP address host, on port port, using TCP. host is the ip value passed into the module, and the port value usually is a standard port for the service; however, it also can be user-defined. The function returns a socket value used in sending and receiving operations, or -1 on error.


int hydra_connect_ssl(unsigned long int host, int port)

Used to make a connection to a service using SSL.

Synopsis

This function makes a connection to the host defined by the IP address host, on port port, using SSL. host is the ip value passed into the module, and the port value is either the standard SSL port for the service, or user-defined. The function returns a socket value used in sending and receiving operations, or -1 on error.


int hydra_connect_udp(unsigned long int host, int port)

Used to make a connection to a service using UDP

Synopsis

This function sets up a socket for communicating to the host defined by the IP address host on port port, using UDP. host is the ip value passed into the module, and the port value is either the standard port for the service, or user-defined. The function returns a socket value used in sending and receiving operations, or -1 on error.


int hydra_disconnect(int socket)

Disconnects a socket opened by one of the Hydra connection functions

Synopsis

This function closes the socket supplied and returns -1.


int hydra_data_ready_writing_timed(int socket, long sec, long usec)

Checks whether the socket is ready to have data written to it

Synopsis

This function waits up to sec seconds and usec microseconds to see if the socket socket is available for writing. This function returns a value greater than zero if the socket is ready for writing, 0 if the socket is not ready for writing, and -1 on error.


int hydra_data_ready_writing(int socket)

Checks whether the socket is ready to have data written to it

Synopsis

This function calls hydra_data_ready_writing_timed() to see if the socket socket is available for writing. This function returns a value greater than zero if the socket is ready for writing, 0 if the socket is not ready for writing, and -1 on error.


int hydra_data_ready_timed(int socket, long sec, long usec)

Checks whether the socket has data ready to be read

Synopsis

This function waits up to sec seconds and usec microseconds to see if the socket socket has data available for reading. This function returns a value greater than zero if the socket has data for reading, 0 if no data is available, and -1 on error.


int hydra_data_ready(int socket)

Checks whether the socket has data ready to be read

Synopsis

This function calls hydra_data_ready_timed( ) to see if the socket socket has data to be read. This function returns a value greater than zero if the socket has data for reading, 0 if no data is available, and -1 on error.


int hydra_recv(int socket, char *buf, int length)

Receives data from the supplied socket

Synopsis

This function reads up to length data from the socket socket into the buffer buf. The function returns the amount of data read, or -1 on error. No translation of any type is done to the data received. This function should be used for binary protocols, as hydra_receive_line( ) performs some translation on data read.


char *hydra_receive_line(int socket)

Receives data in a line-oriented mode from the supplied socket

Synopsis

This function attempts to read all data available from the socket socket. It returns a pointer to a buffer which is allocated within the function. These buffers should be deallocated using a free( ) call after use to conserve memory usage. All NULL characters in the data received are translated into space characters (0x20).


int hydra_send(int socket, char *buf, int size, int options)

Sends the supplied data on the supplied socket

Synopsis

This function sends the data in the buffer buf, of length size, out on the socket defined by socket. The options variable is not commonly used (it is set to 0), but is the flags variable for the underlying socket's API send( ) command. This function returns the amount of data sent, or -1 on error.


int make_to_lower(char *buf)

Converts the supplied buffer to lowercase

Synopsis

This function converts the buffer pointed to by buf to lowercase. The function always returns 1.


unsigned char hydra_conv64(unsigned char in)

Converts a single character to Base64 encoding

Synopsis

This function returns the Base64-encoded representation of the character supplied to the function in the in parameter, or 0 on error.


void hydra_tobase64(unsigned char *buf)

Converts a string to Base64 encoding

Synopsis

This function converts the string pointed to by buf to Base64 encoding. If an error occurs during encoding, the value pointed to by buf is in an undefined state.


void hydra_dump_asciihex(unsigned char *string, int length)

Prints a hex and ASCII dump

Synopsis

This function takes the data in string, of length length, and prints a hex and ASCII table to standard output. This can be very useful for debugging a module under development .


Adding Service Signatures to Nmap

Recent versions of the popular port scanner Nmap can detect the type and version of services running on a network, as illustrated in Example 3-2.

Example 3-2. Example Nmap version scan

>nmap -sV 127.0.0.1  

Starting nmap 3.50 ( http://www.insecure.org/nmap/ ) at 2003-07-05 17:12 EDT
Interesting ports on localhost (127.0.0.1):
(The 1658 ports scanned but not shown below are in state: closed)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 3.8.1p1 (protocol 2.0)

Nmap run completed -- 1 IP address (1 host up) scanned in 1.104 seconds

This scan is implemented as a series of probes and responses in the file nmap-service-probes . This file defines the probes that will be sent to the service to elicit some response, as well as a series of regular expressions against which to match responses to determine which services are running and, where possible, their versions.

At a high level, the version-scanning methodology follows this process:

  • If the port is a TCP port, connect to it and listen. This is called the NULL probe. Many services will return a banner on connection. If a match is made, processing stops.
  • If no match is given, or if the protocol is UDP, probes defined in the nmap-service-probesfile will be attempted if the protocol and the port ranges in the file match. If a response matching a probe is found, processing stops. If a soft match occurs (whereby a service is recognized, but not its type or version), follow-on probes will be limited to relevant ones.
  • If no match is found, each probe in the nmap-service-probes file will be tried, regardless of the ports on which the service usually runs. This will be limited where a soft match has already occurred.
  • If SSL was found, Nmap will connect using SSL (if available) to run the version-detection process again.

If a service responds to a probe sent during this process, but Nmap does not recognize the response, Nmap prints a fingerprint for the service that you can use to report the signature to the Nmap developers, as shown in Example 3-3. You can use this, together with the version and service information, to include a signature that recognizes this service in the nmap-service-probes file in the future.

Example 3-3. Nmap unrecognized service

>nmap -sV -p 4738 127.0.0.1

Starting nmap 3.50 ( http://www.insecure.org/nmap/ ) at 2003-07-05 17:39 EDT
Interesting ports on localhost (127.0.0.1):
PORT     STATE SERVICE VERSION
4738/tcp open  unknown
1 service unrecognized despite returning data. If you know the service/version, 
please submit the following fingerprint at http://www.insecure.org/cgi-bin/
servicefp-submit.cgi :
SF-Port4738-TCP:V=3.50%D=7/5%Time=40E9CA80%P=i686-pc-linux-gnu%r(NULL,59,"
SF:Login\x20with\x20USER\x20<name>\x20followed\x20by\x20PASS\x20<password>
SF:\x20or\x20ANON\r\nCheck\x20privileges\x20with\x20PRIVS\r\n")%r(GenericL
SF:ines,59,"Login\x20with\x20USER\x20<name>\x20followed\x20by\x20PASS\x20<
SF:password>\x20or\x20ANON\r\nCheck\x20privileges\x20with\x20PRIVS\r\n")%r
SF:(GetRequest,59,"Login\x20with\x20USER\x20<name>\x20followed\x20by\x20PA
SF:SS\x20<password>\x20or\x20ANON\r\nCheck\x20privileges\x20with\x20PRIVS\
SF:r\n")%r(HTTPOptions,59,"Login\x20with\x20USER\x20<name>\x20followed\x20 
SF:by\x20PASS\x20<password>\x20or\x20ANON\r\nCheck\x20privileges\x20with\x
<cut>

Nmap run completed -- 1 IP address (1 host up) scanned in 75.504 seconds

At this point we have several options:

  • Submit the signature to the URL provided and wait for the next version of Nmap. If responses were received from the probes sent, and the service is something that could be expected to be running on someone else's environment, this might be the best choice.
  • Create a working match and/or probe statement, and submit that to Fyodor at fyodor@insecure.org. For services that require a custom probe and can be expected to be found in another environment, this might be the best choice.
  • Create a working match and/or probe statement for your own use. You might choose this option if your environment contains custom-written software running proprietary services or protocols. In this case it is necessary to know how to write the probes and matches to detect these proprietary services running on the environment being tested.

Regardless of which option you choose, it is very useful to know how to write your own probe and match signatures.

The nmap-service-probes File

The keywords contained in the nmap-service-probes file are listed in Table 3-5.

Table 3-5. nmap-service-probes keywords

Keyword Format
Probe
Probe <protocol> <probe name> <probe string>
match
match <service> <pattern> [version info]
softmatch
softmatch <service> <pattern>
ports
ports <portlist>
sslports
sslports <portlist>
Totalwaitms
totalwaitms <milliseconds>

Probes

A probe entry consists of the values shown in Table 3-6.

Table 3-6. Probe values

Parameter Description
Protocol TCP or UDP.
Probe name Name of the probe (human-readable).
Probe string String starting with a q, then a delimiter that will start and end the string sent. The string can consist of printable characters, as well as quoted unprintable characters and control characters in standard C or Perl notation.


Here are some example probe strings:

Probe TCP NULL q||
Send nothing, waiting the amount of time specified in totalwaitms.
Probe TCP GenericLines q|\r\n\r\n|
Send carriage return, newline, carriage return, newline.
Probe UDP DNSStatusRequest q|\0\0\x10\0\0\0\0\0\0\0\0\0|
Send the binary string 0x00 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00.

Matches

A match entry consists of the values defined in Table 3-7.

Table 3-7. Match values

Parameter Description
Service Name of the service the pattern matches.
Pattern A Perl-compatible regular expression to match the expected response for this service. This is of the format m/regex/opts.
Version info A field specifying additional version information. This is of the format v/productname/version/info/. This can contain variables matched from the matching pattern, such as $1, $2, where the matching pattern contains ( ) matches. Any or all entries can be empty.


Nmap uses the Perl Compatible Regular Expressions (libpcre) library for evaluating regular expressions. Perl regular expressions are documented at http://www.perldoc.com/perl5.8.0/pod/perlre.html.

Here are some example match strings:

match ssh m/^SSH-([.\d]+)-OpenSSH[_-](\S+)/ v/OpenSSH/$2/protocol $1/
Match strings such as SSH-1.5-OpenSSH-3.4p1, reading the version string (3.4p1) and protocol (1.5) into the $2 and $1 variables, respectively.
match ftp m/^220[- ].*FTP server \(Version (wu-[-.\w]+)/s v/WU-FTPD/$1//
Match strings such as 220 FTP server (Version wu-2.6.0) and extract the version wu-2.6.0.
match mysql m/^.\0\0\0\n(4\.[-.\w]+)\0...\0/s v/MySQL/$1//
Match the version of MySQL 4.x from the binary response.

Soft matches

A soft match occurs when a service can be identified, but no additional information can be derived. A soft-match entry consists of the values defined in Table 3-8.

Table 3-8. Soft-match values

Parameter Description
Service Name of the service the pattern matches.
Pattern A Perl-compatible regular expression to match the expected response for this service. This is of the format m/regex/opts.


Here are some example soft-match strings:

  • softmatch ftp m/^220[- ].*ftp server.*\r\n/i
  • softmatch imap m/^\* OK [-.\w,:+ ]+imap[-.\w,:+ ]+\r\n$/i

ports

ports is a comma-separated list of ports, as well as port ranges (e.g., 35067-35090) on which the service will commonly run. This is used to ensure that probing is done efficiently, and therefore the ports entry should follow the Probe entry in nmap-service-probes.

sslports

sslports is a comma-separated list of ports, as well as port ranges (e.g., 55522-55525) on which the service will commonly run over SSL. This is used to ensure that probing is done efficiently, and therefore the sslports entry should follow the Probe and ports entries in nmap-service-probes.

totalwaitms

totalwaitms is used to specify the timeout for a Probe. It is not needed unless the service you are probing does not respond immediately. If it is used, it should follow the Probe entry.

Personal tools