Team LiB
Previous Section Next Section

2.2. Writing an Ettercap Dissector

A dissector captures protocol-specific information from the network. Most Ettercap dissectors are designed to capture usernames and passwords transmitted over the network in real time. Here is an example of how to run Ettercap in console mode to sniff passwords:

[root]# ettercap --text --quiet

ettercap NG-0.7.0 copyright 2001-2004 ALoR & NaGA

Listening on en0... (Ethernet)
   eth0 ->       00:0B:25:30:11:B      192.168.1.1     255.255.255.0

Privileges dropped to UID 65534 GID 65534...
   0 plugins
  39 protocol dissectors
  53 ports monitored
6312 mac vendor fingerprint
1633 tcp OS fingerprint
2183 known services

Starting Unified sniffing...


Text only Interface activated...
Hit 'h' for inline help

FTP : 10.0.0.1:21 -> USER: john  PASS: try4ndgu355m3!!

In the preceding example, the FTP dissector successfully sniffed the FTP password try4ndgu355m3!! of user john logged on to an FTP server running on host 10.0.0.1.

In the following paragraphs, we will discuss the dissector responsible for capturing FTP usernames and passwords. First we will discuss the FTP authentication mechanism, followed by a detailed analysis of the FTP dissector source code.

2.2.1. Overview of FTP Authentication

This section discusses how FTP performs authentication. We need to understand this before we step through FTP dissector source code for Ettercap.

FTP is a plain-text protocol, and it uses no encryption. FTP servers listen on TCP port 21 by default. To authenticate with an FTP server, the client establishes a connection to TCP port 21 and expects a banner that is preceded with 220:

220 Welcome to ftp.example.com

The banner string is irrelevant and can be changed by the FTP server administrator. By default, banner strings of some FTP servers provide the FTP server name and version number. With respect to the Ettercap dissector, we are concerned with only the 220 response code, which signifies that the FTP server is ready to serve further requests.

To authenticate with the FTP server, a client sends the USER command followed by the user's username:

USER john

If the FTP server is ready to authenticate the user, it responds with a 331 response code:

331 Please specify the password.

Next, the FTP client sends the PASS command followed by the user's password:

PASS try4ndgu355m3!!

If the supplied password is correct, the FTP server responds with a 230 response code:

230- Welcome to ftp.example.com
230 Login successful.

The outcome of a request to an FTP server depends mainly on the first digit of the three-digit response code. Table 2-1 lists FTP response codes and their meanings, based on the first digit of the code.

Table 2-1. FTP response codes

Response code

Description

1yz

Positive preliminary reply

2yz

Positive completion reply

3yz

Positive intermediate reply

4yz

Transient negative completion reply


Because FTP is a plain-text protocol, you can use a telnet client to connect to the FTP server and test the authentication mechanism. Here is an example:

[notroot]$ telnet ftp.example.com 21
Trying 192.168.1.2...
Connected to ftp.example.com.
Escape character is '^]'.
220 Welcome to ftp.example.com.
USER john
331 Please specify the password. PASS try4ndgu355m3!! 230- Welcome to ftp.example.com 230 Login successful.

For more details on the FTP protocol, see RFC 959, available at http://www.faqs.org/rfcs/rfc959.html.


2.2.2. The FTP Password Dissector

The FTP dissector's goal is to analyze FTP traffic on the network to obtain and display FTP usernames and passwords. The dissector, ec_ftp.c, is located in the src/dissectors directory of the Ettercap source tree. The first few lines of the code use the include directive to include required header files for writing dissectors:

#include <ec.h>
#include <ec_decode.h>
#include <ec_dissect.h>
#include <ec_session.h>

Prototypes for defined functions are declared next. We will discuss these functions in the next few paragraphs.

FUNC_DECODER(dissector_ftp);
void ftp_init(void);

The ftp_init( ) function adds an entry into appropriate Ettercap data structures by invoking the dissect_add( ) function:

void _  _init ftp_init(void)
{
    dissect_add("ftp", APP_LAYER_TCP, 21, dissector_ftp);
}

Note that the _ _init macro is defined in ec.h as:

#define _ _init _ _attribute_  _ ((constructor))

The _ _attribute_ _((constructor)) directive causes all functions to be invoked before main( ). Therefore, the ftp_init( ) function is automatically invoked when the ettercap executable is run. The dissect_add( ) function should be called by every dissector because it is used to add an entry into dissect_list, a structure used by Ettercap to manage enabled dissectors. The function prototype for dissect_add( ) is:

