Warning: chmod(): Operation not permitted in /data/html/improve/wp-content/plugins/wordfence/wordfence.php on line 8
403WebShell
403Webshell
Server IP : 52.9.100.119  /  Your IP : 216.73.216.194
Web Server : Apache/2.4.61 (Amazon) OpenSSL/1.0.2k-fips PHP/7.3.30 Phusion_Passenger/5.1.5
System : Linux ip-172-31-9-35 4.14.355-196.618.amzn1.x86_64 #1 SMP Mon Apr 7 17:09:50 UTC 2025 x86_64
User : root ( 0)
PHP Version : 7.3.30
Disable Function : exec,passthru,shell_exec,system,popen,proc_open
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : OFF
Directory :  /usr/bin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/bin/log4j-cve-2021-44228-hotpatch
#!/bin/bash

# This reduces hotpatch usage from 75 MiB RSS to 11 MiB
JVMOPTS="-Xint -XX:+UseSerialGC -Dlog4jFixerVerbose=false"

MIN_WAIT=0
WAIT_DELAY=0

# sbin is not included when run as a cron job
PATH="/sbin:/usr/sbin:$PATH"

function log() {
    echo "[log4j-hotpatch] $@"

    # Do not fail if syslog is being wonky
    logger -d -p user.notice "$*" || true
}

function extract_proc_status() {
    PID="$1"
    REGEX=$2

    UIDS=$(grep $REGEX /proc/$PID/status | cut -f2- -d:)
    RC=$?
    if test "$RC" != 0; then
        return $RC
    fi

    echo $UIDS | cut -f 2 -d' '
}

function extract_euid() {
    extract_proc_status $1 '^Uid:'
}

function extract_egid() {
    extract_proc_status $1 '^Gid:'
}

function pidof_sorted() {
    ALL_PIDS=$(pidof "$@")
    RC=$?
    if test "$RC" -ne 0; then
        return $RC
    fi

    echo $(for pid in $ALL_PIDS; do echo $pid; done | sort)
}

function now() {
    date "+%s"
}

function jvm_is_ready() {
    pid=$1

    if [ ! -d /proc/$pid ]; then
	# Process may have exited
	return 0
    fi

    if $(grep -q ReduceSignalUsage /proc/$pid/cmdline); then
	# ReduceSignalUsage means signal will never be registered
	# So we can look at thread names to see
	if test $(ps -T -p $pid | egrep -c '(Signal Dispatcher|Attach Listener)') -gt 0; then
	    return 0;
	fi
	return 1;
    fi

    JVM_SIG_HANDLERS="0x$(grep SigCgt /proc/$pid/status|cut -f 2)"

    # We look at 4 as it's bit 3, and SigCgt is a bitmask of signals
    # and we're looking for signal 3.
    if test $(( $JVM_SIG_HANDLERS & 0x4 )) -ne 0 ; then
	return 0;
    fi

    return 2;
}

function wait_jvm_ready() {
    pid=$1
    timeout=$2

    while test $timeout -gt 0; do
	if jvm_is_ready $pid; then
	    break;
	else
	    log "JVM $pid not started yet, waiting..."
	    sleep 1
	fi
	timeout=$(expr $timeout - 1 )
    done
}

# Usage: get_java_starttime <pid>
#
# Get the start time of java process <pid>
# Note: returns 1 if not a java process
function get_java_starttime() {
	local pid=$1
	local STAT="$(cat /proc/${pid}/stat)"
	local COMM="$(echo $STAT | cut -d ' ' -f 2)"
	local STARTTIME="$(echo $STAT | cut -d ' ' -f 22)"

	if [ $(echo $STAT | awk -F ' ' '{print NF}') -ne 52 ]; then
		# There should be 52 fields in stat. Either the layout of stat
		# has changed of there are spaces in the comm field, either way
		# bail out.
		return 1
	fi

	if [ "$COMM" != "(java)" ]; then
		return 1
	fi

	echo "$STARTTIME"
	return 0
}

