#!/bin/bash
#
# BEGIN LICENSE #
#
# Copyright 2022 CERT/CC
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# END LICENSE #

tld="$1"

if [ -z "$tld" ]; then
    tld='.'
fi

unzip=$(which unzip)
if [ -z "$unzip" ]; then
    echo "This script requires unzip to function properly"
    exit 1
fi

process_jar() {
    islog4j=""
    patched=""
    hasjndi=""
    printed=""
    if [ -z "$2" ]; then
        parent="$1"
    else
        parent="$2"
    fi

    if [ -z "$3" ]; then
        subjarfilename=""
    else
        subjarfilename="$3"
    fi


    jndifile=$(unzip -l "$1" 2> /dev/null | grep -E "/JndiLookup.class$" | awk '{print $NF}')
    mpcfile=$(unzip -l "$1" 2> /dev/null | grep -E "/MessagePatternConverter.class$" | awk '{print $NF}')
    jarfiles=$(unzip -l "$1" 2> /dev/null | grep -Ei  ".jar$|.war$|.ear$|.zip$" | grep -v "Archive: " | awk '{print $NF}')
    if [ -n "$mpcfile" ]; then
        local outfile="$(mktemp)"
        unzip -p "$1" "$mpcfile" 2> /dev/null > "$outfile"
        ispatched=$(grep 'Message Lookups are no longer supported' "$outfile")
        if [ -n "$ispatched" ]; then
            # 2.16 is patched
            # https://github.com/apache/logging-log4j2/commit/27972043b76c9645476f561c5adc483dec6d3f5d#diff-22ae074d2f9606392a3e3710b34967731a6ad3bc4012b42e0d362c9f87e0d65bR97
            patched=1
        fi
        if [ -f "$outfile" ]; then
            rm "$outfile"
        fi
    fi

    if [ -n "$jndifile" ]; then
        hasjndi=1
        if [ -n "$subjarfilename" ]; then
            outputstring="$parent contains $subjarfilename contains JndiLookup.class"
        else
            outputstring="$parent contains JndiLookup.class"
        fi
        local jndioutfile="$(mktemp)"
        unzip -p "$1" "$jndifile" 2> /dev/null > "$jndioutfile"
        islog4j=$(grep 'LogEvent' "$jndioutfile")
        if [ -n "$islog4j" ]; then
            # The JndiLookup.class file is probably from log4j
            ispatched=$(grep 'JNDI is not supported' "$jndioutfile")
            if [ -n "$ispatched" ]; then
                # 2.12.2 is patched
                # https://github.com/apache/logging-log4j2/commit/70edc233343815d5efa043b54294a6fb065aa1c5#diff-4fde33b59714d0691a648fb2752ea1892502a815bdb40e83d3d6873abd163cdeR37
                patched=1
            fi
        fi
        if [ -f "$jndioutfile" ]; then
            rm "$jndioutfile"
        fi
    fi

    if [ ! -z "$jarfiles" ]; then
        for subjar in $jarfiles
            do
                local subjarfile="$(mktemp)"
                unzip -p "$1" "$subjar" 2> /dev/null > "$subjarfile"
                process_jar "$subjarfile" "$parent" "$subjar"
                if [ -f "$subjarfile" ]; then
                    rm "$subjarfile"
                fi
            done

    fi

    if [ -n "$patched" ]; then
        if [ -n "$islog4j" ]; then
            outputstring="$outputstring ** BUT APPEARS TO BE PATCHED **"
        fi
    fi

    if [ -z "$printed" ]; then
        if [ -n "$islog4j" ]; then
            if [ -n "$hasjndi" ]; then
                if [ -n "$patched" ]; then
                    echo "$outputstring"
                else
                    foundvulnerable=1
                    echo "WARNING: $outputstring"
                fi
                printed=1
            fi
        fi
    fi
}

checkfiles=$(find "$tld" -mount -type f \( -iname "*.jar" -o -iname "*.war" -o -iname "*.ear" -o -iname "*.zip" -o -iname "JndiLookup.class" \))


OLDIFS=$IFS
IFS=$'\n'
for checkfile in $checkfiles
    do
        if [[ $checkfile == *JndiLookup.class ]]; then
            islog4j=$(grep 'AbstractLookup' "$checkfile")
            if [ -n "$islog4j" ]; then
                echo "$checkfile *IS* JndiLookup.class"
            fi
        else
            process_jar "$checkfile"
        fi
    done
IFS=$OLDIFS

if [ ! "$foundvulnerable" == "1" ]; then
    echo "No vulnerable components found"
    exit 0
else
    exit 1
fi
