查看首页源代码,有注释
考虑这道题很有可能是用admin或者伪造admin进行登录,
用admin进行登录,随便填写密码进不去,发现页面有注册选项,用admin注册提示已经被注册
方法一:burp爆破
进入登录界面,用户名输admin,密码随便填,抓包对密码进行爆破;
爆破之后,提示429和302,可以看一下429的response,提示的是太多请求的问题,
更改options中的选项
控制数据量,我因为正好吃饭就调成300,很慢,大家可以自行调节,正常的返回结果就有一个payload的长度是不同的
可以确定admin的密码是123,登录即可得到flag
方法2:
随便注册个账户,然后登录进页面,进行最简单的工作,逐个页面查看源代码,看有无有用的信息和漏洞,在change password页面下发现注释
登录下载下来发现是网站的源码文件,打开\hctf_flask-master\app,先看里面的routes.py路由文件,逐个看一下,\hctf_flask-master\app\templates\index.html中
{% include('header.html') %}
{% if current_user.is_authenticated %}
Hello {{ session['name'] }}
{% endif %}
{% if current_user.is_authenticated and session['name'] == 'admin' %}
hctf{xxxxxxxxx}
{% endif %}
Welcome to hctf
{% include('footer.html') %}
关于flask里面的session:
flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中,也就是说其实只是将相关内容进行了加密保存到session中。和服务端的session不同,服务端的session保存在服务端中,依靠客户端cookie值中的sessionId来进行识别。
也就是说可以通过更改session里面的用户名为admin,便可实现伪造admin登录
通过burp抓取session,是一串码,需要进行解密
GitHub - noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder
github上有flask-session的加解密代码,
因为我的电脑上安了python3和python2,所以安装flask的时候出了点问题,pip3 install flask确保代码执行没有问题
C:\Users\86155>python flask-session-cookie-manager-master\flask_session_cookie_manager3.py decode
usage: flask_session_cookie_manager3.py decode [-h] [-s ] -c
flask_session_cookie_manager3.py decode: error: the following arguments are required: -c/--cookie-value
C:\Users\86155>python flask-session-cookie-manager-master\flask_session_cookie_manager3.py decode -h
usage: flask_session_cookie_manager3.py decode [-h] [-s ] -c
optional arguments:
-h, --help show this help message and exit
-s , --secret-key
Secret key
-c , --cookie-value
Session cookie value
可以看到需要secret key和session cookie value两个参数
secret可以在\hctf_flask-master\app\config.py中看到
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:adsl1234@db:3306/test'
SQLALCHEMY_TRACK_MODIFICATIONS = True
session value就是抓包所得,这里有一点session value的获得过程,首先,要用你已经注册的账号先登录进去,此时记录的session记录的用户名是你注册的名字,这时候再将获取的session更改,例如:登录change password,进行抓包,运行解密代码
C:\Users\86155>python flask-session-cookie-manager-master\flask_session_cookie_manager3.py decode -s "ckj123" -c ".eJw9kEGLwjAQhf_KMmcPbWwvgoddUkuFmaBEZXIRXWvbxLhQldWI_32jCx4f783Hm3eH9b6vTy2Mzv2lHsC628HoDh9bGAHrWc4rHqKcO9ZfllZVIFlkZDlHbxwFzjAcWiqnB7OaOlWajkSVoTWd0gtBfmoxLASLl86UjDm9dEYWCYXWYYlBldQ9OSSKgHY2jJmWtUuxXEYuefRVZqxLntrIJlGar_E-Z4E5WWNR71qjeQyPAXyf-v36_OPq4_sF1I3gsLSx8i3WOpB0VyXxl0WRcNQcOEVdDTGWxTC7YTl36nP8wnV-09Rv0tZPFDX_znHjowGbne-OMIDLqe5fu0GawuMPRadr_A.Yi2c_Q.litxhAuL4tDmrJkduha2BNut3I8"
{'_fresh': True, '_id': b'a49af704da0c5b34186692fd76839a4beebd8fb7b826b9566bc356cfb95882ee5dd1478d0c38cb7687a324782aa950ea4cf2b8f940ead84961d19cc966c17ae6', 'csrf_token': b'186c5c66226e491830ca4b6ec651273cf3420dd8', 'image': b'naN6', 'name': 'admin', 'user_id': '11'}
name字段为你登录的账户,根据要求要改成admin, 因为我已经做完了,所以我解密完之后还是admin,我就不改了,大家做的时候注意一点,再将改好的进行加密,encode -s "ckj123" -t "secret structure",
替换转发
方法3:unicode编码覆盖
在查看route.py代码中
@app.route('/register', methods = ['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegisterForm()
if request.method == 'POST':
name = strlower(form.username.data)
if session.get('image').lower() != form.verify_code.data.lower():
flash('Wrong verify code.')
return render_template('register.html', title = 'register', form=form)
if User.query.filter_by(username = name).first():
flash('The username has been registered')
return redirect(url_for('register'))
user = User(username=name)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('register successful')
return redirect(url_for('login'))
return render_template('register.html', title = 'register', form = form)
@app.route('/login', methods = ['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if request.method == 'POST':
name = strlower(form.username.data)
session['name'] = name
user = User.query.filter_by(username=name).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
return redirect(url_for('index'))
return render_template('login.html', title = 'login', form = form)
def strlower(username):
username = nodeprep.prepare(username)
return username
在login和register函数中,均对传参username进行了strlower处理,查看下面的函数,是对username调用函数nodeprep.prepare进行处理,unicode转化,而nodeprep是从Twisted模块导入的,在requirements.txt文件中发现Twisted==10.2.0
,与最新版本差距很大,
注册和登录各调用了一次这个函数,我们倒着进行推算,要使传进去的值服务器识别成admin,
函数的作用是将大写转化为小写
但它同时会将unicode字符ᴬ
转换成A
,而A
再调用一次nodeprep.prepare函数会把A
转换成a
也就是说我们可以使用ᴬdmin
注册登录,登陆后再想办法让服务器再执行一次nodeprep.prepare
,便可以变成admin账户
方法4:条件竞争
这种是理想状态下的解题方式,但是需要大量线程同时进行,精确度也无法控制和硬件关联性很强,而且如果数量很多也会报429
因为是flask框架,并发式运行会产生漏洞,当以session name=admin进行登录,login上的session name为admin,同时进行change函数,当session name=admin ,session保留的name为admin,执行change,就会调用session里的name,这样相当于给admin改了密码,这种更改条件性太强,现实的渗透很少能够用到,就不尝试了。