# Usage: run_in_cgroup <pid> <starttime> command <args>
#
# pid: pid who's cgroups are to be matched
# starttime: expected start time for that process
# Note: this doesn't restore the cgroups after execing the command and so
#       exits after so as to avoid misuse.
#       Also since this is run in a separate shell any functions it calls
#       must be declared
function run_in_cgroup() {
	local pid=$1
	local starttime=$2
	shift 2
	local MYPID=$$

	# Move ourselves into all the cgroups of the target process
	# /proc/<pid>/cgroup in the form:
	#       <hierarchy>:<controller(s)>:<cgroup>
	while read -r line; do
		controller="$(echo $line | cut -d ':' -f 2)"
		if [ -z "$controller" ]; then
			controller="cgroup2"
		fi
		group="$(echo $line | cut -d ':' -f 3)"

		# / is the root group which all processes belong to
		if [ "$group" != "/" ]; then
			# Find the mountpoint of the cgroup
			if [ "$controller" == "cgroup2" ]; then
				options="-t cgroup2"
			else
				options="-O ${controller} -t cgroup"
			fi

			mountpoint="$(findmnt -r -u -n -f -o target ${options})"
			RC=$?
			if [ $RC -ne 0 ]; then
				return $RC
			fi

			# Add ourselves to the cgroup
			# NOTE: we might already be in it
			echo $MYPID > ${mountpoint}${group}/cgroup.procs
		fi
	done </proc/${pid}/cgroup

	# We've read everything about the process we're going to before we
	# exec it. Check one last time that it's the process we expect
	# by verifying the start time.
	TIME="$(get_java_starttime $pid)"
	if [ $? -ne 0 ]; then
		exit 1
	fi
	if [ "$starttime" != "$TIME" ]; then
		exit 1
	fi

	# Run the command
	"$@"
	exit $?
}

# Usage: gen_capsh_cmd <pid> <ambient>
#
# Generates a capsh command to run to match the bounding, inherited and (maybe)
# ambient capability sets of the target <pid>
function gen_capsh_cmd() {
	local PID=$1
	local AMB=$2
	local CMD=$3
	local MASK=0xffffffff
	local OPTIONS=""

	# determine the highest supported capability
	for i in $(seq 0 63); do
		cap="$(capsh --decode=$(printf '%x\n' $((1 << i))) | cut -d '=' -f 2)"
		if ! sudo capsh --drop="$cap" 2>/dev/null; then
			MASK=$(((1 << i) - 1))
			break
		fi
	done

	# Does our bounding set exceed theirs?
	CAPBND=$(extract_proc_status $$ '^CapBnd:')
	CAPBND_TARGET=$(extract_proc_status ${PID} '^CapBnd:')
	DROPSET=$((0x${CAPBND} & ~0x${CAPBND_TARGET}))
	if [ $DROPSET -ne 0 ]; then
		DROPCAPS="$(capsh --decode=$(printf '%x\n' $((${MASK} & ${DROPSET}))) | cut -d '=' -f 2)"
		OPTIONS="${OPTIONS}--drop=${DROPCAPS} "
	fi

	# Does our inherited (and/or ambient) set exceed theirs?
	CAPINH=$(extract_proc_status $$ '^CapInh:')
	CAPINH_TARGET=$(extract_proc_status ${PID} '^CapInh:')
	if [ $AMB -eq 1 ]; then
		CAPAMB=$(extract_proc_status $$ '^CapAmb:')
		CAPAMB_TARGET=$(extract_proc_status ${PID} '^CapAmb:')
	else
		CAPAMB=0
		CAPAMB_TARGET=0
	fi
	DROPSET=$(($((0x${CAPINH} & ~0x${CAPINH_TARGET})) | $((0x${CAPAMB} & ~0x${CAPAMB_TARGET}))))
	if [ $DROPSET -ne 0 ]; then
		DROPCAPS="$(capsh --decode=$(printf '%x\n' $((${MASK} & ${DROPSET}))) | cut -d '=' -f 2)"
		CURRENTCAPS="$(capsh --print | grep "Current: " | cut -d ' ' -f 1 --complement)"
		OPTIONS="${OPTIONS}--caps=\"${CURRENTCAPS} ${DROPCAPS}-i\" "
	fi

	echo "${OPTIONS} "
}

