PHP代码审计

发布时间 2023-10-30 18:44:19作者: 楚颖i

函数用到时再去查用法


_GET

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

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

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



PHP弱类型

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


文件包含

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

include函数

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

https://img2020.cnblogs.com/blog/2293037/202104/2293037-20210410174705799-1460846635.png



php://filter

:可查看被注释的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 包含进来

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


变量覆盖

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

$$

/?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(),执行括号内的语句
例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);


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()

有回显:在命令行执行括号命令


passthru(),exec()

passthru()有回显,exec()回显最后一行,echo输出:执行括号命令,放到一个数组中,php用echo输出内容


shell_exec()

无回显,必须输出


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


反引号``

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


ob_start()

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


popen()

打开文件
需自己手工去指向的区域输出内容
/?command = dir;
$command = $_GET['command']; $handle = popen($command,"r");//打开文件
while(!feof($handle)) echo fread($handle,1024); //读取文件,把command看成文件


proc_open()



php魔术方法

__sleep()

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


__wakeup()

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


__construct()

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


__destruct ()

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


__toString()

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


更多

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

反/序列化

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(),销毁时也会像个对象一样调用)。


函数

htmlspecialchars()

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


htmlspecialchar_decode()

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


addslashes()

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


stripslashes()

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


in_array()

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


strcmp()

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