Class: PacketFu::TCPPacket

Inherits:
Packet
  • Object
show all
Includes:
EthHeaderMixin, IPHeaderMixin, 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')

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 IPHeaderMixin

#ip_calc_id, #ip_calc_len, #ip_calc_sum, #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


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/packetfu/protos/tcp.rb', line 69

def initialize(args={})
  @eth_header = 	(args[:eth] || EthHeader.new)
  @ip_header 	= 	(args[:ip]	|| IPHeader.new)
  @tcp_header = 	(args[:tcp] || TCPHeader.new)
  @tcp_header.flavor = args[:flavor].to_s.downcase

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

  @ip_header.ip_proto=0x06
  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


46
47
48
# File 'lib/packetfu/protos/tcp.rb', line 46

def eth_header
  @eth_header
end

#ip_headerObject

Returns the value of attribute ip_header


46
47
48
# File 'lib/packetfu/protos/tcp.rb', line 46

def ip_header
  @ip_header
end

#tcp_headerObject

Returns the value of attribute tcp_header


46
47
48
# File 'lib/packetfu/protos/tcp.rb', line 46

def tcp_header
  @tcp_header
end

Class Method Details

.can_parse?(str) ⇒ Boolean

Returns:

  • (Boolean)

48
49
50
51
52
53
54
# File 'lib/packetfu/protos/tcp.rb', line 48

def self.can_parse?(str)
  return false unless str.size >= 54
  return false unless EthPacket.can_parse? str
  return false unless IPPacket.can_parse? str
  return false unless str[23,1] == "\x06"
  return true
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.


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/packetfu/protos/tcp.rb', line 183

def peek_format
  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}"
  flags = ' ['
  flags << self.tcp_flags_dotmap
  flags << '] '
  peek_data << flags
  peek_data << "S:"
  peek_data << "%08x" % self.tcp_seq
  peek_data << "|I:"
  peek_data << "%04x" % self.ip_id
  peek_data.join
end

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


56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/packetfu/protos/tcp.rb', line 56

def read(str=nil, args={})
  raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
  @eth_header.read(str)

  # 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])
  end
  super(args)
  self
end

#tcp_calc_flavor(str) ⇒ Object

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

windows, linux, freebsd

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

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. ++


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/packetfu/protos/tcp.rb', line 120

def tcp_calc_sum
  checksum =  (ip_src.to_i >> 16)
  checksum += (ip_src.to_i & 0xffff)
  checksum += (ip_dst.to_i >> 16)
  checksum += (ip_dst.to_i & 0xffff)
  checksum += 0x06 # TCP Protocol.
  checksum +=	(ip_len.to_i - ((ip_hl.to_i) * 4))
  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 (ip_len - ((ip_hl + tcp_hlen) * 4)) >= 0
    real_tcp_payload = payload[0,( ip_len - ((ip_hl + 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.

166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/packetfu/protos/tcp.rb', line 166

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