黑客如何隐藏恶意命令?
作者:FancyPig | 发布时间: | 更新时间:
相关阅读
视频讲解
本期视频我们将给大家分享黑客隐藏恶意命令的小技巧,如何通过一些系统变量、?、*进行混淆,从而一定程度上绕过检测,去运行、查看一些特殊路径的文件
效果展示
我们可以通过下图发现,可以通过?和*等方式进行统配,最终通过iex打开系统计算器,作为POC

图文教程
你可以打开终端或者使用powershell

输入cmd就可以进入我们平常使用的cmd了
使用set命令便可以看到我们的系统环境变量

我们往下拉,可以看到这里有SystemRoot,它将对应我们的系统根目录,C:\WINDOWS

通常情况下,我们使用命令行查看,比方说C:\WINDOWS下都有哪些文件,我们会使用DIR命令
dir C:\WINDOWS

这时,我们思考,能不能用系统变量进行调用呢?
在cmd中,我们要使用下述方式,首先我们需要确认系统变量前后加上%是否可以正常输出
echo %SYSTEMROOT%
然后,我们再使用dir命令,参考下面的命令
dir %SYSTEMROOT%

这种方式在powershell中就不行了哦!我们exit退出cmd,继续使用powershell

在powershell中有其他的方式来输出
echo $env:SYSTEMROOT

这时,你是否会想到,能不能用?来替换,比方说我们先替换一个T
echo $env:SYSTEMROO?

好像可以哦,那我们再把O替换成?
echo $env:SYSTEMRO??

继续替换,直到它认不出来……(你全是?当然认不出来了🤪)

由此我们可以得出结论,我们精简到最后,可以用$env:S?????????来代替系统的C:\WINDOWS,只有echo能输出,我们就可以用它操作,比方说,看看C:\WINDOWS目录下都有哪些文件?你就可以这么操作

然后,可能就会有热心网友准备整活了,试试能不能弹个计算器?
首先,找下计算器的位置C:\Windows\System32\calc.exe
通过,刚才的方式,我们看下C:\Windows就用这个$env:S?????????代替吧
那我们就得到了$env:S?????????\System32\calc.exe,我们尝试运行
IEX $env:S?????????\SYSTEM32\calc.exe
整活成功,发现计算器是可以弹出的

但是,有热心网友肯定会觉得,你这样的话后面是不是也太明显了!没错,SYSTEM32我们也可以把它做成?这种样子,这时我们可以使用Get-ChildItem进行调试
Get-ChildItem $env:S?????????\SYSTEM3?

不妨再替换

再替换

直到我们发现有两个结果,这里代表如果我们使用SYS?????可能将不能指定到我们想要的System32路径,因为SysWOW64也是Sys开头,而且后面也是五位

所以,我们现在就可以把刚才的命令改成
IEX $env:S?????????\SYSt????\calc.exe
发现是可以打开的

这里值得补充的是?出现的位置,不一定是从后往前,你也可以让其出现在中间,甚至没有规律
IEX $env:S?????????\S??????2\calc.exe

都是可以的
IEX $env:S?????????\S????M??\calc.exe

这时,你可能会说了,检测引擎就是检测calc.exe的,你这么做有什么卵用?别急啊,还没完呢!我们可以继续对calc.exe按照上述操作进行替换
Get-childItem $env:S?????????\S????M??\calc.exe
Get-childItem $env:S?????????\S????M??\cal?.exe
Get-childItem $env:S?????????\S????M??\ca??.exe

直到我们发现匹配到多个值,我们就选择上一个即可
Get-childItem $env:S?????????\S????M??\ca??.exe

最终我们打开计算器的命令如下
IEX $env:S?????????\S????M??\ca??.exe

