1 Expanding on Part 1
Again, using the existing bro scripts is going to be our best bet for not only learning new things but for learning how to do them in a way that is consistent with how Bro works and the vision the devs have for it. It might be useful to take a look at what the most commonly used events might be.
mac@securityonion-Analyst:/usr/local/share/bro$ grep -hri "^\W*event" * | grep -v "bro_init\|bro_done" | sort | uniq -c | sort -rn | head -10 24 event connection_state_remove(c: connection) 9 event connection_established(c: connection) 8 event connection_state_remove(c: connection) &priority=-5 7 event http_request(c: connection, method: string, original_URI: string, 7 event file_transferred(c: connection, prefix: string, descr: string, 7 event connection_finished(c: connection) 6 event protocol_violation(c: connection, atype: count, aid: count, 5 event smtp_reply(c: connection, is_orig: bool, code: count, cmd: string, 5 event remote_connection_closed(p: event_peer) 5 event new_connection(c: connection)Top of the list is the event connection_state_remove() and if it's that high it has to have some serious power behind it! Let's take a quick look at its documentation.
314 ## Generated when a connection's internal state is about to be removed from 315 ## memory. Bro generates this event reliably once for every connection when it 316 ## is about to delete the internal state. As such, the event is well-suited for 317 ## scrip-level cleanup that needs to be performed for every connection. The 318 ## ``connection_state_remove`` event is generated not only for TCP sessions but 319 ## also for UDP and ICMP flows. 320 ## 321 ## c: The connection. 322 ## 323 ## .. bro:see:: connection_EOF connection_SYN_packet connection_attempt 324 ## connection_established connection_external connection_finished 325 ## connection_first_ACK connection_half_finished connection_partial_close 326 ## connection_pending connection_rejected connection_reset connection_reused 327 ## connection_status_update connection_timeout expected_connection_seen 328 ## new_connection new_connection_contents partial_connection udp_inactivity_timeout 329 ## tcp_inactivity_timeout icmp_inactivity_timeout conn_stats 330 global connection_state_remove: event(c: connection);Well, we can see why connection_state_remove() gets so much use! Right before Bro decides to stop caring about this connection, it generates this event. Let's take a look at the using this event against the tracefile provided by SANS.
event connection_state_remove(c: connection) { print c; }
[id=[orig_h=10.10.10.70, orig_p=1037/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], orig=[size=0, state=1, num_pkts=1, num_bytes_ip=48], resp=[size=0, state=6, num_pkts=1, num_bytes_ip=40], start_time=1272498035.258314, duration=0.000076, service={ }, addl=, hot=0, history=Sr, uid=McumIfcvNF1, dpd=<uninitialized>, conn=[ts=1272498035.258314, uid=McumIfcvNF1, id=[orig_h=10.10.10.70, orig_p=1037/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], proto=tcp, service=<uninitialized>, duration=0.000076, orig_bytes=0, resp_bytes=0, conn_state=REJ, local_orig=<uninitialized>, missed_bytes=0, history=Sr, orig_pkts=1, orig_ip_bytes=48, resp_pkts=1, resp_ip_bytes=40], extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>] [id=[orig_h=10.10.10.70, orig_p=1037/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], orig=[size=0, state=1, num_pkts=1, num_bytes_ip=48], resp=[size=0, state=6, num_pkts=1, num_bytes_ip=40], start_time=1272498035.594943, duration=0.000037, service={ }, addl=, hot=0, history=Sr, uid=UX0bcKwPrg4, dpd=<uninitialized>, conn=[ts=1272498035.594943, uid=UX0bcKwPrg4, id=[orig_h=10.10.10.70, orig_p=1037/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], proto=tcp, service=<uninitialized>, duration=0.000037, orig_bytes=0, resp_bytes=0, conn_state=REJ, local_orig=<uninitialized>, missed_bytes=0, history=Sr, orig_pkts=1, orig_ip_bytes=48, resp_pkts=1, resp_ip_bytes=40], extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>] [id=[orig_h=10.10.10.70, orig_p=1037/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], orig=[size=0, state=1, num_pkts=1, num_bytes_ip=48], resp=[size=0, state=6, num_pkts=1, num_bytes_ip=40], start_time=1272498036.141827, duration=0.000045, service={ }, addl=, hot=0, history=Sr, uid=EKKnAEoqQO, dpd=<uninitialized>, conn=[ts=1272498036.141827, uid=EKKnAEoqQO, id=[orig_h=10.10.10.70, orig_p=1037/tcp, resp_h=10.10.10.10, resp_p=4445/tcp], proto=tcp, service=<uninitialized>, duration=0.000045, orig_bytes=0, resp_bytes=0, conn_state=REJ, local_orig=<uninitialized>, missed_bytes=0, history=Sr, orig_pkts=1, orig_ip_bytes=48, resp_pkts=1, resp_ip_bytes=40], extract_orig=F, extract_resp=F, dns=<uninitialized>, dns_state=<uninitialized>, ftp=<uninitialized>, http=<uninitialized>, http_state=<uninitialized>, irc=<uninitialized>, smtp=<uninitialized>, smtp_state=<uninitialized>, ssh=<uninitialized>, ssl=<uninitialized>, syslog=<uninitialized>] ...snip..Something you'll notice is that, unlike new_connection(), connection_state_remove() will result in events in a different order. When using new_connection(), your script will generate events somewhat linearly with what is in the tracefile. If you use connection_state_remove() you'll see events generated after then connection has ended.
Looking at the output of the connection_state_remove() we can see that pertinent fields such as history, duration, and conn_state have been filled out for us. Using these values, we can start answering more questions from the SANS Forensic Challenge.
To get the answer to question #4, we can use a simple if statement to check for a responder port of 4444/tcp and then print the c$start_time and sum of c$start_time and c$duration.
event connection_state_remove(c: connection) { if (c$id$resp_p == 4444/tcp) { print fmt("%s", strftime("%Y/%m/%d %H:%M:%S", c$start_time)); print fmt("%s", strftime("%Y/%m/%d %H:%M:%S", c$start_time + c$duration)); } }
2010/04/28 19:40:00 2010/04/28 19:41:26To start answering some of the other questions, we need to start looking at the state of a connection. Bro uses two shorthand fields that are not only handy in scriptland but also useful to understand while you're looking at logs: the history and conn_state The documentation for the two fields are below.
## ========== =============================================== 40 ## conn_state Meaning 41 ## ========== =============================================== 42 ## S0 Connection attempt seen, no reply. 43 ## S1 Connection established, not terminated. 44 ## SF Normal establishment and termination. Note that this is the same symbol as for state S1. 45 ## REJ Connection attempt rejected. 46 ## S2 Connection established and close attempt by originator seen (but no reply from responder). 47 ## S3 Connection established and close attempt by responder seen (but no reply from originator). 48 ## RSTO Connection established, originator aborted (sent a RST). 49 ## RSTR Established, responder aborted. 50 ## RSTOS0 Originator sent a SYN followed by a RST, we never saw a SYN-ACK from the responder. 51 ## RSTRH Responder sent a SYN ACK followed by a RST, we never saw a SYN from the (purported) originator. 52 ## SH Originator sent a SYN followed by a FIN, we never saw a SYN ACK from the responder (hence the connection was "half" open). 53 ## SHR Responder sent a SYN ACK followed by a FIN, we never saw a SYN from the originator. 54 ## OTH No SYN seen, just midstream traffic (a "partial connection" that was not later closed). 55 ## ========== =============================================== 56 conn_state: string &log &optional; ...snip... ## Records the state history of connections as a string of letters. 71 ## For TCP connections the meaning of those letters is: 72 ## 73 ## ====== ==================================================== 74 ## Letter Meaning 75 ## ====== ==================================================== 76 ## s a SYN w/o the ACK bit set 77 ## h a SYN+ACK ("handshake") 78 ## a a pure ACK 79 ## d packet with payload ("data") 80 ## f packet with FIN bit set 81 ## r packet with RST bit set 82 ## c packet with a bad checksum 83 ## i inconsistent packet (e.g. SYN+RST bits both set) 84 ## ====== ==================================================== 85 ## 86 ## If the letter is in upper case it means the event comes from the 87 ## originator and lower case then means the responder. 88 ## Also, there is compression. We only record one "d" in each direction, 89 ## for instance. I.e., we just record that data went in that direction. 90 ## This history is not meant to encode how much data that happened to 91 ## be. 92 history: string &log &optional;Using either of these fields we can make decisions based on the states of the connections as observed by Bro. Question #8 wants to know when the victim machine finally connected to the attacker's machine on port 4445/tcp. If you recall from the previous post, we showed that the machine attempted to connect to 4445/tcp approximately every 11 seconds or so. Since the originator made multiple attempts to connect, had we stuck with new_connection(), we would have had to store some kind of state and look for a response and session establishment between the two endpoints. Using connection_state_remove() Bro has already done the hard work for us! All we need to do is look for the state that indicates a successful connection and termination. According to the documentation for conn_state, "SF" indicates and normal connection establishment and termination.
event connection_state_remove(c: connection) { if (c$id$resp_p == 4444/tcp) { print fmt("Start of connection to 4444/tcp: %s", strftime("%Y/%m/%d %H:%M:%S", c$start_time)); print fmt("End of connection to 4444/tcp: %s", strftime("%Y/%m/%d %H:%M:%S", c$start_time + c$duration)); } if (c$id$resp_p == 4445/tcp && c$conn$conn_state == "SF") print fmt("End of connection to 4445/tcp: %s", strftime("%Y/%m/%d %H:%M:%S", c$start_time + c$duration)); }
Start of connection to 4444/tcp: 2010/04/28 19:40:00 End of connection to 4444/tcp: 2010/04/28 19:41:26 End of connection to 4445/tcp: 2010/04/28 19:43:17It's easy to see why connection_state_remove() is used so often! In just a few minutes we were able to answer three more questions from the forensics challenge. Refactoring the code from the last post, it doesn't change our code all that much. It will, however, allow us to leverage more information as our scripting requirements expand.
2 The unbroly new_packet()
479 ## Generated for every packet Bro sees. This is a very low-level and expensive 480 ## event that should be avoided when at all possible. Is's usually infeasible to 481 ## handle when processing even medium volumes of traffic in real-time. That 482 ## said, if you work from a trace and want to do some packet-level analysis, 483 ## it may come in handy. 484 ## 485 ## c: The connection the packet is part of. 486 ## 487 ## p: Informattion from the header of the packet that triggered the event. 488 ## 489 ## .. bro:see:: tcp_packet packet_contents 490 global new_packet: event(c: connection, p: pkt_hdr);Using new_packet() generates a lot of overhead! Were you to use it on live traffic, you'd like as not bring your sensor to its knees as it attempts to generate an event for every new packet. For example, running a pair of test events against the evidence trace file from the challenge shows us the extra load brought to bear on Bro.
event new_connection(c: connection) { print "new connection"; } event new_packet(c: connection, p: pkt_hdr) { print "new packet"; }
bro -r evidence06.pcap event_test.bro | grep -i "new packet" | wc -l 2554 bro -r evidence06.pcap event_test.bro | grep -i "new connection" | wc -l 123Seeing the difference between packet level analysis and connection level analysis, we can see why the documentation in Bro includes such a warning. Let's take a look at a sample of the pkt_hdr passed to the new_packet() by looking at init-bare.bro.
999 ## A packet header, consisting of an IP header and transport-layer header. 1000 ## 1001 ## .. bro:see:: new_packet 1002 type pkt_hdr: record { 1003 ip: ip_hdr; ##< The IP header. 1004 tcp: tcp_hdr &optional; ##< The TCP header if a TCP packet. 1005 udp: udp_hdr &optional; ##< The UDP header if a UDP packet. 1006 icmp: icmp_hdr &optional; ##< The ICMP header if an ICMP packet. 1007 };As you can see, much of the pkt_hdr type is a collection of other types. Since we're only interested in the IP and TCP data types for the challenge, we're going to need to identify the fields in iphdr and tcphdr respectively.
944 ## Values extracted from an IP header. 945 ## 946 ## .. bro:see:: pkt_hdr discarder_check_ip 947 type ip_hdr: record { 948 hl: count; ##< Header length in bytes. 949 tos: count; ##< Type of service. 950 len: count; ##< Total length. 951 id: count; ##< Identification. 952 ttl: count; ##< Time to live. 953 p: count; ##< Protocol. 954 src: addr; ##< Source address. 955 dst: addr; ##< Destination address. 956 };
969 ## Values extracted from a TCP header. 970 ## 971 ## .. bro:see:: pkt_hdr discarder_check_tcp 972 type tcp_hdr: record { 973 sport: port; ##< source port. 974 dport: port; ##< destination port 975 seq: count; ##< sequence number 976 ack: count; ##< acknowledgement number 977 hl: count; ##< header length (in bytes) 978 dl: count; ##< data length (xxx: not in original tcphdr!) 979 flags: count; ##< flags 980 win: count; ##< window 981 };Once processed by Bro, pkthdr contains the pertinent information based on the Layer 4 information it observed for the packet.
new packet: [ip=[hl=20, tos=0, len=337, id=47, ttl=128, p=6, src=10.10.10.70, dst=10.10.10.10], tcp=[sport=1035/tcp, dport=8080/tcp, seq=3905816263, ack=3420183379, hl=20, dl=297, new packet: [ip=[hl=20, tos=0, len=40, id=9360, ttl=64, p=6, src=10.10.10.10, dst=10.10.10.70], tcp=[sport=8080/tcp, dport=1035/tcp, seq=3420183379, ack=3905816560, hl=20, dl=0, fl new packet: [ip=[hl=20, tos=0, len=1500, id=9361, ttl=64, p=6, src=10.10.10.10, dst=10.10.10.70], tcp=[sport=8080/tcp, dport=1035/tcp, seq=3420183379, ack=3905816560, hl=20, dl=146Question 7 wants us to determine how often the TCP initial sequence number (ISN) and IP ID change for the repeated failed connection attempts to port 4445/tcp. The pkthdr data type provides those values in p$tcp$seq and p$ip$id respectively. Dumping the contents of these values is easy and we can likely answer our questions just visually inspecting those values. So far, I don't think I've started a single script or a test without first dumping pertinent fields and seeing what kind of information I can gather and how it's going to effect the resulting script. Not only has it been good practice to solidify some of the common data structures in my mind, but it's also been a good way to keep a smooth flow between what I see in trace file and what I attempt to do in script land.
1 event new_packet(c: connection, p: pkt_hdr) 2 { 3 if (c$id$resp_p == 4445/tcp && c$history == "") 4 print fmt("new_packet(): ip_id: %s tcp sequence: %s", p$ip$id, p$tcp$seq); 5 }You'll notice we use an if statement that matches on the responder's port and a blank c$history. What we're testing for is a packet with the SYN bit turned on which in the context of c$history would look like "S". However, it turns out that the c$history field is very aptly named! Bro will start building the c$history field only after it's seen the packet, meaning that the first time you'll see the "S" indicating that it saw an attempted SYN will be on the ACK packet being sent back to the originator. You can see this for yourself by altering if statement above to exclude the c$history test and including it in the print statement. It's a short detour but it illustrates just a tiny bit of the work being done behind the scenes for us when we handle events at a higher level.
1 event new_packet(c: connection, p: pkt_hdr) 2 { 3 if (c$id$resp_p == 4445/tcp) 4 print fmt("new_packet(): ip_id: %s tcp sequence: %s history: %s", p$ip$id, p$tcp$seq, c$history); 5 }
new_packet(): ip_id: 359 tcp_seq: 553522758 history: new_packet(): ip_id: 0 tcp_seq: 0 history: S new_packet(): ip_id: 360 tcp_seq: 553522758 history: new_packet(): ip_id: 0 tcp_seq: 0 history: S new_packet(): ip_id: 361 tcp_seq: 553522758 history: new_packet(): ip_id: 0 tcp_seq: 0 history: S new_packet(): ip_id: 362 tcp_seq: 553800369 history: new_packet(): ip_id: 0 tcp_seq: 0 history: S ...snip... new_packet(): ip_id: 597 tcp_seq: 1979373164 history: new_packet(): ip_id: 0 tcp_seq: 0 history: S new_packet(): ip_id: 598 tcp_seq: 1979373164 history: new_packet(): ip_id: 0 tcp_seq: 1436350344 history: Sh new_packet(): ip_id: 599 tcp_seq: 1979373165 history: Sh new_packet(): ip_id: 24029 tcp_seq: 1436350345 history: ShA new_packet(): ip_id: 24030 tcp_seq: 1436350349 history: ShAd new_packet(): ip_id: 24031 tcp_seq: 1436351809 history: ShAd new_packet(): ip_id: 600 tcp_seq: 1979373165 history: ShAdYou can see the c$history field populating itself one step behind. Of course, if we stick to using connection_state_remove this will be completely transparent to us.
Let's get back to solving the challenge. Running the script that matches based on a blank c$history gives us:
new_packet(): ip_id: 359 tcp sequence: 553522758 new_packet(): ip_id: 360 tcp sequence: 553522758 new_packet(): ip_id: 361 tcp sequence: 553522758 new_packet(): ip_id: 362 tcp sequence: 553800369 new_packet(): ip_id: 363 tcp sequence: 553800369 new_packet(): ip_id: 364 tcp sequence: 553800369 new_packet(): ip_id: 365 tcp sequence: 554100968 new_packet(): ip_id: 366 tcp sequence: 554100968 new_packet(): ip_id: 369 tcp sequence: 554100968 new_packet(): ip_id: 370 tcp sequence: 554399680 new_packet(): ip_id: 371 tcp sequence: 554399680 new_packet(): ip_id: 372 tcp sequence: 554399680 new_packet(): ip_id: 373 tcp sequence: 554670846 ...snip...It looks like the IP ID field and the TCP sequence number are incrementing at an interval of every packet and every three packets respectively. There are 120 connection attempts to the 4445/tcp port which is somewhat unwieldy to check visually. But wait, Bro isn't here to make you do any laborious counting. We can do this in scriptland!
We'll make use of Bro's tables and sets to confirm our suspicions about the intervals. Each time we see a SYN packet heading to port 4445/tcp we'll add that packet's ip id (p$ip$id) to a set. Since sets unique, if we see compare the number of attempts against the number of members in the ipid set (using |ipid|) using a bro_done() event they should be the same if an id is never reused. For the TCP sequence number we're going to need to use a table to track the sequence numbers and count them. We'll then treat the table like a poor man's stack and make comparisons.
1 global attempts_count: count = 0; 2 global ip_id: set[count]; 3 global tcp_seq: table[count] of count; 4 5 event new_packet(c: connection, p: pkt_hdr) 6 { 7 if (c$id$resp_p == 4445/tcp && c$history == "") 8 { 9 ++attempts_count; 10 if (p$ip$id !in ip_id) 11 add ip_id[p$ip$id]; 12 if (p$tcp$seq !in tcp_seq) 13 tcp_seq[p$tcp$seq] = 1; 14 else 15 ++tcp_seq[p$tcp$seq]; 16 } 17 } 18 19 event bro_done() 20 { 21 local sequence_check: count; 22 local div: double; 23 for (seq in tcp_seq) 24 { 25 sequence_check = tcp_seq[seq]; 26 delete tcp_seq[seq]; 27 for (check in tcp_seq) 28 if (sequence_check == tcp_seq[check]) 29 delete tcp_seq[check]; 30 } 31 div = |ip_id| / attempts_count; 32 print fmt("IP ID changes every %.2f packet.", div ); 33 if (|tcp_seq| == 0) 34 print fmt("TCP sequence changes ever %d packets.", sequence_check); 35 }
IP ID changes every 1.00 packet. TCP sequence changes ever 3 packets.
3 Wrapping up
4 Code so far
1 global earliest: time; 2 global source_ports: table[port] of time; 3 global first_contact_4444: time; 4 global last_contact_4444: time; 5 global first_contact_4445: time; 6 global last_contact_4445: time; 7 global attempts_count: count = 0; 8 global ip_id: set[count]; 9 global tcp_seq: table[count] of count; 10 11 event bro_init() &priority=10 12 { 13 print "SANS Forensics Challenge"; 14 print "========================"; 15 earliest = current_time(); 16 } 17 18 event new_packet(c: connection, p: pkt_hdr) 19 { 20 if (c$id$resp_p == 4445/tcp && c$history == "") 21 { 22 ++attempts_count; 23 add ip_id[p$ip$id]; 24 if (p$tcp$seq !in tcp_seq) 25 tcp_seq[p$tcp$seq] = 1; 26 else 27 ++tcp_seq[p$tcp$seq]; 28 } 29 } 30 31 event connection_state_remove(c: connection) 32 { 33 if (c$start_time < earliest ) 34 earliest = c$start_time; 35 if (c$id$orig_h == 10.10.10.70 && c$id$resp_p == 4445/tcp) 36 { 37 if (c$id$orig_p !in source_ports) 38 source_ports[c$id$orig_p] = c$start_time; 39 40 if (c$conn$conn_state == "SF") 41 { 42 first_contact_4445 = c$start_time; 43 last_contact_4445 = c$start_time + c$duration; 44 } 45 } 46 47 if (c$id$resp_p == 4444/tcp) 48 { 49 first_contact_4444 = c$start_time; 50 last_contact_4444 = c$start_time + c$duration; 51 } 52 } 53 54 event bro_done() 55 { 56 local sports: vector of port; 57 local stime: vector of time; 58 local sequence_check: count; 59 60 for (p in source_ports) 61 { 62 sports[|sports|] = p; 63 stime[|sports|] = source_ports[p]; 64 } 65 66 for (seq in tcp_seq) 67 { 68 sequence_check = tcp_seq[seq]; 69 delete tcp_seq[seq]; 70 for (check in tcp_seq) 71 if (sequence_check == tcp_seq[check]) 72 delete tcp_seq[check]; 73 } 74 75 sort(stime); 76 sort(sports); 77 print "Question #4:"; 78 print fmt(" Start of session to 4444/tcp: %s", first_contact_4444 - earliest); 79 print "Question #5:"; 80 print fmt(" End of session to 4444/tcp: %s", last_contact_4444 - earliest); 81 print "Question #7a:"; 82 if (|tcp_seq| == 0) 83 print fmt(" TCP Sequence changes every %s packets.", sequence_check); 84 print "Question #7b:"; 85 print fmt(" Number of attempts: %s", attempts_count); 86 print fmt(" Number of ip id: %d", |ip_id|); 87 print "Question #7c:"; 88 for (j in stime) 89 print fmt(" Delta Time: %s", stime[j+1] - stime[j]); 90 print "Question #8:"; 91 print fmt(" Successful connection to 4445/tcp: %s", first_contact_4445 - earliest); 92 print "Question #10:"; 93 print fmt(" Connection to 4445/tcp closed: %s", last_contact_4445 - earliest); 94 print "Connection Statistics:"; 95 print "======================"; 96 print fmt("First Packet: %s", strftime("%Y/%m/%d %H:%M:%OS", earliest)); 97 print fmt("End of Capture: %s", strftime("%Y/%m/%d %H:%M:%S", network_time())); 98 print "========================"; 99 }
SANS Forensics Challenge ======================== Question #4: Start of session to 4444/tcp: 1.0 sec 265.0 msecs 851.0 usecs Question #5: End of session to 4444/tcp: 1.0 min 27.0 secs 587.0 msecs 153.0 usecs Question #7a: TCP Sequence changes every 3 packets. Question #7b: Number of attempts: 120 Number of ip id: 120 Question #7c: Delta Time: 11.0 secs 785.0 msecs 487.0 usecs Delta Time: 11.0 secs 730.0 msecs 439.0 usecs Delta Time: 11.0 secs 795.0 msecs 35.0 usecs Delta Time: 11.0 secs 735.0 msecs 993.0 usecs Delta Time: 11.0 secs 884.0 msecs 180.0 usecs Delta Time: 11.0 secs 960.0 msecs 521.0 usecs Delta Time: 11.0 secs 907.0 msecs 572.0 usecs Question #8: Successful connection to 4445/tcp: 2.0 mins 3.0 secs 674.0 msecs 198.0 usecs Question #10: Connection to 4445/tcp closed: 3.0 mins 18.0 secs 441.0 msecs 345.0 usecs Connection Statistics: ====================== First Packet: 2010/04/28 19:39:59 End of Capture: 2010/04/28 19:43:17 ========================