时间:2026-02-02 18:41
人气:
作者:admin
文中技术分析仅供交流讨论,poc仅供合法测试,用于企业自查,切勿用于非法测试,未授权测试造成后果由使用者承担,与本公众号以及棉花糖无关。
近日,Ivanti公司披露了Ivanti Endpoint Manager Mobile (EPMM)中存在的代码注入漏洞(CVE-2026-1281和CVE-2026-1340),并确认已存在在野利用。该漏洞源于 Apache HTTPd 调用的 Bash 脚本在处理时间戳比较时,未能有效过滤恶意参数,导致攻击者可利用 Bash 算术扩展特性注入系统命令。
首先拿到补丁包

RPM的包那就好办了,直接查看它执行了什么即可,使用命令:
rpm -qp --scripts ivanti-security-update-1761642-1.0.0L-5.noarch.rpm

emmm内容有点多,不过根据已知条件,该漏洞源于 Apache HTTPd ,补丁包里面很容易看到关键的修改Apache HTTPd配置的命令:
/bin/sed -i \
-e 's|RewriteMap mapAppStoreURL prg:/mi/bin/map-appstore-url|RewriteMap mapAppStoreURL "prg:/bin/java -cp /mi/bin AppStoreUrlMapper"|g' \
-e 's|RewriteMap mapAftStoreURL prg:/mi/bin/map-aft-store-url|RewriteMap mapAftStoreURL "prg:/bin/java -cp /mi/bin AFTUrlMapper"|g' \
/etc/httpd/conf.d/ssl.conf
就是说把map-appstore-url和map-aft-store-url给换掉了不用是吧,ok我们去看看这俩脚本是什么,目录已经给了在mi/bin下,我们直接进终端查一下

