RubyIOC 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,20 +1,20 @@
1
- *.gem
2
- .bundle
3
- Gemfile.lock
4
- pkg/*
5
- *.rbc
6
- .config
7
- coverage
8
- InstalledFiles
9
- lib/bundler/man
10
- pkg
11
- rdoc
12
- spec/reports
13
- test/tmp
14
- test/version_tmp
15
- tmp
16
-
17
- # YARD artifacts
18
- .yardoc
19
- _yardoc
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.rbc
6
+ .config
7
+ coverage
8
+ InstalledFiles
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+
17
+ # YARD artifacts
18
+ .yardoc
19
+ _yardoc
20
20
  doc/
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "http://rubygems.org"
2
- # Specify your gem's dependencies in RubyIOC.gemspec
3
- gemspec
1
+ source "http://rubygems.org"
2
+ # Specify your gem's dependencies in RubyIOC.gemspec
3
+ gemspec
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- Rake::TestTask.new do |t|
5
- t.libs << "test"
6
- t.test_files = FileList['test/test*.rb']
7
- t.verbose = false
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/test*.rb']
7
+ t.verbose = false
8
8
  end
data/RubyIOC.gemspec CHANGED
@@ -1,24 +1,24 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "RubyIOC/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "RubyIOC"
7
- s.version = RubyIOC::VERSION
8
- s.authors = ["Matt Jezorek"]
9
- s.email = ["mjezorek@gmail.com"]
10
- s.homepage = ""
11
- s.summary = %q{RubyIOC is a ruby library used for indicators of compromise}
12
- s.description = %q{RubyIOC is a ruby library used for indicators of compromise}
13
-
14
- s.rubyforge_project = "RubyIOC"
15
-
16
- s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
- s.require_paths = ["lib"]
20
-
21
- # specify any dependencies here; for example:
22
- # s.add_development_dependency "rspec"
23
- s.add_runtime_dependency "roxml"
24
- end
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "RubyIOC/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "RubyIOC"
7
+ s.version = RubyIOC::VERSION
8
+ s.authors = ["Matt Jezorek"]
9
+ s.email = ["mjezorek@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{RubyIOC is a ruby library used for indicators of compromise}
12
+ s.description = %q{RubyIOC is a ruby library used for indicators of compromise}
13
+
14
+ s.rubyforge_project = "RubyIOC"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "roxml"
24
+ end
data/iocaware.iocterms ADDED
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="us-ascii"?>
2
+ <ioctermlist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" last-modified="2013-07-15T21:26:28" description="New IOC Terms File" xmlns="http://schemas.mandiant.com/2010/ioc">
3
+ <iocterm text="FileItem/Md54ksum" title="File MD54K" data-type="MD5Sum" display-type="md5" term-source="application/iocaware" />
4
+ <iocterm text="FileItem/Ssdeep" title="File SSDeep" data-type="xs:string" display-type="string" term-source="application/iocaware" />
5
+ <iocterm text="FileItem/Sha512sum" title="File Sha512sum" data-type="HashSum" display-type="string" term-source="application/iocaware" />
6
+ <iocterm text="ServiceItem/pathMd54ksum" title="Service Path MD54K" data-type="MD5Sum" display-type="md5" term-source="application/iocaware" />
7
+ <iocterm text="ServiceItem/pathSsdeep" title="Service Path SSDeep" data-type="xs:string" display-type="string" term-source="application/iocaware" />
8
+ <iocterm text="ServiceItem/pathSha512sum" title="Service Path Sha512Sum" data-type="HashSum" display-type="string" term-source="application/iocaware" />
9
+ <iocterm text="ServiceItem/serviceDLLMd54Ksum" title="Service DLL MD54K" data-type="MD5Sum" display-type="md5" term-source="application/iocaware" />
10
+ <iocterm text="ServiceItem/serviceDLLSsdeep" title="Service DLL SSDeep" data-type="xs:string" display-type="string" term-source="application/iocaware" />
11
+ <iocterm text="ServiceItem/serviceDLLSha512Sum" title="Service DLL Sha512Sum" data-type="HashSum" display-type="string" term-source="application/iocaware" />
12
+ </ioctermlist>
data/lib/RubyIOC.rb CHANGED
@@ -1,39 +1,39 @@
1
- # Copyright (c) 2013 Matt Jezorek
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4
- # to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5
- # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
- #
7
- # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
- #
9
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
12
- # IN THE SOFTWARE.
13
- require "rexml/document"
14
-
15
- require "RubyIOC/version"
16
- require "RubyIOC/platform"
17
- require "RubyIOC/iocterm"
18
- require "RubyIOC/iocitem"
19
- require "RubyIOC/ioc"
20
- require "RubyIOC/scanner"
21
-
22
- =begin rdoc
23
- RubyIOC is a simple gem that will allow the scanning of a system with indicators of compromise. RubyIOC will not tell you if the machine
24
- is compromised or not but it will give you a score and what indicators have been found. Ideally you will want to see 0% and 0 found indicators.
25
- However you may come back with 1% ond 2 indicators out of 200. It will also provide you a reference to the found indicators. From here you
26
- can investigate whatever machine you wish to investigate.
27
-
28
- Please note that when you use this software you are running on possibly compromised machiens, any credentials you use to facilitate the scan
29
- should be considered compromised
30
- =end
31
-
32
-
33
- class String
34
- def to_bool
35
- return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
36
- return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
37
- raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
38
- end
1
+ # Copyright (c) 2013 Matt Jezorek
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4
+ # to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5
+ # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+ #
7
+ # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+ #
9
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
12
+ # IN THE SOFTWARE.
13
+ require "rexml/document"
14
+
15
+ require "RubyIOC/version"
16
+ require "RubyIOC/platform"
17
+ require "RubyIOC/iocterm"
18
+ require "RubyIOC/iocitem"
19
+ require "RubyIOC/ioc"
20
+ require "RubyIOC/scanner"
21
+
22
+ =begin rdoc
23
+ RubyIOC is a simple gem that will allow the scanning of a system with indicators of compromise. RubyIOC will not tell you if the machine
24
+ is compromised or not but it will give you a score and what indicators have been found. Ideally you will want to see 0% and 0 found indicators.
25
+ However you may come back with 1% ond 2 indicators out of 200. It will also provide you a reference to the found indicators. From here you
26
+ can investigate whatever machine you wish to investigate.
27
+
28
+ Please note that when you use this software you are running on possibly compromised machiens, any credentials you use to facilitate the scan
29
+ should be considered compromised
30
+ =end
31
+
32
+
33
+ class String
34
+ def to_bool
35
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
36
+ return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
37
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
38
+ end
39
39
  end
@@ -16,6 +16,89 @@ module RubyIOC
16
16
  def get_type
17
17
  "ArpEntryItem"
18
18
  end
19
+
20
+ def scan(indicator)
21
+ if RubyIOC::Platform.windows?
22
+ return search_windows_arp(indicator)
23
+ elseif Ruby::Platform::mac?
24
+ return search_mac_arp(indicator)
25
+ else
26
+ puts "Not implemented on this platform yet"
27
+ end
28
+ end
29
+
30
+ def search_windows_arp(indicator)
31
+ arp = get_arp_cache
32
+
33
+ arp.each_line do |line|
34
+ indicator.each { |i|
35
+ content = i[:content]
36
+
37
+ case i[:search]
38
+ when "ArpEntryItem/PhysicalAddress", "ArpEntryItem/CacheType", "ArpEntryItem/IPv4Address"
39
+ if !(line.downcase.include? "interface") && (line.downcase.gsub("-", ":").include? content.downcase)
40
+ return true
41
+ end
42
+ when "ArpEntryItem/Interface"
43
+ if (line.downcase.include? "interface") && (line.downcase.include? content.downcase)
44
+ return true
45
+ end
46
+ end
47
+ }
48
+ end
49
+ #puts arp
50
+ #puts arp.to_yaml
51
+ return false
52
+ end
53
+
54
+ def search_mac_arp(indicator)
55
+ arp = get_arp_cache
56
+ arp.each_line do |line|
57
+ fields = line.split(' ')
58
+ ip = fields[1].gsub('(', '').gsub(')', '')
59
+ physical = fields[3].downcase
60
+ iface = fields[5].downcase
61
+
62
+ indicator.each { |i|
63
+ content = i[:content].downcase
64
+
65
+ case i[:search]
66
+ when "ArpEntryItem/PhysicalAddress"
67
+ octets = physical.split(':')
68
+ i = 0
69
+ while i < octets.length do
70
+ if octets[i].length == 1 then
71
+ octets[i] = '0' + octets[i]
72
+ end
73
+ i += 1
74
+ end
75
+ physical = octets.join(':')
76
+
77
+ if physical == content then
78
+ return true
79
+ end
80
+ when "ArpEntryItem/CacheType"
81
+ when "ArpEntryItem/IPv4Address"
82
+ if ip == content then
83
+ return true
84
+ end
85
+ when "ArpEntryItem/Interface"
86
+ if iface == content then
87
+ return true
88
+ end
89
+ end
90
+ }
91
+ end
92
+ #puts arp
93
+ #puts arp.to_yaml
94
+ return false
95
+ end
96
+
97
+ def get_arp_cache
98
+ arp_cache =`arp -a`
99
+
100
+ return arp_cache
101
+ end
19
102
  end
20
103
 
21
104
  class ArpEntryItemFactory < RubyIOC::IOCItem::IOCItemFactory
@@ -30,4 +113,6 @@ module RubyIOC
30
113
 
31
114
  ArpEntryItemFactory.add_factory(ArpEntryItemFactory)
32
115
  end
33
- end
116
+ end
117
+
118
+
@@ -26,18 +26,16 @@ module RubyIOC
26
26
  end
27
27
 
28
28
  def search_windows_dns(indicator)
29
- dns = get_windows_dns_cache
30
- puts dns.to_yaml
31
- return false
32
- end
33
-
34
- def get_windows_dns_cache
35
- dns = []
36
- dns_cache =`ipconfig /displaydns`
29
+ dns_cache = get_windows_dns_cache
30
+
37
31
  blocks = dns_cache.split(/\n\n/)
32
+
38
33
  blocks.each do | block |
39
- #puts block
34
+ noblanklines = block.gsub(/^$\n/, '').downcase
35
+ host = noblanklines.lines.first
36
+
40
37
  temp = {}
38
+ temp[:host] = host
41
39
  temp[:record_name] = block.match(/\s*Record Name.*:\s(?<record>.*)/).to_a[1]
42
40
  temp[:record_type] = block.match(/\s*Record Type.*:\s(?<record>.*)/).to_a[1]
43
41
  temp[:time_to_live] = block.match(/\s*Time To Live.*:\s(?<record>.*)/).to_a[1]
@@ -45,9 +43,47 @@ module RubyIOC
45
43
  temp[:section] = block.match(/\s*Section.*:\s(?<record>.*)/).to_a[1]
46
44
  temp[:a_record] = block.match(/\s*A \(Host\) Record.*:\s(?<record>.*)/).to_a[1]
47
45
  temp[:cname] = block.match(/\s*CNAME Record.*:\s(?<record>.*)/).to_a[1]
48
- dns << temp
46
+ #puts temp
47
+ indicator.each { |i|
48
+ content = i[:content].downcase
49
+
50
+ case i[:search]
51
+ when "DnsEntryItem/Host"
52
+ if !temp[:host].nil? and temp[:host].downcase.strip == content then
53
+ return true
54
+ end
55
+ when "DnsEntryItem/RecordName"
56
+ if !temp[:record_name].nil? and temp[:record_name].downcase.strip == content then
57
+ return true
58
+ end
59
+ when "DnsEntryItem/RecordType"
60
+ if !temp[:record_type].nil? and temp[:record_type].downcase.strip == content then
61
+ return true
62
+ end
63
+ when "DnsEntryItem/TimeToLive"
64
+ if !temp[:time_to_live].nil? and temp[:time_to_live].downcase.strip == content then
65
+ return true
66
+ end
67
+ when "DnsEntryItem/Flags"
68
+ when "DnsEntryItem/DataLength"
69
+ if !temp[:data_length].nil? and temp[:data_length].downcase.strip == content then
70
+ return true
71
+ end
72
+ when "DnsEntryItem/RecordData/Host"
73
+ when "DnsEntryItem/RecordData/IPv4Address"
74
+ if !temp[:a_record].nil? and temp[:a_record].downcase.strip == content then
75
+ return true
76
+ end
77
+ end
78
+ }
49
79
  end
50
- return dns
80
+
81
+ return false
82
+ end
83
+
84
+ def get_windows_dns_cache
85
+ dns_cache =`ipconfig /displaydns`
86
+ return dns_cache
51
87
  end
52
88
  end
53
89
 
@@ -10,12 +10,61 @@
10
10
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11
11
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
12
12
  # IN THE SOFTWARE.
13
+
14
+ if RubyIOC::Platform.windows?
15
+ require "win32ole"
16
+ end
17
+
13
18
  module RubyIOC
14
19
  module IOCItem
15
20
  class EventLogItem < RubyIOC::IOCTerm
16
21
  def get_type
17
22
  "EventLogItem"
18
23
  end
24
+
25
+ def scan(indicator)
26
+ if RubyIOC::Platform.windows?
27
+ return search_windows_events(indicator)
28
+ else
29
+ puts "Not implemented on this platform yet"
30
+ end
31
+ end
32
+
33
+ def search_windows_events(indicator)
34
+ wmi = WIN32OLE.connect("winmgmts:{impersonationLevel=impersonate,(Security)}!\\")
35
+ query = "Select * from Win32_NTLogEvent where "
36
+
37
+ indicator.each { |i|
38
+ case i[:search]
39
+ when "EventLogItem/category"
40
+ query += "CategoryString = '#{i[:content]}' "
41
+ when "EventLogItem/categoryNum"
42
+ query += "Category = #{i[:content]} "
43
+ when "EventLogItem/genTime"
44
+ when "EventLogItem/EID"
45
+ query += "EventIdentifier = #{i[:content]} "
46
+ when "EventLogItem/log"
47
+ query += "LogFile = '#{i[:content]}' "
48
+ when "EventLogItem/machine"
49
+ query += "ComputerName = '#{i[:content]}' "
50
+ when "EventLogItem/message"
51
+ query += "Message like '%#{i[:content]}%' "
52
+ when "EventLogItem/source"
53
+ query += "SourceName = '#{i[:content]}' "
54
+ when "EventLogItem/type"
55
+ query += "Type = '#{i[:content]}' "
56
+ when "EventLogItem/user"
57
+ query += "User like '%#{i[:content]}%' "
58
+ when "EventLogItem/writeTime"
59
+ end
60
+ }
61
+
62
+ events = wmi.ExecQuery(query)
63
+ events.each { |e|
64
+ return true
65
+ }
66
+ return false
67
+ end
19
68
  end
20
69
 
21
70
  class EventLogItemFactory < RubyIOC::IOCItem::IOCItemFactory
@@ -16,6 +16,123 @@ module RubyIOC
16
16
  def get_type
17
17
  "PortItem"
18
18
  end
19
+
20
+ def scan(indicator)
21
+ if RubyIOC::Platform.windows?
22
+ return search_windows_netstat(indicator)
23
+ else
24
+ puts "Not implemented on this platform yet"
25
+ end
26
+ end
27
+
28
+ def search_windows_netstat(indicator)
29
+ netstat = get_windows_netstat
30
+
31
+ blocks = netstat.split(/\n/)
32
+ i = 1
33
+
34
+ entries = []
35
+ entry = ""
36
+ blocks.each do | block |
37
+ if i<5 then
38
+ i+=1
39
+ next
40
+ end
41
+
42
+ line = block.strip.gsub(/[ ]{2,}/, " ")
43
+ localip = ""
44
+ localport = ""
45
+ pid = ""
46
+ process = ""
47
+ protocol = ""
48
+ remoteip = ""
49
+ remoteport = ""
50
+ state = ""
51
+
52
+ if line.match(/^TCP/) or line.match(/^UDP/) then
53
+ nsentry = line.downcase.split(' ')
54
+ protocol = nsentry[0]
55
+ if nsentry[1].match(/\[/) then
56
+ localip = nsentry[1].split(']:')[0].gsub(/\[/, '')
57
+ localport = nsentry[1].split(']:')[1]
58
+ else
59
+ localip = nsentry[1].split(':')[0]
60
+ localport = nsentry[1].split(':')[1]
61
+ end
62
+ if nsentry[2].match(/\[/) then
63
+ remoteip = nsentry[2].split(']:')[0].gsub(/\[/, '')
64
+ remoteport = nsentry[2].split(']:')[1]
65
+ else
66
+ remoteip = nsentry[2].split(':')[0]
67
+ remoteport = nsentry[2].split(':')[1]
68
+ end
69
+ if nsentry.length < 5 then
70
+ pid = nsentry[3]
71
+ else
72
+ state = nsentry[3]
73
+ pid = nsentry[4]
74
+ end
75
+
76
+ indicator.each { |i|
77
+ content = i[:content].downcase
78
+
79
+ case i[:search]
80
+ when "PortItem/CreationTime"
81
+ when "PortItem/localIP"
82
+ if content == localip then
83
+ return true
84
+ end
85
+ when "PortItem/localPort"
86
+ if content == localport then
87
+ return true
88
+ end
89
+ when "PortItem/path"
90
+ when "PortItem/pid"
91
+ if content == pid then
92
+ return true
93
+ end
94
+ when "PortItem/protocol"
95
+ if content == protocol then
96
+ return true
97
+ end
98
+ when "PortItem/remoteIP"
99
+ if content == remoteip then
100
+ return true
101
+ end
102
+ when "PortItem/remotePort"
103
+ if content == remoteport then
104
+ return true
105
+ end
106
+ when "PortItem/state"
107
+ if content == state then
108
+ return true
109
+ end
110
+ end
111
+ }
112
+ else
113
+ if line.match(/^\[/)
114
+ process = line.downcase.gsub("[", "").gsub("]", "")
115
+
116
+ indicator.each { |i|
117
+ content = i[:content].downcase
118
+ case i[:search]
119
+ when "PortItem/process"
120
+ if content == process then
121
+ return true
122
+ end
123
+ end
124
+ }
125
+ end
126
+ end
127
+ end
128
+
129
+ return false
130
+ end
131
+
132
+ def get_windows_netstat
133
+ netstat = `netstat -anbo`
134
+ return netstat
135
+ end
19
136
  end
20
137
 
21
138
  class PortItemFactory < RubyIOC::IOCItem::IOCItemFactory