在司庆小游戏中利用漏洞屠榜

公司在 22 周年司庆活动期间推出了一款 H5 小游戏。

在游戏中,玩家通过控制小人躲避障碍来不断提高成绩(跑步米数),在游戏中还会随机掉落各种道具,率先收集齐全套道具的玩家则有机会获得奖品。

本着学(na)习(jiang)的精神对小游戏进行来一番研究,发现游戏存在漏洞,能通过伪造请求的方式直接刷满道具。

游戏介绍

游戏内容前面已经做过简单介绍,补充一些游戏截图 ⬇️

游戏分析

通过抓包,找到几个关键请求如下:

在游戏中会有四种随机的道具掉落,4 种道具凑满 22 个时即可有机会获得奖品。

分析上面的请求很明显能够猜到 item_id 应该代表了四种不同的道具类型。

token 可能是某个密钥或者身份标识,通过对比好几个其他请求后发现 token 总是不变的,这里暂且可以认为是代表了当前用户的身份标识。

t 明显是一个时间戳,这些都可以很轻易的进行伪造。

剩下最后一个 sign 则是对当前请求内容的一个签名,表示游戏还是有进行简单的安全验证的。

与上面 drop 请求类似,在碰到障碍后结束游戏并发起 end 请求上报成绩。除了 sign 字段,我们基本上已经可以完全伪造上面的请求。

前后端交互部分我们基本已经摸清,剩下的就是搞清楚 sign 是如何而来的。

Javascript 部分

带着问题出发,我们没有必要完完全全分析整个游戏的 JavaScript,只需要针对性查看 sign 部分即可。

看起来 sign 就是这个 getMd5 方法通过计算参数 e 得到的签名。继续顺藤摸瓜找到 getMd5 方法:

到这里基本可以确定了,sign 是通过将请求数据的 key 按照字典顺序排序后,再将 value 拼接起来,最后进行 sha256 计算得的到 16 进制签名。

伪造请求

将相关代码扒出来,加上伪造的相关逻辑:

const token = ''
const cookie = ''

const dropURL = "xxxxx/api/api/collect/drop";
const endURL =  'xxxxx/api/api/game/end'

const headers = {
  "Content-Type": "application/x-www-form-urlencoded",
  "x-requested-with": "xxx",
  cookie,
};

async function getItems () {
  for (let type = 1; type < 5; type++) {
    for (let i = 0; i < 22; i++) {
      const requestData = {item_id: type, token, t: Date.now()}
      requestData.sign = getMd5(requestData);

      await axios.post(dropURL, requestData, { headers })
        .then((res) => {
          res.data.code === 200 && console.log(`成功获取第 ${i + 1} 个道具 ${type}`)
        })
        .catch(e => {
          console.log(`获取道具 ${type} 失败`)
        });
    }
  }
}

function run (meter) {
  const requestData = {meter, token, t: Date.now()}
  requestData.sign = getMd5(requestData);

  axios.post(endURL, requestData, { headers }).then(res => {
    res.data.code === 200 && console.log(`成功跑了: ${meter} 米`)
  })
}

console.log('开始游戏 ...')
getItems().then(() => {
  run(6666)).then(() => {
    console.log('游戏结束')
  })
})

效果 ⬇️