BUUCTF__[HCTF 2018]admin_题解

一、看题

  • 拿到题目,发现一个欢迎页面。有注册、登录功能。
    在这里插入图片描述
  • 首先习惯性 F12。发现提示。
    在这里插入图片描述
  • 提示不是 admin ,那我们尝试登录用户名 admin ,密码随便输入,提示密码错误。尝试注 册 admin ,提示账号已被注册,那么我们怎么才能用 admin 登录呢。
  • 那先尝试随便注册一个账号登录看看有什么内容,注册用户名和密码都为1,登录。
  • 发现有修改密码功能。
    在这里插入图片描述
  • 可以注册、登录、修改密码,很自然的想到二次注入。
  • 事实上,预期解的确是二次注入,只不过有好几种非预期解法。

二、研究

  • 这题得到flag,实际上可能有四种方式吧。
  • 还有一点没想到的是,在修改密码页面还存在被注释的提示。题目有两处注释提示,没想到这点,还是 F12 没用好。
  • 在这里提示发现了一个 GitHub 项目。
    在这里插入图片描述
  • 访问发现了源码
    在这里插入图片描述
  • 下载到本地,发现是 python 写的,因为还没学 python 。这题超纲了。
  • 不过看wp还是能看懂。

解法一__Unicode欺骗(预期解法)

  • 既然有源码,重点肯定是在注册登录的验证处理和对数据库操作。
  • index.html 发现 当登录用户名为 admin 时 输出 flag
    在这里插入图片描述
  • 但之前用 admin 登录注册都报错。
  • 所以想到二次注入,也就是先注册一个账号和 admin 不同,成功注册后,登录在修改密码时,能修改 admin 的密码。
  • routes.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', 
  • 登录模块
