soak 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ soak (0.0.1)
5
+ asetus (>= 0.0.7)
6
+ logger (>= 1.2.8)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ asetus (0.0.7)
12
+ slop
13
+ logger (1.2.8)
14
+ slop (3.5.0)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ soak!
@@ -0,0 +1,36 @@
1
+ ## SOAK
2
+
3
+ lightweight ARP sponge.
4
+
5
+ ## requirements
6
+
7
+ + ruby 1.9.3
8
+ + asetus 0.1.2
9
+ + logger 1.2.8
10
+
11
+ soakd requires root privileges to run.
12
+
13
+ # install
14
+
15
+ > gem install soak
16
+ >
17
+
18
+ ## usage
19
+
20
+ > soakd
21
+ >
22
+
23
+ ## files
24
+
25
+ the configuration file is generated during the first run at: /root/.config/soak/config
26
+
27
+ ## configuration example
28
+
29
+ ---
30
+ interface: eth0
31
+ local_mac: ff:ff:ff:ff:ff:ff
32
+ sponge: [ '192.0.2.44', '192.0.2.101', '192.0.2.253' ]
33
+ debug: true
34
+
35
+
36
+
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ Bundler.setup
5
+ rescue => e
6
+ warn "FATAL - #{e}"
7
+ exit 1
8
+ end
9
+
10
+ gemspec = eval(File.read(Dir['*.gemspec'].first))
11
+ file = [gemspec.name, gemspec.version].join('-') + '.gem'
12
+
13
+ task :validate do
14
+ gemspec.validate
15
+ end
16
+
17
+ task :build do
18
+ system "gem build #{gemspec.name}.gemspec"
19
+ FileUtils.mkdir_p 'gems'
20
+ FileUtils.mv file, 'gems'
21
+ end
22
+
23
+ task :install => [:validate, :build] do
24
+ system "sudo -E sh -c \'umask 022; gem install gems/#{file} --no-ri --no-rdoc\'"
25
+ FileUtils.rm_rf 'gems'
26
+ end
27
+
28
+ task :remove do
29
+ system "sudo -E sh -c \'umask 022; gem uninstall #{gemspec.name}\'"
30
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'soak'
4
+
5
+ def help
6
+ puts 'usage: soakd'
7
+ exit 1
8
+ end
9
+
10
+ begin
11
+ sponge = Soak::Lookup.new
12
+ end
@@ -0,0 +1,9 @@
1
+ module Soak
2
+ Directory = File.expand_path File.join File.dirname(__FILE__), '../'
3
+ require 'socket'
4
+ require 'ipaddr'
5
+ require 'thread'
6
+ require 'soak/core'
7
+ require 'soak/lookup'
8
+ require 'soak/packet'
9
+ end
@@ -0,0 +1,40 @@
1
+ module Soak
2
+
3
+ require 'logger'
4
+ target = STDOUT
5
+ Log = Logger.new target
6
+
7
+ # import user configuration
8
+ require 'asetus'
9
+ if File.exists? '/etc/soak/config'
10
+ Cfg = Asetus.cfg name: 'soak'
11
+ else
12
+ CFG = Asetus.new :name=>'soak', :load=>false
13
+ CFG.default.interface = 'eth0'
14
+ CFG.default.local_mac = 'ff:ff:ff:ff:ff:ff'
15
+ CFG.default.sponge = []
16
+ CFG.default.debug = false
17
+ CFG.load
18
+ if CFG.create
19
+ CFG.save
20
+ puts '+ base configuration built at: /root/.config/soak/config'
21
+ exit 0
22
+ else
23
+ Cfg = CFG.cfg
24
+ end
25
+ end
26
+ Process.daemon if not Cfg.debug
27
+
28
+ # constants
29
+ ETH_P_ALL = 0x03_00
30
+ SIOCGIFINDEX = 0x89_33
31
+ PF_PACKET = 17
32
+ AF_PACKET = PF_PACKET
33
+ IFREQ = [ Cfg.interface.dup ].pack 'a32'
34
+ RawSocket = Socket.open Socket::PF_PACKET, Socket::SOCK_RAW, ETH_P_ALL
35
+
36
+ # setup sending device
37
+ RawSocket.ioctl(SIOCGIFINDEX, IFREQ)
38
+ RawSocket.bind [AF_PACKET].pack('s') + [ETH_P_ALL].pack('n') + IFREQ[16..20] + ("\x00" * 12)
39
+
40
+ end
@@ -0,0 +1,60 @@
1
+ module Soak
2
+ class Lookup
3
+
4
+ def initialize
5
+ @rx_queue = Queue.new
6
+ @in_sock = Socket.new Socket::PF_PACKET, Socket::SOCK_RAW, 0x03_00
7
+ listen
8
+ end
9
+
10
+ def listen
11
+ enqueue = Thread.new do
12
+ while true do
13
+ r, w, e = IO.select([@in_sock], nil, nil)
14
+ if r[0]
15
+ data = @in_sock.recvfrom_nonblock(1500).first
16
+ @rx_queue << data
17
+ end
18
+ end
19
+ end
20
+ worker = Thread.new do
21
+ while true do
22
+ begin
23
+ @data = @rx_queue.pop
24
+ if @data[0..13].unpack('nnnnnnn')[6].to_s(16).to_i == 806 # process arp packets only
25
+ pkt_lu
26
+ end
27
+ rescue => e
28
+ Log.warn e if Cfg.debug
29
+ end
30
+ end
31
+ end
32
+ worker.join
33
+ end
34
+
35
+ def pkt_lu
36
+ @src_mac = []
37
+ pkt = @data[14..-1].unpack('nnCCnnnnL>nnnL>')
38
+ case pkt[4].to_s(16).to_i
39
+ when 1
40
+ @dst_ip = IPAddr.new(pkt[12], Socket::AF_INET)
41
+ if Cfg.sponge.include? @dst_ip.to_s
42
+ [ pkt[5], pkt[6], pkt[7] ].each do |p|
43
+ if p.to_s(16).size < 4
44
+ @src_mac << [ [ '00' ] + [ p.to_s(16) ] ].join
45
+ else
46
+ @src_mac << p.to_s(16)
47
+ end
48
+ end
49
+ @src_ip = IPAddr.new(pkt[8], Socket::AF_INET)
50
+ Log.debug [ 'matching arp request - target address:', @dst_ip.to_s, '- sender info:', @src_ip, '@', @src_mac ].join(' ') if Cfg.debug
51
+ @src_mac = @src_mac.join.scan(/../).join ':'
52
+ pkt = Packet.new @dst_ip, @src_ip, @src_mac, @data[12..19]
53
+ else
54
+ Log.debug [ 'address is not in the database -', @dst_ip.to_s, '.. ignoring ..' ].join(' ') if Cfg.debug
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,38 @@
1
+ module Soak
2
+ class Packet
3
+
4
+ def initialize dst_ip, src_ip, src_mac, head_data
5
+ @dst_ip = dst_ip
6
+ @src_ip = src_ip
7
+ @src_mac = src_mac
8
+ @head_data = head_data
9
+ pkt_gen
10
+ inject
11
+ end
12
+
13
+ def pkt_gen
14
+ begin
15
+ smac = Cfg.local_mac.split(':').pack 'H2H2H2H2H2H2'
16
+ dmac = @src_mac.split(':').pack 'H2H2H2H2H2H2'
17
+ opcode = [ 2 ].pack 'n'
18
+ sha = Cfg.local_mac.split(':').pack 'H2H2H2H2H2H2'
19
+ spa = @dst_ip.to_s.split('.').map{ |s| s.to_i }.pack 'CCCC'
20
+ tha = @src_mac.split(':').pack 'H2H2H2H2H2H2'
21
+ tpa = @src_ip.to_s.split('.').map{ |s| s.to_i }.pack 'CCCC'
22
+ @packet = [ dmac, smac, @head_data, opcode, sha, spa, tha, tpa ].join
23
+ rescue => e
24
+ Log.warn e
25
+ end
26
+ end
27
+
28
+ def inject
29
+ begin
30
+ RawSocket.send @packet, 0
31
+ Log.debug [ 'arp packet injected - sponge address:', Cfg.local_mac ].join(' ') if Cfg.debug
32
+ rescue => e
33
+ Log.warn e
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'soak'
3
+ s.version = '0.1.0'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.authors = [ 'Samer Abdel-Hafez' ]
6
+ s.email = %w( sam@arahant.net )
7
+ s.homepage = 'http://github.com/nopedial/soak'
8
+ s.summary = 'soak'
9
+ s.description = 'just an arp sponge'
10
+ s.rubyforge_project = s.name
11
+ s.files = `git ls-files`.split("\n")
12
+ s.executables = %w( soakd )
13
+ s.require_path = 'lib'
14
+
15
+ s.required_ruby_version = '>= 1.9.3'
16
+ s.add_dependency 'asetus', '>= 0.1.2'
17
+ s.add_dependency 'logger', '>= 1.2.8'
18
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: soak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Samer Abdel-Hafez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-09-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: asetus
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: logger
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.2.8
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.8
46
+ description: just an arp sponge
47
+ email:
48
+ - sam@arahant.net
49
+ executables:
50
+ - soakd
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - Gemfile
55
+ - Gemfile.lock
56
+ - README.md
57
+ - Rakefile
58
+ - bin/soakd
59
+ - lib/soak.rb
60
+ - lib/soak/core.rb
61
+ - lib/soak/lookup.rb
62
+ - lib/soak/packet.rb
63
+ - soak.gemspec
64
+ homepage: http://github.com/nopedial/soak
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 1.9.3
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project: soak
84
+ rubygems_version: 1.8.23
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: soak
88
+ test_files: []