这样是不是还挺酷的,成功绕过了检测!
自动化代码
main.py
import os
import pathlib
import pprint
import re
import itertools
import glob
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("target")
args = parser.parse_args()
def test_if_env_matches(test,target):
regex_string = rf'^{test.replace("?",".").replace("*",".*")}$'
matches = []
for key in os.environ.keys():
match = re.search(regex_string,key)
if match:
matches.append(match.group())
return len(matches) == 1 and target in matches
def test_if_glob_matches(test,start_path,target):
global glob_cache
matches = [p for p in glob_cache[start_path] if pathlib.Path(p).match(test)]
return len(matches) == 1 and target in matches
def glob_mutate(subpath):
for each_possibility in itertools.product("?X", repeat = len(subpath)):
new_mutation = list(each_possibility)
for i, c in enumerate(each_possibility):
if c == "X":
new_mutation[i] = subpath[i]
to_test = "".join(new_mutation)
yield to_test
def star_replace(subpath_mutation):
return re.sub(r"\?+","*",subpath_mutation)
def path_parts(path_str):
return tuple(piece.rstrip("\\") for piece in pathlib.Path(path_str).parts)
def main():
global glob_cache,args
#target=r"C:\\Windows\\System32\\calc.exe"
target = args.target
short_mode = True
target_norm_str = os.path.normpath(target)
target_path = pathlib.Path(target_norm_str)
if not target_path.is_absolute():
print("[!] error, absolute path required")
env_score = {}
target_parts = path_parts(target_norm_str)
for env_key,value_path in os.environ.items():
if os.path.exists(value_path) and os.path.isdir(value_path):
value_path_norm_str = os.path.normpath(value_path)
value_parts = path_parts(value_path_norm_str)
if len(value_parts) <= len(target_parts):
for i,part in enumerate(value_parts):
if target_parts[i] == value_parts[i]:
if env_key not in env_score:
env_score[env_key] = 1
else:
env_score[env_key] += 1
else:
env_score[env_key] = 0
break
best_envs = []
highest_score_env = max(env_score,key=env_score.get)
highest_score_value = env_score[highest_score_env]
best_envs = [key for key, value in env_score.items() if value == highest_score_value]
starting_matches = []
for env_key in best_envs:
env_matches = []
env = os.path.normpath(os.environ[env_key])
env_parts = path_parts(env)
left_over_parts = target_parts[len(env_parts):]
for to_test in glob_mutate(env_key):
matches = test_if_env_matches(to_test,env_key)
if matches:
env_matches.append(to_test)
starting_matches.append(env_matches)
env = os.path.normpath(os.environ[best_envs[0]])
env_parts = path_parts(env)
left_over_parts = target_parts[len(env_parts):]
print("caching subdirectories")
glob_cache = {}
for i,subpart in enumerate(left_over_parts):
map_path = os.path.join(env,os.path.sep.join(left_over_parts[:i]))
glob_value = os.path.join(map_path,"*")
glob_cache[map_path] = glob.glob(glob_value)
# Handle subdirectory parts
print(f"findg globfuscation options for '{target}'...")
remaining_matches = []
for i,each_part in enumerate(left_over_parts):
remaining_part = os.path.sep.join(left_over_parts[:i])
base_path = os.path.join(env,remaining_part)
full_path = os.path.join(env,remaining_part,each_part)
question_mark_mutations = []
max_length = 0
for each_question_mark_mutation in glob_mutate(each_part):
question_mark_mutation_path = os.path.join(
env,remaining_part,each_question_mark_mutation
)
if test_if_glob_matches(question_mark_mutation_path,base_path,full_path):
question_mark_mutations.append(each_question_mark_mutation)
star_mutation_matches = []
max_length = 10000
for each_mutation in question_mark_mutations:
star_mutation = star_replace(each_mutation)
star_mutation_path = os.path.join(env, remaining_part, star_mutation)
if len(star_mutation) >= max_length:
continue
if star_mutation not in star_mutation_matches:
matches = test_if_glob_matches(star_mutation_path, base_path, full_path)
if matches:
if short_mode:
if max_length == 0:
max_length = len(star_mutation)
max_length = len(star_mutation)
star_mutation_matches.append(star_mutation)
remaining_matches.append(question_mark_mutations + star_mutation_matches)
all_mode = False
shortest = 0
all_options = []
for env_starts in starting_matches:
for start in env_starts:
for every_option in itertools.product(*remaining_matches):
new_option = f"$env:{start}{os.path.sep}{os.path.join(*every_option)}"
if all_mode:
print(new_option)
else:
if shortest == 0:
shortest = len(new_option)
if len(new_option) < shortest:
print(new_option)
shortest = len(new_option)
if __name__ == "__main__":
main()
我们比方说想生成刚才的C:\\Windows\\System32\\calc.exe
python .\main.py "C:\\Windows\\System32\\calc.exe"

当然,你也可以调整代码的模式,第143行
all_mode = False
修改为
all_mode = True
在输入上面的命令,可以全部可能性,我们一开始关闭模式相当于只给我们最短的命令
python .\main.py "C:\\Windows\\System32\\calc.exe"
