nexp — A framework for crafting network packets and processing responses
nexp
[-V
] [-v
] [-t
] [-c
cmd
] [-s
seed
] [-h
] [cmdfile
] [args
]
Network Expect (nexp) is a framework that allows to easily build tools that can interact with network traffic. Following a script, traffic can be injected into the network, and decisions can be taken, and acted upon, based on received network traffic. An interpreted language provides branching and high-level control structures to direct the interaction with the network.
Network Expect was heavily influenced and inspired on the Expect program written by Don Libes, which allows to "talk" to interactive programs in a scripted fashion. Because of this, you will find a lot of similarities between commands in Network Expect and commands in Don Libes' Expect. If you are a regular Expect user, it should not be very difficult to start writing Network Expect scripts because the basics are the same.
In Don Libes' Expect, scripts can send data to a process just as if a user were interactively typing commands. Then, the script would read the responses send by the application and take decisions accordingly. In Network Expect's case, a script could send traffic to a network device and then take decisions based on the received network traffic. The type of things that Network Expect can do are usually very low level network operations, which usually require writing a custom application in a language like C.
Network Expect's philosophy is based on the observation that network applications always operate on an action-reaction principle in which something is sent to an application running on a remote host and a response is then expected.
Some of the things that Network Expect can do include:
Generate arbitrary network traffic and inject it into a network at layer 2 or layer 3.
A wide range of protocols is supported, including IP version 6 as well as protocol options like IPv4, IPv6 and TCP options, something that regular tools don't offer. For example, Network Expect supports TCP MD5 signatures (RFC 2385).
This Network Expect functionality is very similar to the functionality provided by several packet crafting and forging open source tools like Nemesis, Packit, hping, Scapy, and others.
Listen for network traffic and take decisions based on the type of traffic received.
Open a sniffer trace in PCAP format and replay it after changing some values in the original packet capture.
Emulate network protocols to see how they interact with other speakers of that protocol. For example, emulating a TCP server to investigate approaches to randomization of TCP Initial Sequence Numbers (ISN) can be easily done in Network Expect.
Network Expect reads cmdfile for a list of commands to execute. Network Expect may also be invoked implicitly on systems which support the #! notation by marking the script executable, and making the first line in your script:
#!/usr/bin/nexp -f
Of course, the path must accurately describe where Network Expect lives. /usr/bin is just an example.
The -c
flag prefaces a command to be executed
before any in the script. The command should be quoted to
prevent being broken up by the shell. This option may be used
multiple times. Multiple commands may be executed with a single
-c
by separating them with semicolons.
Commands are executed in the order they appear.
-V
causes Network Expect to
print its version number and exit.
The -s
flag allows to specify a random seed
that will cause predicatibility of pseudo-random numbers
generated by Network Expect during execution
of a script. In cases where Network Expect is
used as a protocol fuzzer, this option is useful to be able to
re-generate a specific test case.
-v
increases the verbosity level. Some commands
display additional information when the verbosity level is
higher.
The -t
flag changes the display format used by
commands that display dates or generate strings that represent
dates.
Optional args are constructed into a list and stored in the variable named argv. argc is initialized to the length of argv.
argv0 is defined to be the name of the script (or binary if no script is used). For example, the following prints out the name of the script and the first three arguments:
puts "$argv0 [lrange $argv 0 2]"
An integral part of Network Expect is the concept of network listeners and network speakers, which are the Network Expect equivalent to spawned processes in Don Libes' Expect world. In Expect, the send command sends data to a spawned process, and the expect command waits for a specific pattern in the data received from a spawned process.
In Network Expect, the command to send data to the network, called send_network, uses a speaker that specifies how the traffic will be injected. Network Expect speakers can specify IPv4 or IPv6 sockets, in which case packets will be injected at layer 3 and will be routed by the operating system kernel. Network Expect speakers can also specify a physical interface in which case the packet will be injected at layer 2. And finally, Network Expect speakers can specify a PCAP file (also known as a "savefile") in which case packets will be written to this file instead of injected to the network.
Network Expect listeners, on the other hand, specify where packets will be read from. Listeners can be associated with either a physical interface, or with a PCAP file. In either case an optional PCAP filter can be associated with the listener to limit the type of packets the listener will return. When reading from a PCAP file the inter-packet delay between packets can be kept, or packets can be read at full speed.
Both listeners and speakers are created with the Network Expect command spawn_network, just as in Don Libes' Expect spawned processes are created with the spawn command.
The way to specify network listeners and speakers in commands that require them is the same regardless of the command. Network listeners are always specified using the -i switch (think input) followed by the name of the listener, and network speakers are specified using the -o switch (think output) followed by the name of the speaker.
Network isteners and speakers are created using the spawn_network command. Just as in Don Libes' Expect one cannot choose the spawn ID returned by the spawn command, in Network Expect it is not possible to choose the name of a network listener or speaker. However, one can assign the network listener or speaker name to a variable and use that variable whenever a network listener or speaker needs to be specified.
What follows are a few examples of creation of network listeners and speakers:
spawn_network -i eth1 icmp and src host 172.16.1.1
This command creates a network listener on interface eth1 and assigns the filter "icmp and src host 172.16.1.1", i.e. "listen only for ICMP messages coming from host 172.16.1.1". Since the -o switch has not been specified this command will not create a network speaker.
spawn_network -o eth0 -r /tmp/packets.pcap tcp and host 172.16.1.1
This command creates a network speaker on interface eth0 and a network listener that will read from the PCAP file "/tmp/packets.pcap" TCP segments to or from the host 172.16.1.1.
spawn_network -nolistener -6
This will create a network speaker for injecting IPv6 packets at layer 3. Since the -nolistener switch has been specified, this command only creates a network speaker and no listener.
spawn_network -nolistener -w /tmp/mypackets.pcap
This only creates a speaker that will write packets to the PCAP file "/tmp/mypackets.pcap". Note that when writing packets to a PCAP file, injection implicitley takes place at layer 2, and using an Ethernet header. This must be taken into consideration when specifying the packet to send using the send_network command.
Network Expect listeners and speakers play a very important role in the operation of Network Expect so becoming confortable with them is key to understanding Network Expect's philosophy.
When defining packets, Network Expect allows to specify values for most fields in protocol headers using a syntax that gives great flexibility. This syntax allows to make the value of a field change with each packet that is created. The syntax is better presented with an example. Suppose that we have an hypothetical command-line switch -z that is used to specify a 16-bit value (please note that the same syntax is used for 8, 16 and 32-bit quantities) in the header of certain protocol.
-z telnet (fixed): the generated value will always be 23, telnet's port number.
-z 23 (fixed): the generated value will always be 23.
-z 23+ (increment): the generated value will be 23 initially, and will be incremented by one with each successive packet.
-z 23- (increment): the generated value will be 23 initially, and will be decremented by one with each successive packet.
-z 23+5 (decrement): the generated value will be 23 initially, and will be incremented by 5 with each successive packet.
-z 23-5 (decrement): the generated value will be 23 initially, and will be decremented by 5 with each successive packet.
-z 23:25 (range): the generated value will start with 23, will be incremented by one until it reaches 25, and then will go back to 23.
-z 25:23 (range): the generated value will start with 25, will be decremented by one until it reaches 23, and then will go back to 25.
-z random: the generated value will be a random number in each successive packet.
Network Expect uses Tcl (Tool Command Language). Tcl provides control flow (e.g., if, for, break), expression evaluation and several other features such as recursion, procedure definition, etc. Commands used here but not defined (e.g., set, if, exec) are Tcl commands (see tcl(3)). Network Expect introduces additional commands, described below. Unless otherwise specified, commands return the empty string.
Commands are listed alphabetically so that they can be quickly located. However, new users may find it easier to start by reading the descriptions of spawn_network, send_network, expect_network, and send_expect, in that order.
The barray command is used to perform
management of variables of type barray
(byte array.)
barray new <payload spec> will
create and return a new barray
variable.
barray length <barray variable> will return the number of bytes that the barray variable uses.
barray delete <barray variable>
will delete a barray
variable.
barray examine <barray variable>
[/<FMT>] [<offset>] allows to
inspect the contents of the specified
barray
variable, starting at the
optional offset
, and using the format
optionally specified with
/FMT
. /FMT
can include
an optional count followed by the display format, '<'
or '>' to specify little endian or big endian, and the
size of each element to display. Display formats are
strings ('s'), octal ('o'), hexadecimal ('x'), signed
decimal ('d'), and unsigned decimal ('u'). Sizes are byte
('b'), half-word ('h'), and word ('w').
barray dump <barray variable>
produces a hexadecimal dump of the specified
barray
variable.
barray slice <barray variable> <slice
spec> returns a slice of the specified
barray
variable. slice
spec
must be in the format <[start]:[end]>
where start
and end
are
offset into the barray
variable. If
start
is ommited the start offset is 0,
and if end
is ommited the end offset is
the end of the barray
variable.
barray cmp <barray variable 1> <barray
variable 2> compares two
barray
variables and returns an integer
less than, equal to, or greater than zero if the first
n
bytes of barray variable
1
are found, respectively, to be less than, to
match, or be greater than the first n
bytes of barray variable
2
. n
is calculated to be the
minimum of the lengths of both barray
variables.
This command closes the network listener and/or speaker referenced by <listener/speaker name>. Closing a network listener is not a very important thing to do since the operation just releases system resources used by the listener. However, closing a network speaker is a very important, especially when the speaker is associated with a PCAP file since closing the speaker closes the associated PCAP file.
The expect_network command waits for
network traffic from one or more network listeners
(specified with the -i
option) until a
condition evaluates to true, until a specified time period
has passed, or until an end-of-file is seen (when the
listener is associated with a PCAP file.)
Conditions from the most recent expect_network_before command are implicitly used before any other conditions. Conditions from the most recent expect_network_after command are implicitly used after any other conditions.
Conditions are regular Tcl expressions, and bodies are sets of Tcl commands. If the final body is empty, it may be omitted.
If the arguments to the entire expect_network statement require more than one line, all the arguments may be "braced" into one so as to avoid terminating each line with a backslash. In this one case, the usual Tcl substitutions will occur despite the braces.
If a condition is the keyword eof
, the
corresponding body is executed upon end-of-file (this is
only meaningful when a listener is reading from a PCAP
file, not on a live capture from an interface.) If a
condition is the keyword timeout
, the
corresponding body is executed upon timeout. If no
timeout
keyword is used, an implicit
null action is executed upon timeout. The default timeout
period is 0.5 seconds but may be set, for example to 30,
by the command "set timeout 30". An infinite timeout may
be designated by the value -1.
If a condition is true, then the corresponding body is executed. expect_network returns the result of the body (or the empty string if no condition was true.) In the event that multiple conditions match, the one appearing first is used to select a body.
Each time a new packet arrives, it is decoded and the conditions are evaluated in the order they are listed. When a packet is decoded, the values of the different fields in the packet are made available to Tcl scripts via Tcl variables. Scripts can use these values in an condition for a expect_network command.
In the following example, a listener for ARP requests on interface eth0 is created and then an expect_network command is used to wait for actual ARP requests and respond to them with an ARP reply:
# Spawn a listener for ARP requests spawn_network -i eth0 host 192.168.1.1 and {arp[6:2]} == 1 expect_network {1} { # Received an ARP request, send ARP reply send_network -o eth0 \ ether(src = $mymac, dst = $arp(sha) )/ \ arp-reply(tha = $arp(sha), tip = $arp(sip), \ sha = $mymac, sip = 192.168.1.1) nexp_continue }
The -timeout
flag causes the current
expect_network command to use the
following value as a timeout instead of using the value of
the timeout
variable.
Actions such as break and continue cause control structures (i.e., for, proc) to behave in the usual way. The command nexp_continue allows expect_network itself to continue executing rather than returning as it normally would.
This is useful for avoiding explicit loops or repeated expect_network statements. The following example is part of a fragment to respond to ICMP echo and ARP requests. The nexp_continue avoids having to write a second expect_network statement (to look for the requests again.)
# Spawn a listener for ARP requests spawn_network -i $interface host $myip and {arp[6:2]} == 1 set arpl $listener_id # Spawn a listener for ICMP messages sent to us spawn_network -i $interface icmp and dst host $myip set icmpl $listener_id expect_network -i $arpl {1} { # Received an ARP request, send ARP reply send_network -o $interface \ ether(src = $mymac, dst = $arp(sha) )/ \ arp-reply(tha = $arp(sha), tip = $arp(sip), \ sha = $mymac, sip = $myip) nexp_continue } -i $icmpl {$icmp(type) == 8} { # Received ICMP echo request, send echo reply send_network -o ip \ ip(src = $myip, dst = $ip(src) )/ \ icmp-echoreply(id = $icmp(id), seq = $icmp(seq) )/ \ raw($raw) nexp_continue }
works identically to the expect_network_before except that if conditions from both expect_network and expect_network_after evaluate to true, the expect_network conditions is used. See the expect_network_before command for more information.
takes the same arguments as
expect_network, however it returns
immediately. Conditions are evaluated whenever a new
packet arrives. The expression timeout
and default
are meaningless to
expect_network_background and are
silently discarded. Otherwise, the
expect_network_background command uses
expect_network_before and
expect_network_after conditions just
like expect_network does.
Please note that if a condition of the expect_network_background command evaluates to true, the command will not continue to listen for traffic unless the body includes a nexp_continue command that forces expect_network_background to continue executing.
takes the same arguments as expect_network, however it returns immediately. Condition-action pairs from the most recent expect_network_before with the same network listener ID are implicitly added to any following expect_network commands. If a condition evaluates to true, it is treated as if it had been specified in the expect_network command itself, and the associated body is executed in the context of the expect_network command. If conditions from both expect_network_before and expect_network can evaluate to true, the expect_network_before condition is used.
Unless overridden by a -i
flag,
expect_network_before conditions are
evaluated against the network listener ID defined at the
time that the expect_network_before
command was executed (not when its condition evaluated to
true.)
The -info
flag causes
expect_network_before to return the
current specifications of what conditions will be
evaluated. By default, it reports on the current network
listener. An optional network listener ID specification
may be given for information on that network listener.
Instead of a network listener specification, the flag
-all
will cause -info
to
report on all network listeners.
The output of the -info
flag can be
reused as the argument to
expect_network_before.
iflist returns a list that contains the
name of all interfaces in the system. This list is built
when Network Expect starts. If the
optional argument -refresh
is specified
then the list is refreshed before returning it.
The command nexp_continue allows expect itself to continue executing rather than returning as it normally would.
is useful for assuring that the script is compatible with the current version of Network Expect.
With no arguments, the current version of Expect is returned. This version may then be encoded in your script. If you actually know that you are not using features of recent versions, you can specify an earlier version.
Versions consist of two numbers separated by dots. First is the major number. Scripts written for versions of Network Expect with a different major number will almost certainly not work. nexp_version returns an error if the major numbers do not match.
Second is the minor number. Scripts written for a version with a greater minor number than the current version may depend upon some new feature and might not run. nexp_version returns an error if the major numbers match, but the script minor number is greater than that of the running Network Expect.
With the -exit flag, Expect prints an error and exits if the version is out of date.
The outif command returns the outgoing
interface that would be used to reach
target
. target
can be an
IP address or host name.
The packet command allows to manage
variables of type packet
.
packet decode <packet variable>
will decode the specified packet
variable, just as if the packet was received during the
execution of a expect_network command.
packet hash <packet variable>
calculates a hash of the specified
packet
variable.
packet data <packet variable>
returns a barray
variable that contains
all of the packet's data.
packet dump <packet variable>
displays an hexadecimal dump (on standard output) of the
packet
's data.
packet ts <packet variable> will
return a timeval
variable that
corresponds to the timestamp associated with the specified
packet
variable.
packet tdelta <packet variable 1>
<packet variable 2> calculates the time
delta between the timestamp
associated
with <packet variable 1> and the
timestamp
associated with <packet
variable 2>.
The pdu command is used to manage
variables of type pdu
(Protocol Data
Unit.)
pdu new <PDU definition> will
create and return a new pdu
variable. The PDU definition is the same as used in other
Network Expect commands like
send_network and
send_expect.
pdu delete <pdu variable> will
delete a pdu
variable.
pdu dup <pdu variable> will
duplicate a pdu
variable.
pdu append <pdu variable 1> <pdu
variable 2> will append the
pdu
variable pdu variable
2
to the pdu
variable
pdu variable 1
. The result is
pdu variable 1
.
pdu count <pdu variable> returns the number of packets that would be generated if that PDU were to be sent using the send_network command, for example. The number of packets depends on the numeric specifications used in the definition of the PDU.
pdu build <pdu variable> builds
the specified pdu
variable and returns
a variable of type packet
.
pdu list displays the list of PDUs that Network Expect knows about, i.e. the PDUs that can be created via certain Network Expect commands like send_network, send_expect, and pdu new.
The random command can be used to obtain a variety of random objects. Current supported objects are numbers, IPv4 addresses, and MAC addresses. It is possible to obtain a random number or IP address from within a range. In the case of numbers the range is specified using the notation x:y. In the case of IP addresses the range can be specified using the IP address/netmask bits notation, or using the a.b.c.d:e.f.g.h notation. If no object is specified then a random number is returned.
This command sends a number of packets to a target or list of targets and then waits for responses. After responses are received, the command matches sent packets with received packets. This command was inspired by the sr() family of commands in Scapy, the packet manipulation tool by Philippe Biondi.
The -i
flag specifies what listener to
use to read responses. It is possible to specify multiple
listeners by using this option multiple times.
The -o
flag specifies what speaker to use
when injecting the stimulus.
-timeout
specifies how long to wait,
after all packets have been sent, for answers to the
injected stimulus. The timeout is specified in seconds and
the default is 1 second.
-n
specifies how many times to retry
sending the stimulus if not all sent packets have a
corresponding received answer.
The definition of the PDU to send is the same as it used in the send_network command.
The send_expect command creates three lists: the list of sent packets and the corresponding list of matching answers, and the list of unanswered packets. A script can then use these lists, decode packets, and present some useful information. For example, the following code snippet sends ICMP echo requests to all hosts in a network a displays which hosts replied:
set network 192.168.1.1-192.168.1.254 # Spawn a listener. We don't really have to specify a filter, # like in "spawn_network {icmp[icmptype] == icmp-echoreply}" # because the send_expect command will intelligently match # injected stimulus (ICMP echo requests) with received # answers (ICMP echo replies). A filter means less work for # the send_expect command, but other than that it adds nothing. spawn_network send_expect -n 2 -4 -D $network -icmp-echo random:random \ -payload "12345678901234567890" -delay .001 puts "\n[llength $_(received)] hosts sent echo-replies back:\n" foreach r $_(received) s $_(sent) { packet decode r puts [format "$pdu(1,tot_len) bytes from $ip(src): ttl=$ip(ttl) time=%.3f ms" [expr [packet tdelta r s]*1000] ] }
This command allows the creation custom TCP/IP packets based on packet definitions provided by the user through command-line arguments passed to the program, through a command file specified in the command-line, or through a mix of these two methods. send_network refers to instances of protocols in the TCP/IP protocol suite as protocol data units, or PDUs for short. A protocol data unit always has a header (think of the IP version 4 header, or the TCP header), may or may not have options (think of the TCP options, or IP version 6 extension headers), and may or may not have a payload. Due to the extensive use of encapsulation in the TCP/IP protocol suite, the payload of a PDU can be another payload. For example, an Ethernet frame can have as its payload an IP version 4 packet, which in itself is another PDU. Then this IP version 4 packet can have as its payload a TCP segment, which is another PDU, and so on.
When defining PDUs that send_network will create you start by creating the definitions for PDUs that are closer to the physical layer, and then move up the protocol stack until you reach, in some cases, and if that is what you want, the application layer. To implement this approach through a command-line interface (CLI) you start by entering the lower-level PDUs closer to the beginning of the command-line, or, if you are reading your packet definitions from a file, by entering definitions for lower-level PDUs at the beginning of the file. After you have defined a certain PDU you cannot define another PDU that is lower than the previous in the protocol stack. For example, you cannot define a TCP segment and then create an Ethernet frame as the segment's payload, just because that does not make sense (defining a TCP segment and then a BGP message does make sense, for example.) For this reason, order does matter in the command-line (or in the file, if defining PDUs in a file) when creating the PDUs.
causes the script to sleep for the given number of
seconds. seconds
may be a decimal
number. Interrupts are processed while Network
Expect sleeps.
The spawn_network command is used to find information about existing listeners and speakers or to create network speakers and network listeners.
To find information about existing network listeners and
speakers the spawn_network command
needs to be invoked with only the -info
option.
Listeners are created by using the options
-i
or
-r
. -i
specifies an
interface to listen for traffic on, and
-r
specifies the use of a PCAP file for
reading instead of reading live traffic from an
interface. In both cases, an optional PCAP
filter
can be specified to limit the type of
traffic that will be read. If -i
is not
used then Network Expect will try to
find a suitable interface. -fullspeed
causes reading from a PCAP file at full speed, without
preserving the inter-packet delay present in the
savefile. If this option is not specified then the
inter-packet delay present in the savefile will not be
preserved.
Speakers are created using one of -o
,
-w
, hexdump
,
stdout
, -4
, or
-6
. -o
specifies the use
of an interface for injecting traffic into the
network. This implies the use of layer 2 injection in
which the Ethernet header is specified by the user.
-w
specifies that all traffic will be
sent to the PCAP file PCAP file
. This
option requires the use of layer 2 injection, i.e. the
Ethernet header must be
included. -hexdump
specifies that all
packets be sent to standard output in hexadecimal format.
-stdout
causes all packets to be sent to
standard output in raw format. The -4
and
-6
options specify the creation of layer
3 speakers to inject IPv4 and IPv6 traffic
respectively. In this case all routing decisions are
performed by the kernel.
The -p
and -s
options
are only meaningful for listener
creation. -p
specifies that the interface
be not put in promiscuous mode when creating a listener
that will listen on an interface. -s
specifies the snapshot length of captured packets. These
two options are equivalent to the
tcpdump(8) options of the same names.
Note that if no options to create a speaker are specified,
i.e. -o
, -w
,
hexdump
, stdout
,
-4
, or -6
, the default
action is to only create a listener. If only a speaker is
desired one of the options to create a speaker must be
specified and the -nolistner
option must
be used.
As an example, the follow command creates a listener on interface eth0 for ARP requests for the MAC address of host 192.168.1.1:
spawn_network -i eth0 host 192.168.1.1 and {arp[6:2]} == 1
Note that curly braces are necessary around "arp[6:2]" because brackets have special meaning in Tcl.
The system command allows to run arbitrary system commands from within a Network Expect script.
Please note that this command does no input validation at all, which means that it is very insecure. For example "system {ls;/bin/sh}" will give you a shell. If nexp is run as roon then the shell will be a root shell.
The exec command in the standard Tcl distribution is a much better alternative to the Network Expect system command. Please see the Tcl documentation for the exec command for additional information.
The timeval command allows to manage
variables of type timeval
.
timeval new; returns a
timeval
variable that corresponds to
the current system time.
timeval tdelta <timeval variable 1>
<timeval variable 2> calculates the time
delta between the timeval variable 2
and
timeval variable 1
.
Every time a packet is sent, the time the packet was sent
is saved to the network speaker that was used to send
that packet. The txdelta (short for
"transmission delta") command returns the number of
seconds that have elapsed since the last packet that was
sent via the specified speaker ID
.
The following example provides the foundation for a very simple ping program, and shows how the txdelta command can be used:
for {set id 0; set seq 0} {1} {incr seq} { send_network -4 -D $target -icmp-echo $id:$seq -payload "12345678901234567890" expect_network -timeout 1 {$icmp(type) == 0 && $icmp(id) == $id} { puts [format "$pdu(2,tot_len) bytes from $ip(src): icmp_seq=$seq ttl=$ip(ttl) time=%.3f ms" [expr [txdelta ip]*1000 ] ] sleep [expr 1.0 - [txdelta ip] ] } }
The examples
directory in
the Network Expect distribution contains
plenty of examples that should provide a very good idea of how
to use Network Expect. A few selected
examples have been selected for this manual page.
The following code snippet performs a TCP three-way handshake. Everything is done by hand, which allows to play with TCP initial sequence numbers (ISNs), window sizes, etc. The code is a bit more complex that necessary because an effort is made to handle error coditions (timeout, remote host not listening on the destination port, strange combination of TCP flags received in response to our initial SYN.)
# Some useful constants set SYN 0x02 set RST 0x04 set ACK 0x10 set retries 3 set isn [random] set myip 192.168.1.2 set target 192.168.1.1 set sport [random 20000:65535] set dport 21 set window 4096 # Spawn a listener for TCP segments coming from the FTP server to us spawn_network -i $interface "tcp and src host $target and dst host $myip and src port $dport and dst port $sport" # Send TCP SYN send_network ip(src = $myip, dst = $target)/ \ tcp(src = $sport, dst = $dport, window = $window, \ syn, seq = $isn, ack-seq = 0) # Wait for response from the server expect_network {$tcp(flags) == ($SYN | $ACK)} { # Got a SYN+ACK so we need to send the final segment of the # 3-way HS send_network ip(dst = $myip, dst = $target)/ \ tcp(dst = $tcp(dstport), dst = $tcp(srcport), \ window = $window, ack, seq = $tcp(ack), \ ack-seq = [expr $tcp(seq) + 1]) } {$tcp(flags) & $RST} { puts "Connection refused" exit 1 } {1} { # Any other weird combination of TCP flags we respond to # with a RST send_network ip(src = $myip, dst = $target)/ \ tcp(src = $tcp(dstport), dst = $tcp(srcport), \ rst) exit 1 } timeout { # Our SYN got lost in transit or it was filtered - perform # exponential backoff and retransmit the SYN... if {$retries > 0} { incr retries -1 set timeout [expr $timeout*2] puts "SYN timeout, increasing timeout to $timeout" send_network ip(src = $myip, dst = $target)/ \ tcp(src = $sport, dst = $dport, \ window = $window, syn, seq = $isn, \ ack-seq = 0) nexp_continue } else { puts "Connection timed out" exit 1 } } # TCP connection has been established. Now do something...
This illustrates how to create a phantom host on the network that can respond to ICMP echo requests, and therefore, to ARP requests as well.
set interface eth0 set myip 192.168.1.1 set mymac [random mac] # Spawn a listener for ARP requests spawn_network -i $interface host $myip and {arp[6:2]} == 1 set arpl $listener_id # Spawn a listener for ICMP messages sent to us spawn_network -i $interface icmp and dst host $myip set icmpl $listener_id expect_network_background -i $arpl {1} { # Received an ARP request, send ARP reply send_network -o $interface \ ether(src = $mymac, dst = $arp(sha) )/ \ arp-reply(tha = $arp(sha), tip = $arp(sip), \ sha = $mymac, sip = $myip) nexp_continue } -i $icmpl {$icmp(type) == 8} { # Received ICMP echo request, send echo reply send_network -o ip \ ip(src = $myip, dst = $ip(src) )/ \ icmp-echoreply(id = $icmp(id), seq = $icmp(seq) )/ \ raw($raw) nexp_continue }
This example how to create a very simple traceroute program that uses TCP probes to port 80 of the target host. It uses the send_expect command.
set target "www.example.com" set ttlrange "1:30" set interface [outif $target] # Spawn a listener. We don't really have to specify a filter because the # send_expect command will intelligently match injected stimulus with # received answers. spawn_network -i $interface send_expect -tries 2 -delay 0.001 \ ip(id = random, dst = $target, ttl = $ttlrange)/ \ tcp(src = random, dst = 80, syn) foreach r $_(received) s $_(sent) { packet decode r set source $ip(src) set pdu_type $pdu(1,type) packet decode s puts [format "$ip(ttl) $source %.3f ms $pdu_type" [expr [packet tdelta r s]*1000] ] }
This shows how to perform an ARP scan using regular send_network and expect_network commands:
set interface eth0 set network "$iface($interface,ip)/$iface($interface,netmask)" set arprequest [pdu new -o $interface ether(dst = BROADCAST)/ \ arp-request(tha = BROADCAST, tip = $network, \ sha = $iface($interface,hw_addr), \ sip = $iface($interface,ip) ) ] # Spawn a listener for ARP replies spawn_network -i $interface {arp[6:2]} == 2 for {set i 0} {$i < [pdu count arprequest]} {incr i} { # Send ARP request send_network -count 1 arprequest # Read ARP reply expect_network -timeout .05 {1} { puts "$arp(sip) is at $arp(sha)" } }
This example shows how to do an ARP scan but in a more efficient manner using the send_expect command:
set interface eth0 set network "$iface($interface,ip)/$iface($interface,netmask)" # Spawn a listener for ARP replies spawn_network -i $interface {arp[6:2]} == 2 send_expect -o $interface -delay 0.001 -tries 2 \ ether(dst = BROADCAST)/ \ arp-request(tha = BROADCAST, tip = $network, \ sha = $iface($interface,hw_addr), sip = $iface($interface,ip) ) puts "\nFound [llength $_(received)] hosts alive:\n" foreach r $_(received) { packet decode r puts "$arp(sip) is at $arp(sha)" }
Network Expect does not run very well under the Solaris operating system because in that operating system select() does not seem to work well with packet capture file descriptors (select() returns when there is no data ready to be read.)
Network Expect does not work at all in Microsoft Windows because select() does not work at all with packet capture file descriptors (pcap_get_selectable_fd() does not exist under Microsoft Windows.)
Error-checking is almost non-existant.
These isn't input validation for the numeric specifications.
The parser of PDU definitions (and therefore the number of tokens that the parser handles) has become a big, wild beast. It's very easy to add new tokens and PDU definitions are elegant and pretty, but the parser is huge. Guess can't have everything.
nexp-numspec(1), nexp-payload(1), nexp-ether(5), nexp-gre(5), nexp-ip(5), nexp-mpls(5), expect(1)
Network Expect was written by Eloy Paris <peloy@netexpect.org>. However, Network Expect borrows ideas from lots of Open Source tools like Nemesis, Packit, hping, Expect, and Scapy. The Network Expect author is indebted to the authors of these tools for their contribution.
This man page was written by Eloy Paris although it borrows heavily from Expect's manual page.