Class: PacketFu::TCPPacket

Inherits:
Packet
  • Object
show all
Includes:
EthHeaderMixin, IPHeaderMixin, IPv6HeaderMixin, TCPHeaderMixin
Defined in:
lib/packetfu/protos/tcp.rb

Overview

TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.

Example

tcp_pkt = PacketFu::TCPPacket.new
tcp_pkt.tcp_flags.syn=1
tcp_pkt.tcp_dst=80
tcp_pkt.tcp_win=5840
tcp_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"

tcp_pkt.ip_saddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
tcp_pkt.ip_daddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')

tcp_pkt.recalc
tcp_pkt.to_f('/tmp/tcp.pcap')

tcp6_pkt = PacketFu::TCPPacket.new(:on_ipv6 => true)
tcp6_pkt.tcp_flags.syn=1
tcp6_pkt.tcp_dst=80
tcp6_pkt.tcp_win=5840
tcp6_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"
tcp6_pkt.ipv6_saddr="4::1"
tcp6_pkt.ipv6_daddr="12:3::4567"
tcp6_pkt.recalc
tcp6_pkt.to_f('/tmp/udp.pcap')

Parameters

:eth
  A pre-generated EthHeader object.
:ip
  A pre-generated IPHeader object.
:flavor
  TODO: Sets the "flavor" of the TCP packet. This will include TCP options and the initial window
  size, per stack. There is a lot of variety here, and it's one of the most useful methods to
  remotely fingerprint devices. :flavor will span both ip and tcp for consistency.
 :type
  TODO: Set up particular types of packets (syn, psh_ack, rst, etc). This can change the initial flavor.
:config
 A hash of return address details, often the output of Utils.whoami?

Instance Attribute Summary collapse

Attributes inherited from Packet

#flavor, #headers, #iface, #inspect_style

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TCPHeaderMixin

#tcp_ack, #tcp_ack=, #tcp_ack_readable, #tcp_calc_hlen, #tcp_calc_seq, #tcp_calc_src, #tcp_dport, #tcp_dport=, #tcp_dst, #tcp_dst=, #tcp_ecn, #tcp_ecn=, #tcp_flags, #tcp_flags=, #tcp_flags_dotmap, #tcp_flags_readable, #tcp_hlen, #tcp_hlen=, #tcp_options, #tcp_options=, #tcp_opts, #tcp_opts=, #tcp_opts_len, #tcp_opts_readable, #tcp_reserved, #tcp_reserved=, #tcp_seq, #tcp_seq=, #tcp_seq_readable, #tcp_sport, #tcp_sport=, #tcp_src, #tcp_src=, #tcp_sum, #tcp_sum=, #tcp_sum_readable, #tcp_urg, #tcp_urg=, #tcp_win, #tcp_win=

Methods included from IPv6HeaderMixin

#ipv6?, #ipv6_calc_len, #ipv6_calc_sum_on_addr, #ipv6_class, #ipv6_class=, #ipv6_daddr, #ipv6_daddr=, #ipv6_dst, #ipv6_dst=, #ipv6_dst_readable, #ipv6_hop, #ipv6_hop=, #ipv6_label, #ipv6_label=, #ipv6_len, #ipv6_len=, #ipv6_next, #ipv6_next=, #ipv6_recalc, #ipv6_saddr, #ipv6_saddr=, #ipv6_src, #ipv6_src=, #ipv6_src_readable, #ipv6_v, #ipv6_v=

Methods included from IPHeaderMixin

#ip_calc_id, #ip_calc_len, #ip_calc_sum, #ip_calc_sum_on_addr, #ip_daddr, #ip_daddr=, #ip_dst, #ip_dst=, #ip_dst_readable, #ip_frag, #ip_frag=, #ip_hl, #ip_hl=, #ip_hlen, #ip_id, #ip_id=, #ip_id_readable, #ip_len, #ip_len=, #ip_proto, #ip_proto=, #ip_recalc, #ip_saddr, #ip_saddr=, #ip_src, #ip_src=, #ip_src_readable, #ip_sum, #ip_sum=, #ip_sum_readable, #ip_tos, #ip_tos=, #ip_ttl, #ip_ttl=, #ip_v, #ip_v=

Methods included from EthHeaderMixin

#eth_daddr, #eth_daddr=, #eth_dst, #eth_dst=, #eth_dst_readable, #eth_proto, #eth_proto=, #eth_proto_readable, #eth_saddr, #eth_saddr=, #eth_src, #eth_src=, #eth_src_readable

