使用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 字符串定义
Sep 25 Python
Python检测生僻字的实现方法
Oct 23 Python
基于Django用户认证系统详解
Feb 21 Python
python使用tcp实现局域网内文件传输
Mar 20 Python
python用BeautifulSoup库简单爬虫实例分析
Jul 30 Python
Python 输入一个数字判断成绩分数等级的方法
Nov 15 Python
Python3.4学习笔记之常用操作符,条件分支和循环用法示例
Mar 01 Python
基于python爬取有道翻译过程图解
Mar 31 Python
解决Python在导入文件时的FileNotFoundError问题
Apr 10 Python
flask项目集成swagger的方法
Dec 09 Python
python3 googletrans超时报错问题及翻译工具优化方案 附源码
Dec 23 Python
在Pycharm中安装Pandas库方法(简单易懂)
Feb 20 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
Dedecms V3.1 生成HTML速度的优化办法
2007/03/18 PHP
关于php连接mssql:pdo odbc sql server
2011/07/20 PHP
php绘图之生成饼状图的方法
2015/01/24 PHP
Mac环境下php操作mysql数据库的方法分享
2015/05/11 PHP
PHP实践教程之过滤、验证、转义与密码详解
2017/07/24 PHP
用jquery实现等比例缩放图片效果插件
2010/07/24 Javascript
Jquery进度条插件 Progress Bar小问题解决
2011/07/12 Javascript
JQuery+CSS提示框实现思路及代码(纯手工打造)
2013/05/07 Javascript
NodeJS与Mysql的交互示例代码
2013/08/18 NodeJs
appendChild() 或 insertBefore()使用与区别介绍
2013/10/11 Javascript
jQuery实现带滚动线条导航效果的方法
2015/01/30 Javascript
JQuery ztree带筛选、异步加载实例讲解
2016/02/25 Javascript
BootStrap注意事项小结(五)表单
2017/03/10 Javascript
详解vue-router的导航钩子(导航守卫)
2020/11/02 Javascript
[01:45]DOTA2新英雄“神谕者”全方位展示
2014/11/21 DOTA
python实现的登陆Discuz!论坛通用代码分享
2014/07/11 Python
Python定义函数时参数有默认值问题解决
2019/12/19 Python
postman和python mock测试过程图解
2020/02/22 Python
python爬虫要用到的库总结
2020/07/28 Python
Python脚本调试工具安装过程
2021/01/11 Python
Napapijri西班牙在线商店:夹克、外套、运动衫等
2020/11/05 全球购物
物业经理求职自我评价
2013/09/22 职场文书
职专应届生求职信
2013/11/16 职场文书
大学生作弊检讨书
2014/09/11 职场文书
机械制造专业大学生自我鉴定
2014/09/19 职场文书
中共广东省委常委会党的群众路线教育实践活动整改方案
2014/09/23 职场文书
实习协议书范本
2014/09/25 职场文书
如何写观后感
2015/06/19 职场文书
职位证明模板
2015/06/23 职场文书
中学后勤工作总结2015
2015/07/22 职场文书
班级管理经验交流材料
2015/11/02 职场文书
周一给客户的问候语
2015/11/10 职场文书
2016年清明节网上祭英烈活动总结
2016/04/01 职场文书
.Net Core导入千万级数据至Mysql的步骤
2021/05/24 MySQL
关于springboot配置druid数据源不生效问题(踩坑记)
2021/09/25 Java/Android
Java线程的6种状态与生命周期
2022/05/11 Java/Android