免责声明:由于传播、利用本文所发布的而造成的任何直接或者间接的后果及损失,均由使用者本人承担

前言

我的学习记录

重点并不是在溯源,而是因为这个过程,使我从docker逃逸中学到很多思考

Start

​ 起因是改造WAF的时候,顺带看了一眼攻击ip,批量识别了一下ip反制一下。这里忘记截图了

​ 只发现了一个IDC服务器比较有价值

image-20251216163635312

如图,也就是这个160.*.*.93

攻击次数非常多,并且实打实的payload 不是误报

image-20251216165011497

溯源过程

由于一些我的个人问题与线索指向人之间的摩擦

所以这里不描写具体的线索梳理过程,也不写溯源到的个人信息,当然这个也不重要,本身也没有什么新颖的操作

反制

这里先扫了全端口

image-20251216165957670

直接从2375端口入手

2375是Docker远程API的默认端口,通过这个端口可以直接对远程的Docker守护进程进行操作,然后就是操作逃逸到宿主机,也就是那个2375端口未授权访问漏洞的常见利用

漏洞利用

访问http://ip:2375

当时的截图个人信息太多,打码又影响阅读,所以随便找了一张图凑数

回显内容大概如下

image-20251216172200112

客户端连接列出所有容器

docker -H tcp://160.*.*.93:2375/ ps -a

image-20251216172905424

hacker字段很显眼

当时想的是先进容器看看能不能翻出来点什么数据和配置,之后再做逃逸

连接这个hacker容器

docker -H tcp://160.202.230.93:2375 exec -it 9422f /bin/bash

image-20251216182140892

这里失败了,于是开始了学习与排查

具体报错信息

1
OCI runtime exec failed: exec failed: unable to start container process:error adding SSB flag to seccomp filter:SetSSB requires libseccomp ≥2.5.0 and API level ≥ 4 (current version: 2.5.5, API level:1): unknown
排错

当时着急出成果,直接丢给了ChatGPT,几个参数就解决了

当然CDK,还有这个漏洞的exp,都可以一把梭,只是我平常不太用这些,一来渗透本身就是一个很考验细节和耐心的东西,工具里是别人的理解 别人的经历的场景,多一个符合少一个符合可能都会影响最终结果的走向,二来发现长时间用AI复制粘贴解决问题,自己变的唐唐的

image-20251216184022187

抱着学习的心态 今天再来看这个问题,想着尽可能用AI做辅助 而不是被牵着鼻子走

根据”:“的分隔,我把报错分成3部分

首先,看第一层报错

OCI runtime exec failed: exec failed: unable to start container process

这句一目了然,docker想在容器里启动一个新进程,但是exec指向的进程启动失败了,OCI runtime眼熟报错经常碰到,创建Linux进程用的

第二层报错

error adding SSB flag to seccomp filter

”添加 SSB 标志到 seccomp 过滤器出错“

到这里就不懂了:SSB、seccomp 这两个是什么东西

seccomp

这个涉及到了Linux系统的底层,seccomp全称Secure Computing,在了解seccomp之前,需要先知道system call ,

简称syscall,是操作系统提供给程序与内核交互的接口,简单来说,用户程序不能直接操作硬件和核心资源,需要通过 syscall 向内核请求服务,通过 syscall,程序可以完成如文件操作、进程管理、网络通信这些功能,比如go里面 就提供了syscall的包,可以调用系统的调用

以及dockerdocs中也有相关文档 https://docs.docker.com/engine/security/seccomp/

image-20251217114630242

seccomp 的功能 就是限制程序可调用的系统调用,就类似java里JEP 290一样的概念

SSB

全称Speculative Store Bypass,他是干什么的呢?我当时也理解了非常久,简单来说CPU运行的时候为了提高速度,涉及到一种做法叫投机行为,就是先假设一个题解,等答案,修改题解,对了就不用管了,这样岂不是增加了速度和效率吗,但是出现了一个问题,CPU先假设好题解,然后直接去修改题解,但是这个时候答案还没有公布,并且改的过程中是有行为痕迹的,这些痕迹就可以被其他人看到,然后推测出答案,我能理解到的差不多这个意思吧

SSB的作用就是约束CPU禁止它跳过存储顺序,也是为了防御 CVE-2018-3639 漏洞

官方的说法

image-20251216203554611

到这里就有点清晰了,docker exec是在容器创建新的进程,那么必然会加载seccomp,而seccomp会启用SSB,这个报错就说在添加SSB到seccomp失败了

