---
# System prepended metadata

title: 'Week10 r3:0 異世界網站挑戰心得'
tags: [進度筆記第十周, Lidemy心得, BE101, PHP]

---

# Week10 r3:0 異世界網站挑戰心得
###### tags: `BE101` `PHP`  `進度筆記第十周` `Lidemy心得`

***

一共十關，看你能闖到第幾關：r3:0 異世界網站挑戰（特別感謝第三期 @minw 製作遊戲）。

***

## lv0

https://r30challenge.herokuapp.com/lv0.php

想辦法利用 token 來獲取網址位置，系統提示如下：

\[系統\] 例如： /lv1.php?token=xxx，xxx 不包含大括號

\[系統\] 得到技能 hint: 利用 help 可以縮小 token 所在的範圍

\[系統\] 例如： /lv1.php?hint=help

\[系統\] 前往 lv1 : 請透過 query string 傳入 token

透過這些資訊用 `/lv1.php?token=r30:start` 就可以去往 lv1 的位址~

---

## lv1

為了去到 lv2 系統給了幾個資訊：

\[求救訊息\] 100101001001100001110

\[系統\] 前往 lv2: 透過 token 將怪物的名字轉成十八進位傳給 m3nt0r

經過宣告求救訊息 `parseInt()` 、 `toString` 可以得知怪物名字是 `bad18`

~~先用 http://www.kwuntung.net/hkunit/base/base.php 偷看了答案~~  

### 複習了一下 [`parseInt()`](https://medium.com/unalai/%E8%AA%8D%E8%AD%98-parseint-parsefloat-%E8%88%87-number-%E8%BD%89%E6%8F%9B%E6%88%90%E6%95%B8%E5%AD%97%E7%9A%84%E4%B8%89%E7%A8%AE%E6%96%B9%E6%B3%95-276640aedb4e) 的用法

```
let num = '100101001001100001110';
// 先做字串轉換, 將 num 當做2進位值, 並轉成 10 進位。  
let result = parseInt(num, '2');

console.log(result);

let res18 = result.toString(18);
console.log(res18);
```
// 依照 ECMA 規範, 基數如果是 10 以外的值, 如填入 18 則轉成 18 進位。  
代入 token 得到: `lv2.php?token=bad18`  

--- 

## lv2  

進入 lv2 後看到:  

