plunder 2.0.0a
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.
- checksums.yaml +7 -0
- data/bin/agent +48 -0
- data/bin/plunder +158 -0
- data/ext/fsdb/extconf.rb +13 -0
- data/ext/fsdb/fsdb-c.c +134 -0
- data/ext/fsdb/fsdb-c.h +49 -0
- data/ext/fsdb/fsdb.c +267 -0
- data/ext/fsdb/hash.c +140 -0
- data/ext/fsdb/hash.h +52 -0
- data/ext/fsdb/test.c +90 -0
- data/ext/fsdb/utilities.c +125 -0
- data/ext/fsdb/utilities.h +32 -0
- data/ext/fsdb/vault.c +78 -0
- data/ext/fsdb/vault.h +44 -0
- data/ext/smbclient/extconf.rb +32 -0
- data/ext/smbclient/smb.c +234 -0
- data/ext/smbclient/smb.h +61 -0
- data/ext/smbclient/smbclient.c +116 -0
- data/lib/core/agent.rb +67 -0
- data/lib/core/analyzer.rb +84 -0
- data/lib/core/client.rb +163 -0
- data/lib/core/commands.rb +53 -0
- data/lib/core/config.rb +103 -0
- data/lib/core/io.rb +84 -0
- data/lib/core/plunder.rb +208 -0
- data/lib/core/report.rb +78 -0
- data/lib/core/target.rb +33 -0
- data/lib/core/tree.rb +59 -0
- metadata +74 -0
data/lib/core/agent.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Plunder - SMB scanning and auditing tool
|
|
3
|
+
# Copyright (C) 2017 Joshua Stone
|
|
4
|
+
#
|
|
5
|
+
# This program is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the GNU General Public License
|
|
7
|
+
# as published by the Free Software Foundation; either version 2
|
|
8
|
+
# of the License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU General Public License
|
|
16
|
+
# along with this program; if not, write to the Free Software
|
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
module Plunder
|
|
21
|
+
class Agent
|
|
22
|
+
attr_accessor :free
|
|
23
|
+
|
|
24
|
+
def initialize(domain, user, pass)
|
|
25
|
+
path = File.dirname(__FILE__)
|
|
26
|
+
@pipe = IO.popen("#{path}/../../bin/agent", "r+")
|
|
27
|
+
@pipe.puts domain
|
|
28
|
+
@pipe.puts user
|
|
29
|
+
@pipe.puts pass
|
|
30
|
+
@free = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def checkout
|
|
34
|
+
@free = false
|
|
35
|
+
return self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def checkin
|
|
39
|
+
@free = true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def list(target)
|
|
43
|
+
dirs = []
|
|
44
|
+
files = []
|
|
45
|
+
|
|
46
|
+
@pipe.puts target
|
|
47
|
+
|
|
48
|
+
while true
|
|
49
|
+
line = @pipe.readline.chomp
|
|
50
|
+
case line[0]
|
|
51
|
+
when "D"
|
|
52
|
+
dirs << line[2..-1]
|
|
53
|
+
when "F"
|
|
54
|
+
files << line[2..-1]
|
|
55
|
+
when "C"
|
|
56
|
+
break
|
|
57
|
+
when "E"
|
|
58
|
+
Report.error("'<cya>#{target}<rst>' authentication failure!")
|
|
59
|
+
exit!
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
return [dirs, files]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Plunder - SMB scanning and auditing tool
|
|
3
|
+
# Copyright (C) 2017 Joshua Stone
|
|
4
|
+
#
|
|
5
|
+
# This program is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the GNU General Public License
|
|
7
|
+
# as published by the Free Software Foundation; either version 2
|
|
8
|
+
# of the License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU General Public License
|
|
16
|
+
# along with this program; if not, write to the Free Software
|
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
require 'thread'
|
|
21
|
+
|
|
22
|
+
module Plunder
|
|
23
|
+
class Analyzer
|
|
24
|
+
attr_accessor :config, :queue
|
|
25
|
+
|
|
26
|
+
def initialize(conf)
|
|
27
|
+
@config = conf
|
|
28
|
+
@queue = Queue.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def mirror?(dir, file)
|
|
32
|
+
url = "#{dir}#{file}"
|
|
33
|
+
ext = File.extname(file).downcase
|
|
34
|
+
@config[:mirror].each do |(kind, match, size)|
|
|
35
|
+
case kind
|
|
36
|
+
when :filename
|
|
37
|
+
return size if file =~ /#{match}/i
|
|
38
|
+
when :path
|
|
39
|
+
return size if url =~ /#{match}/i
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
return nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def <<(value)
|
|
46
|
+
@queue << value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def alert?(dir, file)
|
|
50
|
+
url = "#{dir}#{file}"
|
|
51
|
+
ext = File.extname(file).downcase
|
|
52
|
+
@config[:alert].each do |(kind, match)|
|
|
53
|
+
case kind
|
|
54
|
+
when :filename
|
|
55
|
+
return true if file =~ /#{match}/i
|
|
56
|
+
when :path
|
|
57
|
+
return true if url =~ /#{match}/i
|
|
58
|
+
when :extensions
|
|
59
|
+
match.each do |entry|
|
|
60
|
+
return true if ext == entry
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
return false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def run
|
|
68
|
+
Thread.new do
|
|
69
|
+
Report.notify("Started analysis thread")
|
|
70
|
+
while true
|
|
71
|
+
(path, file, url, id) = @queue.pop
|
|
72
|
+
begin
|
|
73
|
+
if alert?(path, file)
|
|
74
|
+
Report.alert(sprintf("%8d %s", id, URI::decode(url)))
|
|
75
|
+
end
|
|
76
|
+
rescue Exception => e
|
|
77
|
+
STDERR.puts "#{e.class} : #{e.message}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/core/client.rb
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Plunder - SMB scanning and auditing tool
|
|
3
|
+
# Copyright (C) 2017 Joshua Stone
|
|
4
|
+
#
|
|
5
|
+
# This program is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the GNU General Public License
|
|
7
|
+
# as published by the Free Software Foundation; either version 2
|
|
8
|
+
# of the License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU General Public License
|
|
16
|
+
# along with this program; if not, write to the Free Software
|
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
require 'pp'
|
|
21
|
+
|
|
22
|
+
module Plunder
|
|
23
|
+
class Client
|
|
24
|
+
def initialize(plunder)
|
|
25
|
+
@plunder = plunder
|
|
26
|
+
@done = false
|
|
27
|
+
@current = []
|
|
28
|
+
@next = list_targets
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def repl
|
|
32
|
+
until @done
|
|
33
|
+
begin
|
|
34
|
+
handle(prompt)
|
|
35
|
+
rescue EOFError
|
|
36
|
+
puts ""
|
|
37
|
+
exit!
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def where
|
|
43
|
+
return "smb://" + @current.join("/")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def prompt
|
|
47
|
+
print(Report.colorize(" <blu>Plunder <cya>#{where} <blu>><rst> "))
|
|
48
|
+
STDOUT.flush
|
|
49
|
+
return readline
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def handle(cmd)
|
|
53
|
+
cmd.strip!
|
|
54
|
+
fields = cmd.split(/\s(?=(?:[^"]|"[^"]*")*$)/)
|
|
55
|
+
command = fields.shift
|
|
56
|
+
case command
|
|
57
|
+
when "cd"
|
|
58
|
+
path = fields[0].gsub(/\"/, "")
|
|
59
|
+
if path == "/"
|
|
60
|
+
chdir("/")
|
|
61
|
+
elsif path.start_with? "smb://"
|
|
62
|
+
chdir("/")
|
|
63
|
+
path.split(/\//)[2..-1].each do |seg|
|
|
64
|
+
break unless chdir(seg)
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
path.split(/\//).each do |seg|
|
|
68
|
+
break unless chdir(seg)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
when "whoami"
|
|
72
|
+
whoami
|
|
73
|
+
when "ls"
|
|
74
|
+
list
|
|
75
|
+
when "quit"
|
|
76
|
+
@done = true
|
|
77
|
+
when "exit"
|
|
78
|
+
@done = true
|
|
79
|
+
when "cat"
|
|
80
|
+
if fields[0].start_with? "smb://"
|
|
81
|
+
cat fields[0]
|
|
82
|
+
else
|
|
83
|
+
cat(where + "/" + fields[0])
|
|
84
|
+
end
|
|
85
|
+
when "mirror"
|
|
86
|
+
if fields.length > 1
|
|
87
|
+
fields.each do |file|
|
|
88
|
+
path = URI::encode(where + "/" + file)
|
|
89
|
+
puts(Report.colorize(" mirroring '<cya>#{path}<rst>'"))
|
|
90
|
+
@plunder.mirror(path)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
puts(Report.colorize("Unknown or malformed command <red>#{cmd}<rst>!"))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def list
|
|
99
|
+
here = where
|
|
100
|
+
puts ""
|
|
101
|
+
@next[0].each do |dir|
|
|
102
|
+
puts(Report.colorize(" <grn>D<cya> #{dir}<rst>"))
|
|
103
|
+
end
|
|
104
|
+
@next[1].each do |file|
|
|
105
|
+
puts(Report.colorize(" #{file}"))
|
|
106
|
+
end
|
|
107
|
+
puts ""
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def list_next
|
|
111
|
+
if @current == []
|
|
112
|
+
@next = list_targets
|
|
113
|
+
else
|
|
114
|
+
@next = @plunder.client.list(where + "/")
|
|
115
|
+
@next.each { |i| i.map! { |j| URI::decode j } }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def chdir(sub)
|
|
120
|
+
if sub == ".."
|
|
121
|
+
@current.pop
|
|
122
|
+
elsif sub == "."
|
|
123
|
+
elsif sub == "/"
|
|
124
|
+
@current = []
|
|
125
|
+
elsif available(sub)
|
|
126
|
+
@current.push(sub)
|
|
127
|
+
else
|
|
128
|
+
puts(Report.colorize(" <red>ERROR<rst> cannot find or access '<cya>#{sub}<rst>'"))
|
|
129
|
+
end
|
|
130
|
+
list_next
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def available(child)
|
|
134
|
+
@next[0].each do |dir|
|
|
135
|
+
return true if dir.downcase == child.downcase
|
|
136
|
+
end
|
|
137
|
+
return false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def display(res)
|
|
141
|
+
pp res
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def list_targets
|
|
145
|
+
@next = [@plunder.targets.collect { |i| i.host }, []]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def cat(url)
|
|
149
|
+
puts @plunder.client.slurp(url)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def whoami
|
|
153
|
+
dom = @plunder.config[:domain]
|
|
154
|
+
usr = @plunder.config[:user]
|
|
155
|
+
pwd = @plunder.config[:pass]
|
|
156
|
+
|
|
157
|
+
puts ""
|
|
158
|
+
puts(Report.colorize(" User: <grn>#{dom}<gra>\\<grn>#{usr}<rst>"))
|
|
159
|
+
puts(Report.colorize(" Password: <grn>#{pwd}<rst>"))
|
|
160
|
+
puts ""
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Plunder - SMB scanning and auditing tool
|
|
3
|
+
# Copyright (C) 2017 Joshua Stone
|
|
4
|
+
#
|
|
5
|
+
# This program is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the GNU General Public License
|
|
7
|
+
# as published by the Free Software Foundation; either version 2
|
|
8
|
+
# of the License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU General Public License
|
|
16
|
+
# along with this program; if not, write to the Free Software
|
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
require_relative 'report.rb'
|
|
21
|
+
|
|
22
|
+
module Plunder
|
|
23
|
+
class Commands
|
|
24
|
+
attr_accessor :commands
|
|
25
|
+
|
|
26
|
+
def initialize(&block)
|
|
27
|
+
@commands = {}
|
|
28
|
+
instance_eval(&block)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def command(name, &block)
|
|
32
|
+
@commands[name] = block
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def run(args)
|
|
36
|
+
@args = args
|
|
37
|
+
cmd = next_arg
|
|
38
|
+
punt("not enough arguments") unless @commands[cmd]
|
|
39
|
+
instance_eval(&@commands[cmd])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def next_arg()
|
|
43
|
+
punt("not enough arguments") unless @args.length > 0
|
|
44
|
+
return @args.shift
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def punt(message)
|
|
48
|
+
Report.error(message)
|
|
49
|
+
puts
|
|
50
|
+
exit 2
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/core/config.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Plunder - SMB scanning and auditing tool
|
|
3
|
+
# Copyright (C) 2017 Joshua Stone
|
|
4
|
+
#
|
|
5
|
+
# This program is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the GNU General Public License
|
|
7
|
+
# as published by the Free Software Foundation; either version 2
|
|
8
|
+
# of the License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU General Public License
|
|
16
|
+
# along with this program; if not, write to the Free Software
|
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
module Plunder
|
|
21
|
+
class Config
|
|
22
|
+
attr_accessor :config, :path
|
|
23
|
+
|
|
24
|
+
def Config.default(name)
|
|
25
|
+
return {
|
|
26
|
+
:domain => "WORKGROUP",
|
|
27
|
+
:user => "guest",
|
|
28
|
+
:pass => "",
|
|
29
|
+
:file => "#{name}.dat",
|
|
30
|
+
:threads => 8,
|
|
31
|
+
:blacklist => {
|
|
32
|
+
:dirs => ['winsxs'],
|
|
33
|
+
:shares => ['admin%24', 'ipc%24', 'print%24', 'c%24']
|
|
34
|
+
},
|
|
35
|
+
:tarpit => 10000,
|
|
36
|
+
:mirror => [
|
|
37
|
+
|
|
38
|
+
# type regex max download (bytes)
|
|
39
|
+
# ---- ----- --------------------
|
|
40
|
+
[:filename, '^web\.config$', 1E6],
|
|
41
|
+
[:filename, '^passwd$', 1E6],
|
|
42
|
+
[:filename, '^shadow$', 1E6],
|
|
43
|
+
[:filename, '\.netrc$', 1E6],
|
|
44
|
+
[:filename, 'password.*(docx|doc|xls|xlsx)$', 1e6],
|
|
45
|
+
[:filename, 'credit.*card', 1e9],
|
|
46
|
+
[:filename, '\.yaml$', 1E6],
|
|
47
|
+
[:filename, '\.htaccess$', 1E6],
|
|
48
|
+
[:filename, '\.kdbx$', 1E7],
|
|
49
|
+
[:path, 'SYSVOL.*groups.xml$', 1E6],
|
|
50
|
+
[:path, 'SYSVOL.*drives.xml$', 1E6],
|
|
51
|
+
[:path, 'SYSVOL.*datasources.xml$', 1E6],
|
|
52
|
+
[:path, 'SYSVOL.*printers.xml$', 1E6],
|
|
53
|
+
[:path, 'SYSVOL.*services.xml$', 1E6],
|
|
54
|
+
[:path, 'SYSVOL.*scheduledtasks.xml$', 1E6],
|
|
55
|
+
[:path, 'NETLOGON.*\.(bat|vbs)$', 1E6]
|
|
56
|
+
],
|
|
57
|
+
:alert => [
|
|
58
|
+
[:filename, 'passw'],
|
|
59
|
+
[:filename, 'datab'],
|
|
60
|
+
[:filename, 'card'],
|
|
61
|
+
[:filename, 'account'],
|
|
62
|
+
[:extensions, ['.bat', '.asp', '.aspx', '.php', '.vbs', '.xml', '.conf',
|
|
63
|
+
'.config', '.sql', '.vmdk']]
|
|
64
|
+
],
|
|
65
|
+
:targets => [ ]
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def initialize(path)
|
|
70
|
+
unless File.exists? path
|
|
71
|
+
puts "\x1b[31;1mError:\x1b[0m #{path} does not exist"
|
|
72
|
+
exit 3
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
@path = path
|
|
76
|
+
@config = YAML.load(File.read(path))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def [](value)
|
|
80
|
+
return @config[value]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def []=(name, value)
|
|
84
|
+
return @config[name] = value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def save
|
|
88
|
+
File.open(@path, "w") do |file|
|
|
89
|
+
file.write(@config.to_yaml)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def add_target(url)
|
|
94
|
+
if @config[:targets].member? url
|
|
95
|
+
puts " [+] '\x1b[31;1m#{url}\x1b[0m' already in target list"
|
|
96
|
+
else
|
|
97
|
+
@config[:targets] << url
|
|
98
|
+
save
|
|
99
|
+
puts " [+] '\x1b[31;1m#{url}\x1b[0m' added to target list"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|