比赛情况

  • 比赛时间(UTC-8):2022.04.23 09:00:00 ~ 2022.04.24 21:00:00,共计 36 h
  • 比赛模式:动态分值
  • 队伍总分:5402.347107438017
  • 队伍排名:8 / 460
  • 贡献分数:757.2231404958677

前言

没啥好说的,难。

跟着彪哥恰到了烂钱,打算拿去吃 K(不是

因为前 20 要写 WP,这里就把之前写的玩意直接搬过来了。

ppd

首先尝试正常抽奖,发现获取 FLAG 进度到达 99.999999999999% 之后,抽奖要么在这个进度死循环,要么倒退到 99.999999%,那么显然不能直接抽奖。

源码在 Webpack/src/App.vue 里面,拿出来审计一下,同时结合抓包分析,发现以下事实:

  • 服务器端的主要逻辑位于 lucky.php 中
  • 当 localStorage 和 sessionStorage 中有 enc 值时,进入页面:
    • 本地调用 getUserInfo 函数,将 enc 字段通过 POST 请求传递给 /lucky.php?action=info
    • 服务端对 enc 进行解析,并返回解析结果
  • 当 localStorage 和 sessionStorage 中没有 enc 值时,进入页面:
    • 本地调用 start 函数,通过 randomString 函数生成一个格式固定的 username,并使用 POST 请求将 username 字段传递给 /lucky.php?action=start
    • 服务器返回一个随机生成的 money(整形数据,数值在 50 到 80 之间),并将 time 记为 0
    • 利用以上数据(username、money、time)生成 enc 值,并返回其解析结果

抽奖机制对于解决本题 P 用没有,所以这里就不细说了。

在对于 enc 的值进行多方分析之后,发现其原理为:使用 AES-128 的 ECB 模式对 php 序列化字符串进行加密,而序列化字符串可以在 response 的数据里找到。

AES 加密的 key 是未知的,但由于我们能够自由控制 username 区段,考虑到 AES 的块加密特性,我们可以通过构造 username,并发送到服务端进行解析,通过返回的解析结果来找到我们需要的 128 位数据块 “明文->密文” 的对应关系,而不用关心如何求出具体的 key。这样,通过构造出数个 enc,并分别提取出它们不同的有效部分进行拼接,就可以获得我们的目标 enc。

具体构造的过程如下:

  • 调用 /lucky.php?action=start,并将 username 置为 AAAAAAAAAAAAA:"money";i:100;},POST 发送,得到第一个 enc
    • 此时整个序列化字符串为 O:4:"User":3:{s:8:"username";s:29:"AAAAAAAAAAAAA:"money";i:100;}";s:5:"times";i:0;s:5:"money";i:57;}
    • 用于获取第 49 ~ 64 Byte 的 :"money";i:100;} 区块
  • 调用 /lucky.php?action=start,并将 username 置为 AAAAAAAAAAAAAAAAAAAAAAAA,POST 发送,得到第二个 enc
    • 此时整个序列化字符串为 O:4:"User":3:{s:8:"username";s:25:"AAAAAAAAAAAAAAAAAAAAAAAAA";s:5:"times";i:0;s:5:"money";i:57;}
    • 用于获取 AES 加密时最后一行的 padding 密文
  • 调用 /lucky.php?action=start,并将 username 置为 AAAAAAAAAAAAAAAAAAAAAAA,POST 发送,得到第三个 enc
    • 此时整个序列化字符串为 O:4:"User":3:{s:8:"username";s:24:"AAAAAAAAAAAAAAAAAAAAAAAA";s:5:"times";i:0;s:5:"money";i:57;}
    • 用于将 :"money";i:57;} 对齐到最后一个加密块里,便于整体覆盖

将上面的三个 enc 进行 base64 解码,并在十六进制编辑器下拼接,最终得到我们需要的 enc:SRMr2xR0uuLsQScgoAegY5ru3gAqkC8Fe56Jv+SbDaRVViKl7DEegptDPcmPWKHkxUVEERuSp+J9pGsh2LdoMhaX0OMnP2NzLvNgOF+lczbUEOm4KpsJyCAzosg2pzGiJSK1AIGuHo/qjfgpPEkCZA==

将上述 enc 通过 POST 请求发送到 /lucky.php?action=getFlag 即可。

贴个截图:

大成功