在docker官方仓库的issue也找到了相关讨论https://github.com/moby/moby/issues/42619

image-20251217112326722

Docker/Moby currently enables the Speculative Store Bypass mitigation when using seccomp. This is default for the seccomp syscall and for libseccomp.

Docker 自动会启用SSB缓解,当使用 seccomp 时,这个是默认行为

第三层报错

SetSSB requires libseccomp ≥2.5.0 and API level ≥ 4 (current version: 2.5.5, API level:1)

这个就比较简单了,说了一下设置SSB需要具备的条件,

  • libseccomp ≥2.5.0 (现版本2.5.5 已满足)

  • API level ≥ 4(当前宿主机系统等级是1)

libseccomp

docker启动容器或者启动进程的时候,通过libseccomp实现 seccomp的功能

在一些文章和仓库中也有描述

https://segmentfault.com/a/1190000016366810

image-20251217114943416

https://github.com/docker-archive-public/nestybox.libseccomp-golang

image-20251217115129039

https://libseccomp.readthedocs.io/en/latest/

image-20251217121441877

综合推导出来就是

Docker 通过 containerd 调用 runc ->runc 在创建进程时要安装 seccomp 过滤器>runc 需要 libseccomp 来实现 seccomp 加载>libseccomp把规则(也就是seccomp filter)加载到内核

API level

从这个文档中 https://man7.org/linux/man-pages/man3/seccomp_api_get.3.html

image-20251217122114399

可以得知API level 就是用来定义 libseccomp 功能等级的指标,一级能干什么 二级能干什么…

写的也很明显,4级就可以设置SSB

解决

把所有的名词都搞清楚了,触发错误的原因就是:docker 在执行 docker exec 时,尝试给新进程启用 SSB 缓解,但是宿主机上的 libseccomp 功能等级(API level 是 1)太低,不支持 SSB,导致容器进程无法启动

在浏览dockerdocs那篇文档的时候,也就是https://docs.docker.com/engine/security/seccomp/?utm_source=chatgpt.com#run-without-the-default-seccomp-profile

末尾写了 可以用-security-opt seccomp=unconfined来声明, 不使用默认 seccomp profile

image-20251217140209610

这样就可以直接绕过直接绕过 SSB 设置问题,不设置了 SetSSB就不会调用接口,那么就不存在libseccomp和API level的条件检查

逃逸

启动一个新容器 然后挂载宿主机硬盘,并且不加载seccomp进入shell

docker -H tcp://160.*.*.93:2375 run -rm -it --security-opt seccomp=unconfined -v /:/mnt --entrypoint /bin/bash xxxxx

image-20251217141338516

成功进入容器

考虑到网络情况,刚开始打算写ssh公钥,但是因为宿主机的authorized_keys文件只有r权限,正常来说是755才可以,后面尝试用计划任务执行chmod改authorized_keys文件权限也是一直没效果,所以放弃

写计划任务拿宿主机shell

这里还涉及到ubuntu计划任务反弹shell的细节问题:可以看这篇博客解决,属于是老掉牙了

https://m3lon.github.io/2019/03/18/%E8%A7%A3%E5%86%B3ubuntu-crontab%E5%8F%8D%E5%BC%B9shell%E5%A4%B1%E8%B4%A5%E7%9A%84%E9%97%AE%E9%A2%98/

然后就发现计划任务写进去不执行,

当时找了好久,翻了好多日志,尝试了几乎所有的计划任务路径,/etc/crontab/var/spool/cron/crontabs//etc/systemd/system/

当时用了个笨办法,先从计划任务日志里查看正在执行的任务,然后根据日志中的关键字,去各个计划任务目录里查找对应的文件,从而确认这些任务是由哪个目录下的计划任务触发的

最终确认在/etc/cron.d/zzh写入,不管是系统级还是用户级计划任务,只有这一个zzh文件中的计划任务在执行,非常离谱,所以像这种自动化的梭哈工具 怎么可能找到,不动手看就利用失败了

image-20251217143650525

成功收到shell

image-20251217144352891

这也就是160开头 93结尾的攻击着IP,逃逸成功拿到了宿主机的权限

现在回头看,直接去读官方开发文档和源码来定位问题,似乎比单纯向AI复制粘贴命令更有主动性,也更容易真正理解问题的本质,但解决问题的时间线会被拉长

或许只需要平衡好时间投资和知识投资之间的关系……

End