Network Expect Examples

This page contains useful examples of Network Expect scripts. Some of these scripts are included in the examples/ directory of the NetworkExpect distribution. If you write a script that you think could be useful to others please send it to the NetworkExpect developers for inclusion in the official examples or here.

Note: The scripts below were written for versions of NetworkExpect that do not use libwireshark for packet dissection. If the version of NetworkExpect that you are using uses libwireshark for packet dissection then some minor changes (mostly changes in variable names) are required. The examples distributed with the libwireshark-enabled versions of NetworkExpect already have these changes incorporated.

To run these scripts you will need to do something like:

shell# nexp <script name> <script parameters>

For example:

shell# nexp ping.nexp google.com

The examples need to be run with root privileges because of the low level operations (packet sniffing and crafting) that need to be done.

While NetworkExpect is usually used via scripts, it can also be used interactively by issuing commands from a prompt. A sample InteractiveSession session with NetworkExpect shows some of the most important features of NetworkExpect, so make sure you check that out too.

Doing a TCP three-way handshake by hand

Sometimes full control over a TCP session is required from the start. Maybe you need to send some special TCP options that the TCP stack of your operating system does not support. Maybe you need to establish the TCP session and then send some malformed TCP segment. Maybe you need to play tricks with TCP sequence numbers.

Establishing a TCP session is something that is normally done by the operating system's TCP stack. If you want to establish a TCP session by hand your options are limited, and there's always the risk of having the operating system's TCP stack getting on the way. The use raw sockets is an option, but it requires tedious programming.

NetworkExpect can be used to craft a TCP three-way handshake without much work. The following script will connect to a TCP server. The source IP address of the client is an unused address in the network (must be configured by changing the variable "myip"). The script handles ARP requests for this address. This allows to have complete control over the TCP session since the TCP stack of the host where NetworkExpect is running will not see anything, and therefore will not interfere.

# Some useful constants
set SYN 0x02
set RST 0x04
set ACK 0x10

set target 10.10.10.1
set sport [random 20000:65535]
set dport 22
set interface [outif $target]
set window 4096

# We'll use a ghost IP. Make sure $myip is not being used...
set myip 10.10.10.123
set mymac [random mac]

# Spawn a listener for ARP requests
spawn_network -i $interface host $myip and {arp[6:2]} == 1

expect_network_before {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
}

# Start TCP 3-way handshake

# 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"

set retries 3
set isn [random]

# 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(src = $myip, dst = $target)/ \
                 tcp(src = $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
    }
}

# We're done with the 3-way handshake. If we want to send more stuff
# we need to use correct sequence numbers. Our sequence number is
# $tcp(ack) and the server's is $tcp(seq) + 1.
#

puts Done.

A simple TCP traceroute program

#
# A very simple TCP traceroute program. Uses the send_expect command.
#
# How to use:
#
# shell# nexp ./tcp-traceroute.nexp google.com 1:25
#

if {$argc != 2} {
    puts "Usage: $argv0 <IP address> <TTL range>"
    exit 1
}

set target [lindex $argv 0]
set ttlrange [lindex $argv 1]

set verbose 1

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.010 \
    ip(id = random, dst = $target, ttl = $ttlrange)/ \
    tcp(src = random, dst = 80, syn)

foreach r $_(received) s $_(sent) {
    packet dissect r
    set source $ip(src)
    set pdu_type $pdu(1,type)

    packet dissect s

    puts [format "$ip(ttl) $source %.3f ms $pdu_type" [expr [packet tdelta r s]*1000] ]
}

A simple ping program

if {$argc != 1} {
    puts "Usage: $argv0 <hostname or IP address>"
    exit 1
}

set target [lindex $argv 0]

# Spawn a listener for ICMP messages from our target
spawn_network -i [outif $target] icmp and src host $target

puts "PING $target 20(48) bytes of data."

for {set id [pid]; set seq 0} {1} {incr seq} {
    send_network ip(dst = $target)/ \
                 icmp-echo(seq = $seq)/ \
                 raw(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] ]
    }
}

Simple ARP scanner using the send_expect command

if {$argc != 1} {
    puts "Usage: $argv0 <interface>"
    exit 1
}

