Class: PacketFu::TCPPacket

Inherits:
Packet
  • Object
show all
Defined in:
lib/packetfu/protos/tcp.rb

Overview

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



994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
# File 'lib/packetfu/protos/tcp.rb', line 994

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.



968
969
970
# File 'lib/packetfu/protos/tcp.rb', line 968

def eth_header
  @eth_header
end

#ip_headerObject

Returns the value of attribute ip_header.



968
969
970
# File 'lib/packetfu/protos/tcp.rb', line 968

def ip_header
  @ip_header
end

#tcp_headerObject

Returns the value of attribute tcp_header.



968
969
970
# File 'lib/packetfu/protos/tcp.rb', line 968

def tcp_header
  @tcp_header
end

Class Method Details

.can_parse?(str) ⇒ Boolean

Returns:

  • (Boolean)


970
971
972
973
974
975
976
# File 'lib/packetfu/protos/tcp.rb', line 970

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.



1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
# File 'lib/packetfu/protos/tcp.rb', line 1108

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



978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
# File 'lib/packetfu/protos/tcp.rb', line 978

def read(str=nil, args={})
  raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
  @eth_header.read(str)
  @ip_header.read(str[14,str.size])
  @eth_header.body = @ip_header
  if args[:strip]
    tcp_len = str[16,2].unpack("n")[0] - 20
    @tcp_header.read(str[14+(@ip_header.ip_hlen),tcp_len])
  else
    @tcp_header.read(str[14+(@ip_header.ip_hlen),str.size])
  end
  @ip_header.body = @tcp_header
  super(args)
  self
end

#tcp_calc_flavor(str) ⇒ Object

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

windows, linux, freebsd


1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
# File 'lib/packetfu/protos/tcp.rb', line 1015

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



1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
# File 'lib/packetfu/protos/tcp.rb', line 1045

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.


1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
# File 'lib/packetfu/protos/tcp.rb', line 1091

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