考点:CVE-2020-7245 CTFd v2.0.0 – v2.2.2漏洞分析复现
一、漏洞介绍
在 CTFd v2.0.0 – v2.2.2 的注册过程中,如果知道用户名并在 CTFd 实例上启用电子邮件,则攻击者可以修改任意帐户的密码。
漏洞影响范围
V2.0.0-V2.2.2
二、漏洞分析
首先找到它注册部分的代码
def register():
errors = get_errors()
if request.method == 'POST':
name = request.form['name']
email_address = request.form['email']
password = request.form['password']
name_len = len(name) == 0
names = Users.query.add_columns('name', 'id').filter_by(name=name).first()
emails = Users.query.add_columns('email', 'id').filter_by(email=email_address).first()
pass_short = len(password) == 0
可以看到 当我们通过POST发来注册请求的时候,其中的name参数并没有经过处理,而是直接被再次当作参数传入。
else:
with app.app_context():
user = Users(
name=name.strip(),
email=email_address.lower(),
password=password.strip()
)
当name通过了各种检查之后,即将存入时,使用了strip方法对name进行了处理。
这就意味着,如果我们在用户名的首尾添加上空格的时候,可以绕过用户名重复的检查
存入数据库之前空格会被删去,那么就会导致注册的用户名会和数据库中已有的重复。
然后找到他的找回密码部分的代码
def reset_password(data=None):
if data is not None:
try:
name = unserialize(data, max_age=1800)
except (BadTimeSignature, SignatureExpired):
return render_template('reset_password.html', errors=['Your link has expired'])
except (BadSignature, TypeError, base64.binascii.Error):
return render_template('reset_paassword.html', errors=['Your reset token is invalid'])
if request.method == "GET":
return render_template('reset_password.html', mode='set')
if request.method == "POST":
user = Users.query.filter_by(name=name).first_or_404()
user.password = request.form['password'].strip()
db.session.commit()
log('logins', format="[{date}] {ip} - successful password reset for {name}", name=name)
db.session.close()
return redirect(url_for('auth.login'))
这里发现,会取出data,来找一下data是什么
def forgot_password(email, team_name):
token = serialize(team_name)
text = """Did you initiate a password reset? Click the following link to reset your password:
{0}/{1}
""".format(url_for('auth.reset_password', _external=True), token)
sendmail(email, text)
def verify_email_address(addr):
token = serialize(addr)
text = """Please click the following link to confirm your email address for {ctf_name}: {url}/{token}""".format(
ctf_name=get_config('ctf_name'),
url=url_for('auth.confirm', _external=True),
token=token
)
sendmail(addr, text)
发现发到邮箱里的url是 {url}/{token}。
到这里,漏洞的利用方式就大概出来了。
- 首先注册一个用户名包含空格,默认的管理员是admin,那么就可以注册一个 admin。
- 然后,退出登录,点重置密码。
- 这时候邮箱里就会收到一个url,这个url后面拼接的token实际上是属于用户名admin的
- 将自己注册的账号用户名修改成其他
- 然后点开链接修改密码,admin的密码就被修改了
三、漏洞复现
以BUUCTF上的[V&N2020 公开赛]HappyCTFd为例
因为需要邮箱,BUU中靶机无法访问外网,利用内部资源注册一个邮箱

1)打开题目靶机,注册一个CTFd的号,邮箱为刚刚注册的。

2)在url后加上/reset_password,进入重置密码的界面,输入刚刚注册的邮箱
3)发送完成后选择“settings” 把我们注册的用户名改为123或其他
4)过一会就会在邮箱中收到重置密码的邮件,重置密码为任意,这里改为123

5)接下来跳转到登录界面,输入admin和刚刚重置的密码123成功登录到管理员账户。实际到此漏洞已经复现成功。重置了管理员密码
注:CTFd管理员账号默认admin
对于该题最后登录admin用户后在“Admin Panel”模块里找到“challenges”–>“Files”下载文件即可。

实际测试中,邮箱可以填自己的,只要在目标后加上一个空格再进行注册即可。