使用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 相关文章推荐
python实现电子词典
Apr 23 Python
Python中lambda的用法及其与def的区别解析
Jul 28 Python
python实现调用其他python脚本的方法
Oct 05 Python
Python实现屏幕截图的两种方式
Feb 05 Python
python字典快速保存于读取的方法
Mar 23 Python
Python通过paramiko远程下载Linux服务器上的文件实例
Dec 27 Python
详解python持久化文件读写
Apr 06 Python
python sklearn库实现简单逻辑回归的实例代码
Jul 01 Python
分享PyCharm的几个使用技巧
Nov 10 Python
Python图片的横坐标汉字实例
Dec 04 Python
自定义Django Form中choicefield下拉菜单选取数据库内容实例
Mar 13 Python
Python实现生成bmp图像的方法
Jun 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你的验证码安全码?
2007/01/02 PHP
晋城吧对DiscuzX进行的前端优化要点
2010/09/05 PHP
采用ThinkPHP中F方法实现快速缓存实例
2014/06/13 PHP
laravel Task Scheduling(任务调度)在windows下的使用详解
2019/10/22 PHP
动态加载图片路径 保持JavaScript控件的相对独立性
2010/09/06 Javascript
详谈 Jquery Ajax异步处理Json数据.
2011/09/09 Javascript
js判断变量是否未定义的代码
2020/03/28 Javascript
不使用ajax实现无刷新提交表单
2014/12/21 Javascript
JavaScript中的函数声明和函数表达式区别浅析
2015/03/27 Javascript
JavaScript对象反射用法实例
2015/04/17 Javascript
举例讲解AngularJS中的模块
2015/06/17 Javascript
JavaScript遍历求解数独问题的主要思路小结
2016/06/12 Javascript
微信小程序 传值取值的几种方法总结
2017/01/16 Javascript
从零开始学习Node.js系列教程之设置HTTP头的方法示例
2017/04/13 Javascript
bootstrap daterangepicker双日历时间段选择控件详解
2017/06/15 Javascript
史上最全JavaScript常用的简写技巧(推荐)
2017/08/17 Javascript
vue 父组件调用子组件方法及事件
2018/03/29 Javascript
node.js利用socket.io实现多人在线匹配联机五子棋
2018/05/31 Javascript
详解vue添加删除元素的方法
2018/06/30 Javascript
JavaScript去掉数组重复项的方法分析【测试可用】
2018/07/19 Javascript
nodejs实现用户登录路由功能
2019/05/22 NodeJs
jquery将json转为数据字典的实例代码
2019/10/11 jQuery
JavaScript中的全局属性与方法深入解析
2020/06/14 Javascript
解决vue打包报错Unexpected token: punc的问题
2020/10/24 Javascript
Python实现的简单发送邮件脚本分享
2014/11/07 Python
Python基于select实现的socket服务器
2016/04/13 Python
Python使用wxPython实现计算器
2018/01/30 Python
python对列进行平移变换的方法(shift)
2019/01/10 Python
Python中调用其他程序的方式详解
2019/08/06 Python
基于HTML5+CSS3实现简单的时钟效果
2017/09/11 HTML / CSS
英国安全产品购物网站:The Safe Shop
2017/03/20 全球购物
英国时尚女装购物网站:Missguided
2018/08/23 全球购物
户外婚礼策划方案
2014/02/08 职场文书
看上去很美观后感
2015/06/10 职场文书
爱国电影观后感
2015/06/19 职场文书
通讯稿格式及范文
2015/07/22 职场文书