function apparmor_constrained() {
    local pid=$1
    if [ -e /proc/$pid/attr/apparmor/current ] &&
           cat /proc/$pid/attr/apparmor/current | grep -E -q '\(enforce\)'; then
        return 0
    fi
    return 1
}

function rm_tmpfiles() {
	# Unmount any ns files
	for file in $(ls ${NSDIR}); do
		umount ${NSDIR}/$file
		rm -f ${NSDIR}/$file
	done
	umount $NSDIR
	rmdir $NSDIR

	# Remove in case we exited before unlinking it
	if [ -n "$MYNSENTER" ] && [ -e $MYNSENTER ]; then
		rm -f $MYNSENTER
	fi
}

# Usage: run_cmd <pid> <euid> <egid> <seccomp> <file> <apparmor> <starttime> "command <args>"
#
# Run a command as a given euid and egid, and in an environment matching that of
# the target <pid>
# If <seccomp> is 1 match the seccomp state of the target process
# <file> is the name of a file to open an fd to in the spawned process (fd 4)
# <apparmor> is the apparmor profile to match
# <starttime> is the expected start time of process <pid<> (used for verification)
# !!! NOTE WELL !!!
# Anything passed as the command to this function will be run via bash -c "$cmd"
# This means it will be evaluated ON THE HOST AS ROOT so be VERY careful when
# passing anything a container or unprivileged user controls in the command
function run_cmd() {
	local PID="$1"
	local MYEUID=$2
	local MYEGID=$3
	local SECCOMP=$4
	local FILE=${5:-/dev/null}
        local APPARMOR_PROFILE=$6
	local STARTTIME=$7
        shift 7
	MYNSENTER=""
	local NEED_NSENTER=0
	local NSENTER=""
	local NEED_CAPSH=0
	local OPTIONS=""
	local CMD=""
        local APPARMOR

	if [ "$EUID" != "0" ]; then
		log "Function run_cmd() must be run as root"
		return 1
	fi

	for exe in nsenter_aws nsenter; do
		if command -v $exe > /dev/null 2>&1; then
			NSENTER=$exe
			break
		fi
	done

	# Create a tmpdir for all the namespaces
	NSDIR=$(mktemp -d -q --tmpdir log4j-hotpatch.XXXXXXXXXX)
	if [ $? -ne 0 ]; then
		log "unable to create tmp dir"
		return 1
	fi
	# Bind mount then make unbindable; otherwise can't bind mount mount_namespace
	mount --bind $NSDIR $NSDIR && \
	mount --make-unbindable $NSDIR
	if [ $? -ne 0 ]; then
		log "unable to bind mount tmp dir $NSDIR"
		rm_tmpfiles
		return 1
	fi
	# Check which namespaces we need to enter
	for ns in "mnt" "net" "ipc" "uts" "pid"; do
		if [ "$(readlink /proc/$$/ns/${ns})" != "$(readlink /proc/${PID}/ns/${ns})" ]; then
			if [ -z "$NSENTER" ]; then
				log "nsenter required but not present on system"
				rm_tmpfiles
				return 1
			fi

			NEED_NSENTER=1

			# Bind mount the namespace so we have an immutable copy
			touch ${NSDIR}/${ns} && \
			mount --bind /proc/${PID}/ns/${ns} ${NSDIR}/$ns
			if [ $? -ne 0 ]; then
				log "unable to bind mount ns $ns"
				rm_tmpfiles
				return 1
			fi
			# It happens the option is the same as the ns file except for mount
			nsopt="--${ns}"
			if [ "$ns" == "mnt" ]; then
				nsopt="--mount"
			fi
			OPTIONS="${OPTIONS}${nsopt}=${NSDIR}/${ns} "
		fi
	done

	# Check for seccomp filters
	if [ "$SECCOMP" == "1" ]; then
		seccomp="$(grep "^Seccomp:" /proc/${PID}/status | awk '{ print $2 }')"
		if [ "$seccomp" == "1" ]; then
			# Strict mode, we can't match this so just bail out
			log "Target process running in Strict seccomp mode -> we can't match that"
			rm_tmpfiles
			return 1
		elif [ "$seccomp" == "2" ]; then
			# Filters applied, try to match them
			if [ -z "$NSENTER" ]; then
				log "Targeting process with seccomp filters -> require nsenter"
				rm_tmpfiles
				return 1
			elif ! "$NSENTER" --help 2>/dev/null | grep -q "^ -s"; then
				# We need nsenter to support the "-s" option
				log "Targeting process with seccomp filters -> require \"${NSENTER} -s\" support"
				rm_tmpfiles
				return 1
			else
				NEED_NSENTER=1
				OPTIONS="${OPTIONS}-s "
			fi
		fi
	fi

	# Check if the capabilities match
	if [ "$MYEUID" == "0" ]; then
		# For root
		# - We only care about ambient, inherited and bounding capabilities
		# - The effective & permitted sets are recalculated on exec
		if [ "$(grep -E '^CapInh:|^CapBnd:|^CapAmb:' /proc/$$/status)" != \
		     "$(grep -E '^CapInh:|^CapBnd:|^CapAmb:' /proc/${PID}/status)" ]; then
			if [ $NEED_NSENTER -eq 1 ]; then
				# We need NSENTER with the "-P" option
				if ! "$NSENTER" --help 2>/dev/null | grep -q "^ -P"; then
					log "Targeting root process in container with capabilities mismatch -> require \"${NSENTER} -P\""
					rm_tmpfiles
					return 1
				fi

				# NEED_NSENTER == 1
				OPTIONS="${OPTIONS}-P "
			else
				# Since we're not using nsenter we can match the caps with capsh
				# Our capsh may not have the capability of manipulating the ambient set
				# so we have to be careful here so that we don't grant capabilities from
				# our ambient set which the process couldn't otherwise get.
				# Match the bounding and inherited sets of the target process.
				# If our ambient set exceeds the ambient set of the target process further
				# reduce our inherited set to that of the ambient set of the target process
				# which will clear the corresponding caps from our ambient set.
				# Note: The ambient set must be a subset of the inherited set
				NEED_CAPSH=1
				OPTIONS="$(gen_capsh_cmd $PID 1) "
			fi
		fi
	else
		# For non-root:
		# - We only care about inherited and bounding capabilities
		# - The effective, permitted and ambient sets are cleared on uid change
		if [ "$(grep -E '^CapInh:|^CapBnd:' /proc/$$/status)" != \
		     "$(grep -E '^CapInh:|^CapBnd:' /proc/${PID}/status)" ]; then
			if [ $NEED_NSENTER -eq 1 ]; then
				# We need NSENTER with the "-P" option
				if ! "$NSENTER" --help 2>/dev/null | grep -q "^ -P"; then
					log "Targeting process in container with capabilities mismatch -> require \"${NSENTER} -P\""
					rm_tmpfiles
					return 1
				fi

				NEED_NSENTER=1
				OPTIONS="${OPTIONS}-P "
			else
				# Since we're not using nsenter we can match the caps with capsh
				NEED_CAPSH=1
				OPTIONS="$(gen_capsh_cmd $PID 0) "
			fi
		fi
	fi

	# If we don't need nsenter we can get away with just using setpriv
	# Set no_new_privs so that the process can't gain capabilities through exec
	if ! setpriv --help >/dev/null 2>&1; then
		log "setpriv required but not present on system"
		rm_tmpfiles
		return 1
	fi

        # On systems that enable AppArmor, Docker applies a default profile
        # allowing the containerized process ptrace access to other processes
        # with the same profile.  So we assume the target process's profile in
        # order to allow the process to access our file descriptors via
        # /proc/<pid/fd/.  In the case where a process is running with a custom
        # profile that doesn't permit ptrace, patching will fail.
        if [ -n "$APPARMOR_PROFILE" ]; then
            if command -v aa-exec > /dev/null 2>&1; then
                APPARMOR="aa-exec -p $APPARMOR_PROFILE"
            fi
        fi

	if [ $NEED_NSENTER -eq 1 ]; then
		# Everywhere which sets NEED_NSENTER already checks for it's presence
		# We need to be a bit careful here so that the nsenter binary can't be
		# modified from within the mount namespace nsenter itself is joining.
		# Do this by creating a tmp file which is made read/exec only and unlinked
		MYNSENTER=$(mktemp)
		if [ $? != 0 ]; then
			log "unable to create tmp file"
			rm_tmpfiles
			return 1
		fi

		if ! install -m 500 $(command -v ${NSENTER}) ${MYNSENTER} >/dev/null 2>&1; then
			log "unable to install ${NSENTER} to ${MYNSENTER}"
			rm_tmpfiles
			return 1
		fi

		CMD="setpriv --nnp $APPARMOR /proc/self/fd/5 -t $PID -S $MYEUID -G $MYEGID -Z ${OPTIONS}$@"
	else
		CMD="setpriv --nnp --reuid $MYEUID --regid $MYEGID --clear-groups $APPARMOR $@"
		if [ $NEED_CAPSH -eq 1 ]; then
			if ! capsh --help >/dev/null 2>&1; then
				log "capsh required but not present on system"
				rm_tmpfiles
				return 1
			fi
			CMD="capsh ${OPTIONS}-- -c \"${CMD}\""
		fi
	fi

	# Run as a new process to avoid needing to restore the cgroups.
	# As a bit of fun, not all cgroups are preserved across sudo so DON'T
	# call sudo AFTER run_in_cgroup. Use e.g. setpriv instead.
	# Still call bash with sudo so as to close any open FDs
	# In the new process fd's are assigned as follows
	# 4 -> file provided as arg to function
	# 5 -> nsenter binary as an fd
	FUNC_A="$(declare -f run_in_cgroup)"
	FUNC_B="$(declare -f get_java_starttime)"
	sudo bash -c "
exec 4<${FILE}
exec 5<${MYNSENTER:-/dev/null}
if [ -n \"$MYNSENTER\" ] && ! unlink ${MYNSENTER}; then
	exit 1
fi
$FUNC_A
$FUNC_B
run_in_cgroup $PID $STARTTIME $CMD
"
	local RC=$?

	rm_tmpfiles

	return $RC
}

# Usage: is_dumpable <pid> <euid> <egid>
#
# Checks if a process <pid> is dumpable (MMF_DUMPABLE_MASK)
# Use the fact that if a process is _NOT_ dumpable then CAP_SYS_PTRACE is needed
# to read certain /proc/<pid> files
function is_dumpable() {
	capsh --drop="cap_sys_ptrace" -- -c "sudo -u \"#$2\" -g \"#$3\" ls -l /proc/${1}/root" >/dev/null 2>&1
}

# Usage: can_patch <pid> <euid> <egid>
#
# Checks if a process can be patched.
# The below are reasons it might not be able to be:
# - The process is _NOT_ dumpable and we won't have CAP_SYS_PTRACE
function can_patch() {
	local PID=$1
	local MYEUID=$2
	local MYEGID=$3

	# 1. Process can't be patched if it's not dumpable and no cap_sys_ptrace
	# Assume if the target process has cap_sys_ptrace (0x80000) we will to
	CAPEFF=$(extract_proc_status $PID "^CapEff:")
	PTRACE=$((0x${CAPEFF} & 0x80000))
	if is_dumpable $PID $MYEUID $MYEGID || [ $PTRACE -ne 0 ]; then
		return 0
	fi

	return 1
}

function apply_patch() {
    local SECCOMP=1
    local APPARMOR_PROFILE
    log "Starting up now..."

    # Remove these early such that an premature exit doesn't result in a false
    # result.
    rm -f /dev/shm/log4j-cve-2021-44228-hotpatch.good
    rm -f /dev/shm/log4j-cve-2021-44228-hotpatch.bad

    PATCHED_JVMS=0
    TOTAL_JVMS=0

    PIDS=$(pidof_sorted java)
    RC=$?
    if test "$RC" = "0"; then
        log "Found JVMs with pids [$PIDS]"
        for pid in $PIDS; do
            log "Attempting to patch $pid"

            wait_jvm_ready $pid 120

            # We want to make sure that we're patching the java process that
            # we think we are. That is that all of the information which we
            # read about a process (exe, uid, gid, cgroup, ns etc.) relates to
            # the same java process and is the one we ultimately end up
            # patching. There is a window between where we read all the java
            # pids, where we read the associated information and where we
            # patch where the original java process could have exited and the
            # pid been reused meaning some of the information we read could be
            # stale. Thus we want to store a way to identify the process which
            # we can check right before we patch it to ensure that it's the same
            # process and we might not be executing a protentially malicious
            # java exe in for example the wrong cgroup or namespace. Use the
            # start time for this and check it right before we perform the
            # actual patching.
            STARTTIME=$(get_java_starttime $pid)
            if [ $? -ne 0 ]; then
                log "Process pid $pid no longer a java process, skipping."
                continue
            fi
            # Wait 2 seconds since start time has 1 second granularity to ensure
            # there is no window the java process could have been replaced by
            # another java process while we read critical information. If it
            # was replaced in that first 1 second where the start time would
            # match that's fine since we'd patch the new java process on the
            # next iteration anyway. If it was replaced by a java process which
            # started later than 1 sec after then skip it, it will be patched on
            # the next iteration. And if it was replaced by not a java process
            # definitely skip it.
            sleep 2
            TIME=$(get_java_starttime $pid)
            if [ $? -ne 0 ]; then
                log "Process $pid no longer a java process, skipping."
                continue
            fi
            if [ "$STARTTIME" != "$TIME" ]; then
                log "Process $pid replaced, skipping."
                continue
            fi

            # Check if the PID is associated with a container.
            # Get the PID in the lowest namespace; if this isn't the same as the
            # PID we see, then we need to use nsenter for operations below
            container_pid=$(echo $(grep "^NSpid:" /proc/$pid/status) | awk '{ print $NF }')
            if [ "$container_pid" != "$pid" ]; then
                log "Running in a container with container pid: $container_pid"
            else
                container_pid=""
            fi
            TOTAL_JVMS=$(($TOTAL_JVMS + 1))

            MYEUID=$(extract_euid $pid)
            RC=$?
            if test "$RC" -ne "0"; then
                log "Failed to find effective UID for pid $pid, skipping."
                continue
            fi

            MYEGID=$(extract_egid $pid)
            RC=$?
            if test "$RC" -ne "0"; then
                log "Failed to find effective GID for pid $pid, skipping."
                continue
            fi

            log "Found JVM running with effective UID of $MYEUID"

            APPARMOR_PROFILE=""
            if apparmor_constrained $pid; then
                APPARMOR_PROFILE=$(cat /proc/$pid/attr/apparmor/current | awk '{print $1}')
            fi

            JVM="$(readlink /proc/$pid/exe)"
            RC=$?
            if test "$RC" -ne "0"; then
                log "Failed to read exe name from pid $pid, skipping."
                continue
            fi
            if grep -e "[^a-zA-Z0-9/_. -]" <<<$JVM; then
                log "JVM binary \"$JVM\" for pid $pid contains special characters, skipping for safety."
                continue
            fi

            log "Found JVM for pid $pid at $JVM"

            FULL_VERSION_OUT=$(run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" "" \
                                       "$APPARMOR_PROFILE" "$STARTTIME" "$JVM -version" 2>&1)
            RC=$?
            if [ $RC -ne 0 ]; then
                # Try without seccomp first
                SECCOMP=0
                FULL_VERSION_OUT=$(run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" "" \
                                           "$APPARMOR_PROFILE" "$STARTTIME" "$JVM -version" 2>&1)
                RC=$?
            fi
            if [ $RC -ne 0 ]; then
                log "Failed to execute JVM to determine version, skipping"
                continue
            fi
            if [ $SECCOMP -ne 1 ]; then
                log "Warning: Continuing without applying seccomp filters"
            fi
            FULL_VERSION=$(echo "${FULL_VERSION_OUT}" | head -1)

            log "JVM version is $FULL_VERSION"

            KIND=$(echo $FULL_VERSION | cut -f1 -d' ')
            SEMVER=$(echo $FULL_VERSION | cut -f3 -d' ' | sed -e 's:"::g')
            MAJOR=$(echo $SEMVER | cut -f1 -d'.')
            MAJMIN=$(echo $SEMVER | cut -f1-2 -d'.')

            if test "$KIND" != "openjdk" -a "$KIND" != "java"; then
                log "Skipping unsupported JVM kind: $KIND"
                continue
            fi

            log "Identified JVM[$KIND] of ($FULL_VERSION) with major version $MAJOR"

            if ! can_patch $pid $MYEUID $MYEGID; then
                log "Skipping unpatchable java process: $pid"
                continue
            fi

            if test "$MAJOR" = "11" -o "$MAJOR" = "15"; then
                log "Using Java 11 hotpatch"

                if [ ! -z "$container_pid" ]; then
		    # The jar is provided to the java process via fd-4
                    run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" \
                            "/usr/share/log4j-cve-2021-44228-hotpatch/jdk11/Log4jHotPatch.jar" "$APPARMOR_PROFILE" "$STARTTIME" \
                            "$JVM $JVMOPTS -cp /proc/self/fd/4 Log4jHotPatch $container_pid"
                    RC=$?
                else
                    run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" "" "$APPARMOR_PROFILE" "$STARTTIME" \
                            "$JVM $JVMOPTS -cp /usr/share/log4j-cve-2021-44228-hotpatch/jdk11/Log4jHotPatch.jar Log4jHotPatch $pid"
                    RC=$?
                fi

                log "Hotpatch application returned $RC"

                if test "$RC" = 0; then
                    PATCHED_JVMS=$(($PATCHED_JVMS + 1))
                fi
            elif test "$MAJOR" = "17"; then
                log "Using Java 17 hotpatch"

                if [ ! -z "$container_pid" ]; then
		    # The jar is provided to the java process via fd-4
                    run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" \
                            "/usr/share/log4j-cve-2021-44228-hotpatch/jdk17/Log4jHotPatchFat.jar" "$APPARMOR_PROFILE" "$STARTTIME" \
                            "$JVM $JVMOPTS -cp /proc/self/fd/4 -DfatJar=/proc/self/fd/4 Log4jHotPatch17 $container_pid"
                    RC=$?
                else
                    run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" "" "$APPARMOR_PROFILE" "$STARTTIME" \
                            "$JVM $JVMOPTS -cp /usr/share/log4j-cve-2021-44228-hotpatch/jdk17/Log4jHotPatchFat.jar \
                            -DfatJar=/usr/share/log4j-cve-2021-44228-hotpatch/jdk17/Log4jHotPatchFat.jar Log4jHotPatch17 $pid"
                    RC=$?
                fi

                log "Hotpatch application returned $RC"

                if test "$RC" = 0; then
                    PATCHED_JVMS=$(($PATCHED_JVMS + 1))
                fi
            elif test "$MAJMIN" = "1.8"; then
                log "Using Java 8 hotpatch"

                BINDIR=$(dirname "$JVM")
                log "Extracted BINDIR of $BINDIR"

                BASEDIR=$(dirname "$BINDIR")
                log "Extracted BASEDIR of $BASEDIR"

                DIRNAME=$(basename "$BASEDIR")
                log "Extracted DIRNAME of $DIRNAME"

                # Sometimes java is invoked as $JAVAHOME/jre/bin/java verses $JAVAHOME/bin/java, try to correct for this.
                if test "$DIRNAME" = "jre"; then
                    BASEDIR=$(dirname "$BASEDIR")
                fi

                if [ ! -z "$container_pid" ]; then
                    # Test for existence of the tools.jar file in the container via
                    # /proc/<pid>/root. Since this is done in our mount_namespace
                    # the container could have created a symlink to somewhere the jar
                    # doesn't exist to avoid being patched, but to do this you would
                    # need root or to be in control of the make-up of the container
                    # anyway in which case you can just delete/omit it to the same end.
                    if ! test -e "/proc/$pid/root/$BASEDIR/lib/tools.jar"; then
                        log "Could not find tools.jar in JVM lib, skipping"
                        continue
                    fi
                else
                    if ! test -e "$BASEDIR/lib/tools.jar"; then
                        log "Could not find tools.jar in JVM lib, skipping"
                        continue
                    fi
                fi

                if [ ! -z "$container_pid" ]; then
		    # The jar is provided to the java process via fd-4
                    run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" \
                            "/usr/share/log4j-cve-2021-44228-hotpatch/jdk8/Log4jHotPatch.jar" "$APPARMOR_PROFILE" "$STARTTIME" \
                            "$JVM $JVMOPTS -cp \"$BASEDIR/lib/tools.jar:/proc/self/fd/4\" Log4jHotPatch $container_pid"
                    RC=$?
                else
                    run_cmd "$pid" "$MYEUID" "$MYEGID" "$SECCOMP" "" "$APPARMOR_PROFILE" "$STARTTIME" \
                            "$JVM $JVMOPTS -cp \"$BASEDIR/lib/tools.jar:/usr/share/log4j-cve-2021-44228-hotpatch/jdk8/Log4jHotPatch.jar\" Log4jHotPatch $pid"
                    RC=$?
                fi

                log "Hotpatch application returned $RC"

                if test "$RC" = 0; then
                    PATCHED_JVMS=$(($PATCHED_JVMS + 1))
                fi
            else
                log "Unsupported Java major version $MAJOR, skipping"
            fi
        done
    else
        log "No JVMs found, exiting gracefully"
    fi

    # Leave a file to indicate that we're either patched or unpatched
    if test "$PATCHED_JVMS" -lt "$TOTAL_JVMS"; then
        touch /dev/shm/log4j-cve-2021-44228-hotpatch.bad
    else
        touch /dev/shm/log4j-cve-2021-44228-hotpatch.good
    fi
}

function main() {
    while test "$1"; do
        OPT="$1"
        shift

        case "$OPT" in
            -w)
                WAIT_DELAY="$1"
                shift
                ;;
            -m)
                MIN_WAIT="$1"
                shift
                ;;
            *)
                echo "Unexpected option '$OPT'"
                break
                ;;
        esac
    done

    # This file allows disabling the hotpatch from being reapplied
    # in case something goes wrong and we need an Andon Cord.
    if test -e /dev/shm/log4j-cve-2021-44228-hotpatch.kill -o -e /etc/log4j-cve-2021-44228-hotpatch.kill; then
        log "Found kill file, not applying patch."
    else
        apply_patch
    fi

    sleep "$MIN_WAIT"

    START="$(now)"
    NOW="$START"
    END=$(($START + $WAIT_DELAY))

    while test "$NOW" -lt "$END"; do
        NEW_PIDS=$(pidof_sorted java)

        if test "$PIDS" = "$NEW_PIDS"; then
            sleep 1
        else
            break
        fi

        NOW="$(now)"
    done
}

main "$@"

Youez - 2016 - github.com/yon3zu
LinuXploit