知识点
1、过滤函数缺陷绕过
2、CTF考点与代码审计
详细点
==与===
md5
intval
strpos
in_array
preg_match
str_replace
案例
==与===
=赋值 ==弱类型对比 ===还会比较类型
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';
$a=1;
if($a==$_GET['x']){
echo $flag;
}
当x=1或x=1.0或x=+1或x=1admin时,都会正确的输出flag,所以当使用“==”时,进行对比会有缺陷,不是非要1才能相等,甚至“1a”也可以。这种判断是不具有唯一性的,会有多个字符使条件成立。
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';
$a=1;
if($a===$_GET['y']){
echo $flag;
}
当y=1时,正确,但是1.0或+1或1admin就不可以了,因为譬如说1admin是字符串类型,和原本的1整型是不一样的。
md5
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';
if($_GET['name']) != $_GET['password']){
if(MD5($_GET['name']) == MD5($_GET['password'])){
echo $flag;
}
echo '?';
}
代码逻辑就是让name和password的字符不相等,但是让两者的MD5值相等,这看起来是有点矛盾的。这里用到的绕过知识就是0e绕过
分别输入两个字符,他们在md5加密后都是0e开头即可绕过
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';
if($_GET['name']) != $_GET['password']){
if(MD5($_GET['name']) === MD5($_GET['password'])){
echo $flag;
}
echo '?';
}
如果是”===”,就要用到数组绕过,譬如说你给输入name[]=1,它接受到的结果就是null,同时输入password[]=2,它接受的结果也是null,这样就实现数组绕过
实际应用:如果在后台的账号密码验证存在==或===的缺陷,那就可以通过绕过直接登录后台了。
intval
相关链接:https://www.runoob.com/php/php-intval-function.html
函数的具体解释
$flag='xiaodi ai chi xigua!';
$i='666';
$ii=$_GET['n'];
if(intval($ii==$i)){
echo $flag;
}
当给ii赋值为+666时,会自动将+666转换为10进制的整数即666,与i相等
$flag='xiaodi ai chi xigua!';
$i='666';
$ii=$_GET['n'];
if(intval($ii==$i,0)){
echo $flag;
}
譬如说加上0x就会使用16进制转化,666的16进制是0x29a,这时两者就相等了
strpos
相关链接:https://www.runoob.com/php/func-string-strpos.html
查找 “php” 在字符串中第一次出现的位置:
$flag='xiaodi ai chi xigua!';
$i='666'
$ii=$_GET['h'];
if(strpos($i,$ii,"0")){
echo $flag;
}
对于strpos()函数,我们可以利用换行进行绕过(%0a),空格%20也可以绕过
in_array
相关链接:https://www.runoob.com/php/func-array-in-array.html
在数组中搜索值 “Runoob” ,并输出一些文本:
$flag='xiaodi ai chi xigua!';
$whitelist = [1,2,3];
$page=$_GET['i'];
if (in_array($page, $whitelist)) {
echo $flag;
}
这是第三个参数,如果没有设置,就相当于是”==”缺陷,默认是不会去检测类型
再来看源码,这里就存在绕过了,如果输入?i=1e也是会输出flag的,如果第三个参数没有设置,就可以绕过
preg_match
相关链接:https://www.runoob.com/php/php-preg_match.html
$flag='xiaodi ai chi xigua!';
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/",$num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
进行数组绕过,就可以让preg_match函数失效,直接进行下一个if语句的判断,达到绕过的目的
str_replace
相关链接:https://www.runoob.com/php/func-string-str-replace.html
$sql=$_GET['s'];
$sql=str_replace('select',$sql);
echo $sql;
就是把select去掉的意思
简单的过滤sql语句,存在缺陷就是无法迭代,我们可以输入sselectelect进行绕过,这个函数只能过滤一次
CTFshow题目
preg_match
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/,$num")){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
直接数组绕过,?num[]=1即可得到flag
intval-进制转换
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}
直接进制转换一下,将4476转换为16进制即0x117c即可
preg_match-换行绕过
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im',$a)){
if(preg_match('/^php/i',$a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
注:”^”这个符号代表着前面有没有字符都无所谓,只要有php这个字符就行。
/i 表示不区分大小写(如果表达式里面有a,那么A也是匹配对象)
/m表示可以进行换行
换行绕过,%0a表示换行,输入%0aphp即可得到flag
intval-化整绕过
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
输入?num=4476.1即可,intval会将4476.1转换成整数即4476,但是如果输入4476.0在第一步进行比较的时候与4476是弱相等的
intvla-化整绕过(2)
include("flag.php"):
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i,$num")){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
同样输入?num=4476.1即可绕过,首先4476.1不等于4476,他里面也不包含有a-z的字母,同样进行整数化后等于4476
strpos
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i,$num"){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
这里就不能用4476.1来绕过了,因为最后一步是”===”,他会严格比较大小,所以这里应该用进制转化+换行绕过,输入?num=%20010574,其中%20相当于空格也就是换行了,010574是4476的8进制。
注:还可以输入4476.0也可以成功绕过
strpos(2)
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num=="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/\./i,$num"){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}
同样输入?num=%20010574即可绕过,因为这题他会过滤了”.”,所以4476.0是不可以使用了。
==-弱类型比较
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
既要读出flag.php同时又得通过弱类型比较,可以加上路径,输入?u=./flag.php,即可得到flag
md5
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a'])) and isset($_POST['b'])) {
if ($_POST['a']) != $_POST['b'])
if (md5($_POST['a'])) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
post输入a和b,md5数组绕过一下就行了,a[]=1&b[]=2
实例
要想绕过str_replace,可以利用它无法迭代过滤的特性-只能过滤一次
输入…..///http\…..//\config\config_db.php即可绕过,函数会先都分别将../和./删掉,然后再执行你输入的语句,就可以成功访问到数据库的内容了
in_array第三个参数没有设置,可以执行sql注入
红日安全的代码审计项目:https://github.com/hongriSec/PHP-Audit-Labs