AnalyticDB for PostgreSQL

容器提权

首先发现了一个ROOT定时任务,该定时任务每分钟执行一次二进制文件/usr/bin/tsar:

$: ls -lah /etc/cron.d/tsar 
-rw-r--r-- 1 root root 99 Apr 192021 /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

由于第二个容器是临时生成的,作者利用创建了一个持久化的容器,但是具体技术细节没有说明。

⬆︎TOP