https://t.me/RX1948
Server : nginx/1.24.0
System : Linux ip-172-31-33-48 6.14.0-1011-aws #11~24.04.1-Ubuntu SMP Fri Aug 1 02:07:25 UTC 2025 x86_64
User : www-data ( 33)
PHP Version : 8.3.6
Disable Function : NONE
Directory :  /usr/share/ec2-instance-connect/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //usr/share/ec2-instance-connect/eic_parse_authorized_keys
#!/bin/sh

# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

# Reads authorized keys blob $3 and prints verified, unexpired keys
# Openssl to use provided as $1
# Signer public key file path provided as $2

set -e

# Set umask so only we can touch temp files
umask 077

# Failure reporting & exit
# Format: fail [is_debug] [message]
fail () {
    if [ "${1}" = true ] ; then
        /bin/echo "${2}"
    else
        /usr/bin/logger -i -p authpriv.info "${2}"
    fi
    exit 1
}

# Helper to determine if a string starts with a given prefix
# Format: startswith [string] [prefix]
startswith () {
    [ "${1#${2}}x" != "${1}x" ]
}

# Helper function to strip out a prefix from a string
# Format: removeprefix [string] [prefix]
removeprefix () {
    /usr/bin/printf '%s' "${1#${2}}"
}

# Helper to verify an arbitrary ocsp response body given the certificate and issuer
# Format: verifyocsp [is_debug] [openssl command] [certificate] [issuer] [ocsp directory]
verifyocsp() {
    # First check if this cert is already trusted
    cname=$("${2}" x509 -noout -subject -in "${3}" 2>/dev/null | /bin/sed -n -e 's/^.*CN[[:blank:]]*=[[:blank:]]*//p')
    fingerprint=$("${2}" x509 -noout -fingerprint -sha1 -inform pem -in "${3}"  2>/dev/null | /bin/sed -n 's/SHA1 Fingerprint[[:space:]]*=[[:space:]]*\(.*\)/\1/pI' | tr -d ':')
    ocsp_out=$("${2}" ocsp -no_nonce -issuer "${4}" -cert "${3}" -VAfile "${4}" -respin "${5}/${fingerprint}" 2>/dev/null)
    ocsp_exit="${?}"
    if [ "${ocsp_exit}" -ne 0 ] || ! startswith "${ocsp_out}" "${3}: good" ; then
        fail "${1}" "EC2 Instance Connect could not verify certificate ${cname} has not been revoked. No keys have been trusted."
    fi
}

while getopts ":x:p:o:d:s:i:c:a:v:f:" o; do
    case "${o}" in
        x)
            is_debug="${OPTARG}"
            ;;
        p)
            keys_path="${OPTARG}"
            ;;
        o)
            OPENSSL="${OPTARG}"
            ;;
        d)
            tmpdir="${OPTARG}"
            ;;
        s)
            signer="${OPTARG}"
            ;;
        i)
            current_instance_id="${OPTARG}"
            ;;
        c)
            expected_cn="${OPTARG}"
            ;;
        a)
            ca_path="${OPTARG}"
            ;;
        v)
            ocsp_dir_path="${OPTARG}"
            ;;
        f)
            expected_key="${OPTARG}"
            ;;
        *)
            /bin/echo "Usage: $0 [-x debug] [-r key read command] [-o openssl command] [-d tmpdir] [-s signer certificate] [-i instance id] [-c cname] [-a ca path] [-v ocsp dir] [-f key fingerprint]"
            exit 1
            ;;
    esac
done

