#!/usr/local/bin/ruby # usage: ./dump.rb /path/to/storagedir/ -i eth0 # dumps http documents whose len > MinSize (+ octet/stream first 256k, experimental) Dir.chdir ARGV.shift MinSize = 3<<17 # 400k #14 # 48k File.umask 0 class HttpObj attr_reader :lasttime, :pkts attr_accessor :host, :rq def initialize(rq, dhost = '.') @lasttime = Time.now @host = dhost @pkts = {} @rq = rq end def <<(pkt) @lasttime = Time.now @pkts[pkt.tcp_seq] = pkt.tcp_data end def inidata data @lasttime = Time.now @pkts[0] = data end def timeout? Time.now - @lasttime > 2*60 end def dump if @pkts.map { |k, v| v.length }.inject(0) { |a, b| a+b } < MinSize if @rq =~ /\/gateway.dll/ @pkts.keys.sort.each { |k| puts ' MSN: ' + @pkts[k].inspect } end return end Dir.mkdir(@host, 0777) unless File.directory? @host Dir.chdir(@host) { fn = @rq.gsub(/[^a-zA-Z0-9.%+=-]/, '_')[0..60] rfn = fn i = 0 while File.exist?(rfn) rfn = fn + '.' + (i+=1).to_s end puts " [+] dumping to #{@host}/#{rfn} (#{@pkts.length} packets)" File.open(rfn, 'wb') { |fd| @pkts.keys.sort.each { |k| fd.write @pkts[k] } } } end end def ps(*a) a.each { |aa| puts aa.inspect[1..-2] } end track = {} def sync(track) track.sort_by { |k, v| v.lasttime }.each { |k, v| break unless v.timeout? track.delete k v.dump } end trap('INT') { puts "emerg syncing" track.each { |k, v| v.dump } exit } require 'pcap' cap = Pcap::Capture.open_live 'eth1:0', 10000, false # device, snaplen, promisc cap.setfilter 'tcp and dst port 80 or src port 80' cap.loop { |pkt| next if not data = (pkt.tcp_data rescue nil) body = false o = 0 if pkt.dport == 80 tag = "#{pkt.src}.#{pkt.sport}-#{pkt.dst}.#{pkt.dport}" if data.length > 0 and not %w[POST HEAD GET].include? data[0..5][/\w+/] if data.length < 1000 and data != "\0\0\0\0" File.open('sniffhttp.postdata', 'a') { |fd| fd.puts tag + ' ' + data.inspect } p data end next end meth = nil data.each { |l| o += l.size l.chomp! case l when /^(GET|POST|HEAD)\s+(\S+)/ meth = $1 track[tag].dump if track[tag] rq = $2 track[tag] = HttpObj.new rq, pkt.dst.to_s unless rq =~ /\/tor\/.*\.z$/ sync track ps l when /^host: (.*)/i track[tag].host = $1 if track[tag] ps l when /^(cookie|user-agent): /i ps l when /^referer: /i ps l if track[tag] File.open('sniffhttp.log', 'a') { |fd| fd.puts "#{tag} #{Time.now.to_i} #{meth} #{track[tag].host.inspect} #{track[tag].rq.inspect} #{l.inspect}" } end when /^Authorization: Basic /i File.open('sniffhttp.auth', 'a') { |fd| fd.puts tag + ' ' + "AUTH #{track[tag].host.inspect} #{track[tag].rq.inspect} #{l.inspect}" } when /^$/ if data[o..-1].empty? File.open('sniffhttp.postdata', 'a') { |fd| fd.puts tag + ' ' + "POST #{track[tag].host.inspect} #{track[tag].rq.inspect}" } if meth == 'POST' puts else File.open('sniffhttp.postdata', 'a') { |fd| fd.puts tag + ' ' + "POST #{track[tag].host.inspect} #{track[tag].rq.inspect}" fd.puts tag + ' ' + data[o..-1].inspect } ps data[o..-1] puts end break end } elsif pkt.sport == 80 tag = "#{pkt.dst}.#{pkt.dport}-#{pkt.src}.#{pkt.sport}" if track[tag] and not track[tag].pkts.empty? track[tag] << pkt next end wasa = false clen = data.length data.each { |l| o += l.size l.chomp! case l when /^HTTP\// wasa = true ps l when /^content-length: (\d+)/i track.delete tag if (clen = $1.to_i) < MinSize and track[tag] and track[tag].rq !~ /\/gateway.dll/ clen = 1<<28 if clen > (1<<28) ps l when /^content-type: (.*)/i track.delete tag if $1 =~ /^text\/|javascript/i ps l when /^(transfer-encoding|content-encoding|set-cookie): /i ps l when /^$/ track[tag].pkts[0] = data[o, clen] || '' if track[tag] puts if wasa break end } end }