Ettercap is a network analyzer that is free and open source. Advanced features such as ARP poisoning, packet filtering, and OS fingerprinting, along with support for password dissectors and plug-ins make Ettercap a powerful tool and a favorite among many network administrators. Ettercap has been known to compile on various Unix and Linux flavors, and has been successfully ported to run on Microsoft Windows operating systems.

This chapter introduces the concept of writing dissectors and plug-ins for Ettercap. Dissectors allow you to grab important information, such as usernames and passwords, that are transmitted over a network. For the purposes of understanding how to write a dissector, we will step through a dissector that captures and displays FTP usernames and passwords. Then, to demonstrate how to write an Ettercap plug-in, we will step through a plug-in that alerts the user when one host on the network attempts to establish a new TCP connection with another host.

Make sure you obtain and install an Ettercap version that is equal to or greater than 0.7.0. Ettercap APIs of versions older than 0.7.0 differ significantly, and are no longer supported.

You can run Ettercap in console mode, curses mode, or GTK mode, the latter of which is shown in Figure 2-1.

Figure 2-1. Ettercap in GTK mode

Run ettercap -h to discover the plethora of options and features Ettercap provides. See the ettercap manpage for more details on available options and features.

Tip

The Ettercap web site consists of a publicly available message board dedicated to providing support in case you experience problems. Access the message board by visiting http://ettercap.sourceforge.net/forum/.

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:

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.

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:

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:

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:

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( ) :

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:

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:

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.

Tip

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.

Writing an Ettercap Plug-in

You can enable or disable Ettercap plug-ins on the fly, and therefore you can use them to extend Ettercap functionality on demand. Ettercap comes bundled with a variety of plug-ins that you can find in the plug-ins directory of the Ettercap source tree. The following sections show you how to write find_tcp_conn, a plug-in that detects the initiation of a new TCP connection on the network.

The find_tcp_conn Plug-in

To establish a TCP connection with a remote host, the source host sends a TCP packet with the SYN flag set to the remote host. If the remote host is listening on a particular port, it responds with a TCP packet with the SYN and ACK flags set. The source host then sends a TCP packet with the ACK bit set to formally establish the TCP connection. This sequence is known as the three-way TCP handshake . Therefore, to detect new TCP connections with other hosts, our plug-in has to analyze the network traffic for TCP packets that have the SYN flag set. The find_tcp_conn plug-in described in the following paragraphs analyzes TCP packets for the SYN flag, and if one is found, it alerts the Ettercap user that a host on the network is attempting to establish a new TCP connection with another host.

The find_tcp_conn plug-in alerts the Ettercap user whenever a TCP packet with the SYN flag set is captured. Therefore, the plug-in alerts the Ettercap user even if the server host does not respond to the connection attempt. This plug-in can be useful for noticing when a SYN port-scan is being performed on a network.

Warning

The find_tcp_conn plug-in will not detect new TCP connections when the host running Ettercap is on a network switch because network switches attempt to segregate network traffic. Therefore, the find_tcp_conn plug-in will detect SYN packets from other hosts only when the host running Ettercap is on a network hub, or when Ettercap is instructed to perform ARP poisoning.

Every Ettercap plug-in needs to include ec.h and ec_plugins.h. These files contain required global variables and plug-in APIs. The plug-in uses the packet_object structure defined in ec_packet.h along with various functions defined in ec_hook.h , so these header files need to be included as well:

Most of the items defined by find_tcp_conn_ops are self-explanatory. Note that ettercap_version must be set to EC_VERSION. Ettercap uses this value to prevent a plug-in compiled for a different version of Ettercap from attempting to load. The init item declares the function that is called when the user enables the plug-in, and the fini item declares the function that is called when the user disables the plug-in. For example, in the Ettercap GTK frontend, you can enable or disable plug-ins by selecting "Manage the plugins" from the Plugins menu and double-clicking the plug-in names.