![](https://i.imgur.com/7boCZIS.png)  

有個 `<div class="hidden"> {divsurprise} </div>` 固定躲在最上層。  
~~如果是 Firefox 瀏覽器有可能會直接顯示~~  
但其實可以開啟 Devtool 來看~  
因此帶入 token : `lv3.php?token=divsurprise` 。  

---

## lv3  

進入 lv3 後看到:  

![](https://i.imgur.com/M97Eb29.png)  


水面一片黑, 這就是小提示了, 再度開啟除錯工具, 點 `body` 的部分可以看到在 `body` 最底下藏了一個 ` <--!{commentfaker}--> `  於是代入。  

Token:`lv4.php?token=commentfaker`  

---

## lv4  

![](https://i.imgur.com/Z7eXBD3.png)  

仔細觀察後, 會發現 CSS 有動過, 是 CSS 偽元素 ``:: after`` 隱藏了字串。  

`html` 的部分:  
```
<span class="special">{csspersona!}</span>
```

行內樣式表的部分:  
```
.special::after {
  content: '{tRaNspar3nT}';
  margin-left: -160px;
  background-color: #000;
}
```

以 ``csspersona!`` 代入, 馬上就 fail XD  
但其實已經到了 lv5 !  

---

## lv5  

原因是出在網頁的跳轉, 仔細一看, 輸入 token 的瞬間, 網址列瞬間從 lv5 跳到 lv6 XDDD  
轉址的瞬間按下 ESC 會看到 windowhack  。  
p.s. 別忘了 ESC 可以停止載入網頁!  

---

## lv6  

基於停止載入網頁後, 使用 token: `lv6.php?token=windowhack`   
會進入到新的挑戰, 這關的挑戰更ㄎ一ㄤ, 更有趣了XD  
前往 lv7：解出檔案裡面顏文字的秘密，或找到 window 裡面隱藏的 token 資訊。  
短短的提示僅有這樣?!  

開啟除錯看之後, 我到底看了些甚麼????  

![](https://i.imgur.com/OqswdYf.png)

於是去 console 輸入提到的關鍵字 "window" 試試看。  

會跳出, ``__IamToken: "emojicute"``:  
![](https://i.imgur.com/6z8nBKh.png)

試著代入看看: `lv7.php?token=emojicute`  

---

## lv7  

進來之後, 這個勇者真的沒問題嗎XD  開始要賄賂惡魔是哪招XDDD"  

這時候的提示是, [系統] 請找到放在包包深處的餅乾 token 。  

打開除錯會發現, 由於 Cookie「TokenIsMe」的「SameSite」屬性設定為「None」或無效值，卻缺少「secure」屬性，此 Cookie 未來將被拒絕。  

因此前往儲存空間, 找到餅乾 Cookie 並且看到了值 `%7Bcookieyumyum%7D` !  
![](https://i.imgur.com/O8NN1xu.png)  

`%7B %7D` 相當於 `{}` 因此去掉後代入 token: `lv8.php?token=cookieyumyum`  。  

---

## lv8  

惡魔會吃個資啊...看到一個無頭騎士說他的 KDA 很高, 嗯???  
提示告訴你 [系統] 前往 lv9：請在 response 找出無頭騎士的頭的密碼，讓他解開封印。  
這個一定是工程師笑話XD  Response Header 我猜, 除錯下去找後果然發現 `Tokenisme {headshot}`:  

![](https://i.imgur.com/hD468AQ.png)  

代入 token: `lv9.php?token=headshot`  

---

## lv9  

一進來, 話不多說, 看到了:
[系統] 前往 lv10: 請解開留在 html 裡面的 php 程式碼，輸入正確的 token  

這時候開啟除錯來看~  

![](https://i.imgur.com/7VlI6BT.png)  

稍微翻了一下資料可以發現 php 語法的 `strlen()` 是計算字串長度的函數, 但它不會判斷字串的編碼( `mb_strlen()`則會 ), 而 `ord()` 則可將字串轉成 ASCII 碼。  

也因此這段程式碼的規則是:  

```
<?php
     function isTokenValid($token) {
       if (strlen($token) !== 8) return false;
       for($i = 1; $i <= 7; $i+=2) {
         if ((ord($token[$i]) * ord($token[$i - 1])) % $i !== 0) {
            return false;
        }
            }
            return true;
          }
?>

當我有個函式名為 `($token)`  
假如這個 Token 的字母數 不相等於 8 的時候返回
也因此 Token 字母數應不超過 8 位
迴圈內的 ``$i = 1`` 會一直加總直到 ``<=7``
假如 2 * (2-1) % 1 不會等於 0 則返回
以此類推找 8 個值餘數為 0 應該可以過關。  
```

將 `aaccddii` 代入 Token~  

參考資料:  
https://www.wibibi.com/info.php?tid=91  
https://www.wibibi.com/info.php?tid=92  
https://expect7.pixnet.net/blog/post/44229026  
https://www.wibibi.com/info.php?tid=PHP_ord_%E5%87%BD%E6%95%B8  

---

## lv10  

看了一下提示 "[系統] 前往 lv11: 請從 POST 中找到密碼進入城堡" 。  
這次大概要看一下 CORS 的甚麼東西, 嗯 果然主控台告訴你:  

``` 
已封鎖跨來源請求: 同源政策不允許讀取 https://glacial-everglades-11859.herokuapp.com/api.php 的遠端資源。（原因: 缺少 CORS 'Access-Control-Allow-Origin' 檔頭）。
```
大概要 request CORS, 在裝有 NPM 的情況下試著寫寫看:  
```
const request = require('request')

request(
    'https://glacial-everglades-11859.herokuapp.com/api.php',
    function (error, response, body){
        console.log(body)
    }
)
```
並用 `node.js` 去執行, 不過搜了幾次, 也試著用 `request.post` 還是代不到 token 出來, 上網搜了一下 CORS 的議題後, 也發現頁面是 404 的狀態:  

![](https://i.imgur.com/7ALp2ep.png)  

~~試著從網路上代 Token: sosdan 進入下一關~~

---

## lv11  

一進來點開, 先開 Dev tool (O), 發現:  

![](https://i.imgur.com/YUts0wy.png)  

然後畫面中有個前往另一個網站: http://r30challenge.herokuapp.com/news.php  的位址。  

點進去後還以為跑到八卦版(?),  每個文章左上角似乎都有文章 ID , 有個文章被刪除了, 試著代入那個被刪除的文章 ID 到 Token看看。  

```
const request = require('request')

request(
    'http://r30challenge.herokuapp.com/news_api.php?id=888888',
    function (error, response, body){
        console.log(body)
    }
)
```
總之就是到偽八卦的留言板那邊使用代入 `id` 的方式, 進去被刪除的文章頁面。  
出現: 能看到這則留言的你，想必就是天選之人吧！ {fakeituntilyoumakeit} 拯救這個世界吧！  

---

## lv12  

這次的提示是 [系統] 前往 lv13: 找找看關於 cookie 的秘密打倒小餅乾, 利用除錯去看看 Cookie 發生甚麼事情。  

![](https://i.imgur.com/DVxjuAC.png)  

嗯, Cookie 一切正常, 看看 response header 有沒有甚麼狀況:  

![](https://i.imgur.com/DJh8QgA.png)  
 
答案好像藏在這裡?  代 `you_are_cookie_master` 進去看看!  
然後就進到下一關了?!  伺服器會透過 set-cookie 傳遞 response header 給瀏覽器, 瀏覽器收到後把會把 cookie 存起來, 如果之後登入相同網站會傳同樣的 cookie 給伺服器。  
 
 網路上找一下 set-cookie 的相關捏他:  
 https://segmentfault.com/a/1190000019586281  
 https://shubo.io/cookies/  
 
 ---
 
 ## lv13  
 
 這次的提示是: [系統] 前往 lv14: 請拿四碼數字測試看看送信人的拖延症，直到找出正確的密碼出來。  
 
 提示數字會造成 delay 的話, 只好代代看到 token 內。  
 代入 8901, 5678, 1357, 0157 都會比較慢, 只好慢慢找是哪幾個數字會造成回應延遲:  
 
 ![](https://i.imgur.com/ZUdN5vA.png)  

發現這幾個數字會比較慢後, 代到 5371, 就成功了~  

---

## lv.14  

一進去就發現左上角有個 timer, 試著用除錯看看網頁發生甚麼事情:  

```
 secret logic
          function isTokenValid($token) {
            $h = date('H');
            $m = date('i'); 
            $a = $h * $m + 42;
            $count = 0;
            for($i = 0; $i < 8; $i++) {
              $count += ord($token[$i]) - 65;
            }
            if ($count <= 100) {
              return false;
            }
            return $a % $count === 0;
          }
        
```

看了一下這段 code, 猜測:  
token 的條件可能小於八個字
count 從 0 開始
這幾個字的 ASCII 減去 65 後必須大於 100
返回到 `$a` 的條件是 小時乘以分鐘 +42 後必須能整除 `count `  

