匹配過濾
在有的 Web 題目中會使用字符串匹配或正則表達式過濾傳入的變量,此時看懂匹配規則構造正確的變量就很重要。
JavaScript match() 方法
match() 方法可在字符串內檢索指定的值,或找到一個或多個正則表達式的匹配。該方法類似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置。語法如下,函數的返回值為存放匹配結果的數組,該數組的內容依賴於 regexp 是否具有全局標志 g。
stringObject.match(searchvalue)
stringObject.match(regexp)
參數 | 說明 |
---|---|
searchvalue | 必需,規定要檢索的字符串值。 |
regexp | 必需,規定要匹配的模式的 RegExp 對象。 |
PHP preg_match() 函數
preg_match 函數用於執行匹配正則表達式,搜索 subject 與 pattern 給定的正則表達式的一個匹配。函數返回 pattern 的匹配次數,值是 0 次(不匹配)或 1 次,因為 preg_match() 在第一次匹配后 將會停止搜索。preg_match_all() 不同於此,它會一直搜索subject 直到到達結尾。如果發生錯誤 preg_match() 返回 FALSE。函數語法為:
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
參數 | 說明 |
---|---|
pattern | 要搜索的模式,字符串形式。 |
subject | 被正則匹配的字符串。 |
matches | 如果提供了參數matches,它將被填充為搜索結果。matches[0] 將包含完整模式匹配到的文本,$matches[1] 將包含第一個捕獲子組匹配到的文本以此類推。 |
flags | 可以被設置為標記值。 |
offset | 搜索從目標字符串的開始位置開始。 |
PHP ereg() 函數
ereg() 函數用於正則表達式匹配,以區分大小寫的方式在 string 中尋找與給定的正則表達式 pattern 所匹配的子串。函數語法如下:
ereg ( string $pattern , string $string [, array &$regs ] ) : int
如果找到與 pattern 中圓括號內的子模式相匹配的子串並且函數調用給出了第三個參數 regs,則匹配項將被存入 regs 數組中。regs[1]包含第一個左圓括號開始的子串,regs[1]包含第一個左圓括號開始的子串,regs[2] 包含第二個子串以此類推,$regs[0] 包含整個匹配的字符串。如果在 string 中找到 pattern 模式的匹配則返回 所匹配字符串的長度,如果沒有找到匹配或出錯則返回 FALSE。如果沒有傳遞入可選參數 regs 或者所匹配的字符串長度為 0,則本函數返回 1。
- 使用 Perl 兼容正則表達式語法的 preg_match() 函數通常是比 ereg() 更快的替代方案。
例題:bugku-字符?正則?
源碼如下,目的很明確,傳入一個符合正則匹配的參數 id 即可得到 flag。
<?php highlight_file('2.php'); $key ='KEY{********************************}'; $IM = preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match); if( $IM ){ die('key is: '.$key); } ?>
接下來分析匹配的正則表達式,所用的表達式語法如下:
正則表達式 | 匹配內容 |
---|---|
. | 匹配除 "\n" 之外的任何單個字符 |
* | 匹配它前面的表達式 0 次或多次,等價於{0,} |
.{4,7} | 匹配 4 到 7 個任意字符 |
/ | 匹配 /,\ 是為了轉義 |
[a-z] | 匹配所有小寫字母 |
[:punct:] | 匹配任何標點符號 |
/i | 表示不分大小寫 |
根據以上規則,構造出一個符合要求的字符串,提交獲得 flag。
?id=keyakeyaaaakey:/a/akeya;
例題:bugku-ereg 正則 %00 截斷
源碼如下,這題首先要求通過第一個分支語句,使用 ereg() 實現一個正則匹配,匹配的內容是一個以上的數字或字母。第二個分支語句要判斷輸入的字符串長度是否小於 8,同時它的值要大於 9999999。最后還要判斷字符串是否有包含“-”,如果包含則輸出 flag。
<?php $flag = "xxx"; if (isset ($_GET['password'])) { if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) { echo 'You password must be alphanumeric'; } else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999) { if (strpos ($_GET['password'], '*-*') !== FALSE) //strpos — 查找字符串首次出現的位置 { die('Flag: ' . $flag); } else { echo('*-* have not been found'); } } else { echo 'Invalid password'; } } ?>
這里需要實現的內容很多,首先需要傳入的字符串長度小於 8,也就是說最大傳入的數字不會大於 9999999。這里可以用科學技術法來替代,10000000 的科學計數法的表達為 1e7。接着在字符串中要包含字符 “-”,但是第一個分支的正則匹配會阻止繼續,這時可以使用 “%00” 來階段字符串,使得正則匹配只匹配到前面的部分,而字符 “-” 就放在 “%00” 的后面。綜上所述,構造 payload:
?password=1e7%00*-*
例題:bugku-數字驗證正則繞過
題目的源碼如下,首先要用 preg_match() 一個正則表達式匹配,[: graph:] 表示任意一個可打印字符,此處要求 password 長度大於 12。接下來要用 preg_match_all() 進行全局匹配,每匹配成功一次就加 1,一直匹配到字符串結束。這要求 password 中必須包含標點符號、數字、大寫字母、小寫字母等,並且被檢測到 6 次以上才能繞過。
<?php error_reporting(0); $flag = 'flag{test}'; if ("POST" == $_SERVER['REQUEST_METHOD']) { $password = $_POST['password']; if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 執行一個正則表達式匹配 { echo 'flag'; exit; } while (TRUE) { $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; if (6 > preg_match_all($reg, $password, $arr)){ break; } $c = 0; $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何標點符號 [[:digit:]] 任何數字 [[:upper:]] 任何大寫字母 [[:lower:]] 任何小寫字母 foreach ($ps as $pt) { if (preg_match("/[[:$pt:]]+/", $password)) $c += 1; } if ($c < 3) break; //>=3,必須包含四種類型三種與三種以上 if ("42" == $password) echo $flag; else echo 'Wrong password'; exit; } } ?>
最后要判斷 password 是否等於 42,注意到這是使用 “==” 進行判斷的,可以使用熟悉的弱類型。綜上所述,password 的長度應該大於 12,同時包含數字、大寫字母、小寫字母和符號,而且開頭是 42 后接字母,使得弱類型轉換時可以轉換為 42。此時可以使用 HackBar 的 POST 提交:
password= 42Aaaaaaaaaa+
例題:攻防世界-NaNNaNNaNNaN-Batman
打開文件,看到一堆亂碼,根據 “script” 標簽判斷這是 JavaScript 代碼。
<script> _='function $(){e=getEleById("c").value; length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i]; for(o=0;o<13;++o){ [0]); .splice(0,1)}}} \'<input id="c">< οnclick=$()>Ok</>\');delete _var ","docu.)match(/"];/)!=null=[" write(s[o%4]buttonif(e.ment'; for(Y in $=' ') with(_.split($[Y]))_=join(pop()); eval(_) </script>
修改后綴為 “.html”,打開文件是一個輸入框,此時我們要知道需要輸入什么東西。
首先我們先看懂上述代碼,注意到代碼定義了一個參數 “_”,然后給出了一個字符串看上去是個函數,然后用 eval() 函數可計算字符串,執行其中的的 JavaScript 代碼。這里可以把 eval() 函數改為 alert() 函數,該函數可以顯示一段文本,從而進行程序的調試,我們可以把這段 JavaScript 代碼的正確形式顯示出來。
<script> _='function $(){e=getEleById("c").value; length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i]; for(o=0;o<13;++o){ [0]); .splice(0,1)}}} \'<input id="c">< οnclick=$()>Ok</>\');delete _var ","docu.)match(/"];/)!=null=[" write(s[o%4]buttonif(e.ment'; for(Y in $=' ') with(_.split($[Y]))_=join(pop()); alert(_) </script>
修改后打開網頁,就能得到正確的 JavaScript 代碼如下。在正則中 “^” 表示開頭,“$” 表示末尾,在這里用 match() 函數匹配在輸入框里輸入的值要滿足輸入的字符串長度必須為 16 個字符。字符串的開頭必須要匹配 “be0f23”,字符串的結尾必須要匹配 “e98aa”,字符串中要能匹配到 “233ac” 和 “c7be9”。
function $() { var e = document.getElementById("c").value; if(e.length == 16) if(e.match(/^be0f23/)!=null) if(e.match(/233ac/)!=null) if(e.match(/e98aa$/)!=null) if(e.match(/c7be9/)!=null) { var t=["fl","s_a","i","e}"]; var n=["a","_h0l","n"]; var r=["g{","e","_0"]; var i=["it'","_","n"]; var s=[t,n,r,i]; for(var o = 0; o < 13; ++o) { document.write(s[o%4][0]); s[o%4].splice(0,1) } } } document.write('<input id="c"><button onclick=$()>Ok</button>'); delete _
因為限制了字符串的長度,因此這里要利用重疊來構造長度為 16 且滿足所有正則表達式的字符串為 “be0f233ac7be98aa”,輸入就可以獲得 flag。或者是把下面的代碼拿到瀏覽器運行,也能跑出需要的字符串。
var t = ["fl", "s_a", "i", "e}"]; var n = ["a", "_h0l", "n"]; var r = ["g{", "e", "_0"]; var i = ["it'", "_", "n"]; var s = [t, n, r, i]; for (var o = 0; o < 13; ++o) { document.write(s[o % 4][0]); s[o % 4].splice(0, 1) }
轉載自:https://www.cnblogs.com/linfangnan/p/13588411.html