Unicode的危害(HCTF_admin) - 图1

    题目的大致思路就应该让我们成为admin吧

    随便注册了一个后,发现有一个更改密码

    Unicode的危害(HCTF_admin) - 图2

    之前题目的经验,那应该找回密码有漏洞的。抓包后发现,我们用户名买这个id参数修改不了

    Unicode的危害(HCTF_admin) - 图3

    session到是有,那我们进去以后源码就出来了

    Unicode的危害(HCTF_admin) - 图4

    Unicode的危害(HCTF_admin) - 图5

    将所给地址源码下载下来,便是这道题的源码了,使用flask框架写的,先看看routes.py,看看都允许访问哪些路径吧

    1. @app.route('/code')
    2. ...//生成验证码
    3. @app.route('/')
    4. @app.route('/index')
    5. ...
    6. @app.route('/register', methods = ['GET', 'POST'])
    7. def register():
    8. if current_user.is_authenticated:
    9. return redirect(url_for('index'))
    10. form = RegisterForm()
    11. if request.method == 'POST':
    12. name = strlower(form.username.data)
    13. if session.get('image').lower() != form.verify_code.data.lower():
    14. flash('Wrong verify code.')
    15. return render_template('register.html', title = 'register', form=form)
    16. if User.query.filter_by(username = name).first():
    17. flash('The username has been registered')
    18. return redirect(url_for('register'))
    19. user = User(username=name)
    20. user.set_password(form.password.data)
    21. db.session.add(user)
    22. db.session.commit()
    23. flash('register successful')
    24. return redirect(url_for('login'))
    25. return render_template('register.html', title = 'register', form = form)
    26. @app.route('/login', methods = ['GET', 'POST'])
    27. def login():
    28. if current_user.is_authenticated:
    29. return redirect(url_for('index'))
    30. form = LoginForm()
    31. if request.method == 'POST':
    32. name = strlower(form.username.data)
    33. session['name'] = name
    34. user = User.query.filter_by(username=name).first()
    35. if user is None or not user.check_password(form.password.data):
    36. flash('Invalid username or password')
    37. return redirect(url_for('login'))
    38. login_user(user, remember=form.remember_me.data)
    39. return redirect(url_for('index'))
    40. return render_template('login.html', title = 'login', form = form)
    41. @app.route('/logout')
    42. ...
    43. @app.route('/change', methods = ['GET', 'POST'])
    44. def change():
    45. if not current_user.is_authenticated:
    46. return redirect(url_for('login'))
    47. form = NewpasswordForm()
    48. if request.method == 'POST':
    49. name = strlower(session['name'])
    50. user = User.query.filter_by(username=name).first()
    51. user.set_password(form.newpassword.data)
    52. db.session.commit()
    53. flash('change successful')
    54. return redirect(url_for('index'))
    55. return render_template('change.html', title = 'change', form = form)
    56. @app.route('/edit', methods = ['GET', 'POST'])
    57. .....
    58. @app.errorhandler(404)
    59. .....
    60. def strlower(username):
    61. username = nodeprep.prepare(username)
    62. return username

    这里省略了一些不太会用到的路径,接着进行代码审计,发现了一个问题这里在注册时,会将我们传入的用户名数据转变为小写,

    Unicode的危害(HCTF_admin) - 图6

    并且在改密码处也有相同的操作

    Unicode的危害(HCTF_admin) - 图7

    这里使用的不是python自带的lower()方法,而是自定义的strlower(),或许有什么端倪,先来看看.

    Unicode的危害(HCTF_admin) - 图8

    这里使用了一个nodeprep.prepare()进行小写操作,我们并不知道是什么,然后进行谷歌

    Unicode的危害(HCTF_admin) - 图9

    发现这是Twisted包中的一个方法,接着查看requirements.txt发现当前环境Twisted为10.2.0,然后去github上查看当前最新版本已经19.2.1了,说明这里会有问题

    Unicode的危害(HCTF_admin) - 图10

    最后找到两篇文章

    <a href=”https://labs.spotify.com/2013/06/18/creative-usernames/

    “>有关Twisted某个版本转换问题

    Unicode编码安全问题

    最后得到信息:

    Unicode的危害(HCTF_admin) - 图11

    说明在11.0版本以下的Twisted均存在这样的问题,我们环境版本为10.2.0刚好符合,同时提到nodeprep.prepare()不具有幂等性,即对某个数据反复进行某个操作,总会得到相同的结果举个例子

    Unicode的危害(HCTF_admin) - 图12

    这种情况a字符串无论进行多少次lower()都会得到一个结果,因此lower()方法具有幂等性,而nodeprep.prepare(),则会出现下面的情况

    Unicode的危害(HCTF_admin) - 图13

    也就是说,同样举个例子,也就说:

    ᴺ—->N—->n(Unicode中相同的字母和符好,对应的编码可能不同,如:Ω和Ω)

    nodeprep.prepare()会执行类似于这样的转换,在第一次进行转化时会将其匹配为类似的大写字母,然后第二次才将其匹配为小写字母,两次相同操作所得结果不一样,因此这个方法不具有幂等性.

    首先我们注册一个

    ᴬᴰmin的账户,这时候我们传入的数据会进行一次转化,这时ᴬᴰmin—>ADmin,服务器端会判断该用户是否存在,然后成功注册

    Unicode的危害(HCTF_admin) - 图14

    登陆该账号,因为登陆时也会被进行一次转化,所以使用ᴬᴰmin登陆,但后台的账号是ADmin

    Unicode的危害(HCTF_admin) - 图15

    我们可以看到账号登陆成功,且为ADmin

    Unicode的危害(HCTF_admin) - 图16

    然后执行一次更改密码操作,改密码时也会进行一次转化,这时我们便从ADmin—>admin,完成了admin账户的密码更改操作,这时候再登陆admin账号,就可以得到flag了

    Unicode的危害(HCTF_admin) - 图17