CVE-2020-7245 CTFd v2.0.0 – v2.2.2漏洞分析復現
一、漏洞介紹
在 CTFd v2.0.0 - v2.2.2 的注冊過程中,如果知道用戶名並在 CTFd 實例上啟用電子郵件,則攻擊者可以修改任意帳戶的密碼。
漏洞影響范圍
V2.0.0-V2.2.2
二、漏洞環境搭建
在github上下載對應版本的CTFd,文件夾中有dockerfile,可以選擇手動搭建,或者在docker中搭建。這里選擇使用docker-compose來搭建,比較方便。
輸入
docker-compose up
等待安裝完成,之后訪問ip:8000端口即可。
注:
如果過程太過漫長,建議修改 docker源,pip源,以及alpine源 至國內鏡像源
docker源可以通過阿里雲的 容器鏡像服務控制台中的說明進行修改。(阿里雲的說明還是挺棒的)
pip源如果不想永久修改,可以打開Dockerfile,修改它的命令達到臨時修改的目的。
在兩處命令后都加上-i https://mirrors.aliyun.com/pypi/simple/來臨時指定下載地址
安裝過程中使用了alpine系統
依然通過修改Dockerfile來達到加速的目的
修改 alpine的鏡像源。
三、漏洞分析
首先找到它注冊部分的代碼
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的密碼就被修改了
四、漏洞復現
- 注冊賬號
賬號名字取為 "空格admin"
- 退出登錄,點擊忘記密碼
輸入郵箱
- 修改用戶名為其他
- 點開郵箱中的鏈接
- 重置成功