Methods inherited from Packet

#==, #clone, #dissect, #dissection_table, force_binary, #handle_is_identity, #hexify, inherited, #inspect, #inspect_hex, #kind_of?, layer, #layer, #layer_symbol, layer_symbol, #method_missing, #orig_kind_of?, parse, #payload, #payload=, #peek, #proto, #recalc, #respond_to?, #size, #to_f, #to_pcap, #to_s, #to_w, #write

Constructor Details

#initialize(args = {}) ⇒ TCPPacket

Returns a new instance of TCPPacket.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/packetfu/protos/tcp.rb', line 85

def initialize(args={})
  if args[:on_ipv6] or args[:ipv6]
    @eth_header = EthHeader.new(args.merge(:eth_proto => 0x86dd)).read(args[:eth])
    @ipv6_header = IPv6Header.new(args).read(args[:ipv6])
    @tcp_header = TCPHeader.new(args).read(args[:tcp])

    @ipv6_header.body = @tcp_header
    @eth_header.body = @ipv6_header
    @headers = [@eth_header, @ipv6_header, @tcp_header]

    @ipv6_header.ipv6_next = 0x06
  else
    @eth_header = EthHeader.new(args.merge(:eth_proto => 0x0800)).read(args[:eth])
    @ip_header = IPHeader.new(args).read(args[:ip])
    @tcp_header = TCPHeader.new(args).read(args[:tcp])

    @ip_header.body = @tcp_header
    @eth_header.body = @ip_header
    @headers = [@eth_header, @ip_header, @tcp_header]

    @ip_header.ip_proto = 0x06
  end
  @tcp_header.flavor = args[:flavor].to_s.downcase

  super
  if args[:flavor]
    tcp_calc_flavor(@tcp_header.flavor)
  else
    tcp_calc_sum
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class PacketFu::Packet

Instance Attribute Details

#eth_headerObject

Returns the value of attribute eth_header.



60
61
62
# File 'lib/packetfu/protos/tcp.rb', line 60

def eth_header
  @eth_header
end

#ip_headerObject

Returns the value of attribute ip_header.



60
61
62
# File 'lib/packetfu/protos/tcp.rb', line 60

def ip_header
  @ip_header
end

#ipv6_headerObject

Returns the value of attribute ipv6_header.



60
61
62
# File 'lib/packetfu/protos/tcp.rb', line 60

def ipv6_header
  @ipv6_header
end

#tcp_headerObject

Returns the value of attribute tcp_header.



60
61
62
# File 'lib/packetfu/protos/tcp.rb', line 60

def tcp_header
  @tcp_header
end

Class Method Details

.can_parse?(str) ⇒ Boolean

Returns:

  • (Boolean)


62
63
64
65
66
67
68
69
70
71
# File 'lib/packetfu/protos/tcp.rb', line 62

def self.can_parse?(str)
  return false unless str.size >= 54
  return false unless EthPacket.can_parse? str
  if IPPacket.can_parse? str
    return true if str[23,1] == "\x06"
  elsif IPv6Packet.can_parse? str
    return true if str[20,1] == "\x06"
  end
  return false
end

Instance Method Details

#peek_formatObject

TCP packets are denoted by a “T ”, followed by size, source and dest information, packet flags, sequence number, and IPID.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/packetfu/protos/tcp.rb', line 216

def peek_format
  if ipv6?
    peek_data = ["6T "]
    peek_data << "%-5d" % self.to_s.size
    peek_data << "%-31s" % "#{self.ipv6_saddr}:#{self.tcp_src}"
    peek_data << "->"
    peek_data << "%31s" % "#{self.ipv6_daddr}:#{self.tcp_dst}"
  else
    peek_data = ["T  "]
    peek_data << "%-5d" % self.to_s.size
    peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
    peek_data << "->"
    peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
  end
  flags = ' ['
  flags << self.tcp_flags_dotmap
  flags << '] '
  peek_data << flags
  peek_data << "S:"
  peek_data << "%08x" % self.tcp_seq
  unless ipv6?
    peek_data << "|I:"
    peek_data << "%04x" % self.ip_id
  end
  peek_data.join
end

#read(str = nil, args = {}) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/packetfu/protos/tcp.rb', line 73