void dissect_add(char *name, u_int8 level, u_int32 port, FUNC_DECODER_PTR(decoder))

Parameters accepted by dissect_add( ) are described in Table 2-2.

Table 2-2. Parameters for dissect_add( )

Parameter

Description

Name

Name of dissector. This name is also used in the Ettercap configuration file located in share/etter.conf to enable or disable dissectors upon startup.

Level

Layer on which the dissector operates. Possible values are IFACE_LAYER, LINK_LAYER, NET_LAYER, PROTO_LAYER, APP_LAYER, APP_LAYER_TCP, and APP_LAYER_UDP.

Port

Port number on which the dissector operates.

FUNC_DECODER_PTR(decoder)

Pointer to "main" function of the dissector.


Notice that the last parameter to dissect_add( ) is dissector_ftp. This designates the dissector_ftp( ) function as the entry point to the dissector code whenever traffic on TCP port 21 is captured. The FUNC_DECODER( ) macro is used to define dissector_ftp:

FUNC_DECODER(dissector_ftp)

The FUNC_DECODER macro is just a wrapper around dissector_ftp that defines it as a pointer. This is useful because, as we previously noted, dissector_ftp is passed to dissect_add( ), whose last parameter accepts only a pointer to a function.

Because dissector_ftp( ) is invoked every time a packet on TCP port 21 is captured, DECLARE_DISP_PTR_END( ) is called to set ptr to point to the beginning of the data buffer, and end to point to the end of the buffer:

DECLARE_DISP_PTR_END(ptr, end);

Dissectors in Ettercap need to keep track of individual TCP connections. You initiate a TCP connection by sending a TCP packet with the SYN flag set, followed by a response TCP packet from the server that contains the SYN and ACK flags set. Therefore, the FTP dissector calls CREATE_SESSION_ON_SYN_ACK(), which creates a new session for the connection as soon as a packet with the SYN and ACK flags set is captured:

CREATE_SESSION_ON_SYN_ACK("ftp", s, dissector_ftp);

The first parameter to CREATE_SESSION_ON_SYN_ACK( ) indicates the name of the dissector, which in our case is ftp. The second parameter to CREATE_SESSION_SYN _ACK( ) is s, which is a pointer to the ec_session structure defined in ec_session.h. This structure holds individual session data, and is therefore used to keep track of individual TCP connections.

The first TCP packet sent from the FTP server most likely contains the banner, including the 220 response code, and this is analyzed by calling the IF_FIRST_PACKET_FROM_SERVER() function. The IF_FIRST_PACKET_FROM_SERVER( ) macro expects the block to end with ENDIF_FIRST_PACKET_FROM_SERVER( ):

IF_FIRST_PACKET_FROM_SERVER("ftp", s, ident, dissector_ftp)
{            
    DEBUG_MSG("\tdissector_ftp BANNER");
   
    if (!strncmp(ptr, "220", 3)) 
    {
        PACKET->DISSECTOR.banner = strdup(ptr + 4);
         
        if ( (ptr = strchr(PACKET->DISSECTOR.banner, '\r')) != NULL )
            *ptr = '\0';
    }
} ENDIF_FIRST_PACKET_FROM_SERVER(s, ident)

The ident parameter is a void pointer, and is assigned to a new session identifier of type struct dissect_ident. As the name suggests, ident is used to identify sessions. PACKET is a global structure of type struct packet_object . It holds the actual network packet data. (See ec_packet.h for the definition of packet_object.) Using strncmp(), the FTP dissector code looks for the string 220 within the first three characters pointed to by ptr because 220 is sent by an FTP server upon connect, followed by the FTP server banner. PACKET->DISSECTOR.banner is then set to the banner of the FTP server, which is basically everything after the 220 string. Next, strchr() is used to point ptr to the end of the banner by searching for the \r character.

The dissector makes sure to skip packets that contain no data. These packets are mainly ACK TCP packets that serve only as acknowledgments:

if (PACKET->DATA.len == 0)  
      return NULL;

The FROM_SERVER macro is used to skip all subsequent packets from the server. After having obtained the 220 server string and banner, we do not care about any data coming from the FTP server. From there on, the dissector is concerned only with username and password data that is transmitted to the server:

if (FROM_SERVER("ftp", PACKET))
      return NULL;

