The Azure Lab Diaries - Hunting Common File Transfer Activity

Hello You#
Many features have been added to the lab since the last post, including Microsoft Defender for Endpoint (MDE) telemetry, DNS-related logs, and more. These additions, however, will not be the focus of this article, as a separate blog post is being prepared to cover them in detail
While working through the HTB File Transfers module, I tested many of the techniques discussed and discovered several effective ways to hunt for them. What really surprised me was the quality of MDE telemetry as we’ll explore in this blog
Here’s what the lab setup currently looks like:
We’ll be focusing on the activity happening between atklnx01
& ws01
as I only installed Microsoft Defender for Endpoint MDE
with P2
license on it, the rest let’s say are spiritually protected and we’re just focusing on our golden child ws01
Let’s get started
FTP Activity#
File Download#
On the attacking machine atklnx01
I installed pyftpdlib
and started an ftp server
sudo pip3 install pyftpdlib
sudo python3 -m pyftpdlib --port 21
Tried here to download note.txt
from atklnx01
using Net.WebClient
I observed many DeviceNetworkEvents
with FtpConnectionInspected
which gives you a really similar details as zeek
FTP logs but the ws01
point of view
You can use this KQL
to look for FTP activities in your tenant
DeviceNetworkEvents
| where ActionType == "FtpConnectionInspected"
| where AdditionalFields.command == "RETR"
| extend cwd_ = tostring(AdditionalFields.cwd)
| where AdditionalFields.direction == "Out"
| extend reply_msg_ = tostring(AdditionalFields.reply_msg)
| extend user_ = tostring(AdditionalFields.user)
| where RemotePort == 21
| extend mime_type_ = tostring(AdditionalFields.mime_type)
| extend command_ = tostring(AdditionalFields.command)
| extend arg_ = tostring(AdditionalFields.arg)
| project-reorder TimeGenerated, LocalIP, RemoteIP, DeviceName, reply_msg_, user_, command_, arg_, cwd_
RETR FTP command - A client issues the RETR command after successfully establishing a data connection when it wishes to download a copy of a file on the server.
I wanted to check another way of file download using ftp.exe
, I created ftpcommands.txt
file which contains commands to connect and get flag.txt
and exit
This activity triggered the Windows Defender Firewall. From the attacker’s point of view, if enough privileges were available, an additional command could be executed to modify the firewall settings and allow the file transfer to proceed without interruption
From the atklnx01
we see that the activity timed out
The command execution and connection status were logged
We also see that Windows security alert was logged as well
as this command was executed
rundll32.exe C:\Windows\System32\FirewallControlPanel.dll,ShowNotificationDialog /ETOnly 0 /OnProfiles 7 /OtherAllowed 0 /OtherBlocked 0 /OtherEdgeAllowed 0 /NewBlocked 4 "C:\windows\system32\ftp.exe"
The command uses rundll32.exe
to invoke a function (ShowNotificationDialog
) from the FirewallControlPanel.dll
library in order to display a Windows Firewall notification.
File Upload#
We’ll add the switch --write
to the python
command we ran earlier on atklnx01
to allow file upload
Similarly we used net.WebClient
object
Really similiar to the previous query but we look for STOR
STOR FTP command - A client issues the STOR command after successfully establishing a data connection when it wishes to upload a copy of a local file to the server.
DeviceNetworkEvents
| where ActionType == "FtpConnectionInspected"
| where AdditionalFields.command == "STOR"
| extend cwd_ = tostring(AdditionalFields.cwd)
| where AdditionalFields.direction == "Out"
| extend reply_msg_ = tostring(AdditionalFields.reply_msg)
| extend user_ = tostring(AdditionalFields.user)
// | where RemotePort == 21
| extend mime_type_ = tostring(AdditionalFields.mime_type)
| extend command_ = tostring(AdditionalFields.command)
| extend arg_ = tostring(AdditionalFields.arg)
| project-reorder TimeGenerated, LocalIP, RemoteIP, DeviceName, reply_msg_, user_, command_, arg_, cwd_
Impacket SMB Sever Activity#
To start impacket-smbserver
with a folder as our share, now the server is running we just have to copy a file from that share
We connected to that share and we see a file creation during that period, I checked here sysmon
logs but the same commands can be observed in DeviceEvents
& DeviceProcessEvents
Unfortunately here I didn’t enable or get the PowerShell\ScriptBlockLogging
logs however it would be good idea to implement it, but still MDE
telemetry saves the day because it logs some PowerShell
command execution
You can check the PowerShellCommand
action type in DeviceEvents
to see PowerShell
commands but I’m not 100% to which extent it logs PowerShell
commands, that’s why I need to get the PowerShell\ScriptBlockLogging
logs and compare them with what I already have
However, for now this does the job but you can make it better by also search for domains instead of just looking for IPs
DeviceEvents
| where ActionType == "PowerShellCommand"
| extend ParsedFields = parse_json(AdditionalFields)
| extend CommandString = tostring(ParsedFields.Command)
| where CommandString matches regex @"copy \\\\[0-9]{1,3}(\.[0-9]{1,3}){3}\\[^\\]+(\\[^\\]+)*"
| parse CommandString with "copy \\\\" IP:string "\\" ShareName:string "\\" *
| project-reorder TimeGenerated, DeviceName, InitiatingProcessAccountUpn, ActionType, CommandString, IP, ShareName, ProcessId, InitiatingProcessId, InitiatingProcessParentId
Immediately after this event a DeviceNetworkEvents
will be generated with connection success to the attacker machine
DeviceNetworkEvents
| where ActionType == "ConnectionSuccess"
| where InitiatingProcessAccountDomain == "nt authority"
| where InitiatingProcessAccountName == "system"
| where LocalIPType == "Private"
| where RemotePort == 445
| project-reorder TimeGenerated, DeviceName, LocalIP, Protocol,RemoteIPType, RemotePort
What really stand out in detecting Impacket-smbserver
is some unique names and version type that can be found in the DeviceNetworkEvents
with the action type NtlmAuthenticationInspected
Which can be detected using:
DeviceNetworkEvents
| where ActionType == "NtlmAuthenticationInspected"
| where AdditionalFields.direction == "Out"
| where RemotePort == 445
| extend hostname_ = tostring(AdditionalFields.hostname)
| extend server_dns_computer_name_ = tostring(AdditionalFields.server_dns_computer_name)
| extend server_dns_domain_name_ = tostring(AdditionalFields.server_dns_domain_name)
| extend server_nb_computer_name_ = tostring(AdditionalFields.server_nb_computer_name)
| extend server_nb_domain_name_ = tostring(AdditionalFields.server_nb_domain_name)
| extend server_version_ = tostring(AdditionalFields.server_version)
| where server_version_ == "255.255 65535 255"
| extend ts_ = tostring(AdditionalFields.ts)
| extend uid_ = tostring(AdditionalFields.uid)
| extend username_ = tostring(AdditionalFields.username)
| where strlen(server_dns_computer_name_) == 8
| where strlen(server_dns_domain_name_) == 8
| where strlen(server_nb_computer_name_) == 8
| where strlen(server_nb_domain_name_) == 8
When a connection is made to a system running impacket-smbserver
, MDE
logs this interaction with some key characteristics:
- Several server identity fields (such as DNS and NetBIOS names) contain randomly generated 8-character strings and they re-generate everytime you run the tool
- The server version is reported as
255.255 65535 255
, which stands out as highly suspicious as in normal environments we don’t see such version
Detecting Impacket-smbserver with Authentication#
We can use the -user
and -password
flags to specify authentication credentials for the SMB share
sudo impacket-smbserver share -smb2support /tmp/smbshare -user test -password test
Back to ws01
we mount the share using:
net use n: \\192.168.220.133\share /user:test test
We observed an explicit login attempt to atklnx01
machine, recorded with Event ID 4648
:
This activity is also captured in the DeviceLoginEvents
table:
In this scenario, access to the SMB share was initiated via Windows Explorer:
When a remote share is accessed this way, shell link (.lnk
) files are created and stored under:
AppData\Roaming\Microsoft\Windows\Recent\<share-name>\
These .lnk
files follow the naming format:
<share name> (<share IP address>) (<mounted drive letter>).lnk
This activity is also logged in the Security Event Log:
Additionally, In the DeviceProcessEvents
we can see also all execution related to both mounting and unmounting operations:
Lolbins - CertReq#
LOLBins (Living-off-the-Land Binaries) are legitimate system binaries that are sometimes abused by adversaries to perform malicious actions while evading detection by avoiding the use of their own tools
I’m considering writing a query to detect all types of file upload and download activity involving LOLBins. But for now, here’s one scenario I simulated using CertReq.exe along with a detection query I wrote for it
DeviceProcessEvents
| extend
RemoteUrl = extract(@"-config\s+(http[s]?://[^\s]+)", 1, ProcessCommandLine),
UploadedFile = extract(@"-config\s+http[s]?://[^\s]+\s+([^\s]+)", 1, ProcessCommandLine)
| where FileName =~ "certreq.exe"
| where ProcessCommandLine has "-Post"
and ProcessCommandLine contains "http"
| extend parent = tostring(InitiatingProcessFileName)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ProcessCount = count()
by DeviceName, AccountName, ProcessCommandLine, parent, InitiatingProcessCommandLine, InitiatingProcessSHA256, RemoteUrl, UploadedFile
| join kind=inner (
DeviceNetworkEvents
| where InitiatingProcessFileName =~ "certreq.exe"
| project DeviceName, NetworkTime=TimeGenerated, RemoteIP, RemotePort, InitiatingProcessCommandLine
) on DeviceName
The End#
And yeah this is it for today’s article, hope you enjoyed it
Until Next Time