def read(str=nil, args={})
  super
  # Strip off any extra data, if we are asked to do so.
  if args[:strip]
    tcp_body_len = self.ip_len - self.ip_hlen - (self.tcp_hlen * 4)
    @tcp_header.body.read(@tcp_header.body.to_s[0,tcp_body_len])
    tcp_calc_sum
    @ip_header.ip_recalc
  end
  self
end

#tcp_calc_flavor(str) ⇒ Object

Sets the correct flavor for TCP Packets. Recognized flavors are:

windows, linux, freebsd


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/packetfu/protos/tcp.rb', line 119

def tcp_calc_flavor(str)
  ts_val = Time.now.to_i + rand(0x4fffffff)
  ts_sec = rand(0xffffff)
  case @tcp_header.flavor = str.to_s.downcase
  when "windows" # WinXP's default syn
    @tcp_header.tcp_win = 0x4000
    @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
    @tcp_header.tcp_src = rand(5000 - 1026) + 1026
    @ip_header.ip_ttl = 64
  when "linux" # Ubuntu Linux 2.6.24-19-generic default syn
    @tcp_header.tcp_win = 5840
    @tcp_header.tcp_options="MSS:1460,SACKOK,TS:#{ts_val};0,NOP,WS:7"
    @tcp_header.tcp_src = rand(61_000 - 32_000) + 32_000
    @ip_header.ip_ttl = 64
  when "freebsd" # Freebsd
    @tcp_header.tcp_win = 0xffff
    @tcp_header.tcp_options="MSS:1460,NOP,WS:3,NOP,NOP,TS:#{ts_val};#{ts_sec},SACKOK,EOL,EOL"
    @ip_header.ip_ttl = 64
  else
    @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
  end
  tcp_calc_sum
end

#tcp_calc_sumObject

tcp_calc_sum() computes the TCP checksum, and is called upon intialization. It usually should be called just prior to dropping packets to a file or on the wire. – This is /not/ delegated down to @tcp_header since we need info from the IP header, too. ++



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/packetfu/protos/tcp.rb', line 149

def tcp_calc_sum
  if @ipv6_header
    checksum = ipv6_calc_sum_on_addr
    tcp_len = ipv6_len
  else
    checksum = ip_calc_sum_on_addr
    tcp_len = ip_len.to_i - ((ip_hl.to_i) * 4)
  end

  checksum += 0x06 # TCP Protocol.
  checksum += tcp_len
  checksum += tcp_src
  checksum += tcp_dst
  checksum += (tcp_seq.to_i >> 16)
  checksum += (tcp_seq.to_i & 0xffff)
  checksum += (tcp_ack.to_i >> 16)
  checksum += (tcp_ack.to_i & 0xffff)
  checksum += ((tcp_hlen << 12) + 
               (tcp_reserved << 9) + 
               (tcp_ecn.to_i << 6) + 
               tcp_flags.to_i
              )
  checksum += tcp_win
  checksum += tcp_urg

  chk_tcp_opts = (tcp_opts.to_s.size % 2 == 0 ? tcp_opts.to_s : tcp_opts.to_s + "\x00") 
  chk_tcp_opts.unpack("n*").each {|x| checksum = checksum + x }
  if (tcp_len - (tcp_hlen * 4)) >= 0
    real_tcp_payload = payload[0, (tcp_len - (tcp_hlen * 4))] # Can't forget those pesky FCSes!
  else
    real_tcp_payload = payload # Something's amiss here so don't bother figuring out where the real payload is.
  end
  chk_payload = (real_tcp_payload.size % 2 == 0 ? real_tcp_payload : real_tcp_payload + "\x00") # Null pad if it's odd.
  chk_payload.unpack("n*").each {|x| checksum = checksum+x }
  checksum = checksum % 0xffff
  checksum = 0xffff - checksum
  checksum == 0 ? 0xffff : checksum
  @tcp_header.tcp_sum = checksum
end

#tcp_recalc(arg = :all) ⇒ Object

Recalculates various fields of the TCP packet.

Parameters

:all
  Recomputes all calculated fields.
:tcp_sum
  Recomputes the TCP checksum.
:tcp_hlen
  Recomputes the TCP header length. Useful after options are added.


199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/packetfu/protos/tcp.rb', line 199

def tcp_recalc(arg=:all)
  case arg
  when :tcp_sum
    tcp_calc_sum
  when :tcp_hlen
    @tcp_header.tcp_recalc :tcp_hlen
  when :all
    @tcp_header.tcp_recalc :all
    tcp_calc_sum
  else
    raise ArgumentError, "No such field `#{arg}'"
  end
end