hotfix securing many log4j jars against log4shell

627 views Asked by At

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.
1

There are 1 answers

0
Marcus On BEST ANSWER

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”.