web buuctf [HCTF 2018]admin1

查看首页源代码,有注释

考虑这道题很有可能是用admin或者伪造admin进行登录,

用admin进行登录,随便填写密码进不去,发现页面有注册选项,用admin注册提示已经被注册

方法一:burp爆破

进入登录界面,用户名输admin,密码随便填,抓包对密码进行爆破;

web buuctf [HCTF 2018]admin1_第1张图片

 web buuctf [HCTF 2018]admin1_第2张图片

爆破之后,提示429和302,可以看一下429的response,提示的是太多请求的问题,

更改options中的选项

web buuctf [HCTF 2018]admin1_第3张图片

web buuctf [HCTF 2018]admin1_第4张图片

控制数据量,我因为正好吃饭就调成300,很慢,大家可以自行调节,正常的返回结果就有一个payload的长度是不同的

web buuctf [HCTF 2018]admin1_第5张图片

 可以确定admin的密码是123,登录即可得到flag

web buuctf [HCTF 2018]admin1_第6张图片

 

方法2:

随便注册个账户,然后登录进页面,进行最简单的工作,逐个页面查看源代码,看有无有用的信息和漏洞,在change password页面下发现注释

 

登录下载下来发现是网站的源码文件,打开\hctf_flask-master\app,先看里面的routes.py路由文件,逐个看一下,\hctf_flask-master\app\templates\index.html中

{% include('header.html') %}
{% if current_user.is_authenticated %}

Hello {{ session['name'] }}

{% endif %} {% if current_user.is_authenticated and session['name'] == 'admin' %}

hctf{xxxxxxxxx}

{% endif %}

Welcome to hctf

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

关于flask里面的session:

flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中,也就是说其实只是将相关内容进行了加密保存到session中。和服务端的session不同,服务端的session保存在服务端中,依靠客户端cookie值中的sessionId来进行识别。

也就是说可以通过更改session里面的用户名为admin,便可实现伪造admin登录

通过burp抓取session,是一串码,需要进行解密

GitHub - noraj/flask-session-cookie-manager: Flask Session Cookie Decoder/Encoder

github上有flask-session的加解密代码,

因为我的电脑上安了python3和python2,所以安装flask的时候出了点问题,pip3 install flask确保代码执行没有问题

C:\Users\86155>python flask-session-cookie-manager-master\flask_session_cookie_manager3.py decode
usage: flask_session_cookie_manager3.py decode [-h] [-s ] -c 
flask_session_cookie_manager3.py decode: error: the following arguments are required: -c/--cookie-value

C:\Users\86155>python flask-session-cookie-manager-master\flask_session_cookie_manager3.py decode -h
usage: flask_session_cookie_manager3.py decode [-h] [-s ] -c 

optional arguments:
  -h, --help            show this help message and exit
  -s , --secret-key 
                        Secret key
  -c , --cookie-value 
                        Session cookie value

可以看到需要secret key和session cookie value两个参数

secret可以在\hctf_flask-master\app\config.py中看到 

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

session value就是抓包所得,这里有一点session value的获得过程,首先,要用你已经注册的账号先登录进去,此时记录的session记录的用户名是你注册的名字,这时候再将获取的session更改,例如:登录change password,进行抓包,运行解密代码

C:\Users\86155>python flask-session-cookie-manager-master\flask_session_cookie_manager3.py decode -s "ckj123" -c ".eJw9kEGLwjAQhf_KMmcPbWwvgoddUkuFmaBEZXIRXWvbxLhQldWI_32jCx4f783Hm3eH9b6vTy2Mzv2lHsC628HoDh9bGAHrWc4rHqKcO9ZfllZVIFlkZDlHbxwFzjAcWiqnB7OaOlWajkSVoTWd0gtBfmoxLASLl86UjDm9dEYWCYXWYYlBldQ9OSSKgHY2jJmWtUuxXEYuefRVZqxLntrIJlGar_E-Z4E5WWNR71qjeQyPAXyf-v36_OPq4_sF1I3gsLSx8i3WOpB0VyXxl0WRcNQcOEVdDTGWxTC7YTl36nP8wnV-09Rv0tZPFDX_znHjowGbne-OMIDLqe5fu0GawuMPRadr_A.Yi2c_Q.litxhAuL4tDmrJkduha2BNut3I8"
{'_fresh': True, '_id': b'a49af704da0c5b34186692fd76839a4beebd8fb7b826b9566bc356cfb95882ee5dd1478d0c38cb7687a324782aa950ea4cf2b8f940ead84961d19cc966c17ae6', 'csrf_token': b'186c5c66226e491830ca4b6ec651273cf3420dd8', 'image': b'naN6', 'name': 'admin', 'user_id': '11'}

name字段为你登录的账户,根据要求要改成admin, 因为我已经做完了,所以我解密完之后还是admin,我就不改了,大家做的时候注意一点,再将改好的进行加密,encode -s "ckj123" -t "secret structure",

web buuctf [HCTF 2018]admin1_第7张图片

替换转发

web buuctf [HCTF 2018]admin1_第8张图片 

方法3:unicode编码覆盖

在查看route.py代码中

@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)

def strlower(username):
    username = nodeprep.prepare(username)
    return username

 在login和register函数中,均对传参username进行了strlower处理,查看下面的函数,是对username调用函数nodeprep.prepare进行处理,unicode转化,而nodeprep是从Twisted模块导入的,在requirements.txt文件中发现Twisted==10.2.0,与最新版本差距很大,

注册和登录各调用了一次这个函数,我们倒着进行推算,要使传进去的值服务器识别成admin,

函数的作用是将大写转化为小写

但它同时会将unicode字符转换成A,而A再调用一次nodeprep.prepare函数会把A转换成a

也就是说我们可以使用ᴬdmin注册登录,登陆后再想办法让服务器再执行一次nodeprep.prepare,便可以变成admin账户

方法4:条件竞争

这种是理想状态下的解题方式,但是需要大量线程同时进行,精确度也无法控制和硬件关联性很强,而且如果数量很多也会报429

因为是flask框架,并发式运行会产生漏洞,当以session name=admin进行登录,login上的session name为admin,同时进行change函数,当session name=admin ,session保留的name为admin,执行change,就会调用session里的name,这样相当于给admin改了密码,这种更改条件性太强,现实的渗透很少能够用到,就不尝试了。

 

你可能感兴趣的:(安全)