Previous Page Next Page

5.2. Emulating Services

Low-interaction honeypots like Honeyd are powerful, but their illusion is only as good as the services they provide. Usually, each service is implemented by an elaborate script: a program written in a scripting language. Most services employ simple-state machines and do not simulate all possible interactions.

We discuss the basics of emulating services in the advanced chapter because most of you probably just go to http://www.honeyd.org/contrib.php to download scripts written by others. However, to get you started with creating your own services, we will show you how to write a script to emulate the Simple Mail Transport Protocol (SMTP) that is used to send e-mail over the Internet.

We differentiate between service scripts and subsystems. Service scripts have a very simple input output model. They read some information from stdin and provide responses if necessary on stdout. A subsystem is an application that is capable of acting as an Internet server by itself but uses the virtualization provided by Honeyd. Subsystems can open new ports, make outgoing connections, and run multiple user sessions in parallel. They provide much better performance than service scripts but are also more complicated to implement.

5.2.1. Scripting Languages

Our scripting language of choice is Python. Python is easy to understand, even to readers who are not very familiar with programming, but it is also very powerful. We recommend the book Python Essential Reference [4]. It explains the basic syntax of Python and has detailed documentation for all the relevant modules. For readers that lack experience in Python, the following example might be easier to understand with the reference book in hand.

5.2.2. SMTP

SMTP is used to send e-mail from one computer to another. It is a very simple protocol and easy to emulate in a honeypot. A honeypot that accepts e-mail is a great tool for understanding how spammers work, so we have a separate chapter on it. In this section, we explain the protocol and how to simulate and implement it.

First of all, we need to understand what SMTP does and what the protocol looks like. Go to your favorite search engine and look for smtp rfc. You should quickly find "RFC 821 — Simple Mail Transfer Protocol" by Postel, one of the Internet pioneers. Figure 5.2 shows what a protocol might look like.

Figure 5.2. An example of an SMTP conversation taken from RFC 821 [66]: "This SMTP example shows mail sent by Smith at host Alpha.ARPA, to Jones, Green, and Brown at host Beta.ARPA. Here we assume that host Alpha contacts host Beta directly."

S: MAIL FROM:<Smith@Alpha.ARPA>
R: 250 OK

S: RCPT TO:<Jones@Beta.ARPA>
R: 250 OK

S: RCPT TO:<Green@Beta.ARPA>
R: 550 No such user here

S: RCPT TO:<Brown@Beta.ARPA>
R: 250 OK

S: DATA
R: 354 Start mail input; end with <CRLF>.<CRLF>
S: Blah blah blah...
S: ...etc. etc. etc.
S: <CRLF>.<CRLF>
R: 250 OK

In Figure 5.2, we see that SMTP supports the commands MAIL FROM:, RCPT TO:, and DATA. We can find a complete list of all SMTP commands in Section 4.1 of the RFC. However, for this particular example, we just implement the three commands from the example. Many of the early text-based protocols are relatively straightforward to implement. In this case, we need a simple-state machine that remembers which stage of the protocol is currently executing.

#!/usr/bin/python
import re # for matching the command
import sys # to read stdin

current_state = 'initial'
sender = ''
recipients = []
for line in sys.stdin:
 if current_state == 'initial':
  res = re.match('mail from:(.*)', line, re.IGNORECASE)
  if not res:
   print >>sys.stdout, '500 Syntax Error'
   continue
  argument = res.group(1)
  # do some parsing on the argument - make sure that it's an email address
  sender = argument
  current_state = 'need_recipient'
  print >>sys.stdout, '250 OK'
 else:
  print >>sys.stdout, '500 Syntax Error'


Let's save the code snippet as scripts/smtp-test.py in your Honeyd directory. Make sure that the script is executable by running

chmod a+xr scripts/smtp-test.py

Before we can test the script, it must be added to a template on port 25. Something like the following might work:

add testtemplate tcp port 25 "scripts/smtp-test.py"

Make sure that the template is bound to an IP address; start Honeyd and telnet to port 25 of your virtual honeypot. Once you are connected, you should be able to type commands. For all commands except MAIL FROM, the script will give you an error message. If you type MAIL FROM with an appropriate e-mail address, the script will store it and advance to the next stage of the protocol.

The implementation of RCPT TO might look as follows. Insert this code snippet before the else: clause in the preceding code:

elif current_state == 'need_recipient':
 res = re.match('rcpt to:(.*)', line, re.IGNORECASE)
 if res:
    argument = res.group(1)
    # check that it is an e-mail address --- you need to do that!
    recipients.append(argument)
    continue
 res = re.match('rcpt to:(.*)', line, re.IGNORECASE):
 if res:
    # we are expecting data now. tell the user
    print >>sys.stdout, '354 Start mail input; end with <CRLF>.<CRLF>'
    current_state = 'getting_data'
    continue
 print >>sys.stdout, '500 Syntax Error'


The final stage is responsible for receiving the e-mail data from the user. When the user types . by itself on a line, the data phase ends and our script needs to spool the data somewhere and inform the user of success. At that point, we can go back into the initial stage and start over again. Of course, there are a number of other SMTP commands that one could implement, and every command has its own set of error messages. This example just demonstrates how you could go about creating your own service emulations.

Previous Page Next Page