Search code examples
linuxlog4jlog4shell

hotfix securing many log4j jars against log4shell


I have to secure some servers against CVE-2021-44228 aka log4shell. Those machines are running Linux and have a huge amount of log4j jars all over the place, some from app servers, some from legacy software, etc. I fear it is not possible to update all of them to the latest log4j.

However, if I understand correctly one can prevent log4shell in log4j 1.x by removing the impacted class like this:

zip -q -d log4j.jar org/apache/log4j/net/JMSAppender.class

And in log4j 2.x like this (see logging.apache.org):

zip -q -d log4j-core.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

I think of a shell script to find all log4j jars, maybe like this:

find / -name "log4j*.jar"

and call those two zip commands on all results. Unfortunately I’m not good at shell scripting. Might anyone write a script based on this idea?

Warnings:

  • Of course you loose all JNDI lookup and/or JMS functionality from log4j by doing so.
  • log4j 1.x has other security issues. So it’s better to upgrade e.g. to latest logj 2.x.

Solution

  • Well, here is my own shot at this. Not very elegant (quite repetitive), but with nicely colored output. And it only removes the class if the log4j version is compromised (FIX_VERSION="2.17.1" to also protect against CVE-2021-45105) and CVE-2021-44832.

    #!/bin/bash
    echo " ┌──────────────────────────────────────────────┐"
    echo " │ This is securelog4j version 1.5              │"
    echo " │ https://stackoverflow.com/a/70362694/1948252 │"
    echo " └──────────────────────────────────────────────┘"
    preventLog4Shell() {
        readonly FIX_VERSION="2.17.1" # see https://logging.apache.org/log4j/2.x/security.html
        readonly VERSION=$(unzip -p "$1" META-INF/MANIFEST.MF | grep Implementation-Version | cut -d " " -f2 | sed -r 's/\s*$//g')
        readonly RED="\033[31m"
        readonly GREEN="\033[32m"
        readonly YELLOW="\033[33m"
        readonly BOLD=$(tput bold)
        readonly RESET=$(tput sgr0)
        readonly SMALL=$(echo -e $VERSION'\n'$FIX_VERSION | sort -V | head -n1)
        if [ "$SMALL" = "$FIX_VERSION" ]
        then
            echo -e $BOLD$GREEN good version $VERSION:$RESET $1
        else
            hasJms=$(zip -v "$1" org/apache/log4j/net/JMSAppender.class | grep including)
            hasJndi=$(zip -v "$1" org/apache/logging/log4j/core/lookup/JndiLookup.class | grep including)
            if [ "$hasJms" ] || [ "$hasJndi" ]
            then
                if [ "$hasJms" ]
                then
                    zip -d -q "$1" org/apache/log4j/net/JMSAppender.class
                    hasJms=$(zip -v "$1" org/apache/log4j/net/JMSAppender.class | grep including)
                    if [ "$hasJms" ]
                    then
                        echo -e $BOLD$RED unable to secure:$RESET $1
                    else
                        echo -e $BOLD$YELLOW secured:$RESET $1
                    fi
                else 
                    zip -d -q "$1" org/apache/logging/log4j/core/lookup/JndiLookup.class
                    hasJndi=$(zip -v "$1" org/apache/logging/log4j/core/lookup/JndiLookup.class | grep including)
                    if [ "$hasJndi" ]
                    then
                        echo -e $BOLD$RED unable to secure:$RESET $1
                    else
                        echo -e $BOLD$YELLOW secured:$RESET $1
                    fi
                fi
                else echo -e $BOLD$GREEN found clean:$RESET $1
            fi
        fi
    }
    
    if command -v zip &> /dev/null
    then 
        export -f preventLog4Shell
        find / -name "log4j*.jar" -exec bash -c 'preventLog4Shell "$0"' {} \;
    else 
        echo You have to install “zip” first.
    fi
    

    Output looks like this:

    enter image description here

    Screenshot notes:

    • Two files were already cleaned before, thus “found clean”.
    • The last file was readonly, thus “unable to secure”.