BrokenSesame-阿里云数据库远程命令执行
AnalyticDB for PostgreSQL
容器提权
首先发现了一个ROOT定时任务,该定时任务每分钟执行一次二进制文件/usr/bin/tsar
:
$: ls -lah /etc/cron.d/tsar
-rw-r--r-- 1 root root 99 Apr 19 2021 /etc/cron.d/tsar
$: cat /etc/cron.d/tsar
# cron tsar collect once per minute
MAILTO=""
* * * * * root /usr/bin/tsar --cron > /dev/null 2>&1
使用ldd
发现从自定义路径加载共享库,其中的一个路径/u01
对于当前用户adbpgadmin
是可写的。
$: ls -alh /u01/adbpg/lib/libgcc_s.so.1
-rwxr-xr-x 1 adbpgadmin adbpgadmin 102K Oct 27 12:22 /u01/adbpg/lib/libgcc_s.so.1
如果用自己的共享库覆盖这个文件,那么下次就会以root权限执行自定义的代码,攻击流程:
- 编译一个共享库,功能是将
/bin/bash
复制到/bin/dash
,并添加SUID权限。 - 把编译好的共享库使用PatchELF,添加到
libgcc_s.so.1
,这样当libgcc_s.so.1
被加载的时候,恶意代码会被执行。 - 覆盖
libgcc_s.so.1
库 - 等待定时任务执行
最终获取Root权限:
逃逸到K8s Node
根据作者对云服务的测试经验,一般UI界面的操作会导致托管环境创建容器或者进程,从而扩大攻击面。比如在阿里云门户中某些操作(启用SSL加密),可以观察到多个SCP和SSH进程的创建。
# Command lines of the spawned processes
su - adbpgadmin -c scp /home/adbpgadmin/xxx_ssl_files/*
*REDACTED*:/home/adbpgadmin/data/master/seg-1/
/usr/bin/ssh -x -oForwardAgent=no -oPermitLocalCommand=no -oClearAllForwardings=yes
-- *REDACTED* scp -d -t /home/adbpgadmin/data/master/seg-1/
作者发现这些生成的进程包含容器中不存在的路径,推测是在容器共享PID中不同容器中生成的,为了验证者推测,作者写了一个POC,当生成SCP进程的时候,使用/proc/{pid}/root/
访问该进程下的文件系统:
# The Python script we used to access the second container filesystem
import psutil
import os
listed = set()
while True:
for proc in psutil.process_iter():
try:
processName = proc.name()
processID = proc.pid
cmdLine = proc.cmdline()
if processID not in listed and processName == 'scp':
os.system('ls -alh /proc/{}/root/'.format(processID))
listed.add(processID)
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
在阿里云平台门户启用SSL操作之后,观察脚本的输出,可以确认尽管两个容器不同,但它们的主目录(/home/adbpgadmin)是相同的挂载。
在验证了上面想法之后,作者挑选使用SSH来获取第二个容器的权限,修改本地ssh配置文件: /home/adbpgadmin/.ssh/config
这样当ssh命令执行的时候,就会触发执行LocalCommand
指定的脚本,此时是以root权限在第二个容器执行的,并在第二个容器发现了docker.sock
由于第二个容器是临时生成的,作者利用创建了一个持久化的容器,但是具体技术细节没有说明。