Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze CVE-2020-7245 vulnerabilities

2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Network Security >

Share

Shulou(Shulou.com)05/31 Report--

This article will explain in detail how to carry out CVE-2020-7245 vulnerability analysis, the content of the article is of high quality, so the editor will share it for you as a reference. I hope you will have some understanding of the relevant knowledge after reading this article.

Brief introduction

The vulnerability is a CTFd account takeover vulnerability, where there is a logic vulnerability in registering and changing passwords, resulting in the ability to change arbitrary account passwords.

Affected version: v2.0.0-2.2.2

Loophole analysis

First navigate to the user registry: / CTFd/auto.py

Auth.route ("/ register", methods= ["POST", "GET"]) @ check_registration_visibility@ratelimit (method= "POST", limit=10, interval=5) 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) = 0names = 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.strip ()) = 0pass_long = len (password) > 128valid_email = validators.validate_email (request.form ["email"]) team_name_email_check = validators.validate_email (name) if not valid_email:errors.append ("Please enter a valid email address") if email.check_email_is_whitelisted (email_address) is False:errors.append ("Only email") Addresses under {domains} may register ".format (domains=get_config (" domain_whitelist ")) if names:errors.append (" That user name is already taken ") if team_name_email_check is True:errors.append (" Your user name cannot be an email address ") if emails:errors.append (" That email has already been used ") if pass_short:errors.append (" Pick a longer password ") if pass_long:errors.append (" Pick a shorter password ") if name_len:errors. Append ("Pick a longer user name") if len (errors) > 0:return render_template ("register.html") Errors=errors,name=request.form ["name"], email=request.form ["email"], password=request.form ["password"],) else:with app.app_context (): user = Users (name=name.strip (), email=email_address.lower (), password=password.strip ()) ) db.session.add (user) db.session.commit () db.session.flush () login_user (user) if config.can_send_mail () and get_config ("verify_emails"): # Confirming users is enabled and we can send email.log ("registrations", format= "[{date}] {ip}-{name} registered (UNCONFIRMED) with {email}" ) email.verify_email_address (user.email) db.session.close () return redirect (url_for ("auth.confirm")) else: # Don't care about confirming usersif (config.can_send_mail ()): # We want to notify the user that they have registered.email.sendmail (request.form ["email"], "You've successfully registered for {}" .format (get_config ("ctf_name")), log ("registrations") "[{date}] {ip}-{name} registered with {email}") db.session.close () if is_teams_mode (): return redirect (url_for ("teams.private")) return redirect (url_for ("challenges.listing")) else:return render_template ("register.html", errors=errors)

More than half of the above code is for input detection, and the key parts are extracted:

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) = 0names = 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.strip ()) = 0pass_long = len (password) > 128valid_email = validators.validate_email (request.form ["email"]) team_name_email_check = validators.validate_email (name) if len (errors) > 0: # Detection error''Registration account password inserted into database' 'else: With app.app_context (): user = Users (name=name.strip () Email=email_address.lower (), password=password.strip (),) db.session.add (user) db.session.commit () db.session.flush () login_user (user) if config.can_send_mail () and get_config ("verify_emails"): # Confirming users is enabled and we can send email.log ("registrations", format= "[{date}] {ip}-{name} registered (UNCONFIRMED) with {email}" ) email.verify_email_address (user.email) db.session.close () return redirect (url_for ("auth.confirm"))

In the upper part, accept the input information from the user:

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) = 0names = 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.strip ()) = 0pass_long = len (password) > 128valid_email = validators.validate_email (request.form ["email"]) team_name_email_check = validators.validate_email (name)

The key lies here:

Names = Users.query.add_columns ("name", "id") filter_by (name=name) .first ()

In determining whether the user has signed up, name is used directly, that is, the account name entered by the user, and there is no filtering.

In the second half, when the registration is successful, insert the account, password, and mailbox into the database:

With app.app_context (): user = Users (name=name.strip (), email=email_address.lower (), password=password.strip (),) db.session.add (user) db.session.commit () db.session.flush ()

But here, the account entered by the user is removed from the space. (that is, if the m1sn0w account exists in the database, but if the account name I entered when registering is: space m1sn0w, then, instead of indicating that the account already exists, the user name m1sn0w will be inserted into the database, that is, there is a user with the same name in the database)

Next is the second point to use (change the password): extract the main code

Auth.route ("/ reset_password", methods= ["POST", "GET"]) @ auth.route ("/ reset_password/", methods= ["POST", "GET"]) @ ratelimit (method= "POST", limit=10, interval=60) 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_password.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"))

We know that when changing the password, an email will be sent to the corresponding mailbox. After clicking, the password can be changed. (the above data value, that is, a string of values after the URL sent to the specified mailbox)

Next, take a look at what the data value is: / CTFd/utils/email/__init__.py

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) return sendmail (email, text)

As you can see, it serializes the user name, splices it after the corresponding URL, and sends it to the mailbox. (through the previous analysis, we know that two accounts in the database have the same name, so when we change the password, the password of the first user will be changed.)

(some articles say that the current user needs to be changed to another user name, but it doesn't seem to be necessary.)

If request.method = "POST": user = Users.query.filter_by (name=name). First_or_404 () user.password = request.form ["password"]. Strip () db.session.commit ()

The user it takes out here is the first user (that is, the previously registered user).

Therefore, the general methods of utilization are as follows:

1. Register an account with the same name as the user name you want to change, but add a space when registering

2. Click to change the password, confirm it in the mailbox, and then you can change the password of the specified user.

On how to carry out CVE-2020-7245 vulnerability analysis is shared here, I hope that the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Network Security

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report