Level1

代码逻辑

  1. 地图:颜色,不同元素类型(豆子和墙壁)
  2. 按键:省略
  3. User/Ghost:移动方向和速度、坐标设置等

    关键函数/代码

    “”POST””

    肯定有数据交互,所以搜“”POST””,没有就搜“POST”,可以定位到发包处。
    看看发包的数据结构和内容,如果对渗透比较熟的,可以自己用“BurpSuit”构建一个假包发过去。

    “success”

    可以定位到逻辑成功处,看看能不能强跳过来——当然,如果要和服务器交互,过不了服务器的验证,强跳是假跳。

    关键函数completedLevel

    1. function completedLevel() {
    2. fetch("/games/pacman/api/1", {
    3. method: "POST",
    4. headers: {
    5. "Content-Type": "application/json",
    6. },
    7. credentials: "same-origin",
    8. body: JSON.stringify({ action: `${eaten}`, _csrf: $("#csrf").val() })
    9. })
    10. .then((resp) => resp.json())
    11. .then(function (data) {
    12. if (data.success)
    13. window.location.href =
    14. "/games/pacman/level/2?mode_easy=5a6d46736332553d&code=7";
    15. else alert(data.data);
    16. });
    17. setState(WAITING);
    18. level += 1;
    19. map.reset();
    20. user.newLevel();
    21. startLevel();
    22. }

    解构completedLevel()函数逻辑

  4. 给“/games/pacman/api/1”发送“POST”包

  5. “Content-Type”类型是json
  6. “credentials”认证和当前一样
  7. “body”内容是json字符串
    1. action: ${eaten}
    2. _csrf: $(“#csrf”).val(),

      ${eaten}

      搜索“eaten”变量,共有11个,其中:
  • eaten = null”4个
    • eaten === null”1个
    • eaten !== null”1个
  • eaten = 0”1个,处于“newLevel()”函数中,重新赋值
  • eaten = game.getTick()”1个,处于“eat()”函数中
  • **eaten += 1**
  • **eaten === 182**

调用completedLevel()函数的逻辑如下:

if (
  ((isMidSquare(position.y) || isMidSquare(position.x)) &&
    block === Pacman.BISCUIT) ||
  block === Pacman.PILL
) {
  map.setBlock(nextWhole, Pacman.EMPTY);
  addScore(block === Pacman.BISCUIT ? 10 : 50);
  eaten += 1;

  if (eaten === 182) {
    game.completedLevel();
  }

  if (block === Pacman.PILL) {
    game.eatenPill();
  }
}

所以,发送的值必然为182。修改代码如下:

eaten += 1;
改为
eaten += 182;

image.png

HTTP包

除正确调用之外,还有我之前比较擅长,现在没有工具就和废了一样的伪造虚假包,已经可以看到报文内容了,还是很简单的明文,构造完发过去即可。
详见下文~

Level2

与Level1相同处省略。

关键代码/函数

function completedLevel() {
    fetch("/games/pacman/api/2", {
        method: "POST",
        headers: {
        "Content-Type": "application/json",
        },
        credentials: "same-origin",
        body: JSON.stringify({
        action: `hacked${hacked}`,
        _csrf: $("#csrf").val(),
        }),
    })
        .then((resp) => resp.json())
        .then(function (data) {
        if (data.success) window.location.href = "/games/pacman/level/3";
        else alert(data.data);
        });

    setState(WAITING);
    level += 1;
    map.reset();
    user.newLevel();
    startLevel();
}

解构completedLevel()函数逻辑

……省略……

  1. “body”内容是json字符串

    1. action: hacked${hacked},
    2. _csrf: $(“#csrf”).val(),

      hacked${hacked}

      hacked的值为:

      var hacked = url.searchParams.get("mode_easy") == "64484a315a513d3d";
      

      检查URL的参数“mode_easy”值是否为“64484a315a513d3d”。
      ==”相等条件对比,结果只有true或者false,根据文字的意思,选true
      至此,发包的逻辑捋顺了,那么接下来的问题就是如何调用发包的函数completedLevel()

      调用completedLevel()

      调用处

      搜索“completedLevel()”,定位到:

      if (
      ((isMidSquare(position.y) || isMidSquare(position.x)) &&
        block === Pacman.BISCUIT) ||
      block === Pacman.PILL
      ) {
      map.setBlock(nextWhole, Pacman.EMPTY);
      addScore(block === Pacman.BISCUIT ? 10 : 50);
      eaten += 1;
      
      if (eaten === 182) {
        game.completedLevel();
      }
      
      if (block === Pacman.PILL) {
        game.eatenPill();
      }
      }
      

      调用逻辑为“eaten”变量为182,可以把每次得分直接从得1分改为得182分:

      eaten += 1;
      改为
      eaten += 182;
      

      或者无所谓得分,直接if(1),恒成立:

      if (1) {
      game.completedLevel();
      }
      

      同理还有把“eaten”成立的对应数字改成1,改分方式不多赘述:

      if (eaten === 1) {
      game.completedLevel();
      }
      

      action&hacked

      将“hacked”改为true

      var hacked = true
      

      或者发包代码改为:

      action: `hacked${true}`,
      或者
      action: `hackedtrue`,
      

      或者发包内容改为:

      action: "hackedtrue",
      

      image.png

      虚假包

      因为数据很简单,可以构造一个虚假包,如:
      image.png

      关键payload

      action: "hackedtrue"
      

      返回包

      image.png
      image.png

      Level3

      代码逻辑

      发包处逻辑需要分数大于5000。

      使发包逻辑成立

      1. addScore(简单)

      function addScore(nScore) {
      score += 6666;// 大于5000的值
      if (score >= 10000 && score - nScore < 10000) {
      lives += 1;
      }
      

      2. 修改发包逻辑(麻烦一点)

      成立条件“if (score > 5000)”改为恒成立的“if (1)”。
      但是有个值“action:storage${pAcman}`”的“pAcman`”不知道是没获取到还是没运行到,定位其赋值代码:

      setInterval(function () {
      if (sessionStorage.getItem("scorePerCherry") > 0) {
      window.pAcman = 20;
      }
      }, 1000);
      

      不过既然知道“pAcman = 20”,分数要大于5000,那么修改代码如下:

      if (1)
      fetch("/games/pacman/api/3", {
      method: "POST",
      headers: {
      "Content-Type": "application/json",
      },
      credentials: "same-origin",
      body: JSON.stringify({
      action: `storage${20}`,
      score: 6666,
      

      image.png

      HTTP包

      发包

      image.png

      返回包

      image.png

      Level4

      image.png

      代码逻辑

      走出迷宫?

      发包代码

      if (
      md5(userPos.y + "mario") == "5fd3e45cc1044dfe5d5357789968e086" &&
      (md5(userPos.x + "mario") == "8f33eb149d3653873f1be1c66fa46979" ||
      md5(userPos.x + "mario") == "16342dc95b0fce33753a273e0ab598e6")
      ) {
      fetch("/games/pacman/api/4", {
      method: "POST",
      headers: {
      "Content-Type": "application/json",
      },
      credentials: "same-origin",
      body: JSON.stringify({
      action: `maze`,
      score: moveByXy,
      // _csrf: $("#csrf").val(),
      }),
      })
      .then((resp) => resp.json())
      .then(function (data) {
      if (data.success) window.location.href = "/games/mario/level/1";
      else alert(data.data);
      });
      }
      }
      

      MD5值

      16342dc95b0fce33753a273e0ab598e6 = 为“0mario”,即y为0。
      其他的值比较奇怪,从0-100遍历“[0-100]mario”,只有0的“16342dc95b0fce33753a273e0ab598e6 ”匹配上了,“5fd3e45cc1044dfe5d5357789968e086”和“8f33eb149d3653873f1be1c66fa46979”没匹配上:

      Python遍历0-100的“mario”

      ```javascript import hashlib

for i in range(100): str2md5 = str(i)+”mario” strUTF = str2md5.encode( ‘utf-8’ )

encodeMD5 = hashlib.md5()
encodeMD5.update(strUTF)
print( strUTF )
print( encodeMD5.hexdigest() )

i += 1
<a name="XuZx6"></a>
### moveByXy
赋值处为“`for (x of Pacman.border) for (y of x) moveByXy += y;`”<br />根据字符串“`score`”认为,但实际上应该是大于0即可:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/1632223/1647497329524-7a5421fd-352c-47fe-967d-85464faee974.png#clientId=ud7aa81b2-bdca-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=284&id=u41568a9b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=284&originWidth=839&originalType=binary&ratio=1&rotation=0&showTitle=false&size=40179&status=done&style=none&taskId=u93a8dbd0-e69b-45a1-a032-1838430c1b5&title=&width=839)<br />改为:
```javascript
    if (1) {
      fetch("/games/pacman/api/4", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        credentials: "same-origin",
        body: JSON.stringify({
          action: `maze`,
          score: 6,
          // _csrf: $("#csrf").val(),
        }),
      })
        .then((resp) => resp.json())
        .then(function (data) {
          if (data.success) window.location.href = "/games/mario/level/1";
          else alert(data.data);
        });
    }
  }

HTTP包

虚假包

image.png

返回包

image.png