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

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

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

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

游戏介绍

游戏内容前面已经做过简单介绍,下面补充一下游戏截图方便大家理解。

游戏分析

在上手玩了一把游戏后,第一个想法就是看看这个游戏是怎么实现的。

于是开启代理抓包,重新进入游戏再玩一遍。

网络部分

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

游戏中获取道具时发起的请求

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

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

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

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

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

游戏中碰到障碍后结束发起的请求

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

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

JS 部分

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

在抓到的请求包中有相当大一部分的图片、JSON 等内容,过滤出 JS 后找到了一个 bunder.js ,看起来像是打包压缩过后的主要逻辑部分。

Copy 到编辑器格式化后查找关键字 sign,找到一个发起请求的地方有用到 sign

查找 sign 关键字

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

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('游戏结束')
  })
})

效果:

运行结果
后面补的排行榜截图

可以看到在排行榜的第一名已经有大佬更快的找到游戏漏洞,直接跑了 500000000 米。

Web 安全问题无处不在。切记,害人之心不可以有,防人之心不可无。

发表评论

邮箱地址不会被公开。 必填项已用*标注