├── network ├── arpmonitor.sh ├── arpmac-to-ipsg-bindings.rb ├── arptable └── mactable ├── dns.rb ├── README.md ├── outgoing.rb ├── incoming.rb └── exec-notify.c /network/arpmonitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | while [ true ]; do 4 | for i in 10.0.108.31 10.0.108.21; do 5 | echo > /tmp/arpmon 6 | ssh fwadm@$i "/sbin/arp -n | grep ether | grep -v 10.0.108." >> /tmp/arpmon 7 | done 8 | 9 | diff /tmp/arpmon-old /tmp/arpmon 10 | mv /tmp/arpmon /tmp/arpmon-old 11 | 12 | sleep 30 13 | done 14 | -------------------------------------------------------------------------------- /network/arpmac-to-ipsg-bindings.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require "pp" 3 | 4 | arp = {} 5 | 6 | File.readlines("arptable").each do |a| 7 | next unless a =~ /^([\d\.]+)\s+ether\s+([\w:]+)\s+/ 8 | ip = $1 9 | mac = $2 10 | arp[mac.gsub(/:/, "")] = ip 11 | end 12 | 13 | #pp arp 14 | 15 | File.readlines("mactable").each do |m| 16 | next unless m =~ /^(\d+)\s+([\w\.]+)\s+\w+\s+\d+\s+(\w+)/ 17 | vlan = $1 18 | mac = $2 19 | mac2 = mac.gsub(/\./, "") 20 | int = $3 21 | 22 | next unless ["1086", "1087", "1083", "1088", "1084"].include?(vlan) 23 | 24 | if arp[mac2] 25 | puts "ip source binding %s %s vlan %s interface %s" % [arp[mac2], mac, vlan, int] 26 | else 27 | $stderr.puts "unmatched mac: #{m}" 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /dns.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | $stdout.sync = true 4 | 5 | @trap = false 6 | trap("SIGINT") do 7 | File.open("dnsseen", "w") { |f| Marshal.dump(@seen, f) } 8 | @trap = true 9 | end 10 | 11 | @seen = {} 12 | @seen = File.open("dnsseen") { |f| Marshal.load(f) } if File.exists?("dnsseen") 13 | 14 | $stdin.each do |l| 15 | break if @trap 16 | 17 | next unless l =~ /IP (\d+\.\d+\.\d+\.\d+)\.\d+ \> (\d+\.\d+\.\d+\.\d+)\.\d+.*(\w+\?) ([^\s]+)/ 18 | src = $1 19 | dst = $2 20 | qtype = $3 21 | q = $4 22 | 23 | key = [src, dst, qtype, q] 24 | 25 | if @seen[key] 26 | @seen[key] += 1 27 | else 28 | @seen[key] = 1 29 | end 30 | 31 | cseen = @seen[key] 32 | 33 | printf "%-5.5s %s %s %s %s\n", cseen, src, dst, qtype, q if cseen < 5 34 | end 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | My LS13 scripts 2 | =============== 3 | 4 | These were used during the LS13 exercise to pretty much own the red team and their callbacks to home from vulnerable machines. Code is horrible - copypasted together in 5 minutes from other scripts I had laying around. But it worked for us for 16 hours. It might not work for you and it will most definitely not work in real life without serious modifications. 5 | 6 | ### incoming.rb / outgoing.rb 7 | 8 | Used these to monitor all incoming and outgoing traffic and pick out connections that haven't been seen before. Outgoing based on src/dst/dstport tuple and incoming based on payload only (to get rid of all noise and scoring connections). 9 | 10 | These enabled us to very quickly spot malicious downloads, callbacks to red team machines etc. Scripts were fed from SPAN destination that aggregated all ingress and egress traffic from all our machines. 11 | 12 | For some technical reasons I ran these with external tcpdump but there is no reason why it shouldn't work with Pcap::Capture.open_live 13 | 14 | Usage: tcpdump -U -i eth2 -nn -w - tcp or udp | ./outgoing.rb 15 | 16 | NB! No IPv6 support (2013 - shame on me). I just used additional tcpdump to monitor all IPv6 traffic in case red team tries to use it between our machines, unfortunately they didn't. 17 | 18 | ### dns.rb 19 | 20 | This is similar to previous but for monitoring all DNS lookups. I didn't have enough time to improve it during the exercise and it wasn't put into good use. 21 | 22 | ### exec-notify.c 23 | 24 | I created this statically linked binary (for maximum compatibility) for easy process execution logging to syslog (which me immediately sent to trusted central collector) 25 | 26 | ### network/arpmac-to-ipsg-bindings.rb 27 | 28 | For quickly creating IPSG static bindings into switches. Enables you to lock down your network against MAC and ARP spoofing in case red team gains access to any of your machines. 29 | 30 | -------------------------------------------------------------------------------- /network/arptable: -------------------------------------------------------------------------------- 1 | 10.8.3.131 ether 00:50:56:89:68:2c C br0 2 | 10.8.3.5 ether 00:50:56:89:64:fd C br0 3 | 10.8.3.6 ether 00:50:56:89:64:bb C br0 4 | 10.8.6.2 ether 00:50:56:89:67:49 C br1 5 | 10.8.3.2 ether 00:50:56:89:82:be C br0 6 | 10.8.3.133 ether 00:50:56:89:68:32 C br0 7 | 10.8.3.135 ether 00:50:56:89:65:c2 C br0 8 | 10.8.6.4 ether 00:50:56:89:68:9e C br1 9 | 10.8.3.134 ether 00:50:56:89:67:9b C br0 10 | 10.8.5.2 ether 00:50:56:89:81:a7 C eth1 11 | 10.8.3.132 ether 00:50:56:89:65:86 C br0 12 | 10.8.108.5 ether 00:50:56:89:68:c2 C br1 13 | 10.8.108.7 ether 00:50:56:89:67:87 C br1 14 | 10.8.104.141 ether 00:50:56:89:67:f0 C br0 15 | 10.8.104.143 ether 00:50:56:89:68:0a C br0 16 | 10.8.104.3 ether 00:50:56:89:68:36 C br0 17 | 10.8.102.2 ether 00:50:56:89:81:71 C eth1 18 | 10.8.108.6 ether 00:50:56:89:68:6c C br1 19 | 10.8.108.4 ether 00:50:56:89:68:bc C br1 20 | 10.8.104.142 ether 00:50:56:89:65:9a C br0 21 | 10.8.108.8 ether 00:50:56:89:68:7a C br1 22 | 10.8.104.2 ether 00:50:56:89:82:d2 C br0 23 | 10.8.104.145 ether 00:50:56:89:65:ca C br0 24 | 10.8.104.144 ether 00:50:56:89:67:a3 C br0 25 | 10.8.108.2 ether 00:50:56:89:67:69 C br1 26 | 10.0.108.194 ether 00:1f:9e:98:13:69 C br0 27 | -------------------------------------------------------------------------------- /network/mactable: -------------------------------------------------------------------------------- 1 | 1080 0050.5689.81a8 secure 0 Veth4 3 2 | 1080 0050.5689.68dd dynamic 17 Po1 3 3 | 1081 0050.5689.81a9 secure 0 Veth5 3 4 | 1081 0050.5689.68f0 dynamic 6 Po1 3 5 | 1082 0050.5689.66dc secure 0 Veth11 3 6 | 1082 0050.5689.8171 dynamic 23 Po1 3 7 | 1083 0050.5689.6719 static 0 Veth13 3 8 | 1083 0050.5689.64bb secure 0 Veth29 3 9 | 1083 0050.5689.64fd secure 0 Veth26 3 10 | 1083 0050.5689.6586 secure 0 Veth35 3 11 | 1083 0050.5689.65c2 secure 0 Veth32 3 12 | 1083 0050.5689.6711 secure 0 Veth13 3 13 | 1083 0050.5689.679b secure 0 Veth42 3 14 | 1083 0050.5689.682c secure 0 Veth48 3 15 | 1083 0050.5689.6832 secure 0 Veth52 3 16 | 1083 0050.5689.6858 secure 0 Veth56 3 17 | 1083 0050.5689.82be secure 0 Veth23 3 18 | 1084 0050.5689.66df static 0 Veth8 3 19 | 1084 0050.5689.659a secure 0 Veth37 3 20 | 1084 0050.5689.65ca secure 0 Veth34 3 21 | 1084 0050.5689.66db secure 0 Veth8 3 22 | 1084 0050.5689.67a3 secure 0 Veth44 3 23 | 1084 0050.5689.67f0 secure 0 Veth46 3 24 | 1084 0050.5689.680a secure 0 Veth50 3 25 | 1084 0050.5689.6836 secure 0 Veth53 3 26 | 1084 0050.5689.82d2 secure 0 Veth25 3 27 | 1085 0050.5689.6714 secure 0 Veth16 3 28 | 1085 0050.5689.81a7 secure 0 Veth3 3 29 | 1086 0050.5689.6716 secure 0 Veth15 3 30 | 1086 0050.5689.6749 secure 0 Veth19 3 31 | 1086 0050.5689.689e secure 0 Veth66 3 32 | 1087 0050.5689.6717 secure 0 Veth14 3 33 | 1088 0050.5689.66dd secure 0 Veth10 3 34 | 1088 0050.5689.6769 secure 0 Veth21 3 35 | 1088 0050.5689.6787 secure 0 Veth40 3 36 | 1088 0050.5689.686c secure 0 Veth58 3 37 | 1088 0050.5689.687a secure 0 Veth60 3 38 | 1088 0050.5689.68bc secure 0 Veth62 3 39 | 1088 0050.5689.68c2 secure 0 Veth63 3 40 | 1089 0050.5689.66de static 0 Veth9 3 41 | 1508 0050.5689.81aa static 0 Veth6 3 42 | 1508 0050.5689.81ab static 0 Veth7 3 43 | 1551 0050.5689.68df static 0 Veth68 3 44 | 1551 0050.5689.68ed dynamic 221 Po1 3 45 | 1551 0050.5689.8098 dynamic 12 Po1 3 46 | -------------------------------------------------------------------------------- /outgoing.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # MagicIDS 9.0RC1!! 4 | # 5 | # --- Friendly reminder for red team --- 6 | # If you steal this, beware, this has been copypasted together in 20 minutes 7 | # from several other scripts I had created before ;) 8 | 9 | require 'pcap' 10 | 11 | RE_HTTP = Regexp.new('^(GET|POST|PUT|OPTIONS|HEAD) .* HTTP/\d\.\d') 12 | RE_SSH = Regexp.new('^SSH-') 13 | RE_SMTP = Regexp.new('^220 .*SMTP.*\n$') 14 | 15 | @tcpflows = {} 16 | def tcpflow(pkt) 17 | fkey = [pkt.src.to_i, pkt.sport, pkt.dst.to_i, pkt.dport] 18 | rkey = [pkt.dst.to_i, pkt.dport, pkt.src.to_i, pkt.sport] 19 | 20 | if pkt.tcp_flags == 2 21 | @tcpflows[fkey] = pkt 22 | @tcpflows[rkey] = pkt 23 | return 24 | end 25 | 26 | return unless @tcpflows.include?(fkey) 27 | return unless pkt.tcp_data_len > 0 28 | 29 | #proto = if pkt.tcp_data.match(RE_HTTP) then :http 30 | #elsif pkt.tcp_data.match(RE_SSH) then :ssh 31 | #elsif pkt.tcp_data.match(RE_SMTP) then :smtp 32 | #end 33 | 34 | magic(:tcp, @tcpflows[fkey].src.to_s, @tcpflows[fkey].dst.to_s, @tcpflows[fkey].dport, nil, pkt.tcp_data) 35 | 36 | @tcpflows.delete(fkey) 37 | @tcpflows.delete(rkey) 38 | end 39 | 40 | @udpflows = {} 41 | @last_cleanup = Time.now 42 | def udpflow(pkt) 43 | ts = Time.now 44 | 45 | # clean up timeout'ed flows 46 | # we do this first because we are not using another thread for this 47 | # and this might lead to stale flows if there is no traffic for a while 48 | if ts - @last_cleanup > 15 49 | @last_cleanup = ts 50 | @udpflows.delete_if { |k,v| ts - v > 60 } 51 | end 52 | 53 | # UDP fragments don't have checksum field and no ports either 54 | # these can't start new flow so skip them 55 | return unless pkt.respond_to?("udp_sum") 56 | return unless pkt.udp_len > 0 57 | 58 | fkey = [pkt.src.to_i, pkt.sport, pkt.dst.to_i, pkt.dport] 59 | rkey = [pkt.dst.to_i, pkt.dport, pkt.src.to_i, pkt.sport] 60 | 61 | unless @udpflows.include?(fkey) 62 | magic(:udp, pkt.src.to_s, pkt.dst.to_s, pkt.dport, nil, pkt.udp_data) 63 | end 64 | 65 | @udpflows[fkey] = ts 66 | @udpflows[rkey] = ts 67 | 68 | end 69 | 70 | def getcolor(fg = "default", bg = "default", effect = "none") 71 | fgcolors = { 72 | "black" => "30;", 73 | "red" => "31;", 74 | "green" => "32;", 75 | "yellow" => "33;", 76 | "blue" => "34;", 77 | "magenta" => "35;", 78 | "cyan" => "36;", 79 | "white" => "37;", 80 | "default" => "39;" 81 | } 82 | 83 | bgcolors = { 84 | "black" => "40", 85 | "red" => "41", 86 | "green" => "42", 87 | "yellow" => "43", 88 | "blue" => "44", 89 | "magenta" => "45", 90 | "cyan" => "46", 91 | "white" => "47", 92 | "default" => "49" 93 | } 94 | retval = "" 95 | retval << "\033[" 96 | retval << "0;" if effect == "none" 97 | retval << "1;" if effect =~ /bright/ 98 | retval << "4;" if effect =~ /underline/ 99 | retval << "5;" if effect =~ /blink/ 100 | retval << (fgcolors[fg] || fgcolors["default"]) 101 | retval << (bgcolors[bg] || bgcolors["default"]) 102 | retval << "m" 103 | retval 104 | end 105 | 106 | def paint(kw, color, bgcolor = "default") 107 | r = "" 108 | r << getcolor(color, bgcolor) 109 | r << kw 110 | r << getcolor 111 | end 112 | 113 | def ippaint(ip) 114 | color = case ip 115 | when /^10\.8\.(6|108)\./ then "yellow" 116 | when /^10\.8\.(7|109)\./ then "blue" 117 | when /^10\.8\.(3|104)\./ then "green" 118 | when /^10\./ then "red" 119 | else "magenta" 120 | end 121 | paint(ip, color) 122 | end 123 | 124 | def beenseen(proto, src, dst, dport) 125 | key = [proto, src, dst, dport] 126 | 127 | if @seen[key] 128 | @seen[key] += 1 129 | else 130 | @seen[key] = 1 131 | end 132 | end 133 | 134 | OURBC = /^10\.8\.(6|7|3|108|109|100)\.255$/ 135 | OUR = /^10\.8\.(6|7|3|108|109|104)\./ 136 | def magic(proto, src, dst, dport, dpi, data) 137 | 138 | @history.puts "%s %s %s %s %s %s" % [Time.now.to_s, proto, src, dst, dport, data ? data.gsub(/[^[:print:]]/, ".") : nil] 139 | 140 | if src =~ OUR && data && data.gsub(/[^[:print:]]/, ".") =~ /score\.html.*Wget/ && !@scoring.include?(dst) 141 | @scoring << dst 142 | puts "New scoring dst #{dst}" 143 | elsif @scoring.include?(dst) 144 | return 145 | end 146 | 147 | f = false 148 | f = true if proto == :udp && dst == "10.50.51.108" && dport == 514 149 | f = true if proto == :udp && (dst == "10.0.0.2" || dst == "10.8.3.2") && dport == 123 150 | 151 | f = true if proto == :tcp && dst == "10.0.173.34" # scoring 152 | f = true if proto == :tcp && dst == "10.0.128.2" # scoring 153 | f = true if proto == :tcp && dst == "10.0.128.3" # scoring 154 | f = true if proto == :tcp && dst == "10.0.135.205" # scoring 155 | f = true if proto == :tcp && dst == "10.0.130.243" # scoring 156 | 157 | f = true if proto == :udp && src == "10.8.6.2" && dst == "10.0.0.2" && dport == 53 # root dns 158 | f = true if proto == :udp && src == "10.8.108.2" && dst == "10.0.0.2" && dport == 53 # root dns 159 | f = true if proto == :udp && (dst == "224.0.0.252" || dst == "224.0.0.251") && (dport == 53 || dport == 5355) # wpad & shit 160 | f = true if proto == :udp && src =~ OUR && (dst == "10.8.6.2" || dst == "10.8.108.2" || dst == "10.8.3.2" || dst == "10.8.104.2") && dport == 53 # meie recursion 161 | f = true if proto == :tcp && src == "10.8.108.5" && dst == "10.8.108.4" && dport == 3306 162 | #f = true if proto == :udp && dst =~ OURBC && (dport == 137 || dport == 138) 163 | #f = true if proto == :udp && dst =~ OUR && src =~ OUR && dport == 53 164 | f = true if proto == :udp && (dport == 137 || dport == 138) 165 | #f = true if dst !~ OUR || src !~ OUR 166 | f = true if src !~ OUR 167 | #f = true if src 168 | 169 | #return if f 170 | 171 | #return if src =~ OUR and 172 | return if src !~ OUR 173 | seen = beenseen(proto, src, dst, dport) 174 | return if seen > 15 175 | 176 | printf "%s %s %s %s %s %-140.140s\n", 177 | (seen == 1 ? paint(seen.to_s.ljust(6), "default", "red") : seen.to_s.ljust(6)), 178 | proto.to_s.ljust(3), 179 | dport.to_s.ljust(5), 180 | ippaint(src.ljust(16)), 181 | ippaint(dst.ljust(16)), 182 | data ? data.gsub(/[^[:print:]]/, ".") : nil 183 | 184 | end 185 | 186 | @history = File.open("history.#{Time.now.to_i}", "w") 187 | 188 | @trap = false 189 | trap("SIGINT") do 190 | File.open("seen", "w") { |f| Marshal.dump(@seen, f) } 191 | @trap = true 192 | end 193 | 194 | @seen = {} 195 | @seen = File.open("seen") { |f| Marshal.load(f) } if File.exists?("seen") 196 | 197 | @scoring = [] 198 | 199 | cap = Pcap::Capture.open_offline("-") 200 | cap.loop(0) do |pkt| 201 | break if @trap 202 | if pkt.tcp? 203 | tcpflow(pkt) 204 | elsif pkt.udp? 205 | udpflow(pkt) 206 | end 207 | end 208 | cap.close 209 | @history.close 210 | -------------------------------------------------------------------------------- /incoming.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # 3 | # MagicIDS 9.0RC1!! 4 | # 5 | # --- Friendly reminder for red team --- 6 | # If you steal this, beware, this has been copypasted together in 20 minutes 7 | # from several other scripts I had created before ;) 8 | 9 | require 'pcap' 10 | 11 | RE_HTTP = Regexp.new('^(GET|POST|PUT|OPTIONS|HEAD) .* HTTP/\d\.\d') 12 | RE_SSH = Regexp.new('^SSH-') 13 | RE_SMTP = Regexp.new('^220 .*SMTP.*\n$') 14 | 15 | @tcpflows = {} 16 | def tcpflow(pkt) 17 | fkey = [pkt.src.to_i, pkt.sport, pkt.dst.to_i, pkt.dport] 18 | rkey = [pkt.dst.to_i, pkt.dport, pkt.src.to_i, pkt.sport] 19 | 20 | if pkt.tcp_flags == 2 21 | @tcpflows[fkey] = pkt 22 | @tcpflows[rkey] = pkt 23 | return 24 | end 25 | 26 | return unless @tcpflows.include?(fkey) 27 | return unless pkt.tcp_data_len > 0 28 | 29 | #proto = if pkt.tcp_data.match(RE_HTTP) then :http 30 | #elsif pkt.tcp_data.match(RE_SSH) then :ssh 31 | #elsif pkt.tcp_data.match(RE_SMTP) then :smtp 32 | #end 33 | 34 | magic(:tcp, @tcpflows[fkey].src.to_s, @tcpflows[fkey].dst.to_s, @tcpflows[fkey].dport, nil, pkt.tcp_data) 35 | 36 | @tcpflows.delete(fkey) 37 | @tcpflows.delete(rkey) 38 | end 39 | 40 | @udpflows = {} 41 | @last_cleanup = Time.now 42 | def udpflow(pkt) 43 | ts = Time.now 44 | 45 | # clean up timeout'ed flows 46 | # we do this first because we are not using another thread for this 47 | # and this might lead to stale flows if there is no traffic for a while 48 | if ts - @last_cleanup > 15 49 | @last_cleanup = ts 50 | @udpflows.delete_if { |k,v| ts - v > 60 } 51 | end 52 | 53 | # UDP fragments don't have checksum field and no ports either 54 | # these can't start new flow so skip them 55 | return unless pkt.respond_to?("udp_sum") 56 | return unless pkt.udp_len > 0 57 | 58 | fkey = [pkt.src.to_i, pkt.sport, pkt.dst.to_i, pkt.dport] 59 | rkey = [pkt.dst.to_i, pkt.dport, pkt.src.to_i, pkt.sport] 60 | 61 | unless @udpflows.include?(fkey) 62 | magic(:udp, pkt.src.to_s, pkt.dst.to_s, pkt.dport, nil, pkt.udp_data) 63 | end 64 | 65 | @udpflows[fkey] = ts 66 | @udpflows[rkey] = ts 67 | 68 | end 69 | 70 | def getcolor(fg = "default", bg = "default", effect = "none") 71 | fgcolors = { 72 | "black" => "30;", 73 | "red" => "31;", 74 | "green" => "32;", 75 | "yellow" => "33;", 76 | "blue" => "34;", 77 | "magenta" => "35;", 78 | "cyan" => "36;", 79 | "white" => "37;", 80 | "default" => "39;" 81 | } 82 | 83 | bgcolors = { 84 | "black" => "40", 85 | "red" => "41", 86 | "green" => "42", 87 | "yellow" => "43", 88 | "blue" => "44", 89 | "magenta" => "45", 90 | "cyan" => "46", 91 | "white" => "47", 92 | "default" => "49" 93 | } 94 | retval = "" 95 | retval << "\033[" 96 | retval << "0;" if effect == "none" 97 | retval << "1;" if effect =~ /bright/ 98 | retval << "4;" if effect =~ /underline/ 99 | retval << "5;" if effect =~ /blink/ 100 | retval << (fgcolors[fg] || fgcolors["default"]) 101 | retval << (bgcolors[bg] || bgcolors["default"]) 102 | retval << "m" 103 | retval 104 | end 105 | 106 | def paint(kw, color, bgcolor = "default") 107 | r = "" 108 | r << getcolor(color, bgcolor) 109 | r << kw 110 | r << getcolor 111 | end 112 | 113 | def ippaint(ip) 114 | color = case ip 115 | when /^10\.8\.(6|108)\./ then "yellow" 116 | when /^10\.8\.(7|109)\./ then "blue" 117 | when /^10\.8\.(3|104)\./ then "green" 118 | when /^10\./ then "red" 119 | else "magenta" 120 | end 121 | paint(ip, color) 122 | end 123 | 124 | def beenseen(proto, src, dst, dport, data) 125 | #key = [proto, src, dst, dport, data ? data.gsub(/[^[:print:]]/, ".") : nil] 126 | key = data ? data.gsub(/[^[:print:]]/, ".")[0..140] : nil 127 | 128 | if @seen[key] 129 | @seen[key] += 1 130 | else 131 | @seen[key] = 1 132 | end 133 | end 134 | 135 | OURBC = /^10\.8\.(6|7|3|108|109|100)\.255$/ 136 | OUR = /^10\.8\.(6|7|3|108|109|104)\./ 137 | def magic(proto, src, dst, dport, dpi, data) 138 | 139 | @history.puts "%s %s %s %s %s %s" % [Time.now.to_s, proto, src, dst, dport, data ? data.gsub(/[^[:print:]]/, ".") : nil] 140 | 141 | if src =~ OUR && data && data.gsub(/[^[:print:]]/, ".") =~ /score\.html.*Wget/ && !@scoring.include?(dst) 142 | @scoring << src 143 | puts "New scoring src #{src}" 144 | elsif @scoring.include?(src) 145 | return 146 | end 147 | 148 | f = false 149 | f = true if proto == :udp && dst == "10.50.51.108" && dport == 514 150 | f = true if proto == :udp && (dst == "10.0.0.2" || dst == "10.8.3.2") && dport == 123 151 | 152 | f = true if proto == :tcp && dst == "10.0.173.34" # scoring 153 | f = true if proto == :tcp && dst == "10.0.128.2" # scoring 154 | f = true if proto == :tcp && dst == "10.0.128.3" # scoring 155 | f = true if proto == :tcp && dst == "10.0.135.205" # scoring 156 | f = true if proto == :tcp && dst == "10.0.130.243" # scoring 157 | 158 | f = true if proto == :udp && src == "10.8.6.2" && dst == "10.0.0.2" && dport == 53 # root dns 159 | f = true if proto == :udp && src == "10.8.108.2" && dst == "10.0.0.2" && dport == 53 # root dns 160 | f = true if proto == :udp && (dst == "224.0.0.252" || dst == "224.0.0.251") && (dport == 53 || dport == 5355) # wpad & shit 161 | f = true if proto == :udp && src =~ OUR && (dst == "10.8.6.2" || dst == "10.8.108.2" || dst == "10.8.3.2" || dst == "10.8.104.2") && dport == 53 # meie recursion 162 | f = true if proto == :tcp && src == "10.8.108.5" && dst == "10.8.108.4" && dport == 3306 163 | #f = true if proto == :udp && dst =~ OURBC && (dport == 137 || dport == 138) 164 | #f = true if proto == :udp && dst =~ OUR && src =~ OUR && dport == 53 165 | f = true if proto == :udp && (dport == 137 || dport == 138) 166 | #f = true if dst !~ OUR || src !~ OUR 167 | f = true if src !~ OUR 168 | #f = true if src 169 | 170 | #return if f 171 | 172 | #return if src =~ OUR and 173 | return if ["10.0.128.120", "10.0.191.241"].include?(src) 174 | return if src =~ OUR 175 | seen = beenseen(proto, src, dst, dport, data) 176 | return if seen > 15 177 | 178 | printf "%s %s %s %s %s %-140.140s\n", 179 | (seen == 1 ? paint(seen.to_s.ljust(6), "default", "red") : seen.to_s.ljust(6)), 180 | proto.to_s.ljust(3), 181 | dport.to_s.ljust(5), 182 | ippaint(src.ljust(16)), 183 | ippaint(dst.ljust(16)), 184 | data ? data.gsub(/[^[:print:]]/, ".") : nil 185 | 186 | end 187 | 188 | @history = File.open("inhistory.#{Time.now.to_i}", "w") 189 | 190 | @trap = false 191 | trap("SIGINT") do 192 | File.open("inseen", "w") { |f| Marshal.dump(@seen, f) } 193 | @trap = true 194 | end 195 | 196 | @seen = {} 197 | @seen = File.open("inseen") { |f| Marshal.load(f) } if File.exists?("inseen") 198 | 199 | @scoring = [] 200 | 201 | cap = Pcap::Capture.open_offline("-") 202 | cap.loop(0) do |pkt| 203 | break if @trap 204 | if pkt.tcp? 205 | tcpflow(pkt) 206 | elsif pkt.udp? 207 | udpflow(pkt) 208 | end 209 | end 210 | cap.close 211 | @history.close 212 | -------------------------------------------------------------------------------- /exec-notify.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Modified by Tarko Tikan to be totally silent and send (only relevant) things to syslog 3 | * Compile using "cc -Wall -ansi -pedantic -std=c99 exec-notify.c -static" for 4 | * statically linked binary 5 | */ 6 | /* exec-notify, so you can watch your acrobat reader or vim executing "bash -c" 7 | * commands ;-) 8 | * Requires some 2.6.x Linux kernel with proc connector enabled. 9 | * 10 | * $ cc -Wall -ansi -pedantic -std=c99 exec-notify.c 11 | * 12 | * (C) 2007-2010 Sebastian Krahmer original netlink handling 13 | * stolen from an proc-connector example, copyright folows: 14 | */ 15 | /* 16 | * 17 | * Copyright (C) Matt Helsley, IBM Corp. 2005 18 | * Derived from fcctl.c by Guillaume Thouvenin 19 | * Original copyright notice follows: 20 | * 21 | * Copyright (C) 2005 BULL SA. 22 | * Written by Guillaume Thouvenin 23 | * 24 | * This program is free software; you can redistribute it and/or modify 25 | * it under the terms of the GNU General Public License as published by 26 | * the Free Software Foundation; either version 2 of the License, or 27 | * (at your option) any later version. 28 | * 29 | * This program is distributed in the hope that it will be useful, 30 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | * GNU General Public License for more details. 33 | * 34 | * You should have received a copy of the GNU General Public License 35 | * along with this program; if not, write to the Free Software 36 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 37 | */ 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include 46 | #include 47 | 48 | #include 49 | #include 50 | #include 51 | 52 | #include 53 | 54 | #define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \ 55 | sizeof(enum proc_cn_mcast_op))) 56 | #define RECV_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \ 57 | sizeof(struct proc_event))) 58 | 59 | #define SEND_MESSAGE_SIZE (NLMSG_SPACE(SEND_MESSAGE_LEN)) 60 | #define RECV_MESSAGE_SIZE (NLMSG_SPACE(RECV_MESSAGE_LEN)) 61 | 62 | #define max(x,y) ((y)<(x)?(x):(y)) 63 | #define min(x,y) ((y)>(x)?(x):(y)) 64 | 65 | #define BUFF_SIZE (max(max(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE), 1024)) 66 | #define MIN_RECV_SIZE (min(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE)) 67 | 68 | #define PROC_CN_MCAST_LISTEN (1) 69 | #define PROC_CN_MCAST_IGNORE (2) 70 | 71 | 72 | void handle_msg (struct cn_msg *cn_hdr) 73 | { 74 | char cmdline[1024], fname1[1024], ids[1024], fname2[1024], buf[1024]; 75 | int r = 0, fd, i; 76 | FILE *f = NULL; 77 | struct proc_event *ev = (struct proc_event *)cn_hdr->data; 78 | 79 | snprintf(fname1, sizeof(fname1), "/proc/%d/status", ev->event_data.exec.process_pid); 80 | snprintf(fname2, sizeof(fname2), "/proc/%d/cmdline", ev->event_data.exec.process_pid); 81 | 82 | f = fopen(fname1, "r"); 83 | fd = open(fname2, O_RDONLY); 84 | 85 | memset(&cmdline, 0, sizeof(cmdline)); 86 | memset(&ids, 0, sizeof(ids)); 87 | 88 | while (f && fgets(buf, sizeof(buf), f) != NULL) { 89 | if (strstr(buf, "Uid")) { 90 | strtok(buf, "\n"); 91 | snprintf(ids, sizeof(ids), "%s", buf); 92 | } 93 | } 94 | if (f) 95 | fclose(f); 96 | 97 | if (fd > 0) { 98 | r = read(fd, cmdline, sizeof(cmdline)); 99 | close(fd); 100 | 101 | for (i = 0; r > 0 && i < r; ++i) { 102 | if (cmdline[i] == 0) 103 | cmdline[i] = ' '; 104 | } 105 | } 106 | 107 | switch(ev->what){ 108 | /* case PROC_EVENT_FORK: 109 | printf("FORK:parent(pid,tgid)=%d,%d\tchild(pid,tgid)=%d,%d\t[%s]\n", 110 | ev->event_data.fork.parent_pid, 111 | ev->event_data.fork.parent_tgid, 112 | ev->event_data.fork.child_pid, 113 | ev->event_data.fork.child_tgid, cmdline); 114 | break;*/ 115 | case PROC_EVENT_EXEC: 116 | syslog (LOG_NOTICE, "%s [pid %d]\n", 117 | cmdline, 118 | //ids, 119 | ev->event_data.exec.process_pid 120 | //ev->event_data.exec.process_tgid 121 | ); 122 | break; 123 | /* case PROC_EVENT_EXIT: 124 | printf("EXIT:pid=%d,%d\texit code=%d\n", 125 | ev->event_data.exit.process_pid, 126 | ev->event_data.exit.process_tgid, 127 | ev->event_data.exit.exit_code); 128 | break;*/ 129 | /* case PROC_EVENT_UID: 130 | printf("UID:pid=%d,%d ruid=%d,euid=%d\n", 131 | ev->event_data.id.process_pid, ev->event_data.id.process_tgid, 132 | ev->event_data.id.r.ruid, ev->event_data.id.e.euid); 133 | break;*/ 134 | default: 135 | break; 136 | } 137 | } 138 | 139 | 140 | int main(int argc, char **argv) 141 | { 142 | int sk_nl; 143 | int err; 144 | struct sockaddr_nl my_nla, kern_nla, from_nla; 145 | socklen_t from_nla_len; 146 | char buff[BUFF_SIZE]; 147 | int rc = -1; 148 | struct nlmsghdr *nl_hdr; 149 | struct cn_msg *cn_hdr; 150 | enum proc_cn_mcast_op *mcop_msg; 151 | size_t recv_len = 0; 152 | if (getuid() != 0) { 153 | printf("Must run as root\n"); 154 | return 0; 155 | } 156 | if (argc != 1) 157 | return 0; 158 | 159 | setvbuf(stdout, NULL, _IONBF, 0); 160 | 161 | /* 162 | * Create an endpoint for communication. Use the kernel user 163 | * interface device (PF_NETLINK) which is a datagram oriented 164 | * service (SOCK_DGRAM). The protocol used is the connector 165 | * protocol (NETLINK_CONNECTOR) 166 | */ 167 | sk_nl = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); 168 | if (sk_nl == -1) { 169 | printf("socket sk_nl error"); 170 | return rc; 171 | } 172 | my_nla.nl_family = AF_NETLINK; 173 | my_nla.nl_groups = CN_IDX_PROC; 174 | my_nla.nl_pid = getpid(); 175 | 176 | kern_nla.nl_family = AF_NETLINK; 177 | kern_nla.nl_groups = CN_IDX_PROC; 178 | kern_nla.nl_pid = 1; 179 | 180 | err = bind(sk_nl, (struct sockaddr *)&my_nla, sizeof(my_nla)); 181 | if (err == -1) { 182 | printf("binding sk_nl error"); 183 | goto close_and_exit; 184 | } 185 | nl_hdr = (struct nlmsghdr *)buff; 186 | cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr); 187 | mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0]; 188 | 189 | //printf("sending proc connector: PROC_CN_MCAST_LISTEN... "); 190 | memset(buff, 0, sizeof(buff)); 191 | *mcop_msg = PROC_CN_MCAST_LISTEN; 192 | 193 | /* fill the netlink header */ 194 | nl_hdr->nlmsg_len = SEND_MESSAGE_LEN; 195 | nl_hdr->nlmsg_type = NLMSG_DONE; 196 | nl_hdr->nlmsg_flags = 0; 197 | nl_hdr->nlmsg_seq = 0; 198 | nl_hdr->nlmsg_pid = getpid(); 199 | /* fill the connector header */ 200 | cn_hdr->id.idx = CN_IDX_PROC; 201 | cn_hdr->id.val = CN_VAL_PROC; 202 | cn_hdr->seq = 0; 203 | cn_hdr->ack = 0; 204 | cn_hdr->len = sizeof(enum proc_cn_mcast_op); 205 | if (send(sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) { 206 | printf("failed to send proc connector mcast ctl op!\n"); 207 | goto close_and_exit; 208 | } 209 | 210 | //printf("sent\n"); 211 | if (*mcop_msg == PROC_CN_MCAST_IGNORE) { 212 | rc = 0; 213 | goto close_and_exit; 214 | } 215 | //printf("Reading process events from proc connector.\n" 216 | // "Hit Ctrl-C to exit\n"); 217 | openlog ("psexec", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 218 | 219 | for(memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla); 220 | ; memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla)) { 221 | struct nlmsghdr *nlh = (struct nlmsghdr*)buff; 222 | memcpy(&from_nla, &kern_nla, sizeof(from_nla)); 223 | recv_len = recvfrom(sk_nl, buff, BUFF_SIZE, 0, 224 | (struct sockaddr*)&from_nla, &from_nla_len); 225 | if (from_nla.nl_pid != 0) 226 | continue; 227 | if (recv_len < 1) 228 | continue; 229 | while (NLMSG_OK(nlh, recv_len)) { 230 | cn_hdr = NLMSG_DATA(nlh); 231 | if (nlh->nlmsg_type == NLMSG_NOOP) 232 | continue; 233 | if ((nlh->nlmsg_type == NLMSG_ERROR) || 234 | (nlh->nlmsg_type == NLMSG_OVERRUN)) 235 | break; 236 | handle_msg(cn_hdr); 237 | if (nlh->nlmsg_type == NLMSG_DONE) 238 | break; 239 | nlh = NLMSG_NEXT(nlh, recv_len); 240 | } 241 | } 242 | close_and_exit: 243 | closelog(); 244 | close(sk_nl); 245 | exit(rc); 246 | 247 | return 0; 248 | } 249 | 250 | --------------------------------------------------------------------------------