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.
@@ -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
@@ -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
@@ -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