map-appstore-url和map-aft-store-url是个bash脚本,cat就可以直接看内容(另一个脚本内容差不太多,就不展示了)。
#!/bin/bash
set -o nounset
declare -x MI_DATE_COMMAND="date +%Y-%m-%d--%H-%M-%S"
declare -x MI_DATE_FORMAT="%Y-%m-%d--%H-%M-%S"
declare -r kScriptName=$(basename $0)
declare -r kScriptDirectory=$(dirname $0)
declare -r kLogFile="/var/log/${kScriptName}.log"
declare -r kSaltFile="/mi/files/appstore-salt.txt"
declare -r kScriptStartTimeSeconds=$(date +%s)
declare -r kValidTimeStampLength=${#kScriptStartTimeSeconds}
declare -r kAftFileStoreDirectory='/mi/files/aftstore'
# error codes that are used in /etc/httpd/conf.d/ssl.conf
declare -r kPathTraversalAttemptedErrorCode="c91bbeec40aff3fd3fe0c08044c1165a"
declare -r kLinkHashMismatchErrorCode="44b2ff3cf69c5112061aad51e0f7d772"
declare -r kTooLateErrorCode="c6a0e7ca11208b4f11d04a7ee8151a46"
declare -r kTooEarlyErrorCode="80862895184bfa4d00b24d4fbb3d942f"
declare -r kKeyIndexOutOfBoundsErrorCode="f74c27fce7d8e2fecd10ab54eda6bd85"
declare -r kURLStructureInvalidErrorCode="b702087a848177d489a6891bd7869495"
declare -r kTimestampLengthInvalidErrorCode="2ecad569fdaa07e2b66ed2595cf7240f"
declare -r kLinkSpoofErrorCode="cbfa488e9b08d4c5d7b3b2084ffb18e7"
declare -r kLinkUsingOddTraversalErrorCode="f489b91db387b684f56c07e7f5e4308b"
gShouldLogToFile="false"
gSaltFileModificationTime="0"
gTestMode="false"
gErrorCode=0
gErrorMessage=""
declare -a gSaltArray=( )
gCurrentSalt=""
gHostname=""
gPath=""
gStartTime=""
gEndTime=""
if (( $# > 0 )) ; then
gTestMode="true"
fi
#echo "gTestMode=${gTestMode}"
# information
function log() {
if ${gTestMode} ; then
echo "`$MI_DATE_COMMAND` -- ${kScriptName} -- ${1}: ${@:2}"
else
# do not log since it kills performance
echo "$($MI_DATE_COMMAND) -- ${kScriptName} -- ${1}: ${@:2}" >> ${kLogFile}
fi
}
function logDebug() {
if ${gTestMode} ; then
echo "`$MI_DATE_COMMAND` -- ${kScriptName} -- ${1}: ${@:2}"
else
# do not log since it kills performance
${gShouldLogToFile} && echo "$($MI_DATE_COMMAND) -- ${kScriptName} -- ${1}: ${@:2}" >> ${kLogFile}
fi
}
# errorCode
# information
function logDenial() {
local theCurrentDate="$(MI_DATE_COMMAND)"
if ${gTestMode} ; then
echo "$theCurrentDate -- ${kScriptName} -- ${1}: denying: errorCode=${2}: ${@:3}"
else
#echo "$theCurrentDate -- ${kScriptName} -- ${1}: denying: errorCode=${2}: ${@:3}" >> "${kLogFile}"
logger -t "${kScriptName}" -i -p local0.warning "$theCurrentDate -- ${1}: denying: errorCode=${2}: ${@:3}"
fi
}
log "MAIN" "starting"
function dumpSaltArray() {
log "${FUNCNAME}" "entered"
for theSalt in "${gSaltArray[@]}" ; do
log "${FUNCNAME}" "theSalt=$theSalt"
done
}
log "MAIN" "after dumpSaltArray declaration"
function readSaltFile() {
if [[ -f "${kSaltFile}" ]] ; then
theCurrentSaltModificationTime=$(stat -c %Y "${kSaltFile}")
logDebug "${FUNCNAME}" "theCurrentSaltModificationTime=${theCurrentSaltModificationTime}"
theDeltaTime=$(($theCurrentSaltModificationTime - $gSaltFileModificationTime))
logDebug "${FUNCNAME}" "theDeltaTime=${theDeltaTime}"
if [[ "${theDeltaTime}" -ne 0 ]] ; then
log "${FUNCNAME}" "theDeltaTime=${theDeltaTime} not zero; loading salt from kSaltFile=${kSaltFile}"
gSaltArray=( $(cat ${kSaltFile}))
gSaltArray[0]=""
gSaltFileModificationTime=$theCurrentSaltModificationTime
fi
else
log "${FUNCNAME}" "kSaltFile=${kSaltFile} not found"
fi
}
log "MAIN" "after readSaltFile declaration"
#readSaltFile
#dumpSaltArray
#readSaltFile
function lookupSaltByIndex() {
#echo "$1 ${#gSaltArray[*]}"
if [ "$1" -lt ${#gSaltArray[*]} ] ; then
gCurrentSalt=${gSaltArray[$1]}
else
gCurrentSalt=""
fi
logDebug "${FUNCNAME}" "theKeyIndex=$1; gCurrentSalt=$gCurrentSalt"
}
log "MAIN" "after lookupSaltByIndex declaration"
function verifyURLConsistency () {
logDebug "${FUNCNAME}" "${1}"
local ret="" # this is what we eventually echo and it's the name of a file for httpd to send to the client or a pattern that Rewrite is aware of and kill the connection with the right HTTP error code
#theAppStoreString=${1%%:*}
#echo "${theAppStoreString}"
#declare
theOldIFS="${IFS}"
local theArgumentArray
# process what httpd gave us in $1 splitting on the _
IFS="_" && theArgumentArray=(${1})
theAftStoreString=${theArgumentArray[0]}
theAftStoreAssetGUIDWithExtension=${theArgumentArray[1]}
gHostname=${theArgumentArray[2]}
theURLString=${theArgumentArray[3]}
#echo "${theAftStoreString}"
# process what mifs really gave us in $1 splitting on the ,
IFS="," && theAftStoreKeyValueArray=(${theAftStoreString})
IFS="${theOldIFS}"
if (( ${#theArgumentArray[@]} != 4 )) ; then
ret="${kURLStructureInvalidErrorCode}"
log "${FUNCNAME}" "${ret}" "expecting 5 segments; actual=${#theArgumentArray[@]}"
fi
if [[ -z ${ret} ]] ; then
for theKeyMapEntry in "${theAftStoreKeyValueArray[@]}" ; do
theKey="${theKeyMapEntry%%=*}"
theValue="${theKeyMapEntry##*=}"
logDebug "${FUNCNAME}" "theKey=$theKey; theValue=$theValue"
case ${theKey} in
kid)
gKeyIndex="${theValue}"
;;
st)
gStartTime="${theValue}"
if (( ${#gStartTime} != "${kValidTimeStampLength}" )) ; then
ret="${kTimestampLengthInvalidErrorCode}"
fi
;;
et)
gEndTime="${theValue}"
if (( ${#gEndTime} != "${kValidTimeStampLength}" )) ; then
ret="${kTimestampLengthInvalidErrorCode}"
fi
;;
h)
gHashPrefixString="${theValue}"
;;
*)
ret="${kURLStructureInvalidErrorCode}"
logDenial "${FUNCNAME}" "${ret}" "unknown presented key=${theKey}; theValue=${theValue}"
;;
esac
done
fi
if [[ -z ${ret} ]] ; then
lookupSaltByIndex ${gKeyIndex}
if [[ -n "${gCurrentSalt}" ]] ; then
logDebug "${FUNCNAME}" "continuing: gCurrentSalt=$gCurrentSalt"
theCurrentTimeSeconds=$(date +%s)
logDebug "${FUNCNAME}" "theCurrentTimeSeconds=${theCurrentTimeSeconds}"
#theCurrentTimeSeconds=1336011206
#theCurrentTimeSeconds=1336770818
#gHostname="cot-0000001.mobileiron.com"
#gHostname="qa42.mobileiron.com"
if [[ ${theCurrentTimeSeconds} -gt ${gStartTime} ]] ; then
logDebug "${FUNCNAME}" "continuing: not too early"
if [[ ${theCurrentTimeSeconds} -lt ${gEndTime} ]] ; then
logDebug "${FUNCNAME}" "continuing: not too late"
# calculate the path
gPath=${theURLString/\/sha256:${theAftStoreString}