Whitespace in the beginning of packet data is skipped:

while(*ptr == ' ' && ptr != end) ptr++;

If ptr points to end, there is no more data to analyze, so the dissector returns:

if (ptr == end)
    return NULL;

The dissector uses strncasecmp() to look for the USER command sent by the FTP client to the server to capture the FTP username:

if (!strncasecmp(ptr, "USER ", 5))
{
    DEBUG_MSG("\tDissector_FTP USER");

    dissect_create_session(&s, PACKET, DISSECT_CODE(dissector_ftp));

    ptr += 5;

    SAFE_FREE(s->data);

    s->data = strdup(ptr);
    s->data_len = strlen(ptr);

    if ( (ptr = strchr(s->data,'\r')) != NULL )
        *ptr = '\0';

    session_put(s);

    return NULL;
}

The DEBUG_MSG( ) macro prints the given string to a designated debug file if Ettercap is compiled with the --enable-debug option. If Ettercap is unable to write to the debug file, the message is printed to stderr, which causes most Unix and Linux shells to output to the console by default.

Note that the FTP dissector uses the session pointer(s) returned by CREATE_SESSION_SYN_ACK() to invoke IF_FIRST_PACKET_FROM_SERVER(), which requires a session pointer as its second parameter. However, the dissector creates a brand-new session in the preceding block when Ettercap is started after the FTP connection is established, in which case the banner and SYN+ACK packet would have already been sent and never been seen by the dissector.

The dissector advances ptr by 5 to skip the USER command followed by whitespace, so now ptr points to the username sent by the FTP client. The SAFE_FREE( ) macro invokes free( ) to free data only if the data is not null. The session pointer's data and data_len items are set to the username string contained in ptr, and its length. Next, session_put( ) is invoked to store the session pointed to by s. This session is retrieved by the following if block, which attempts to capture the password sent by the FTP client. The strncasecmp() function compares the first five characters of ptr with the PASS string, which signifies that the FTP client has sent the user password to the server:

if ( !strncasecmp(ptr, "PASS ", 5) )
{
    DEBUG_MSG("\tDissector_FTP PASS");

    ptr += 5;

    dissect_create_ident(&ident, PACKET, DISSECT_CODE(dissector_ftp));

    if (session_get_and_del(&s, ident, DISSECT_IDENT_LEN) == -ENOTFOUND)
    {
        SAFE_FREE(ident);
        return NULL;
    }

    if (s->data == NULL) 
    {
        SAFE_FREE(ident);
        return NULL;
    }

    PACKET->DISSECTOR.user = strdup(s->data);
    PACKET->DISSECTOR.pass = strdup(ptr);

    if ( (ptr = strchr(PACKET->DISSECTOR.pass, '\r')) != NULL )
        *ptr = '\0';

    session_free(s);
    SAFE_FREE(ident);

    DISSECT_MSG("FTP : %s:%d -> USER: %s  PASS: %s\n", ip_addr_ntoa(&PACKET->L3.dst, tmp),
ntohs(PACKET->L4.dst), PACKET->DISSECTOR.user,

    return NULL;
}

In the preceding code block, ptr is incremented by 5 to point to the password sent by the FTP client, which occurs after the string PASS. The dissect_create_ident( ) function is used to create a session identifier, ident, which is used to invoke session_get_and_del( ). The session_get_and_del() function obtains the previous session into s, and deletes the session from memory because the dissector no longer needs the session after the current code block. If a previous session is not available, the dissector cannot proceed, and therefore returns after freeing ident.

PACKET->DISSECTOR.user is set to the data stored in s->data, which contains the FTP username as set in the if (!strncasecmp(ptr, "USER ", 5)) block. If s->data is not set (null), the dissector returns because we cannot proceed without the FTP username being available. PACKET->DISSECTOR.pass is set to the password sent by the FTP server as pointed to by ptr. The strchr( ) function is used to parse until the end of the password by looking for \r. Next, s and ident are set free because the dissector no longer needs them. The DISSECT_MSG macro is used to display the FTP server IP address and the username and password sent by the FTP client to the FTP server. Once this is done, the dissector simply returns.

The source code for the FTP dissector is available in the src/dissectors/ec_ftp.c file in the Ettercap source tree. It is written by ALoR and NaGA, authors and maintainers of Ettercap.


    Team LiB
    Previous Section Next Section