@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('/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)
  • 在登录后,发现有 session 储存在本地,而且修改密码也是取的 session 验证用户名。
  • 但本来储存在本地的应该是 cookiesession 应该是存储在服务器的,而这里为什么 session 在本地???
  • 这里引用解释

根据p神的文章 https://www.leavesongs.com/PENETRATION/client-session-security.html
可以知道,flask的session是存在客户端的

在这里插入图片描述

  • 那么问题就出在这里。它取 session 还经过了一下处理,而不是直接 session 解密后的内容

    name = strlower(session['name'])
    
  • 而在代码末可以找到这个函数。

def strlower(username):
    username = nodeprep.prepare(username)
    return username
  • 这个函数的解释是(百度没找到相关文档),在别人的wp里看到的。

nodeprep.prepare函数会将unicode字符ᴬ转换成A,而A在调用一次nodeprep.prepare函数会把A转换成a

  • 这个字符可以在后面网站找到,https://unicode-table.com/en/1D2E/

  • 知道这个结合源码就能理解一下,看到注册、登录、修改密码都经过了处理。

    name = strlower(session['name'])
    
  • 那么如果用 ᴬᴰᴹᴵᴺ 注册,注册经过处理,储存在数据库就变成了 ADMIN

  • 登录后,加密储存在session 的也是 ADMIN

  • 那么修改密码时,取出session解密后的用户名也是 ADMIN

  • 再一次调用 strlower 函数处理就变成了 admin 所以修改密码就是修改admin 的密码,所以实际上修改了数据库中的admin

  • 这时admin 的密码就变成了自定义修改后的密码。登录后就得到 flag 。

  • 只能说看wp不难理解,但还是不简单。

解法二__弱密码(最大非预期)

  • 直接 admin 的密码就是 123 。登录直接出 flag 。
  • 跑一下字典或者随便输入都能出 flag。

解法三__flask session 伪造(非预期)

  • 这个对我来说,是很大的知识盲区。
  • 首先我知道session 可以用来身份验证,就是你在浏览器打开网页登录后,可能关闭后再打开不需要重新登录,这就是 cookie 和 session 的作用。其次,修改 session 绕过验证的操作接触过,之前看着大佬的教程,绕过一个钓鱼网站进入后台。但是我不会利用,我只是知道有这个东西。
  • 主要的知识盲区是这里用了签名,而且我不会解密 session
  • 但知道这个方法后,百度可以找到 flask session 加解密的脚本。
  • 理论上可以做出来。解密 session 修改后替换原 session 再访问即可。
  • 这里不详细介绍,因为没复现成功,详细了解可以看其他师傅的wp。

解法四__条件竞争 (非预期)

  • 这种解法原理更深层,完全的知识盲区。
  • 涉及到了多进程和多线程在共享数据时的逻辑顺序问题。
  • 暂时无法理解。说一下有这种方法。

三、最后

  • 很多知识点和做题技巧,有点难懂。

  • web真的太多知识点了。很难灵活运用。

  • 附上原题链接 https://buuoj.cn/challenges#[HCTF%202018]admin

  • 持续更新BUUCTF题解,写的不是很好,欢迎指正。

  • 最后欢迎来访个人博客 http://ctf-web.zm996.cloud/

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: simple_ssti_1 是一个使用 Flask 框架编写的 web CTF 题目,主要涉及到服务器端模板注入 (Server-Side Template Injection, SSTI) 的问题。 这个题目中,使用者可以在输入框中输入任意字符串,然后后端会将这个字符串嵌入到一个 Flask 模板中,并将渲染后的页面返回给用户。攻击者可以在输入框中输入包含 SSTI 代码的字符串,从而在服务器上执行任意代码。 为了解决这个问题,可以使用 Flask 的 Jinja2 模板引擎提供的 safe 过滤器来过滤用户输入,防止用户输入的字符串被作为代码执行。或者在编写模板时,尽量避免使用可执行的表达式,而是将变量放在 HTML 标签中输出。 这个题目是一个很好的 SSTI 注入练习题目,可以帮助我们了解 SSTI 注入的危害和防御方法。 ### 回答2: Simple_ssti_1是BugsKiller CTF比赛中的一道Web题目,考察的知识点是SSTI(Server-Side Template Injection)模板注入,需要寻找漏洞点并利用SSTI漏洞构造payload,达到读取/执行任意代码的目的。 首先,我们下载题目附件simple_ssti_1.zip,获得题目源代码及相关文件。查看代码,发现以下几点: 1. 程序的入口是index.php文件,包含了一个GET参数tpl,可控注入的点在这里。 2. 向模板文件simple_ssti_1_template.html中传入tpl参数,在该文件中执行了{{tpl}}操作,将tpl参数进行了模板渲染。 3. SSTI的注入点在于,如果我们的攻击payload中包含了一些特殊模板语法的操作符,如{{3*3}}、{{config}}等,这些操作符会被解析器自动执行,导致代码注入进去。 从上述代码的分析可知,我们首先需要构造包含有SSTI操作符的payload才能进行下一步的SSTI构造。继续观察代码,我们发现一个{{config}}变量被渲染在了simple_ssti_1_template.html的头部中,我们可以通过注入payload来构造一个同名的config变量,从而读取根目录的敏感文件/flag.php。 构造payload : {{config.__class__.__init__.__globals__['os'].popen('cat flag.php').read()}} 这个SSTI注入的payload实现了直接运行命令cat flag.php然后读取文件的操作。注入的{{config}}变量实际上是一个自定义的config字典,含有很多内置函数,比如__class__.__init__.__globals__,它的作用是获取全局变量__builtins__,然后通过这个全局字典来获取os模块,之后就可以使用os模块的popen函数运行cat命令来获取flag.php文件内容了。 最终的payload为: ?tpl={{config.__class__.__init__.__globals__['os'].popen('cat /flag.php').read()}} 再通过浏览器发送带有payload的GET请求,就可以读取/root/flag.php中的flag了。 ### 回答3: 简单SSTI 1是一道基于SSTI漏洞的Web安全挑战题目,该题的难度属于初级。本题需要掌握一定的SSTI漏洞的相关知识和技巧,以及对模板中的变量注入点的识别和利用能力。 首先,我们尝试在输入框中输入简单的Python表达式,例如{{2+2}},并提交请求,发现得到的响应结果为4,表明该网站存在SSTI漏洞。接着,我们可以构造一些特殊的表达式进行测试,例如{{123456789}}, {{2**100}}, {{'hello ' + 'world'}}, 发现均能得到正确的响应结果。 接着我们需要进行变量注入点的识别和利用,这里,我们可以通过利用flask框架中的特殊变量,例如request、g等来实现变量注入,例如{{config}},可以获得flask的配置信息,{{request}}可以获得当前请求的一些信息。需要注意的是,在实战中,这些利用方式可能会受到服务器的限制,无法完全实现。 最后,我们需要尝试获取敏感信息或者升级我们的权限,例如{{''.__class__.mro()[1].__subclasses__()[71]('/etc/passwd').read()}},可以获取到服务器上/etc/passwd文件的内容。 总之,简单的SSTI漏洞需要熟练掌握SSTI漏洞的相关知识和技巧,识别变量注入点并利用它们获取敏感信息和升级权限,可以通过CTF题目学习,提高自己的Web安全攻防能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值