Following is the definition of plugin_load( ) , which is called when the plug-in is loaded. Users can load a plug-in by pressing Ctrl-O from the GTK frontend and selecting the appropriate plug-in file.

Every plug-in must be assigned a unique handle, which the Ettercap engine generates when it invokes plugin_load( ). As a plug-in author, you simply need to pass handle to plugin_register( ) as its first parameter. The second parameter, find_tcp_conn_ops, is the structure we declared in the previous paragraphs. As we already have seen, this structure defines plug-in details as well as the init and fini items.

Following is the definition of find_tcp_conn_init( ) , which is defined as our init function and is called when the Ettercap user enables the plug-in:

The USER_MSG( ) macro displays the given string to the Ettercap user. In the case of the GTK frontend, the string is displayed in the lower section of the GUI. In this case, the plug-in displays the string find_tcp_conn: pluginrunning... to let the user know the plug-in has been enabled. The hook_add() function takes in two parameters. Following is its prototype:

void hook_add(int point, void (*func)(struct packet_object *po))

You use the point parameter to decide when the plug-in hook function is to be called. We pass HOOK_PACKET_TCP as the point parameter to hook_add( ) to indicate that we want parse_tcp( ) to be called every time Ettercap captures a TCP packet on the network. (For an explanation of other types of hooking points, see the doc/plugins text file in the Ettercap source tree.) The find_tcp_conn_init( ) function returns PLUGIN_RUNNING, which indicates to the Ettercap engine that the plug-in has initialized successfully.

Here is a definition of find_tcp_conn_fini( ) , which is invoked when the Ettercap user disables the plug-in:

The hook_del( ) function removes the parse_tcp( ) function as the hook function. After hook_del( ) returns, the Ettercap engine no longer invokes parse_tcp( ) when a TCP packet is received. The find_tcp_conn_fini( ) function returns PLUGIN_FINISHED to indicate to the Ettercap engine that the plug-in finished and can be deallocated.

Following is the definition of the parse_tcp( ) function, which is called whenever Ettercap receives a TCP packet:

The if block inspects po, which contains the TCP packet captured by Ettercap. If the packet does not have the SYN flag set, L4.flags will not be equal to TH_SYN, and the function simply returns. The L4 structure signifies " Layer 4," also known as the Transport Layer of the OSI model where TCP operates. L3 signifies " Layer 3," also known as the Network Layer" where the IP operates.

USER_MSG is invoked only if the previous if block did not return, in which case we can be certain that the captured TCP packet has the SYN flag set. Therefore, we call USER_MSG() to alert the user that an attempt to establish a new TCP connection was detected, as shown in Figure 2-2. The ip_addr_ntoa() function accepts an IP address of type ip_addr as the first parameter and returns a string representation when given a char pointer as its second parameter. Because po->L3.src and po->L3.dst contain the source and destination IP addresses of the packet and are of type ip_addr, the plug-in invokes ip_addr_ntoa( ) to convert them to strings to display them to the user via USER_MSG( ). The ntohs( ) function is passed po->L4.dst as the parameter, which contains the destination port. The ntohs( ) function converts a given value from network byte order to host byte order. This is useful in preserving portability because different CPUs use different byte orders.

Figure 2-2. The find_tcp_conn plug-in in action

find_tcp_conn.c

The easiest way to compile this plug-in is to make a new directory called find_tcp_conn in the plug-insdirectory in the Ettercap source tree. Then, copy over the Makefile from another plug-in called find_conn, and replace all occurrences of find_conn with find_tcp_conn. Run make, and you will end up with ec_find_tcp_conn.so in the .libs/ directory. To load this plug-in from the GTK frontend, press Ctrl-O and select this file. Press Ctrl-P to go to the plug-in management section, and double-click the "find_tcp_conn" entry to enable the plug-in. Here is the complete source code for find_tcp_conn.c for easy reference: