Analyzing ShinobuClipper
Tags: #malware
2023-05-20
I’m aware the malware’s name is a reference to Shinobu Kocho from Demon Slayer but I don’t watch mid anime.
While looking at the configs extracted by the ClipBanker pipeline I noticed something odd, the newest sample hadn’t been extracted correctly. And for good reason, it’s from a different family! In this blog post, we’ll analyse the new sample, write a yara rule and write a config extractor.
TL;DR
It’s a shit-tier open-source .NET clipboard hijacker, who would have thought ?
IOCS
Updated as of 23/05/2023.
SHA256(crack.exe):
b3743ecc2d4300e02d09cb45ca5310f0165e17199f24709bf3ac211aa003ed1e
Path:
C:\Users\user\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\Clipper.exe
Pdb:
C:\Users\j0k3r\Downloads\Compressed\crypto-clipper-main\crypto-clipper-main\Clipper\obj\Release\Clipper.pdb
Mutex:
sdh34yszdfgb
Crypto addresses:
- BTC:
16LYjmErwNek2gMQkNrkLm2i1QVhjmxSRo
- ETH:
0xdF0f41d46Dd8Be583F9a69b4a85A600C8Af7f4Ad
- XMR:
42KwLVv18KiFRZNHzuYNocRrrGdnGbPYAGDT9oHzwh6sMk1f53SVNN26X877au2DPq73BGzLAz9VSbkdBdMPjvtn68qd4CP
- Stellar:
GBHLCKFUEXV2P4AFDNR6QMG7NC4GIJTIE7KDWGC2QX32T45V5KMXK3SV
- Ripple:
rj1eZxZbEJbYNuDTQd9kbaXs8WVpiEai1
- Litecoin:
LQDJ9142kMAPZmS6vZqRb2ty1r85t8nABG
- Nectar:
AT1Lp7MWN2X9HbxpNpsjvRdd978oS8ceGv
- Bitcoin Cash:
qpmd7ghltpdjm0n0vm534s7rjre2lh5ehsqcss3na4
- Dash:
XytymtGjkJJpUGsUjQgpNYumcWV9cT2SQA
Analyzing the clipper
Since it’s open-source the analysis won’t be very detailed. The clipper’s not much to write home about anyway, it hasn’t been obfuscated and the function names describe exactly what it does.
- Exit if mutex exists, if it doesn’t create it
- Install persistently (in the startup folder) the clipper if it isn’t already
- Hide file
- Set file to “system”
- Run the clipboard monitor
The clipboard monitor checks for changes in the clipboard regularly and replaces strings matching different cryptocurrency (BTC, XMR, XLM, ETH, XRP, LTC, BCH, NEC and DASH) address patterns. Threads are used to get/set the content of the clipboard.
The config is stored in plaintext in a dedicated class so writing a config extractor won’t be very hard.
Yara rule
rule shinobuclipper {
meta:
description = "Rule for ShinobuClipper"
author = "yarienkiva"
date = "2023-04-23"
reference = "https://heartathack.club/blog/shinobu-clipper"
strings:
$s1 = "Clipper" ascii fullword
$s2 = "clipboard_changed" ascii fullword
$t1 = "AppMutex" ascii fullword
$t2 = "Attributes" ascii fullword
$t3 = "Autorun" ascii fullword
$t4 = "Clipboard" ascii fullword
$t5 = "ClipboardMonitor" ascii fullword
$t6 = "RegexPatterns" ascii fullword
// config
$c1 = "btc" wide fullword
$c2 = "xmr" wide fullword
$c3 = "xrp" wide fullword
$c4 = "ltc" wide fullword
$c5 = "neo" wide fullword
$c6 = "bch" wide fullword
$c7 = "dash" wide fullword
$c8 = "autorun_enabled" ascii fullword
$c9 = "autorun_name" ascii fullword
$c10 = "clipboard_check_delay" ascii fullword
$c11 = "mutex" ascii fullword
condition:
uint16(0) == 0x5a4d
and filesize < 20KB
and all of them
}
Config extractor
Since the config is stored in plaintext in a dedicated class, if we had a way to dump the .cctor
to a JSON object writing a config extractor would be trivial. I couldn’t find any references to this online so I made my own.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
namespace CCTOR_Dumper
{
class Program
{
/// <summary>
/// Extracts the payload from the .cctor of CONF_CLASS
/// The config of the loader is printed to the console.
/// </summary>
/// <param name="file"></param>
static void HandleFile(string file)
{
Console.Error.WriteLine("Processing, " + file);
// Load the file
Assembly assembly = Assembly.LoadFile(file);
var conf_class = Environment.GetEnvironmentVariable("CONF_CLASS");
if (conf_class == null)
return;
Type type = assembly.GetType(conf_class);
if (type == null)
return;
var config = new Dictionary<string, object>();
// For each field in the class constructor, add
// it and its value to the config dictionary
foreach (var field in type.GetFields())
config.Add(field.Name, field.GetValue(null));
string json = JsonSerializer.Serialize(config);
Console.WriteLine(json);
}
/// <summary>
/// Config extractor for ShinobuClipper but this can
/// be easily adapted to anything
/// author: yarienkiva
/// </summary>
static void Main(string[] args)
{
foreach (string file in args)
{
HandleFile(file);
}
Console.Error.WriteLine("Done!");
}
}
}
The program can be called with a quick and dirty Python wrapper.
#!/usr/bin/env python3
from os.path import abspath, expanduser
import subprocess
import json
import sys
print('ShinobuClipper config extractor - @yarienkiva', file=sys.stderr)
if len(sys.argv) == 1:
print(f'{__file__} [sample1] [sample2] ... [sampleN]', file=sys.stderr)
for sample in sys.argv[1:]:
path = abspath(expanduser(sample))
print(subprocess.check_output(['dotnet', 'run', path]).decode())
"""
$ export CONF_CLASS=Clipper.config
$ python3 main.py ~/Downloads/airlock/new_clipper_owo.exe |jq
ShinobuClipper config extractor - @yarienkiva
Processing, /home/alol/Downloads/airlock/new_clipper_owo.exe
Done!
{
"autorun_enabled": true,
"autorun_name": "Clipper",
"attribute_hidden": false,
"attribute_system": true,
"clipboard_check_delay": 1,
"addresses": {
"btc": "16LYjmErwNek2gMQkNrkLm2i1QVhjmxSRo",
"eth": "0xdF0f41d46Dd8Be583F9a69b4a85A600C8Af7f4Ad",
"xmr": "42KwLVv18KiFRZNHzuYNocRrrGdnGbPYAGDT9oHzwh6sMk1f53SVNN26X877au2DPq73BGzLAz9VSbkdBdMPjvtn68qd4CP",
"xlm": "GBHLCKFUEXV2P4AFDNR6QMG7NC4GIJTIE7KDWGC2QX32T45V5KMXK3SV",
"xrp": "rj1eZxZbEJbYNuDTQd9kbaXs8WVpiEai1",
"ltc": "LQDJ9142kMAPZmS6vZqRb2ty1r85t8nABG",
"nec": "AT1Lp7MWN2X9HbxpNpsjvRdd978oS8ceGv",
"bch": "qpmd7ghltpdjm0n0vm534s7rjre2lh5ehsqcss3na4",
"dash": "XytymtGjkJJpUGsUjQgpNYumcWV9cT2SQA"
},
"mutex": "sdh34yszdfgb"
}
"""