set interface [lindex $argv 0]
set network "$iface($interface,ip)/$iface($interface,netmask)"

set verbose 1

# 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 dissect r
    puts "$arp(sip) is at $arp(sha)"
}

Simple ARP scanner that uses send/expect commands

if {$argc != 1} {
    puts "Usage: $argv0 <interface>"
    exit 1
}

set interface [lindex $argv 0]
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)"
    }
}

Simple TCP SYN scanner

# Some useful constants
set SYN 0x02
set RST 0x04
set ACK 0x10

if {$argc != 2} {
    puts "Usage: $argv0 <hostname or IP address> <port range>"
    exit 1
}

set verbose 1
set ntries 3
set timeout 3
set delay 0.000100
set target [lindex $argv 0]
set ports [lindex $argv 1]

# Spawn a listener to get answers from our target
spawn_network -i [outif $target] src host $target

send_expect -tries $ntries -timeout $timeout -delay $delay \
    ip(dst = $target)/tcp(syn, dst = $ports)

foreach r $_(received) {
    packet dissect r
    if {$tcp(flags) & $RST} {
        # Do nothing to avoid printing excessive information, i.e. who
        # cares if what you're scanning has 65000+ ports closed?
    } elseif {$tcp(flags) == ($SYN | $ACK) } {
        puts "$tcp(srcport)/tcp open"
    } else {
        puts "$tcp(srcport)/tcp unknown"
    }
}

A simple TCP traceroute using send_network and expect_network commands

This script sends TCP SYN probes with IP time-to-live values that start at 1 and increment by 1 with each probe. After sending a probe an "expect_network" command waits for a response to the probe. If the response is an ICMP message it is assumed to be an ICMP time-exceeded message and probes continue to be sent. If the response is a TCP segment then it is assumed that we have reached the target. For each probe the device that generated the message, along the round-trip time of the response are printed. If a probe doesn't get a response then "timeout" is printed.

if {$argc == 0} {
    puts "Usage: $argv0 <IP address> \[<port>]"
    exit 1
}

set target [lindex $argv 0]

incr argc -1
if {$argc == 1} {
    set port [lindex $argv 1]
} else {
    set port 80
}

set interface [outif $target]

spawn_network -i $interface src host $target or icmp

for {set reached_target 0; set ttl 1} {!$reached_target && $ttl <= 30} {incr ttl} {
    # Send probe
    send_network \
        ip(id = $ttl, dst = $target, ttl = $ttl)/ \
        tcp(src = random, dst = $port, syn)

    expect_network -timeout 2.0 {1} {
        if {[lsearch $pdu(list) icmp] == -1} {
            # No ICMP in received packet. This means we reached the target.
            set reached_target 1
        }

        puts [format "$ttl ${ip.src} %.3f ms" [expr [txdelta ip]*1000] ]
    } timeout {
        puts "$ttl timeout"
    }
}

0trace - traceroute on established connections

0trace is a proof of concept tool written by security researcher Michal Zalewski that rides an established TCP session to perform a traceroute. The technique and the tool were announced to several security mailing lists on January 7, 2007:

http://seclists.org/fulldisclosure/2007/Jan/0145.html

The original proof of concept written by Michal Zalewski uses: 1) a small C program that uses raw sockets to send custom TCP segments, 2) tcpdump to read network traffic, and 3) a shell script that glues everything together by using a lot of grep, awk, sed, tail, head and other Unix commands. To further complicate things, to use the tool the user needs to spawn a telnet session to the target from outside the tool, and then run the tool. It was a bit... hhhmmm, complicated and messy (after all it was a proof of concept, and it worked and proved the point.)

The 0trace technique and proof of concept tool is a great example of how the NetworkExpect framework can make things much simpler when designing, prototyping, and writing network tools: using Network Expect the whole process is simplified so the user doesn't need to manually use a tool (telnet) outside the main tool. Also, the resulting script is written in a single language (Network Expect), doesn't use any external programs (telnet, tcpdump, grep, sed, awk, cut, head, tail), and is very concise and easy to follow.

Here's the equivalent 0trace Network Expect script that performs the same function as the original proof of concept that Michal wrote:

if {$argc != 2} {
    puts "Usage: $argv0 <IP address> <TTL range>"
    exit 1
}

