RubyIOC 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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