# Verify we have sufficient inputs
if [ $# -lt 1 ] ; then
    # No args whatsoever
    # Unit test log (ignored by sshd)
    /bin/echo "Usage: $0 [-x debug] [-r key read command] [-o openssl command] [-d tmpdir] [-s signer certificate] [-i instance id] [-c cname] [-a ca path] [-v ocsp dir] [-f key fingerprint]"
    # System log
    /usr/bin/logger -i -p authpriv.info "Unable to run EC2 Instance Connect: insufficient args provided.  Please check your sshd configuration."
    exit 1
fi

# Verify the signer certificate we have been provided - CN, trust chain, and revocation status

# Split the chain into pieces
/bin/echo "${signer}" | /usr/bin/awk -v dir="${tmpdir}" 'split_after==1{n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > dir "/cert" n ".pem"}'

# We want visibility into the CA bundle so we can skip verifying entries in the chain that are already trusted
if [ -d "${ca_path}" ] ; then
    ca_path_dir=$ca_path
else
    ca_path_dir=$(dirname "${ca_path}")
fi
ca_bundles_dir=$(/bin/mktemp -d "${tmpdir}/eic-cert-XXXXXXXX")
end=$(/usr/bin/find "${tmpdir}" -maxdepth 1 -type f -name "cert*.pem" -regextype sed -regex ".*/cert[0-9]\+\.pem" | wc -l)
if [ "${end}" -gt 0 ] ; then
    # First see if we already have them
    for i in $(/usr/bin/seq 1 "${end}") ; do
        subject=$("${OPENSSL}" x509 -noout -subject -in "${tmpdir}/cert${i}.pem" | /bin/sed -n -e 's/^.*CN[[:space:]]*=[[:space:]]*//p')
        underscored=$(/bin/echo "${subject}" | /usr/bin/tr -s ' ' '_') 2>/dev/null
        if [ -f "${ca_path_dir}/${underscored}.pem" ] ; then
            # We already have it
            /bin/cp "${ca_path_dir}/${underscored}.pem" "${ca_bundles_dir}/${underscored}"
        else
            if [ ! -d "${ca_path}" ] ; then
                # Try to pull this CN from the CA bundle
                /bin/sed -n -e '/#[[:space:]]'"$subject"'$/,$p' "${ca_path}" 2>/dev/null | /bin/sed '/-----END[[:space:]]CERTIFICATE-----.*/,$d' | /bin/sed -n '1!p' > "${ca_bundles_dir}/${subject}"
                if [ -s "${ca_bundles_dir}/${subject}" ] ; then
                    /bin/echo "-----END CERTIFICATE-----" >> "${ca_bundles_dir}/${subject}"
                else
                    /bin/rm -f "${ca_bundles_dir}/${subject}"
                fi
            fi
        fi
    done
fi

# Build the intermediate trust chain
# We only need to verify the first intermediate certificate since it's signed by Amazon Root CA 1, which
# is already trusted by the system (in /etc/ssl/certs).
/usr/bin/cp "${tmpdir}/cert1.pem" "${tmpdir}/ca-trust.pem"

if [ -d "${ca_path}" ] ; then
    subject=$("${OPENSSL}" x509 -noout -subject -in "${tmpdir}/cert${end}.pem" | /bin/sed -n -e 's/^.*CN[[:space:]]*=[[:space:]]*//p')
    underscored=$(/bin/echo "${subject}" | /usr/bin/tr -s ' ' '_') 2>/dev/null
    /bin/cat "${ca_bundles_dir}/${underscored}" >> "${tmpdir}/ca-trust.pem" 2>/dev/null
else
    /bin/cat "${ca_path}" >> "${tmpdir}/ca-trust.pem" 2>/dev/null
fi
# At this point ca-trust is final
/bin/chmod 400 "${tmpdir}/ca-trust.pem"

# Verify the CN
signer_cn=$("${OPENSSL}" x509 -noout -subject -in "${tmpdir}/cert.pem" | /bin/sed -n -e 's/^.*CN[[:space:]]*=[[:space:]]*//p')
if [ "${signer_cn}" != "${expected_cn}" ] ; then
    fail "${is_debug}" "EC2 Instance Connect encountered an unrecognized signer certificate. No keys have been trusted."
fi

# Verify the trust chain
if [ -d "${ca_path}" ] ; then
    verify_out=$("${OPENSSL}" verify -x509_strict -CApath "${ca_path}" -CAfile "${tmpdir}/ca-trust.pem" "${tmpdir}/cert.pem")
    verify_status=$?
else
    # If the CA path is not a directory then do not use it - openssl will throw errors on versions 1.1.1+
    verify_out=$("${OPENSSL}" verify -x509_strict -CAfile "${tmpdir}/ca-trust.pem" "${tmpdir}/cert.pem")
    verify_status=$?
fi
if [ $verify_status -ne 0 ] || [ "${verify_out}" != "${tmpdir}/cert.pem: OK" ] ; then
    fail "${is_debug}" "EC2 Instance Connect could not verify the signer trust chain. No keys have been trusted."
fi

# Verify no certificates have been revoked
# Iterate from first to second-to-last cert & validate OCSP staples
/bin/mv "${tmpdir}/cert.pem" "${tmpdir}/cert0.pem" # Better naming consistency for loop
for i in $(/usr/bin/seq 0 $((end - 1))) ; do
    subject=$("${OPENSSL}" x509 -noout -subject -in "${tmpdir}/cert${i}.pem" | /bin/sed -n -e 's/^.*CN[[:space:]]*=[[:space:]]*//p')
    if [ -f "${ca_bundles_dir}/${subject}" ] ; then
        # If we encounter a certificate that's in the CA bundle we can skip the rest as implicitly trusted
        hash=$("${OPENSSL}" x509 -hash -noout -in "${tmpdir}/cert${i}.pem" 2>/dev/null)
        trusted_hash=$("${OPENSSL}" x509 -hash -noout -in "${ca_bundles_dir}/${subject}" 2>/dev/null)
        fingerprint=$("${OPENSSL}" x509 -noout -fingerprint -sha1 -in "${tmpdir}/cert${i}.pem" 2>/dev/null | /bin/sed -n 's/SHA1 Fingerprint[[:space:]]*=[[:space:]]*\(.*\)/\1/p' | tr -d ':')
        trusted_fingerprint=$("${OPENSSL}" x509 -noout -fingerprint -sha1 -in "${ca_bundles_dir}/${subject}" 2>/dev/null | /bin/sed -n 's/SHA1 Fingerprint[[:space:]]*=[[:space:]]*\(.*\)/\1/p' | tr -d ':')
        pkey=$("${OPENSSL}" x509 -pubkey -noout -in "${tmpdir}/cert${i}.pem")
        trusted_pkey=$("${OPENSSL}" x509 -pubkey -noout -in "${ca_bundles_dir}/${subject}" )
        if [ "${hash}" = "${trusted_hash}" ] && [ "${fingerprint}" = "${trusted_fingerprint}" ] && [ "${pkey}" = "${trusted_pkey}" ] ; then
            # Already trusted, no need to OCSP verify
            break
        fi
    fi
    verifyocsp "${is_debug}" "${OPENSSL}" "${tmpdir}/cert${i}.pem" "${tmpdir}/cert$((i + 1)).pem" "${ocsp_dir_path}"
done

# At this point we no longer need the CA information
/bin/rm -rf "${ca_bundles_dir}"

# Extract cert public key
/bin/echo "${signer}" | "${OPENSSL}" x509 -pubkey -noout > "${tmpdir}/pubkey" 2>/dev/null
extract="${?}"
if [ "${extract}" -ne 0 ] ; then
    fail "${is_debug}" "EC2 Instance Connect failed to extract the public key from the signer certificate. No keys have been trusted."
fi

# Begin actual parsing of authorized keys data

if [ -n "${expected_key+x}" ] ; then
    # An expected fingerprint was given
    if [ "${is_debug}" = false ] ; then
        /usr/bin/logger -i -p authpriv.info "Querying EC2 Instance Connect keys for matching fingerprint: ${expected_key}"
    fi
fi

# Set current time as expiration marker
curtime=$(/bin/date +%s)

# We want to prevent variables from leaving the parser's scope
# We also want to capture overall exit code
# We also need to redirect the input into our loop
# The simplest solution to all of the above is to take advantage of how sh pipes spawn a subprocess
output=$(
    exitcode=255 # Exit code if no valid keys are provided
    count=0

    # Read loop - pull timestamp line at start of iteration
    while read -r line
    do
        pathprefix="${tmpdir}/${count}"

        # Clear our temp buffers to prevent any sort of injection
        /bin/rm -f "${pathprefix}-key"
        /bin/rm -f "${pathprefix}-signedData"
        /bin/rm -f "${pathprefix}-sig"
        /bin/rm -f "${pathprefix}-decoded"

        # Pre-initialize key validation fields
        timestamp=0
        instance_id=""
        caller=""
        request=""
        # We do not pre-initialize the actual key or signature fields
        /bin/touch "${pathprefix}-signedData"

        # Loop to read keys & parse out values
        # This is not sub-shelled as we want to maintain variable scope with the outer loop
        # Loop condition is until we reach a line that lacks the "#Key" format
        while startswith "${line}" "#"
        do
            # Note that not all of these may be present depending on service deployments
            # Similarly, this list is not meant to be exhaustive - there may be new fields to be checked in a later version
            if startswith "${line}" "#Timestamp=" ; then
                timestamp=$(removeprefix "${line}" "#Timestamp=")
            elif startswith "${line}" "#Instance=" ; then
                instance_id=$(removeprefix "${line}" "#Instance=")
            elif startswith "${line}" "#Caller=" ; then
                caller=$(removeprefix "${line}" "#Caller=")
            elif startswith "${line}" "#Request=" ; then
                request=$(removeprefix "${line}" "#Request=")
            # Otherwise it's a #Key we don't recognize (i.e., this version of AuthorizedKeysCommand is outdated)
            fi

            # We verify on all fields in-order, whether we recognize them or not.  Similarly, we don't force fields not present.
            # As such we always add this to the signature verification file
            /usr/bin/printf '%s\n' "${line}" >> "${pathprefix}-signedData"

            # Read the next line
            read -r line
        done

        # At this point, line should contain the key
        if startswith "${line}" "ssh" ; then
            key="${line}"
            /usr/bin/printf '%s\n' "${key}" >> "${pathprefix}-signedData"

            # At this point we do not need to modify signedData
            /bin/chmod 400 "${pathprefix}-signedData"

            # Read key signature - may be multi-line
            encodedsigfile="${pathprefix}-sig"
            /bin/touch "${encodedsigfile}"
            read -r sigline || sigline=""
            while [ "${sigline}" != "" ]
            do
                /usr/bin/printf '%s\n' "${sigline}" >> "${encodedsigfile}"
                read -r sigline || sigline=""
            done
            /bin/chmod 400 "${encodedsigfile}"

            /usr/bin/printf '%s\n' "${key}" > "${pathprefix}-key"

            # Begin validation
            if [ -n "${instance_id}" ] && [ "${timestamp}" -ne 0 ] ; then
                fingerprint=$(/usr/bin/ssh-keygen -lf "${pathprefix}-key" | cut -d ' ' -f 2) # Get only the actual fingerprint, ignore key size & source
                # If we were told to expect a specific key and this isn't it, skip it
                if [ -z "${expected_key}" ] || [ "$fingerprint" = "${expected_key}" ] ; then
                    # Check instance ID matches & timestamp is still valid
                    if [ "${current_instance_id}" = "${instance_id}" ] && [ "${timestamp}" -gt "${curtime}" ] ; then
                        # Decode the signature
                        (/usr/bin/base64 --decode "${encodedsigfile}" > "${pathprefix}-decoded" || rm -f "${pathprefix}-decoded") 2>/dev/null
                        if [ -f "${pathprefix}-decoded" ] ; then
                            # Verify signature
                            $OPENSSL dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -verify "${tmpdir}/pubkey" -signature "${pathprefix}-decoded" "${pathprefix}-signedData" 1>/dev/null 2>/dev/null
                            verify="${?}"
                            if [ "${verify}" -eq 0 ] ; then
                                # Signature verified.
                                # Record this in syslog
                                callermessage="Providing ssh key from EC2 Instance Connect with fingerprint: ${fingerprint}"
                                if [ "${request}" != "" ] ; then
                                    callermessage="${callermessage}, request-id: ${request}"
                                fi
                                if [ "${caller}" != "" ] ; then
                                    callermessage="${callermessage}, for IAM principal: ${caller}"
                                fi
                                if [ "${is_debug}" = false ] ; then
                                    /usr/bin/logger -i -p authpriv.info "${callermessage}"
                                fi
                                # Return key to the ssh daemon
                                /bin/echo "${key}"
                                exitcode=0
                            fi
                        fi
                    fi
                fi
            fi
        else
            # We didn't find a key.  Skip until we hit a blank line or EOF
            while [ "${line}" != "" ]
            do
                read -r line || line=""
            done
        fi

        # Clean up any tempfiles
        /bin/rm -f "${pathprefix}-key"
        /bin/rm -f "${pathprefix}-signedData"
        /bin/rm -f "${pathprefix}-sig"
        /bin/rm -f "${pathprefix}-decoded"

        count=$((count + 1))
    done < "${keys_path}"
    # This is the loop subprocess's exit code, not the script's
    exit $exitcode
)

# Re-capture the exit code
exitcode=$?
# Print keys & exit
/bin/rm -rf "${tmpdir}/pubkey"
/bin/echo "${output}"
exit $exitcode

https://t.me/RX1948 - 2025