set verbose 1

set target [lindex $argv 0]
set ttlrange [lindex $argv 1]
set interface [outif $target]
set port 80

# Open a TCP session to the target
spawn_network -tcp $target $port

# Obtain the actual IP address of the server we are connected to, as well
# as the local port number
set 4tuple [spawn_network -4tuple $listener_id]
set targetip [lindex $4tuple 2]
set myport [lindex $4tuple 1]

# Spawn a listener before we generate more traffic
spawn_network -i $interface (src host $targetip and dst port $myport) or icmp

# Generate some traffic over this TCP session, but do not do anything that
# would cause the server to tear down the session.
send_network {data('GET / HTTP/1.0\r\n')}

expect_network -timeout 1 {1} {
    # Keep reading data from target as long as it keeps sending. We will
    # leave this expect statement 1 second after there is no more data to
    # read.
    nexp_continue
}

# Figure out our sequence number and the server's.
set myseq ${tcp.ack}
set hisseq ${tcp.seq}

# Now send our probes while riding on the established TCP session
send_expect -tries 2 -delay 0.010 -o ip \
    ip(id = 1++, dst = $targetip, ttl = $ttlrange)/ \
    tcp(src = $myport, dst = $port, ack, seq = $myseq, \
        ack-seq = $hisseq, window = 65535)/ \
    data(\\x00)

# Process and display results
foreach r $_(received) s $_(sent) {
    packet dissect r
    set source ${ip.src}
    set proto $pdu(3,proto)

    packet dissect s

    puts [format "${ip.ttl} $source %.3f ms $proto" [expr [packet tdelta r s]*1000] ]
}

Here's an example of the tool in action:

# ./0trace.nexp ebay.com 1:20
1 172.18.172.1 0.593 ms icmp
2 14.0.254.57 0.231 ms icmp
3 14.0.2.11 0.457 ms icmp
4 172.18.127.229 0.521 ms icmp
5 172.18.0.25 0.541 ms icmp
6 10.81.254.61 0.529 ms icmp
7 10.81.254.181 0.557 ms icmp
8 10.81.254.194 1.298 ms icmp
9 64.102.241.135 0.862 ms icmp
10 64.102.254.197 1.541 ms icmp
11 157.130.42.53 3.064 ms icmp
12 152.63.39.106 1.971 ms icmp
13 152.63.48.94 74.545 ms icmp
14 152.63.57.1 72.373 ms icmp
15 157.130.128.94 73.439 ms icmp
16 66.135.207.58 73.182 ms icmp
17 66.135.192.87 93.653 ms tcp
18 66.135.192.87 93.480 ms tcp
19 66.135.192.87 73.481 ms tcp
20 66.135.192.87 76.415 ms tcp
# 

This shows that ebay.com was 17 hops away. A regular, UDP-based traceroute fails to show the last few hops:

$ traceroute -n ebay.com
traceroute to ebay.com (66.135.192.87), 30 hops max, 38 byte packets
 1  172.18.172.1  0.675 ms  0.648 ms  0.664 ms
 2  14.0.254.57  0.280 ms  0.238 ms  0.233 ms
 3  14.0.2.11  0.689 ms  0.593 ms  0.592 ms
 4  172.18.127.229  0.717 ms  0.637 ms  0.626 ms
 5  172.18.0.25  0.708 ms  0.660 ms  0.663 ms
 6  10.81.254.61  0.692 ms  0.668 ms  0.666 ms
 7  10.81.254.181  0.726 ms  0.680 ms  0.677 ms
 8  10.81.254.194  1.096 ms  0.976 ms  0.930 ms
 9  64.102.241.135  1.471 ms  1.142 ms  1.412 ms
10  64.102.254.197  1.285 ms  1.326 ms  1.510 ms
11  157.130.42.53  2.803 ms  2.724 ms  2.252 ms
12  152.63.39.106  2.372 ms  3.318 ms  2.650 ms
13  152.63.48.94  72.617 ms  72.900 ms  73.257 ms
14  152.63.57.1  72.921 ms  72.656 ms  73.011 ms
15  * * *
16  * * *
[...]

