使用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中列表生成式的用法
Mar 31 Python
python使用socket进行简单网络连接的方法
Apr 29 Python
Python与R语言的简要对比
Nov 14 Python
Python数据分析之获取双色球历史信息的方法示例
Feb 03 Python
Python基础学习之时间转换函数用法详解
Jun 18 Python
python批量处理文件或文件夹
Jul 28 Python
python实现通过队列完成进程间的多任务功能示例
Oct 28 Python
Python实现线性判别分析(LDA)的MATLAB方式
Dec 09 Python
Python字符串中删除特定字符的方法
Jan 15 Python
python异常处理、自定义异常、断言原理与用法分析
Mar 23 Python
python如何写出表白程序
Jun 01 Python
pycharm 关闭search everywhere的解决操作
Jan 15 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实现图片缩放功能类
2013/12/18 PHP
PHP冒泡算法详解(递归实现)
2014/11/10 PHP
PHP通过加锁实现并发情况下抢码功能
2016/08/10 PHP
写出更好的JavaScript之undefined篇(上)
2009/11/22 Javascript
js查找某元素中的所有图片地址的方法
2014/01/16 Javascript
再谈javascript原型继承
2014/11/10 Javascript
小议JavaScript中Generator和Iterator的使用
2015/07/29 Javascript
jQuery实现点击弹出背景变暗遮罩效果实例代码
2016/06/24 Javascript
AngularJs Understanding the Controller Component
2016/09/02 Javascript
jquery版轮播图效果和extend扩展
2017/07/18 jQuery
JS实现调用本地摄像头功能示例
2018/05/18 Javascript
详解一些适用于Node.js的命名约定
2019/12/08 Javascript
Element-ui el-tree新增和删除节点后如何刷新tree的实例
2020/08/31 Javascript
微信小程序实现点赞业务
2021/02/10 Javascript
python操作xml文件详细介绍
2014/06/09 Python
python实现在windows下操作word的方法
2015/04/28 Python
Python自动调用IE打开某个网站的方法
2015/06/03 Python
Python3实现并发检验代理池地址的方法
2016/09/18 Python
一个基于flask的web应用诞生 flask和mysql相连(4)
2017/04/11 Python
Python之Scrapy爬虫框架安装及简单使用详解
2017/12/22 Python
python用opencv批量截取图像指定区域的方法
2019/01/24 Python
python把转列表为集合的方法
2019/06/28 Python
Python 生成一个从0到n个数字的列表4种方法小结
2019/11/28 Python
基于FME使用Python过程图解
2020/05/13 Python
高清安全摄像头系统:Lorex Technology
2018/07/20 全球购物
伊莱克斯(Electrolux)俄罗斯网上商店:瑞典家用电器品牌
2021/01/23 全球购物
成人教育自我鉴定
2013/11/01 职场文书
迟到早退检讨书
2014/02/10 职场文书
文案策划专业自荐信
2014/07/07 职场文书
员工激励培训演讲稿
2014/09/16 职场文书
设立有限责任公司出资协议书
2014/11/01 职场文书
食品安全责任书范本
2015/05/09 职场文书
爱的教育读书笔记
2015/06/26 职场文书
Java中常用解析工具jackson及fastjson的使用
2021/06/28 Java/Android
MySQL 表锁定 LOCK和UNLOCK TABLES的 SQL语法
2022/04/18 MySQL
Windows server 2016服务器基本设置
2022/08/14 Servers