使用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的Django框架的dj-scaffold项目
May 30 Python
如何在sae中设置django,让sae的工作环境跟本地python环境一致
Nov 21 Python
Windows环境下python环境安装使用图文教程
Mar 13 Python
Python判断变量名是否合法的方法示例
Jan 28 Python
Python面向对象程序设计示例小结
Jan 30 Python
python向字符串中添加元素的实例方法
Jun 28 Python
python使用writerows写csv文件产生多余空行的处理方法
Aug 01 Python
Python CVXOPT模块安装及使用解析
Aug 01 Python
pandas 选取行和列数据的方法详解
Aug 08 Python
python pip安装包出现:Failed building wheel for xxx错误的解决
Dec 25 Python
pyqt5 textEdit、lineEdit操作的示例代码
Aug 12 Python
解决Pycharm 运行后没有输出的问题
Feb 05 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
Session保存到数据库的php类分享
2011/10/24 PHP
探讨:如何使用PhpDocumentor生成文档
2013/06/25 PHP
PHP实现显示照片exif信息的方法
2014/07/11 PHP
微信公众平台开发之配置与请求
2015/08/26 PHP
PHP实现将base64编码字符串转换成图片示例
2018/06/22 PHP
PHP开发实现快递查询功能详解
2019/04/08 PHP
thinkphp5实现微信扫码支付
2019/12/23 PHP
PHP pthreads v3下worker和pool的使用方法示例
2020/02/21 PHP
js用图作提交按钮或超连接
2008/03/26 Javascript
IE与Firefox在JavaScript上的7个不同写法小结
2009/09/14 Javascript
JQuery模板插件 jquery.tmpl 动态ajax扩展
2011/11/10 Javascript
JavaScript如何从listbox里同时删除多个项目
2013/10/12 Javascript
Javascript 浮点运算精度问题分析与解决
2014/03/26 Javascript
jQuery中$.get、$.post、$.getJSON和$.ajax的用法详解
2014/11/19 Javascript
js获取表格的行数和列数的方法
2015/10/23 Javascript
简单介绍JavaScript数据类型之隐式类型转换
2015/12/28 Javascript
JSON创建键值对(key是中文或者数字)方式详解
2017/08/24 Javascript
vue抽出组件并传值实例
2020/07/31 Javascript
Python中常用操作字符串的函数与方法总结
2016/02/04 Python
Python利用IPython提高开发效率
2016/08/10 Python
Python中序列的修改、散列与切片详解
2017/08/27 Python
Python栈算法的实现与简单应用示例
2017/11/01 Python
Python标准模块--ContextManager上下文管理器的具体用法
2017/11/27 Python
Python编程实现线性回归和批量梯度下降法代码实例
2018/01/04 Python
浅谈python中真正关闭socket的方法
2018/12/18 Python
python实现多层感知器MLP(基于双月数据集)
2019/01/18 Python
AJax面试题
2014/11/25 面试题
历史系毕业生自荐信
2013/10/28 职场文书
医学生临床实习自我评价
2014/03/07 职场文书
机械电子工程专业自荐书
2014/06/10 职场文书
音乐学专业求职信
2014/07/22 职场文书
个人主要事迹材料
2014/08/26 职场文书
亲属关系公证书样本
2015/01/23 职场文书
会计工作检讨书
2015/02/19 职场文书
留学推荐信怎么写
2015/03/26 职场文书
《正面管教》读后有感:和善而坚定的旅程
2019/12/19 职场文书