The way the tool works is as follows:

  1. A TCP session to the target is established
  2. An incomplete HTTP request is sent
  3. This incomplete request allows to obtain the sequence numbers that both the server and the client are using
  4. One we know the sequence numbers a series of TCP segments that only carry one byte of data ('\x0') are sent to the server. Each segment has an IP TTL that starts at 1 and increments by one in each segment
  5. Normal traceroute behavior then follows, i.e. ICMP time exceeded received means that we hit a new hop and haven't reached the target yet

All this is implemented in the simple Network Expect script above.

PCAP File Re-writing and Replay

A separate wiki page has been written to discuss this topic. See RewriteAndReplay.

Naptha TCP State Exhaustion Testing With Network Expect

A separate wiki page has been written to discuss this topic. See Naptha.

IPv4 Fragmentation

When using IP to send data, sometimes the data to send is too large for the maximum transmission unit (MTU) of the outgoing interface used to reach the destination. In these cases, the data needs to be sent in multiple IP packets, each with a size smaller than the outgoing interface's MTU, through a process called IP fragmentation.

Network Expect's PDU creation and transmission facilities are very low level, and do not have support to automatically handle IP fragmentation. In cases where a large chunk of data (greater than the outgoing interface's MTU) is used as the payload of an IP PDU, the resulting large packet will never make it to the wire since the operating system will produce an error when Network Expect tries to send the packet. For example, the command send_network ip(dst = 192.168.1.1)/icmp-echo()/data(data = 'random:15000') will not result in a packet that is seen in the wire since the size of the Ethernet frame will be 15042 bytes (14 bytes Ethernet header, 20 IP header, 8 ICMP header, 15000 ICMP payload), which is greater than the 1500 bytes Ethernet MTU.

Therefore, if we want to be able to send large amounts of data using IP we need to write code to fragment the data. At some point we may provide a Tcl command written in C to do this, but in the meantime, the following procedure written in Tcl does the job nicely:

proc fragmented_send {pkt dst proto} {
    upvar $pkt p
    global iface

    set mtu $iface([outif $dst],mtu)
    set adjusted_mtu [expr $mtu - 20] ;# "20" accounts for IP header
    set id [random]

    for {set remaining [packet length p]; set fragoff 0} \
        {$remaining > $adjusted_mtu} \
        {incr remaining -$adjusted_mtu; incr fragoff $adjusted_mtu} {
        # Send fragment. Note that the MF flag is set.
        send_network ip(dst = '$dst', mf, id = $id, \
                        fragoff=[expr $fragoff >> 3], proto = $proto)/ \
                     data(data = '[packet data p ${fragoff}l$adjusted_mtu]')
    }

    # Send final fragment. Note that the MF flag is not set.
    send_network ip(dst = '$dst', id = $id, \
                    fragoff=[expr $fragoff >> 3], proto = $proto)/ \
                 data(data = '[packet data p ${fragoff}:]')
}

This procedure receives in argument pkt the data to send via IP, in dst the destination IP address, and in proto the IP protocol to use, e.g. 1 for ICMP, 6 for TCP and 17 for UDP. pkt must be a Tcl packet object.

Here's an example of how the above procedure would be used. This example builds a large ICMP echo request message and sends it using the above procedure:

# Define the ICMP PDU
set icmppdu [pdu new icmp-echo()/data(data = 'random:15000')]

# Build the ICMP PDU
set icmp [pdu build icmppdu]

# Transmit ICMP PDU
fragmented_send icmp 192.168.1.1 1

IPv6 Fragmentation

Similarly to the previous example, we can also create a simple fragmentation procedure for IPv6:

