The story of iCR finding the nastiest injection bugs
Munawar Hafiz, CEO of OpenRefactory writes this blog post.
I was scheduled to give a talk at JavaOne on Intelligent Code Repair (iCR) being able to find the LDAP injection vulnerability aka Log4Shell in the vulnerable Log4J code. I have given a short version of that talk a couple of times before; so the core materials were there. But there are always last minute tweaks that you need to make.
As I was doing that yesterday (Oct 17), I met with Carter K. of Palantir who gave me a fresh challenge. He was saying that it is good to talk about Log4Shell, but it has been almost a year since that vulnerability was first reported. He informed me that there is a new vulnerability in town. He was pointing to the new Text4Shell vulnerability that has been circulating the news in the past 3-4 days. The challenge was, could iCR detect that bug?
I spoke with my R&D team who reported back to me in less than an hour that we did, indeed, detect that. So, all my plans for the original talk needed to change. I wanted to show the demo of iCR being able to find this new bug. And we broke the news at the JavaOne talk!
Text4Shell (aka Act4Shell) has been given CVE-2022-42889 as the ID. The actual vulnerability was reported as a part of a coordinated disclosure effort by the GitHub Security Lab (GHSL) team back in March. Apache Commons Text versions 1.5 to 1.9 were marked as vulnerable.
It took the Apache Commons team 7 months to eventually create a fix. The vulnerability was publicly disclosed on October 13 along with Apache Commons Text v1.10.0 which has the fix.
The issue is related to Java’s string interpolation. Paul D., in his great article, showed an example of interpolation which I will borrow here. In the bash shell you can write
$ echo USER
and you get the output
USER
But if you write
$ echo ${USER}
you will get
munawar
This is because the magic character sequence ${USER} means to look in the environment (a memory-based collection of data values typically storing the computer name, current username, TEMP directory, command path and so on), retrieve the value of the variable USER (by convention, the current user’s login name), and use that instead.
When a malicious user can pass untrusted input, it opens up a remote code execution scenario.
We created a small code snippet that shows the code sequence that is problematic.
public class BugReproducer { public static void main(String[] args) { final StringSubstitutor interpolator = StringSubstitutor.createInterpolator(); // Do this, or worse do an eval String out = interpolator.replace("${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}"); System.out.println(out); } }
StringSubstitutor is defined in the Apache Commons Text library. It substitutes variables within a string by values. This class takes a piece of text and substitutes all the variables within it. The default definition of a variable is ${variableName}. The prefix and suffix can be changed via constructors and set methods.
The StringSubstitutor API definition gives an example:
- In ScriptStringLookup.java code, in line 86, we have the line
return Objects.toString(scriptEngine.eval(script),null);
Here we are doing an eval on an object (script) that may contain malicious input passed by an attacker. This is eerily similar to the JNDI call in Log4Shell case.
And that is it. Another day! Another security vulnerability! And the rush to perform incident response! Where are the bug detection tools when we need them?
End Note:
- The Rapid7 security team did a review and commented that this is much harder to exploit that Log4Shell. So, the impact is not nearly as high as Log4shell. Read the article: CVE-2022-42889: Keep Calm and Stop Saying “4Shell”.
- It is strange but not uncommon to have a coordinated disclosure to take 7 months. Open source maintainers need a lot of support.