使用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进程管理工具supervisor使用实例
Sep 17 Python
以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
Jan 20 Python
python实现class对象转换成json/字典的方法
Mar 11 Python
Python 基础之字符串string详解及实例
Apr 01 Python
Python读取和处理文件后缀为.sqlite的数据文件(实例讲解)
Jun 27 Python
python实现名片管理系统
Nov 29 Python
pyqt弹出新对话框,以及关闭对话框获取数据的实例
Jun 18 Python
Python简易版图书管理系统
Aug 12 Python
Python动态导入模块和反射机制详解
Feb 18 Python
最新版 Windows10上安装Python 3.8.5的步骤详解
Nov 28 Python
用60行代码实现Python自动抢微信红包
Feb 04 Python
Python if else条件语句形式详解
Mar 24 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
重料打造自己的“宝马”---第三代
2021/03/02 无线电
php中通过数组进行高效随机抽取指定条记录的算法
2013/09/09 PHP
php获取域名的google收录示例
2014/03/24 PHP
php将图片文件转换成二进制输出的方法
2015/06/10 PHP
php多线程并发实现方法
2016/09/30 PHP
CodeIgniter整合Smarty的方法详解
2017/08/25 PHP
javascript 面向对象编程 万物皆对象
2009/09/17 Javascript
js浮点数精确计算(加、减、乘、除)
2013/12/26 Javascript
js中split函数的使用方法说明
2013/12/26 Javascript
javascript模拟php函数in_array
2015/04/27 Javascript
JavaScript代码轻松实现网页内容禁止复制(代码简单)
2015/10/23 Javascript
JavaScript实现点击单元格改变背景色的方法
2016/02/12 Javascript
Javascript点击按钮随机改变数字与其颜色
2016/09/01 Javascript
JS 实现Base64编码与解码实例详解
2016/11/07 Javascript
利用javascript如何随机生成一定位数的密码
2017/09/22 Javascript
vue.js获得当前元素的文字信息方法
2018/03/09 Javascript
JS实现数组的增删改查操作示例
2018/08/29 Javascript
js正则取值的结果数组调试方法
2018/10/10 Javascript
JavaScript数据结构之栈实例用法
2019/01/18 Javascript
Vue使用localStorage存储数据的方法
2019/05/27 Javascript
layui的面包屑或者表单不显示的解决方法
2019/09/05 Javascript
Layui事件监听的实现(表单和数据表格)
2019/10/17 Javascript
Vue实现boradcast和dispatch的示例
2020/11/13 Javascript
Vue项目打包部署到apache服务器的方法步骤
2021/02/01 Vue.js
python获得图片base64编码示例
2014/01/16 Python
Windows下Python的Django框架环境部署及应用编写入门
2016/03/10 Python
python学习基础之循环import及import过程
2018/04/22 Python
Python依赖包整体迁移方法详解
2019/08/15 Python
keras slice layer 层实现方式
2020/06/11 Python
Keras预训练的ImageNet模型实现分类操作
2020/07/07 Python
婚礼主持词
2014/03/13 职场文书
公司建议书怎么写
2014/05/15 职场文书
个人自我剖析材料
2014/09/30 职场文书
python实现批量移动文件
2021/04/05 Python
Lombok的详细使用及优缺点总结
2021/07/15 Java/Android
MyBatis配置文件解析与MyBatis实例演示
2022/04/07 Java/Android