proc fragmented_send6 {pkt src dst next_header mtu} {
    upvar $pkt p

    set adjusted_mtu [expr $mtu - 40 - 8] ;# Account for size of IPv6 header
                                           # (40) and size of the fragmentation
                                           # header (8).
    set id [random]

    for {set remaining [packet length p]; set fragoff 0} \
        {$remaining > $adjusted_mtu} \
        {incr remaining -$adjusted_mtu; incr fragoff $adjusted_mtu} {
        # Send fragment. Note that the M (More) flag is set. Also
        # note that we add 40 to the offset we get data from to
        # bypass the IPv6 header, which is being recreated with
        # each sent packet.
        send_network \
                ether()/ \
                ip6(src = '$src', dst = '$dst')/ \
                ip6frag(id = $id, offset = [expr $fragoff >> 3], \
                        next-header = $next_header, more)/ \
                data(data = '[packet data p [expr ${fragoff} + 40]l$adjusted_mtu]')
    }

    # Send fragment. Note that the M (More) flag is set. Also
    # note that we add 40 to the offset we get data from to
    # bypass the IPv6 header, which is being recreated with
    # each sent packet.
    send_network \
        ether()/ \
        ip6(src = '$src', dst = '$dst')/ \
        ip6frag(id = $id, offset = [expr $fragoff >> 3], \
                next-header = $next_header)/ \
        data(data = '[packet data p [expr ${fragoff} + 40]:]')
}

This procedure receives as input an IPv6 packet that may need to be fragmented (if its size is greater than the MTU that is also received as an input parameter). To make things simple, the procedure also receives as input parameters the source and destination IPv6 addresses as well as the next header of the data being transmitted (ideally we should be able to extract this information from the IPv6 packet received as input, but passing this data as input parameters is easier).

You can see that the function is very similar to the function in the previous example that does IPv4 fragmentation. A major difference is that, unlike IPv4, the IPv6 basic header has no provisions for fragmentation, so that function falls into the fragmentation extension header, which is represented by the ip6frag() PDU in the send_network command.

The above fragmentation function can be used in the following way:

# For testing purposes, just send the packets we generate to a PCAP
# file so we can look at the packets in Wireshark. To send
# to a real network just comment this out and make sure that
# your send_network statements do not include an "ether()"
# PDU at the beginning.
spawn_network -w /tmp/ip6-frag.cap

set datalen 15000 ;# size of ICMP payload

# Define the ICMP PDU
set icmp6pdu [pdu new \
                ip6(src = ::1, dst = ::2)/ \
                icmp6-echo(seq = 1073)/ \
                data(data = 'random:$datalen')]

# Build the ICMP PDU
set icmp6 [pdu build icmp6pdu]

# Transmit ICMP PDU
fragmented_send6 icmp6 ::1 ::2 58 1280

This code fragment creates a large ICMPv6 echo request and sends it using the above IPv6 fragmentation procedure. Because the size of the packet (> 15000 bytes) is greater than the MTU (1280 bytes), the packet needs to be fragmented.

A Little Network Steganography Game

This is a silly example, but I actually used this to resolve a security challenge I participated in during a conference that I attended, and it makes for a nice little game and example: in that challenge, one of the parts required to figure out a hidden message that contained instructions on how to reach the next step. The nice thing was that the message was hidden (encoded, actually) in network traffic (in ICMP echo requests, specifically), and required a little bit of automation to decode in a short amount of time.

The network traffic looked like this (try to spot where the message could be hidden):

