- SMSで「重要なお知らせ」というメッセージが届きました。
- リンク先を確認してみると、「システム警告!」と表示されましたが、これは偽物。
セキュリティアプリと称して、怪しいアプリをインストールさせようとするページでした。 - もし、間違えて指示通りにアプリをインストールすると、本当にマルウェアが侵入してしまうので、気をつけてください。
1. 「auからの大事なお知らせ」で「マルウェアが検出されました」?
SMSで「auからの重要なお知らせ」として、メッセージが届きました。
システム警告
マルウェアが検出されました
リンクを押すと「マルウェアが検出されました」と表示されます。
びっくりしますが、これは偽物です。
この Googleの「メッセージ」アプリの画面ではセキュリティ機能によって「スパム」として検知されています。
しかし、利用しているメッセージアプリによっては、通常のメッセージと見分けにくい可能性があります。
1-1. 「KDDIセキュリティ」という偽アプリ
偽メッセージに従っていくと、「KDDIセキュリティ」と称する偽アプリのページにたどりつきます。
そのまま従うと、「〜.apk」というファイルをダウンロードしてしまいます。
偽アプリの「インストーラ」です。
1-2. apkファイルと「提供元不明のアプリのインストール許可」
ただし、「インストーラ」は、ダウンロードしただけでは害はありません。
インストーラを実行して、システムにアプリを追加しなければ、動作しないからです。
スマートフォンでは、アプリストア以外からはアプリを追加しにくい制限があります。
Androidシステムのセキュリティです。
しかし、偽サイトでは「ご丁寧」にも、これを解除してインストールする手順まで書いてありました。
「提供元不明のアプリのインストール許可」は、マルウェアが入りこむ一番多い経路で、許可するのは慎重にな必要があります。
1-3. apkファイルに潜むマルウェア
ダウンロードフォルダを確認してみると、「KDDIセキュリティ.apk」ファイルが保存されています。
しかし、これをインストールしてはいけません。
ダウンロードした「KDDIセキュリティ.apk」ファイルのウイルスをチェックしてみると、しっかりマルウェアが検出されました。
VirusTotalによれば、主な動作は「SMS送信(sends-sms)」と記載されています。
間違ってインストールしてしまうと、スマホ内で動き出します。
外部からの侵入を許してしまったり、データを送信・破壊してしまう危険性が高いので、絶対にしないでください。
2. まずアドレスバーを見る
そもそも、怪しいリンクをタップしてはいけません。
しかし、ページの画面上部にあるアドレスバーを見ると、そこでも偽ページであることに気づけます。
アドレスにランダムな文字列が含まれるのは、偽サイトのわかりやすい特徴の一つです。
「この接続は安全ではありません」というのは、相手と自分の間で通信が傍受される可能性がある、という意味です。
機密情報を入力すると、途中経路の端末で見られてしまうという危険です。
しかし、アクセスするだけで危険という意味ではありません。
ただ、暗号化通信のためのセキュリティ証明書を用意するのは手間がかかるので、すぐに逃げる偽サイトの場合は取得していないことが多いです。
2-1. HTMLソースから動作を確認する
この「マルウェアが検出されました」って表示されているけど、ほんとに大丈夫なの?
このメッセージは、見せかけだけの「ハリボテ」。
どうして表示が出ているのかは、ページの中身をみることで確認できます。
パソコンからアクセスして、このページのHTMLソースを見てみました。
このように、JavaScriptのプログラムが丸見えです。
とくにスマホの中身をチェックしたわけでもなく、適当に「2個の脅威が見つかりました」と表示させているだけのようです。
誰がアクセスしても同じメッセージなのね
2-2. 偽サイトで記録されたCookie
ブラウザにどんな Cookieが記録されるのかも確認してみました。
保存されていたCookieは、全部で4項目でした。
「count_download」だけは、apkファイルをダウンロードをクリックした後に記録されていました。
4つのCookieの値を見てみます。
HTMLソースに直接 参照されているのは、count_downloadだけでした。この値がセットされていると、「My auログイン」にリダイレクトするようになっています。
つまり、一度ダウンロードしたスマホは、auの公式ページに飛ばす仕掛けになっています。
2-3. __tins__21143359
「__tins__21143359」の値だけが、長い文字列です。
%7B%22sid%22%3A%201636611960753%2C%20%22vd%22%3A%203%2C%20%22expires%22%3A%201636613777718%7D
「%##」というパターンが含まれているので、URLエンコードされた文字列のようですので、デコードします。
{"sid": 1636611960753, "vd": 3, "expires": 1636613777718}
・URLエンコード・デコード| Tech-Unlimited
「sid」は、セッションID、
「expires」は、セッション有効期限 のようです。
ページ内で呼び出されている外部スクリプト「https://js.users.51.la/21143359.js」内で記録されていました。
おそらく「セッション」を記録している、つまり同一端末からのアクセスを確認しているだけだと思います。
もし、興味があればコードを読んでみて下さい。
(function() {
var config = {
itv: 1800000,
url1: '//ia.51.la/go1?id=21143359',
ekc: ''
};
! function(e) {
function t(r) {
if (n[r]) return n[r].exports;
var o = n[r] = {
exports: {},
id: r,
loaded: !1
};
return e[r].call(o.exports, o, o.exports, t), o.loaded = !0, o.exports
}
var n = {};
return t.m = e, t.c = n, t.p = "", t(0)
}([function(e, t, n) {
"use strict";
function r() {
var e = void 0,
t = /id=(\d+)/.exec(config.url1)[1] || "";
try {
e = u.get("__tins__" + t)
} catch (t) {
e = !1
}
var n = e && i.isN(e.sid) && i.isN(e.expires) && g - e.sid < 18e5 ? 0 : 1,
r = n ? 1 : e.vd + 1,
o = n ? g : e.sid,
c = g + 18e5;
return u.set("__tins__" + t, s.stringify({
sid: o,
vd: r,
expires: c
}), null, "/"), [n, n ? o : u.get("__tins__" + t).sid, r]
}
function o() {
var e = s.parse(s.stringify(i.extend({}, y, v))),
t = i.obj2url(e),
n = config.url1 + "&rt=" + g + "&" + t,
r = new Image(1, 1);
r.src = n
}
var i = n(4),
c = n(5),
u = n(7).store,
s = n(6),
a = window,
f = a.location,
l = a.screen,
p = a.navigator,
g = i.now(),
d = !0,
m = r(),
v = {
ekc: config.ekc,
sid: m[1],
tt: c.getMeta.tt,
kw: c.getMeta.kw,
cu: f.href,
pu: c.getRef()
},
y = {
rl: l.width + "*" + l.height,
lang: p.language || p.browserLanguage,
ct: function() {
var e = p.connection || p.mozConnection || p.webkitConnection || p.oConnection,
t = i.hasIt(p.userAgent, "mobile") && e ? e.type : "unknow";
return t
}(),
pf: function() {
var e = d ? 1 : 0;
return d = 0, e
}(),
ins: m[0],
vd: m[2],
ce: p.cookieEnabled ? 1 : 0,
cd: l.colorDepth || l.pixelDepth,
ds: c.getMeta.ds
};
o.version = "2.2.1.2", n(10)(y), o()
}, , , , function(e, t) {
"use strict";
function n(e, t) {
return void 0 !== e && e.indexOf(t) !== -1
}
function r(e) {
return function(t) {
return Object.prototype.toString.call(t) === "[object " + e + "]"
}
}
function o() {
for (var e = 0, t = {}; e < arguments.length; e++) {
var n = arguments[e];
for (var r in n) t[r] = n[r]
}
return t
}
function i(e) {
return e.replace(/&/g, "~_~")
}
function c(e) {
var t = "";
for (var n in e) "" !== t && (t += "&"), t += n + "=" + a(a(i(String(e[n]))));
return t
}
function u(e) {
return e.replace(/^\s+|\s+$/g, "")
}
function s() {
return +new Date
}
var a = encodeURIComponent,
f = r("Object"),
l = r("Number"),
p = r("String"),
g = r("Array"),
d = r("Function"),
m = r("RegExp");
e.exports = {
isO: f,
isN: l,
isF: d,
isR: m,
isS: p,
isA: g,
hasIt: n,
extend: o,
obj2url: c,
trim: u,
now: s
}
}, function(e, t, n) {
"use strict";
function r(e) {
return u.getElementsByTagName(e.toLowerCase())
}
function o() {
var e = "";
try {
e = c.top.document.referrer
} catch (t) {
if (c.parent) try {
e = c.parent.document.referrer
} catch (t) {
e = ""
}
}
return "" === e && (e = u.referrer), e
}
var i = n(4),
c = window,
u = c.document,
s = function() {
var e = r("meta"),
t = r("title"),
n = {
kw: "",
ds: ""
},
o = void 0;
n.tt = i.trim(t.length ? t[0].innerHTML : "");
for (var c = 0; c < e.length; c++) e[c].name && (o = e[c].name.toLowerCase(), i.hasIt("keywords", o) && (n.kw = e[c].content.slice(0, 100)), i.hasIt("description", o) && (n.ds = e[c].content.slice(0, 30)));
return n
}();
e.exports = {
getMeta: s,
getRef: o
}
}, function(module, exports) {
"use strict";
var _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(e) {
return typeof e
} : function(e) {
return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e
};
module.exports = {
parse: function parse(sJSON) {
return eval("(" + sJSON + ")")
},
stringify: function() {
function e(o) {
if (null == o) return "null";
if ("number" == typeof o) return isFinite(o) ? o.toString() : "null";
if ("boolean" == typeof o) return o.toString();
if ("object" === ("undefined" == typeof o ? "undefined" : _typeof(o))) {
if ("function" == typeof o.toJSON) return e(o.toJSON());
if (r(o)) {
for (var u = "[", s = 0; s < o.length; s++) u += (s ? ", " : "") + e(o[s]);
return u + "]"
}
if ("[object Object]" === t.call(o)) {
var a = [];
for (var f in o) n.call(o, f) && a.push(e(f) + ": " + e(o[f]));
return "{" + a.join(", ") + "}"
}
}
return '"' + o.toString().replace(c, i) + '"'
}
var t = Object.prototype.toString,
n = Object.prototype.hasOwnProperty,
r = Array.isArray || function(e) {
return "[object Array]" === t.call(e)
},
o = {
'"': '\\"',
"\\": "\\\\",
"\b": "\\b",
"\f": "\\f",
"\n": "\\n",
"\r": "\\r",
"\t": "\\t"
},
i = function(e) {
return o[e] || "\\u" + (e.charCodeAt(0) + 65536).toString(16).substr(1)
},
c = /[\\"\u0000-\u001F\u2028\u2029]/g;
return e
}()
}
}, function(e, t, n) {
"use strict";
var r = n(5),
o = n(6),
i = {
get: function(e) {
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(e).replace(/[-.+*]/g, "\\$&") + "\\s*\\=s*([^;]*).*$)|^.*$"), "$1")) || null
},
set: function(e, t, n, r, o, i) {
if (!e || /^(?:expires|max-age|path|domain|secure)$/i.test(e)) return !1;
var c = "";
if (n) switch (n.constructor) {
case Number:
c = n === 1 / 0 ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + n;
break;
case String:
c = "; expires=" + n;
break;
case Date:
c = "; expires=" + n.toUTCString()
}
return document.cookie = encodeURIComponent(e) + "=" + encodeURIComponent(t) + c + (o ? "; domain=" + o : "") + (r ? "; path=" + r : "") + (i ? "; secure" : ""), !0
}
},
c = {
get: function(e) {
return o.parse((r.isMobi ? window.localStorage.getItem(e) : i.get(e)) || "{}")
},
set: function(e, t, n, o) {
return r.isMobi ? window.localStorage.setItem(e, t) : i.set(e, t, n, o)
}
};
e.exports = {
cookie: i,
store: c
}
}, , , function(e, t, n) {
"use strict";
var r = n(4),
o = n(7);
e.exports = function(e) {
var t = o.store.get("__51laig__");
t = r.isN(t) ? parseInt(t) + 1 : 1, o.cookie.set("__51cke__", config.ekc, null, "/"), e.ing = t, o.store.set("__51laig__", t, null, "/")
}
}]);
}());
3. 「ドコモの重要なお知らせ」を偽装するパターンもあった
家族に聞いてみると、「ドコモの重要なお知らせ」として、同じようなメッセージが届いていたそうです。
安全なスマホでアクセスしてみると、メッセージでは「ドコモ」を騙っているわりに、アクセスしたページでは au が表示されました。
アクセスしたスマホをページ内部で選別しているのかと思って、ページのHTMLを比較してみましたが、完全に一緒です。
3-1. ドメインのトップページ
ちなみに「http://lvkfcfczio.duckdns.org/」にアクセスすると、このように中国語簡体字でエラーが表示されました。
「http://ffvsvoolhi.duckdns.org/」の方は、パソコンからアクセスすると、「?」とだけ表示されました。
不審に思って、HTMLソースを見てみると、iPhoneやAndroidなど端末の種類によって、アクセスページを振り分けるコードが書かれていました。
iPhoneで、「http://erysowzsxb.duckdns.org/」にアクセスしてみましたが、ページにアクセスできませんでした。
いずれにしても、
・http://ffvsvoolhi.duckdns.org/
・http://lvkfcfczio.duckdns.org/
・http://erysowzsxb.duckdns.org/
と、同じドメイン「duckdns.org」で、いくつもサブドメインを作成して、偽サイトを構築しているようです。