Class: BetterCap::StreamLogger

Inherits:
Object
  • Object
show all
Defined in:
lib/bettercap/proxy/stream_logger.rb

Overview

Raw or http streams pretty logging.

Constant Summary collapse

@@MAX_REQ_SIZE =
50
@@CODE_COLORS =
{
  '2' => :green,
  '3' => :light_black,
  '4' => :yellow,
  '5' => :red
}
@@services =
nil
@@lock =
Mutex.new

Class Method Summary collapse

Class Method Details

.addr2s(addr, alt = nil) ⇒ Object

Search for the addr IP address inside the list of collected targets and return its compact string representation ( @see BetterCap::Target#to_s_compact ).



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/bettercap/proxy/stream_logger.rb', line 31

def self.addr2s( addr, alt = nil )
  ctx = Context.get
  # check for the local address
  return 'local' if addr == ctx.iface.ip
  # is it a known target?
  target = ctx.find_target addr, nil
  return target.to_s_compact unless target.nil?
  # fix 0.0.0.0 if alt argument was specified
  return alt if addr == '0.0.0.0' and !alt.nil?
  # fix broadcast -> *
  return '*' if addr == '255.255.255.255'
  # nothing found, return the address as it is
  addr
end

.dump_form(request) ⇒ Object



83
84
85
86
87
88
89
90
91
92
# File 'lib/bettercap/proxy/stream_logger.rb', line 83

def self.dump_form( request )
  msg = ''
  request.body.split('&').each do |v|
    name, value = v.split('=')
    name ||= ''
    value ||= ''
    msg << "  #{name.blue} : #{URI.unescape(value).yellow}\n"
  end
  msg
end

.dump_gzip(request) ⇒ Object



116
117
118
119
120
# File 'lib/bettercap/proxy/stream_logger.rb', line 116

def self.dump_gzip( request )
  msg = ''
  uncompressed = Zlib::GzipReader.new(StringIO.new(request.body)).read
  self.hexdump( uncompressed )
end

.dump_json(request) ⇒ Object



122
123
124
125
126
127
# File 'lib/bettercap/proxy/stream_logger.rb', line 122

def self.dump_json( request )
  obj = JSON.parse( request.body )
  json = JSON.pretty_unparse(obj)
  json.scan( /("[^"]+"):/ ).map { |x| json.gsub!( x[0], x[0].blue )}
  json
end

.hexdump(data, opts = {}) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/bettercap/proxy/stream_logger.rb', line 94

def self.hexdump( data, opts = {} )
  bytes     = data
  msg       = ''
  width     = IO.console.winsize[1]
  line_size = opts[:line_size] || ( width / 4 ).round
  padding   = opts[:padding] || ''

  while bytes
    line  = bytes[0,line_size]
    bytes = bytes[line_size,bytes.length]
    d     = ''

    line.each_byte {|i| d += "%02X " % i}
    d += '   ' * (line_size-line.length)
    d += ' '
    line.each_byte{|i| d += ( i.chr =~ /[[:print:]]/ ? i.chr : '.' ) }

    msg += "#{padding}#{d}\n"
  end
  msg
end

.log_http(request, response) ⇒ Object

Log a HTTP ( HTTPS if is_https is true ) stream performed by the client with the request and response most important informations.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/bettercap/proxy/stream_logger.rb', line 191

def self.log_http( request, response )
  response_s = ""
  response_s += " ( #{response.content_type} )" unless response.content_type.nil?
  request_s  = request.to_url( nil )
  code       = response.code.to_s[0]

  if @@CODE_COLORS.has_key? code
    response_s += " [#{response.code}]".send( @@CODE_COLORS[ code ] )
  else
    response_s += " [#{response.code}]"
  end

  Logger.raw "[#{self.addr2s(request.client)}] #{request.method.light_blue} #{request_s}#{response_s}"

  # Log post body if the POST sniffer is enabled.
  if Context.get.options.sniff.enabled?('POST')
    self.log_post( request )
  end

  if Context.get.options.proxies.log_response
    self.log_response( response )
  end
end

.log_post(request) ⇒ Object

If request is a complete POST request, this method will log every header and post field with its value.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/bettercap/proxy/stream_logger.rb', line 131

def self.log_post( request )
  # the packet could be incomplete
  if request.post? and !request.body.nil? and !request.body.empty?
    msg = "\n[#{'REQUEST HEADERS'.green}]\n\n"
    request.headers.each do |name,value|
      msg << "  #{name.blue} : #{value.yellow}\n"
    end
    msg << "\n[#{'REQUEST BODY'.green}]\n\n"

    case request['Content-Type']
    when /application\/x-www-form-urlencoded.*/i
      msg << self.dump_form( request )

    when /text\/plain.*/i
      msg << request.body + "\n"

    when /gzip.*/i
      msg << self.dump_gzip( request )

    when /application\/json.*/i
      msg << self.dump_json( request )

    else
      msg << self.hexdump( request.body )
    end

    Logger.raw "#{msg}\n"
  end
end

.log_raw(pkt, label, payload) ⇒ Object

Log a raw packet ( pkt ) data payload using the specified label.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/bettercap/proxy/stream_logger.rb', line 68

def self.log_raw( pkt, label, payload )
  nl    = label.include?("\n") ? "\n" : " "
  label = label.strip
  from  = self.addr2s( pkt.ip_saddr, pkt.eth2s(:src) )
  to    = self.addr2s( pkt.ip_daddr, pkt.eth2s(:dst) )

  if pkt.respond_to?('tcp_dst')
    to += ':' + self.service( :tcp, pkt.tcp_dst ).to_s.light_blue
  elsif pkt.respond_to?('udp_dst')
    to += ':' + self.service( :udp, pkt.udp_dst ).to_s.light_blue
  end

  Logger.raw( "[#{from} > #{to}] [#{label.green}]#{nl}#{payload.strip}" )
end

.log_response(response) ⇒ Object

This method will log every header and the body of response.



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/bettercap/proxy/stream_logger.rb', line 162

def self.log_response( response )
  msg = "\n[#{'RESPONSE HEADERS'.light_red}]\n\n"
  response.headers.each do |name,value|
    msg << "  #{name.blue} : #{value.to_s.yellow}\n"
  end
  msg << "\n[#{'RESPONSE BODY'.light_red}]\n\n"

  case response['Content-Type']
  when /application\/x-www-form-urlencoded.*/i
    msg << self.dump_form( response )

  when /text\/plain.*/i
    msg << response.body + "\n"

  when /gzip.*/i
    msg << self.dump_gzip( response )

  when /application\/json.*/i
    msg << self.dump_json( response )

  else
    msg << self.hexdump( response.body )
  end

  Logger.raw "#{msg}\n"
end

.service(proto, port) ⇒ Object

Given proto and port return the network service name if possible.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/bettercap/proxy/stream_logger.rb', line 47

def self.service( proto, port )
  @@lock.synchronize {
    if @@services.nil?
      @@services = { :tcp => {}, :udp => {} }
      filename = File.dirname(__FILE__) + '/../network/services'
      File.open( filename ).each do |line|
        if line =~ /([^\s]+)\s+(\d+)\/([a-z]+).*/i
          @@services[$3.to_sym][$2.to_i] = $1
        end
      end
    end
  }

  if @@services.has_key?(proto) and @@services[proto].has_key?(port)
    @@services[proto][port]
  else
    port
  end
end