16:46:23.666778 IP (tos 0x0, ttl 64, id 7849, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.108: ICMP echo request, id 3929, seq 54387, length 8
16:46:24.668506 IP (tos 0x0, ttl 64, id 20100, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.108: ICMP echo request, id 3929, seq 9517, length 8
16:46:25.670248 IP (tos 0x0, ttl 64, id 3083, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.111: ICMP echo request, id 3929, seq 9849, length 8
16:46:26.671970 IP (tos 0x0, ttl 64, id 25993, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.32: ICMP echo request, id 3929, seq 39572, length 8
16:46:27.673677 IP (tos 0x0, ttl 64, id 47035, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.116: ICMP echo request, id 3929, seq 46667, length 8
16:46:28.675406 IP (tos 0x0, ttl 64, id 25859, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.104: ICMP echo request, id 3929, seq 18718, length 8
16:46:29.677159 IP (tos 0x0, ttl 64, id 10325, offset 0, flags [none], proto ICMP (1), length 28)
    10.116.188.62 > 1.2.3.101: ICMP echo request, id 3929, seq 53485, length 8

After looking at this network traffic during the challenge, it became evident that something was going on with the last octet of the destination IP address of all these ICMP echo requests, and deeper look at a few of these octets revealed that these octets were carrying what seemed a message in plain ASCII. The problem was that the message was long, and decoding the message by hand was going to take some time. To handle that, the following short Network Expect script was written:

spawn_network -i wlan0 {icmp[icmptype] == icmp-echo}

expect_network -timeout -1 {1} {
    set c [barray slice _(packet) 33:33] ;# Offset 33 in an Ethernet frame carrying IP is last octect of dest. IP addr.
    puts -nonewline [barray string c]
    flush stdout
    nexp_continue ; Loop forever
}

The output from this script was the actual decoded message. Operation of this script is simple: we spawn a network listener that only looks at ICMP echo requests, we then loop forever using the expect_network statement, and inside the loop we obtain the byte at offset 33 and send it to standard output. The byte at offset 33 happens to be the last octect of the destination IP address, assuming Ethernet and IP header with no options.

Sample output generated by this script is provided below, but before that, here is another Network Expect script that can be used to encode test messages:

proc chr c {
    set v 0
    scan $c %c v
    return $v
}

if {$argc < 1} {
    puts "Usage: $argv0 <string to encode>"
    exit 1
}

for {set s ""; set i 0} {$argc > $i} {incr i} {
    set s "$s[lindex $argv $i] "
}

set s [string range $s 0 [expr [string length $s] - 2]]

while {1} {
    for {set i 0} {$i < [string length $s]} {incr i} {
        puts -nonewline [string index $s $i]; flush stdout
        send_network ip(id = 'random', dst=1.2.3.[chr [string index $s $i]])/icmp-echo(seq = 'random')
        sleep 0.1
    }
    puts ""
    send_network ip(id = 'random', dst=1.2.3.10)/icmp-echo(seq = 'random') ;# Send a carriage return so output doesn't look garbled on the receiving side
}

Running this encoding script like this:

shell# ./ip-encode.nexp 'hello, world'

and running the message decoder script in some place where the traffic generated by the message encoder script can be seen, will produce the following ouput:

shell# ./ip-decoder.nexp
hello, world
hello, world
hello, world
hello, world
[...]

Recovering URIs From HTTP Traffic

Let's say that you want to get a list of all URIs that you see in network traffic that you can capture. The following little script will allow you to do that:

if {$argc != 1} {
    puts "Usage: $argv0 <interface to capture on>"
    exit 1
}

spawn_network -i [lindex $argv 0]

expect_network -timeout -1 {1} {
    if {[info exists http.request.method]} {
        puts "${http.request.method} http://${ip.dst}${http.request.uri} ${http.request.version}"
    }
    clear dissection vars ;# Need to remove all Tcl variables created
                           # during the dissection process. Otherwise we
                           # may re-process data.
    nexp_continue
}

Sample output:

shell# ./get-http-uris.nexp wlan0
GET http://157.166.255.18/ HTTP/1.1
GET http://157.166.224.25/ HTTP/1.1
GET http://157.166.224.25/.element/ssi/www/breaking_news/3.0/banner.html?csiID=csi1 HTTP/1.1
GET http://157.166.224.25/cnn_adspaces/3.0/homepage/main/bot1.120x90.ad HTTP/1.1
GET http://204.2.241.161/r?c2=6035748&d.c=gif&d.o=cnn3global&d.x=5860585&d.t=page&d.u=http%3A%2F%2Fwww.cnn.com%2F HTTP/1.1
GET http://157.166.226.32/local/cnn/json?time=48&q=27502&csiID=csi3 HTTP/1.1
GET http://157.166.224.31/weather/getForecast?time=46&mode=json_html&zipCode=27502&csiID=csi2 HTTP/1.1
GET http://157.166.224.31/weather/getForecast?time=19&mode=json_html&zipCode=27502&locCode=NCAX&celcius=false&csiID=csi4 HTTP/1.1
GET http://157.166.226.32/.element/js/2.0/csi_include.js HTTP/1.1
GET http://209.68.22.65/forum/syndication.php HTTP/1.1
GET http://76.74.252.198/node/feed HTTP/1.1
GET http://74.125.45.118/linuxjournalcom HTTP/1.1

Extracting Information From a Hexadecimal Packet Dump

A separate wiki page has been written to discuss this topic. See PacketHexDump.

Examples (last edited 2011-05-18 15:32:08 by EloyParis)