今日表示しない

トップへ戻る

トップへ戻る

脅威インテリジェンス

脅威インテリジェンス

脅威インテリジェンス

APT Down - The North Korea Files

APT Down - The North Korea Files

APT Down - The North Korea Files

エンキホワイトハット

エンキホワイトハット

コンテンツ

コンテンツ

コンテンツ

概要

本レポートは、Phrack Magazineで公開された APT Down–The North Korea Files のデータを基に追加分析した内容を扱っている。攻撃者が実際に使用したVMware VM、VPSのダンプファイルも公開されており、攻撃者の活動履歴を詳細に分析することができ、これによって攻撃の背後関係も推測することができた。

特に公開されたファイルの中にルートキットのソースコードが存在するが、これは当社が2022年の金融機関におけるインシデント調査の過程で確認したルートキットのソースコードであることが判明した。コードのロジックはもちろん、使用された暗号化キーまで一致していた。また、2022年のインシデント調査時に確認されたルートキットの、2025年最新バージョンであるソースコードが追加で確認された。

このほかにも、Ivantiの1-Dayエクスプロイト、流出したと推定される外交部(外務省に相当)ホームページのソースコード、GPKISecureWebXのソースコード、そして検察、防諜司令部などを対象としたフィッシング攻撃も確認された。これらの状況は、攻撃者が過去から現在にかけて韓国を主要な標的とし、持続的かつ活発な攻撃活動を行ってきたこと、一般的には外部からの奪取が困難な資料が流出している点、ルートキットや1-Dayエクスプロイトを活用しているという点から、攻撃者が高いレベルの攻撃能力を保有しており、内部ネットワークにまで侵入して機密情報を奪取したことを示唆している。

分析ファイルの概要

本章では、分析内容を説明する前に、APT Down – The North Korea Files レポートと共に公開されたファイルの構成を簡略に説明する。

caption - .onion サイトに掲載された公開ファイルの一覧

主要なファイルは work.zip、vps.zip、file-lists_and_misc.zip で、説明は以下の通りである。

work.zip

  • 攻撃者のVMware VM(Deepin 20.9)ダンプファイルで、実際に使用されたワークステーション環境が含まれており、マルウェアのソースコード、攻撃ツール、奪取された情報、各種ログなどが存在する。

  • ホストの C:\ が hgfs でマウントされた状態で、攻撃者のホスト上に存在するファイルも確認できる。

vps.zip

  • 攻撃者が所有するVPSのダンプファイルで、スピアフィッシング攻撃に使用された履歴や各種ログが存在する。

  • 国軍防諜司令部、検察など複数の機関をターゲットとしたスピアフィッシング攻撃が確認できる。

file-lists_and_misc.zip

  • 攻撃者のワークステーションにおけるGoogleタイムライン、コマンド履歴、ファイル一覧全体などが存在する。

  • ファイル一覧全体から、ダンプファイルには存在しないパスやファイルを確認できる。

1. マルウェアおよび攻撃の分析

本章では、攻撃者のVMware VMダンプファイルから確認されたマルウェアと、VPSダンプファイルから確認されたスピアフィッシング攻撃およびインフラ運用の状況について解説する。特にtomcat20220420_rootkittomcat20250414_rootkit_linux234ディレクトリで確認されたルートキットとバックドアは、過去に当社が調査した国内の金融機関を標的とした侵害事故と関連している。

1.1. tomcat20220420_rootkit

1.1.1. Backdoor

work/home/user/Desktop/tomcat20220420_rootkit/tomcat20220420_rootkit/workディレクトリ内にバックドアのソースコードが存在する。バックドアの主な動作は、コマンドコードに応じて悪意のあるアクティビティを実行するか、プロキシとして通信を中継することである。バックドアは単独では実行されず、常にルートキットによって実行される。バックドアの起動引数の情報は以下の表の通りである。

| 번째 인자  | 인자 개수 1개일  | 인자 개수 2개일  |
| --- | --- | --- |
| cb | 하드코딩된 아이피, 포트 사용 | 2번째 인자 값을 포트로 사용, 하드코딩된 아이피 사용 |
| proxy | 프록시 플래그 활성화 | X |
| cb, proxy 이외의  | 인자 값을 포트로 사용 | X

| 번째 인자  | 인자 개수 1개일  | 인자 개수 2개일  |
| --- | --- | --- |
| cb | 하드코딩된 아이피, 포트 사용 | 2번째 인자 값을 포트로 사용, 하드코딩된 아이피 사용 |
| proxy | 프록시 플래그 활성화 | X |
| cb, proxy 이외의  | 인자 값을 포트로 사용 | X

| 번째 인자  | 인자 개수 1개일  | 인자 개수 2개일  |
| --- | --- | --- |
| cb | 하드코딩된 아이피, 포트 사용 | 2번째 인자 값을 포트로 사용, 하드코딩된 아이피 사용 |
| proxy | 프록시 플래그 활성화 | X |
| cb, proxy 이외의  | 인자 값을 포트로 사용 | X

caption - 引数オプション

プロキシフラグが有効な場合、コマンドコード関連のロジックは実行されず、AES-CBC-256で暗号化されたデータ(__step2__)を受信するまで待機する。

master.c 데이터 검사 로직

caption - master.c データ検証ロジック

AESキーとivは以下の通りである。

  • AESキー: 603deb15153a715e2b73aef3857d758b1f552c573e6158d72d9811a33914defe

  • iv: 603deb15153a715e2b73aef3857d758b1f552c573e6158d72d9811a33914defe

バックドアは、通常の通信を装うためにクライアントとのソケット通信を以下のプロトコルのいずれかに設定する。

  • HTTP

  • HTTPS

  • SSL

  • TCP

  • SMTP

実際に使用されるプロトコルは install_common.h 内で定義されている。

install_common.h

caption - install_common.h

コマンドコードを含むすべての送受信データはxorで暗号化・復号される。このとき使用されるキーは「1101link」であるが、暗号化・復号ロジックを見ると、単一バイトと1回xorするのと同等である。したがって、「1101link」によるxor結果は、1によるxor結果と等しい。

encrypt.c EncodeDecode 함수

caption - encrypt.c EncodeDecode関数

コマンドコードごとの動作は以下の表の通りである。

| 명령코드 이름 | 명령코드 | 행위 | 응답코드 이름 | 응답코드 |
| --- | --- | --- | --- | --- |
| CMD_LOGIN | 0x11 | 응답코드를 클라이언트로 전송한다. | CMD_LOGIN_YES | 0x12 |
| CMD_FILE_UP | 0x15 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_UP_YES | 0x16 |
| CMD_FILE_DOWN | 0x18 | 지정된 파일을 클라이언트로 전송한다. | CMD_DOWN_YES | 0x19 |
| CMD_SHELL | 0x1b | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_SHELL_YES | 0x1c |
| CMD_TRANSFER | 0x1f | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드를 전송하여 실행 중인 백도어를 종료하도록 하고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_TRANSFER_YES | 0x20 |
| CMD_PROXY_TRANSFER | 0x21 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 AES로 암호화한 패스워드, "__step2__", "__step3__"차례로 전송하여 새로운 백도어가 프록시 서버로서 동작하도록 한다. 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_PROXY_TRANSFER_YES | 0x22 |
| CMD_SOCKS_PROXY_TRANSFER | 0x23 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 AES로 암호화한 패스워드, "__step2__", "__step3__"차례로 전송하여 새로운 백도어가 프록시 서버로서 동작하도록 한다. 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. 이때 SOCKS5 프록시 연결을 수행한다. | CMD_SOCKS_PROXY_TRANSFER_YES | 0x24 |
| CMD_BACK | 0x13 | 응답코드를 클라이언트로 전송한다. | CMD_BACK_YES | 0x14 |
| CMD_SINGLE_CMD | 0x25 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. 이후 백도어 프로세스를 종료한다. | CMD_SINGLE_CMD_YES | 0x26 |
| CMD_IN_SINGLE_CMD | 0x27 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_IN_SINGLE_CMD_YES | 0x28 |
| CMD_D_SINGLE_CMD | 0x2b | 주어진 명령어를 독립된 프로세스로 실행한다. | CMD_D_SINGLE_CMD_YES | 0x2c |
| CMD_D_OUT_SINGLE_CMD | 0x2d | 주어진 명령어를 독립된 프로세스로 실행한다. 이후 백도어 프로세스를 종료한다. | CMD_D_OUT_SINGLE_CMD_YES | 0x2e

| 명령코드 이름 | 명령코드 | 행위 | 응답코드 이름 | 응답코드 |
| --- | --- | --- | --- | --- |
| CMD_LOGIN | 0x11 | 응답코드를 클라이언트로 전송한다. | CMD_LOGIN_YES | 0x12 |
| CMD_FILE_UP | 0x15 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_UP_YES | 0x16 |
| CMD_FILE_DOWN | 0x18 | 지정된 파일을 클라이언트로 전송한다. | CMD_DOWN_YES | 0x19 |
| CMD_SHELL | 0x1b | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_SHELL_YES | 0x1c |
| CMD_TRANSFER | 0x1f | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드를 전송하여 실행 중인 백도어를 종료하도록 하고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_TRANSFER_YES | 0x20 |
| CMD_PROXY_TRANSFER | 0x21 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 AES로 암호화한 패스워드, "__step2__", "__step3__"차례로 전송하여 새로운 백도어가 프록시 서버로서 동작하도록 한다. 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_PROXY_TRANSFER_YES | 0x22 |
| CMD_SOCKS_PROXY_TRANSFER | 0x23 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 AES로 암호화한 패스워드, "__step2__", "__step3__"차례로 전송하여 새로운 백도어가 프록시 서버로서 동작하도록 한다. 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. 이때 SOCKS5 프록시 연결을 수행한다. | CMD_SOCKS_PROXY_TRANSFER_YES | 0x24 |
| CMD_BACK | 0x13 | 응답코드를 클라이언트로 전송한다. | CMD_BACK_YES | 0x14 |
| CMD_SINGLE_CMD | 0x25 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. 이후 백도어 프로세스를 종료한다. | CMD_SINGLE_CMD_YES | 0x26 |
| CMD_IN_SINGLE_CMD | 0x27 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_IN_SINGLE_CMD_YES | 0x28 |
| CMD_D_SINGLE_CMD | 0x2b | 주어진 명령어를 독립된 프로세스로 실행한다. | CMD_D_SINGLE_CMD_YES | 0x2c |
| CMD_D_OUT_SINGLE_CMD | 0x2d | 주어진 명령어를 독립된 프로세스로 실행한다. 이후 백도어 프로세스를 종료한다. | CMD_D_OUT_SINGLE_CMD_YES | 0x2e

| 명령코드 이름 | 명령코드 | 행위 | 응답코드 이름 | 응답코드 |
| --- | --- | --- | --- | --- |
| CMD_LOGIN | 0x11 | 응답코드를 클라이언트로 전송한다. | CMD_LOGIN_YES | 0x12 |
| CMD_FILE_UP | 0x15 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_UP_YES | 0x16 |
| CMD_FILE_DOWN | 0x18 | 지정된 파일을 클라이언트로 전송한다. | CMD_DOWN_YES | 0x19 |
| CMD_SHELL | 0x1b | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_SHELL_YES | 0x1c |
| CMD_TRANSFER | 0x1f | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드를 전송하여 실행 중인 백도어를 종료하도록 하고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_TRANSFER_YES | 0x20 |
| CMD_PROXY_TRANSFER | 0x21 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 AES로 암호화한 패스워드, "__step2__", "__step3__"차례로 전송하여 새로운 백도어가 프록시 서버로서 동작하도록 한다. 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_PROXY_TRANSFER_YES | 0x22 |
| CMD_SOCKS_PROXY_TRANSFER | 0x23 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 AES로 암호화한 패스워드, "__step2__", "__step3__"차례로 전송하여 새로운 백도어가 프록시 서버로서 동작하도록 한다. 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. 이때 SOCKS5 프록시 연결을 수행한다. | CMD_SOCKS_PROXY_TRANSFER_YES | 0x24 |
| CMD_BACK | 0x13 | 응답코드를 클라이언트로 전송한다. | CMD_BACK_YES | 0x14 |
| CMD_SINGLE_CMD | 0x25 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. 이후 백도어 프로세스를 종료한다. | CMD_SINGLE_CMD_YES | 0x26 |
| CMD_IN_SINGLE_CMD | 0x27 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_IN_SINGLE_CMD_YES | 0x28 |
| CMD_D_SINGLE_CMD | 0x2b | 주어진 명령어를 독립된 프로세스로 실행한다. | CMD_D_SINGLE_CMD_YES | 0x2c |
| CMD_D_OUT_SINGLE_CMD | 0x2d | 주어진 명령어를 독립된 프로세스로 실행한다. 이후 백도어 프로세스를 종료한다. | CMD_D_OUT_SINGLE_CMD_YES | 0x2e

caption - バックドアコマンドコードの動作

1.1.2. syslogk rootkit

work/home/user/Desktop/tomcat20220420_rootkit/tomcat20220420_rootkit/main.cファイルは、syslogkと命名されたルートキットのソースコードである。ルートキットは、バックドアおよび関連するマルウェアが存在するディレクトリを隠蔽し、一般的な検出を回避する。また、マジックパケット受信時のみバックドアを実行するため、攻撃者が望むタイミングでのみバックドアが動作する。

ルートキットは3つの関数をフックして、プロセス、ポート、ディレクトリを隠蔽する。フックに関する情報は以下の表の通りである。

| 후킹 대상 함수 | 후킹 함수 | 행위 |
| --- | --- | --- |
| proc_roo_readdir | hk_proc_readdir | 프로세스 은닉 |
| tcp4_seq_show | hk_t4_seq_show | 열린 포트 은닉 |
| readdir | hk_root_readdir | 디렉토리 은닉

| 후킹 대상 함수 | 후킹 함수 | 행위 |
| --- | --- | --- |
| proc_roo_readdir | hk_proc_readdir | 프로세스 은닉 |
| tcp4_seq_show | hk_t4_seq_show | 열린 포트 은닉 |
| readdir | hk_root_readdir | 디렉토리 은닉

| 후킹 대상 함수 | 후킹 함수 | 행위 |
| --- | --- | --- |
| proc_roo_readdir | hk_proc_readdir | 프로세스 은닉 |
| tcp4_seq_show | hk_t4_seq_show | 열린 포트 은닉 |
| readdir | hk_root_readdir | 디렉토리 은닉

caption - フック情報

フック関数内部では、事前定義されたプロセス名、パス、ポートの文字列と対象のエントリを strstr 関数で比較し、一致した場合はそのエントリを返す前に削除する方式で隠蔽を実装している。

その後、Netfilter関連の nf_register_hook 関数を使用してフックポイントにコールバック関数を登録する。nf_register_hook によってコールバック関数が登録される2つのフックポイントの説明は以下の通りである。

  • NF_INET_LOCAL_IN : ローカルホストにパケットを送信する前

  • NF_INET_LOCAL_OUT : ローカルホストから外部にパケットを送信する前

hkcap.c 후킹 함수 등록 로직

caption - hkcap.c フック関数登録ロジック

上記の方法により、ルートキットはローカルホストで送受信されるすべてのパケットを監視し、攻撃者が送信したマジックパケットを確認すると悪意のあるアクティビティを実行する。

NF_INET_LOCAL_IN 地点のコールバック関数は、受信したパケットのプロトコルがTCPであることを確認し、ペイロードを復号して特定の条件と一致するか検証する。復号に使用されるデータは以下の表の通りである。

| 정보 |  |
| --- | --- |
| AES key | "603deb15153a715e2b73aef3857d758b1f552c573e6158d72d9811a33914defe" |
| iv | "12a3bb47535ec0d53953a6fbad43f573" |
| xor key | "1101link"

| 정보 |  |
| --- | --- |
| AES key | "603deb15153a715e2b73aef3857d758b1f552c573e6158d72d9811a33914defe" |
| iv | "12a3bb47535ec0d53953a6fbad43f573" |
| xor key | "1101link"

| 정보 |  |
| --- | --- |
| AES key | "603deb15153a715e2b73aef3857d758b1f552c573e6158d72d9811a33914defe" |
| iv | "12a3bb47535ec0d53953a6fbad43f573" |
| xor key | "1101link"

caption - 復号キー情報

復号後に検証される条件リストと処理動作は以下の表の通りである。

| 조건  | 행위 | 콜백 함수 |
| --- | --- | --- |
| 1. xor로 복호화된 페이로드 4바이트가 "44332211"(16진수)이다.-n2. 이후 4바이트가 "ssecuremw"이다. | /bin/sh -c 통해 명령어 실행 | nfinpro |
| 1. syn 패킷이다.-n2. 패킷 window 값이 1022이고 id, seq 값이 common.c id_list, seq_list에 존재한다. | /bin/sh -c 통해 백도어를 실행하고, 패킷 페이로드 전송 | nfin, nfinpro |
| 1. 백도어와 통신하는 ip에 대한 패킷이다.-n2. 패킷 페이로드가 "ssecuremw"이다. | 현재 실행중인 백도어 프로세스 종료 | nfin, nfinpro |
| 1. 백도어와 통신하는 ip에 대한 패킷이다.-n2. 패킷 페이로드가 "ssecuremw"아니다. | 패킷의 dest 값을 현재 실행중인 백도어 통신 포트로 변경 | nfin, nfinpro |
| 1. 데이터 4바이트가 0x0000002c이다.-n2. 오프셋 10부터 AES로 암호화된 패스워드(ssecuremw)존재한다. | /bin/sh -c 통해 proxy 인자를 설정하여 백도어 실행 | nfinpro |
| 1. 데이터가 AES로 암호화된 패스워드(ssecuremw)시작한다. | /bin/sh -c 통해 proxy 인자를 설정하여 백도어 실행 | nfinpro |
| 1. 데이터가 AES로 암호화된 데이터(__step3__)시작한다. | 현재 실행중인 백도어와 통신하는 ip를 해당 패킷의 ip로 변경 | nfinpro

| 조건  | 행위 | 콜백 함수 |
| --- | --- | --- |
| 1. xor로 복호화된 페이로드 4바이트가 "44332211"(16진수)이다.-n2. 이후 4바이트가 "ssecuremw"이다. | /bin/sh -c 통해 명령어 실행 | nfinpro |
| 1. syn 패킷이다.-n2. 패킷 window 값이 1022이고 id, seq 값이 common.c id_list, seq_list에 존재한다. | /bin/sh -c 통해 백도어를 실행하고, 패킷 페이로드 전송 | nfin, nfinpro |
| 1. 백도어와 통신하는 ip에 대한 패킷이다.-n2. 패킷 페이로드가 "ssecuremw"이다. | 현재 실행중인 백도어 프로세스 종료 | nfin, nfinpro |
| 1. 백도어와 통신하는 ip에 대한 패킷이다.-n2. 패킷 페이로드가 "ssecuremw"아니다. | 패킷의 dest 값을 현재 실행중인 백도어 통신 포트로 변경 | nfin, nfinpro |
| 1. 데이터 4바이트가 0x0000002c이다.-n2. 오프셋 10부터 AES로 암호화된 패스워드(ssecuremw)존재한다. | /bin/sh -c 통해 proxy 인자를 설정하여 백도어 실행 | nfinpro |
| 1. 데이터가 AES로 암호화된 패스워드(ssecuremw)시작한다. | /bin/sh -c 통해 proxy 인자를 설정하여 백도어 실행 | nfinpro |
| 1. 데이터가 AES로 암호화된 데이터(__step3__)시작한다. | 현재 실행중인 백도어와 통신하는 ip를 해당 패킷의 ip로 변경 | nfinpro

| 조건  | 행위 | 콜백 함수 |
| --- | --- | --- |
| 1. xor로 복호화된 페이로드 4바이트가 "44332211"(16진수)이다.-n2. 이후 4바이트가 "ssecuremw"이다. | /bin/sh -c 통해 명령어 실행 | nfinpro |
| 1. syn 패킷이다.-n2. 패킷 window 값이 1022이고 id, seq 값이 common.c id_list, seq_list에 존재한다. | /bin/sh -c 통해 백도어를 실행하고, 패킷 페이로드 전송 | nfin, nfinpro |
| 1. 백도어와 통신하는 ip에 대한 패킷이다.-n2. 패킷 페이로드가 "ssecuremw"이다. | 현재 실행중인 백도어 프로세스 종료 | nfin, nfinpro |
| 1. 백도어와 통신하는 ip에 대한 패킷이다.-n2. 패킷 페이로드가 "ssecuremw"아니다. | 패킷의 dest 값을 현재 실행중인 백도어 통신 포트로 변경 | nfin, nfinpro |
| 1. 데이터 4바이트가 0x0000002c이다.-n2. 오프셋 10부터 AES로 암호화된 패스워드(ssecuremw)존재한다. | /bin/sh -c 통해 proxy 인자를 설정하여 백도어 실행 | nfinpro |
| 1. 데이터가 AES로 암호화된 패스워드(ssecuremw)시작한다. | /bin/sh -c 통해 proxy 인자를 설정하여 백도어 실행 | nfinpro |
| 1. 데이터가 AES로 암호화된 데이터(__step3__)시작한다. | 현재 실행중인 백도어와 통신하는 ip를 해당 패킷의 ip로 변경 | nfinpro

caption - 条件および動作

NF_INET_LOCAL_OUT 地点におけるコールバック関数は、バックドアが送信するパケットの source(送信元)を、バックドアが通信に使用しないポートに変更する。

Netfilterコールバック関数の登録が完了すると、ルートキットはカーネルモジュールリストから自身を削除して検出を困難にする。これにより、lsmod コマンドを実行してもルートキットは見つからなくなる。

hide_module 함수

caption - hide_module関数

モジュールリストから削除されたルートキットに対し、install.h の MAGIC_DRBIN の値を書き込むと、lsmod コマンドで再び確認できるようになる。

proc_write 함수 로직

caption - proc_write関数のロジック

1.1.3. Backdoor Client

work/home/user/Desktop/tomcat20220420_rootkit/tomcat20220420_rootkit/work/tcat.cファイルは、バックドアクライアントのソースコードである。このファイルは攻撃者がC&Cサーバーから実行したものと思われる。

実行時に複数のオプションを指定可能である。すべてのオプションについては以下の表を参照されたい。

| Long Option | Short Option | 의미 |
| --- | --- | --- |
| HOST | H | 백도어 주소 |
| PORT | P | 백도어 포트 |
| password | p | 백도어 접속 시에 사용할 패스워드 (기본: ssecuremw) |
| callback | c | callback 모드 활성화 |
| single_command | s | 주어진 명령어를 실행하도록 전송 |
| daemon_command | d | 주어진 명령어를 새로운 프로세스에서 실행 하도록 전송 (백그라운드 실행) |
| proxy | x | 프록시 서버 사용 |
| socks_proxy | y | socks5 프록시 서버 사용 |
| socks_aim_hostname | i | 사용할 socks5 프록시 서버의 주소 |
| socks_aim_port | o | 사용할 socks5 프록시 서버의 포트 |
| socks_username | u | socks5 프록시 서버에 로그인할 유저이름 |
| socks_password | a | socks5 프록시 서버에 로그인할 패스워드 |
| knock_protocol | k | 백도어 초기 접속 시도에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSH) |
| ethernet_interface | e | 사용할 이더넷 인터페이스 |
| cookie | 5 | HTTP 혹은 HTTPS를 사용할 때의 쿠키  |
| host | 6 | HTTP 혹은 HTTPS를 사용할 때의 selfdefined host  |
| main_protocol | m | 백도어 통신에 사용할 프로토콜 (0: TCP, 1: HTTP, 99: old HTTP) |
| kc | 1 | 주어진 명령어를 커널 모듈에서 출력 없이 실행하도록 전송

| Long Option | Short Option | 의미 |
| --- | --- | --- |
| HOST | H | 백도어 주소 |
| PORT | P | 백도어 포트 |
| password | p | 백도어 접속 시에 사용할 패스워드 (기본: ssecuremw) |
| callback | c | callback 모드 활성화 |
| single_command | s | 주어진 명령어를 실행하도록 전송 |
| daemon_command | d | 주어진 명령어를 새로운 프로세스에서 실행 하도록 전송 (백그라운드 실행) |
| proxy | x | 프록시 서버 사용 |
| socks_proxy | y | socks5 프록시 서버 사용 |
| socks_aim_hostname | i | 사용할 socks5 프록시 서버의 주소 |
| socks_aim_port | o | 사용할 socks5 프록시 서버의 포트 |
| socks_username | u | socks5 프록시 서버에 로그인할 유저이름 |
| socks_password | a | socks5 프록시 서버에 로그인할 패스워드 |
| knock_protocol | k | 백도어 초기 접속 시도에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSH) |
| ethernet_interface | e | 사용할 이더넷 인터페이스 |
| cookie | 5 | HTTP 혹은 HTTPS를 사용할 때의 쿠키  |
| host | 6 | HTTP 혹은 HTTPS를 사용할 때의 selfdefined host  |
| main_protocol | m | 백도어 통신에 사용할 프로토콜 (0: TCP, 1: HTTP, 99: old HTTP) |
| kc | 1 | 주어진 명령어를 커널 모듈에서 출력 없이 실행하도록 전송

| Long Option | Short Option | 의미 |
| --- | --- | --- |
| HOST | H | 백도어 주소 |
| PORT | P | 백도어 포트 |
| password | p | 백도어 접속 시에 사용할 패스워드 (기본: ssecuremw) |
| callback | c | callback 모드 활성화 |
| single_command | s | 주어진 명령어를 실행하도록 전송 |
| daemon_command | d | 주어진 명령어를 새로운 프로세스에서 실행 하도록 전송 (백그라운드 실행) |
| proxy | x | 프록시 서버 사용 |
| socks_proxy | y | socks5 프록시 서버 사용 |
| socks_aim_hostname | i | 사용할 socks5 프록시 서버의 주소 |
| socks_aim_port | o | 사용할 socks5 프록시 서버의 포트 |
| socks_username | u | socks5 프록시 서버에 로그인할 유저이름 |
| socks_password | a | socks5 프록시 서버에 로그인할 패스워드 |
| knock_protocol | k | 백도어 초기 접속 시도에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSH) |
| ethernet_interface | e | 사용할 이더넷 인터페이스 |
| cookie | 5 | HTTP 혹은 HTTPS를 사용할 때의 쿠키  |
| host | 6 | HTTP 혹은 HTTPS를 사용할 때의 selfdefined host  |
| main_protocol | m | 백도어 통신에 사용할 프로토콜 (0: TCP, 1: HTTP, 99: old HTTP) |
| kc | 1 | 주어진 명령어를 커널 모듈에서 출력 없이 실행하도록 전송

caption - 引수オプション

オプションに応じて、バックドアにコマンドを伝達する方式は以下のように4つに分類される。

  • single cmd : 入力されたコマンドをバックドアで実行させるために送信する。

  • single daemon cmd : 入力されたコマンドをバックドアで新しいプロセスを生成して実行させるために送信する。

  • input loop : exit が入力されるまで無限にコマンドの入力を受け付け、コマンドに応じた動作を実行する。

  • kernel cmd : 入力されたコマンドをカーネルモジュール側で出力を伴わずに実行させるために送信する。

input loop を除き、すべてのコマンド伝達方式は、送信後にクライアントが終了する。input loop においてコマンドに応じて実行される動作は以下の表の通りである。

| 명령어 | 행위 |
| --- | --- |
| shell | exit을 입력 받을 때까지 shell 명령어를 입력 받아 백도어에서 실행하도록 전송한다. |
| trans | CMD_TRANSFER 명령어를 보내 현재 연결된 백도어를 프록시로 사용하여 새로운 백도어에 연결한다. |
| upload | 입력받은 경로의 단일 파일을 백도어에 업로드한다. |
| download | 입력받은 경로의 단일 파일을 백도어에서 다운로드한다. |
| back | 현재 백도어 연결을 종료하고 프록시로 사용하던 백도어에 접속한다. |
| exit | 프로세스를 종료한다. |
| proxy_trans | CMD_PROXY_TRANSFER 명령어를 보내 현재 연결된 백도어를 프록시로 사용하여 새로운 백도어에 연결한다. |
| socks_trans | CMD_SOCKS_PROXY_TRANSFER 명령어를 보내 현재 연결된 백도어를 SOCKS5 프록시로 사용하여 새로운 백도어에 연결한다. |
| old_trans | trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| old_proxy_trans | proxy_trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| old_socks_trans | socks_trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| sh | 입력받은 명령어를 백도어에서 실행하도록 전송한다. |
| dsh | 입력받은 명령어를 백도어에서 새로운 프로세스를 생성하여 실행하도록 전송한다. (백그라운드 실행) |
| cookie | HTTP 통신에 사용하는 쿠키 값을 설정하거나 현재 쿠키값을 출력한다. |
| host | 현재 연결된 백도어 정보를 출력한다

| 명령어 | 행위 |
| --- | --- |
| shell | exit을 입력 받을 때까지 shell 명령어를 입력 받아 백도어에서 실행하도록 전송한다. |
| trans | CMD_TRANSFER 명령어를 보내 현재 연결된 백도어를 프록시로 사용하여 새로운 백도어에 연결한다. |
| upload | 입력받은 경로의 단일 파일을 백도어에 업로드한다. |
| download | 입력받은 경로의 단일 파일을 백도어에서 다운로드한다. |
| back | 현재 백도어 연결을 종료하고 프록시로 사용하던 백도어에 접속한다. |
| exit | 프로세스를 종료한다. |
| proxy_trans | CMD_PROXY_TRANSFER 명령어를 보내 현재 연결된 백도어를 프록시로 사용하여 새로운 백도어에 연결한다. |
| socks_trans | CMD_SOCKS_PROXY_TRANSFER 명령어를 보내 현재 연결된 백도어를 SOCKS5 프록시로 사용하여 새로운 백도어에 연결한다. |
| old_trans | trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| old_proxy_trans | proxy_trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| old_socks_trans | socks_trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| sh | 입력받은 명령어를 백도어에서 실행하도록 전송한다. |
| dsh | 입력받은 명령어를 백도어에서 새로운 프로세스를 생성하여 실행하도록 전송한다. (백그라운드 실행) |
| cookie | HTTP 통신에 사용하는 쿠키 값을 설정하거나 현재 쿠키값을 출력한다. |
| host | 현재 연결된 백도어 정보를 출력한다

| 명령어 | 행위 |
| --- | --- |
| shell | exit을 입력 받을 때까지 shell 명령어를 입력 받아 백도어에서 실행하도록 전송한다. |
| trans | CMD_TRANSFER 명령어를 보내 현재 연결된 백도어를 프록시로 사용하여 새로운 백도어에 연결한다. |
| upload | 입력받은 경로의 단일 파일을 백도어에 업로드한다. |
| download | 입력받은 경로의 단일 파일을 백도어에서 다운로드한다. |
| back | 현재 백도어 연결을 종료하고 프록시로 사용하던 백도어에 접속한다. |
| exit | 프로세스를 종료한다. |
| proxy_trans | CMD_PROXY_TRANSFER 명령어를 보내 현재 연결된 백도어를 프록시로 사용하여 새로운 백도어에 연결한다. |
| socks_trans | CMD_SOCKS_PROXY_TRANSFER 명령어를 보내 현재 연결된 백도어를 SOCKS5 프록시로 사용하여 새로운 백도어에 연결한다. |
| old_trans | trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| old_proxy_trans | proxy_trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| old_socks_trans | socks_trans 명령어와 같은 행위를 수행하지만 통신 프로토콜을 설정하지 않는다. |
| sh | 입력받은 명령어를 백도어에서 실행하도록 전송한다. |
| dsh | 입력받은 명령어를 백도어에서 새로운 프로세스를 생성하여 실행하도록 전송한다. (백그라운드 실행) |
| cookie | HTTP 통신에 사용하는 쿠키 값을 설정하거나 현재 쿠키값을 출력한다. |
| host | 현재 연결된 백도어 정보를 출력한다

caption - バックドアクライアントコマンドの動作

すべての通信は xor キー(1101link)で暗号化して行われ、common.h ファイルで定義された GENERAL_MODULE, GENERAL_PROTOCOL の値をパスワードとともに sha512 でハッシュ化し、常にメッセージの先頭に付与して送信する。バックドアとの通信は、オプションに応じて以下のプロトコルのいずれかに設定される。

  • TCP

  • HTTP

  • OLD HTTP - OLD HTTPは通常のHTTPとは異なり、クッキー値を設定しない。

1.2. tomcat20250414_rootkit_linux234

1.2.1. Backdoor

work/mnt/hgfs/Desktop/tomcat20250414_rootkit_linux234/tomcat20250414_rootkit_linux2345/workディレクトリ内に、2022年バージョンと同一体系の2025年バージョンのバックドアソースコードが存在する。2022年バージョンよりアップグレードされており、callback遅延通信時にパスワードを確認し時間を指定する機能や、ファイルのダウンロード・アップロード速度を制限する機能などが追加されている。すべてのオプションは以下の表の通りである。

| Long Option | Short Option | 행위 |
| --- | --- | --- |
| module | m | callback 모드 지정 |
| protocol | p | 프로토콜 지정 |
| port | P | 포트 지정 |
| HOST | H | callback 프록시로 사용할 주소 지정 |
| ft | f | callback 딜레이 시간 지정 |
| tt | t | 딜레이를 제외한 callback 딜레이 시간 지정 |
| LL | L | 로그파일 경로 지정

| Long Option | Short Option | 행위 |
| --- | --- | --- |
| module | m | callback 모드 지정 |
| protocol | p | 프로토콜 지정 |
| port | P | 포트 지정 |
| HOST | H | callback 프록시로 사용할 주소 지정 |
| ft | f | callback 딜레이 시간 지정 |
| tt | t | 딜레이를 제외한 callback 딜레이 시간 지정 |
| LL | L | 로그파일 경로 지정

| Long Option | Short Option | 행위 |
| --- | --- | --- |
| module | m | callback 모드 지정 |
| protocol | p | 프로토콜 지정 |
| port | P | 포트 지정 |
| HOST | H | callback 프록시로 사용할 주소 지정 |
| ft | f | callback 딜레이 시간 지정 |
| tt | t | 딜레이를 제외한 callback 딜레이 시간 지정 |
| LL | L | 로그파일 경로 지정

caption - 引数オプション

パスワードのほかに、マスターパスワードが追加されている。指定のポートでソケットサーバーを生成し、クライアント接続時にマスターパスワードと通信設定情報を sha512 でハッシュ化する。マスターパスワードには、common.h に定義された !@nf4@#fndskgadnsewngaldfkl を使用する。ハッシュ値はグローバル変数に保存され、ssl通信時に通信設定情報が正しいか検証するが、実際には使用されない。
その後、パスワードをマスターパスワード検証ロジックと同様に通信設定情報とともに sha512 ハッシュ化し、一致するか確認する。

encrypt.c passcheck_check 함수

caption - encrypt.c passcheck_check関数

パスワードは Miu2jACgXeDsxd であり、パスワードを含む設定情報は config.sh ファイルで任意の値に変更すると、ビルド時に適用される。

config.sh 기본 설정 정보

caption - config.sh 基本設定情報

通信に使用されるプロトコルの一覧は2022年版バックドアと同一である。

  • HTTP

  • HTTPS

  • SSL

  • TCP

  • SMTP

データの暗号化・復号に使用される xor キーも同様だが、5つの新しい xor キーと暗号化・復号関数が追加された。

encrypt.c에서 확인한 전체 xor key

caption - encrypt.c から確認されたすべての xor キー

2022年バックドアと比較して複数の動作が追加され、パケット条件が変更されたことに伴い TRANSFER が付随するコマンドコードの動作が変更された。新しく追加されたコマンドコードは以下の通りである。

  • CMD_NEW_UPLOAD

  • CMD_NEW_DOWNLOAD

  • CMD_LISTEN_PROXY_TRANS

  • CMD_TRANSFER

  • CMD_PROXY_TRANSFER

  • CMD_SOCKS_PROXY_TRANSFER

  • CMD_SOCKS_PROXY

  • CMD_NEW_SINGLE_CMD

コマンドコードごとの動作は以下の表の通りである。

| 명령코드 이름 | 명령코드 | 행위 | 응답코드 이름 | 응답코드 |
| --- | --- | --- | --- | --- |
| CMD_LOGIN | 0x11 | 응답코드를 클라이언트로 전송한다. | CMD_LOGIN_YES | 0x12 |
| CMD_FILE_UP | 0x15 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_UP_YES | 0x16 |
| CMD_FILE_DOWN | 0x18 | 지정된 파일을 클라이언트로 전송한다. | CMD_DOWN_YES | 0x19 |
| CMD_NEW_UPLOAD | 0xb3 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_NEW_UPLOAD_YES | 0xb4 |
| CMD_NEW_DOWNLOAD | 0xb1 | 지정된 파일을 지정된 옵션(전송 속도, 분할 개수 ) 맞춰 클라이언트로 전송한다. | CMD_NEW_DOWNLOAD_YES | 0xb2 |
| CMD_SHELL | 0x1b | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_SHELL_YES | 0x1c |
| CMD_LISTEN_PROXY_TRANS | 0x3f | callback 프록시 서버로서 행위를 수행한다. | CMD_LISTEN_PROXY_TRANS_YES | 0x40 |
| CMD_LISTEN_TRANS | 0x3d | callback 서버로서 행위를 수행한다. | CMD_LISTEN_TRANS_YES | 0x3e |
| CMD_TRANSFER | 0x1f | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드를 전송하여 백도어를 실행시키도록 하고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_TRANSFER_YES | 0x20 |
| CMD_PROXY_TRANSFER | 0x21 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드와 통신 설정 정보를 sha512 해시한 값을 보내고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. 이때 패킷의 9바이트를 "0000002C061E00000020"으로 채운다 | CMD_PROXY_TRANSFER_YES | 0x22 |
| CMD_SOCKS_PROXY_TRANSFER | 0x23 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드와 통신 설정 정보를 sha512 해시한 값을 보내고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 SOCKS5 프록시 서버로서 동작한다. 이때 패킷의 9바이트를 "0000002C061E00000020"으로 채운다 | CMD_SOCKS_PROXY_TRANSFER_YES | 0x24 |
| CMD_BACK | 0x13 | 응답코드를 클라이언트로 전송한다. | CMD_BACK_YES | 0x14 |
| CMD_SOCKS_PROXY | 0x29 | 클라이언트를 프록시로 사용한다. | CMD_SOCKS_PROXY_YES | 0x30 |
| CMD_SINGLE_CMD | 0x25 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. 이후 백도어 프로세스를 종료한다. | CMD_SINGLE_CMD_YES | 0x26 |
| CMD_IN_SINGLE_CMD | 0x27 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_IN_SINGLE_CMD_YES | 0x28 |
| CMD_D_SINGLE_CMD | 0x2b | 주어진 명령어를 독립된 프로세스로 실행한다. | CMD_D_SINGLE_CMD_YES | 0x2c |
| CMD_D_OUT_SINGLE_CMD | 0x2d | 주어진 명령어를 독립된 프로세스로 실행한다. 이후 백도어 프로세스를 종료한다. | CMD_D_OUT_SINGLE_CMD_YES | 0x2e |
| CMD_NEW_SINGLE_CMD | 0x2f | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_NEW_SINGLE_CMD_YES | 0x30

| 명령코드 이름 | 명령코드 | 행위 | 응답코드 이름 | 응답코드 |
| --- | --- | --- | --- | --- |
| CMD_LOGIN | 0x11 | 응답코드를 클라이언트로 전송한다. | CMD_LOGIN_YES | 0x12 |
| CMD_FILE_UP | 0x15 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_UP_YES | 0x16 |
| CMD_FILE_DOWN | 0x18 | 지정된 파일을 클라이언트로 전송한다. | CMD_DOWN_YES | 0x19 |
| CMD_NEW_UPLOAD | 0xb3 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_NEW_UPLOAD_YES | 0xb4 |
| CMD_NEW_DOWNLOAD | 0xb1 | 지정된 파일을 지정된 옵션(전송 속도, 분할 개수 ) 맞춰 클라이언트로 전송한다. | CMD_NEW_DOWNLOAD_YES | 0xb2 |
| CMD_SHELL | 0x1b | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_SHELL_YES | 0x1c |
| CMD_LISTEN_PROXY_TRANS | 0x3f | callback 프록시 서버로서 행위를 수행한다. | CMD_LISTEN_PROXY_TRANS_YES | 0x40 |
| CMD_LISTEN_TRANS | 0x3d | callback 서버로서 행위를 수행한다. | CMD_LISTEN_TRANS_YES | 0x3e |
| CMD_TRANSFER | 0x1f | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드를 전송하여 백도어를 실행시키도록 하고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_TRANSFER_YES | 0x20 |
| CMD_PROXY_TRANSFER | 0x21 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드와 통신 설정 정보를 sha512 해시한 값을 보내고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. 이때 패킷의 9바이트를 "0000002C061E00000020"으로 채운다 | CMD_PROXY_TRANSFER_YES | 0x22 |
| CMD_SOCKS_PROXY_TRANSFER | 0x23 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드와 통신 설정 정보를 sha512 해시한 값을 보내고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 SOCKS5 프록시 서버로서 동작한다. 이때 패킷의 9바이트를 "0000002C061E00000020"으로 채운다 | CMD_SOCKS_PROXY_TRANSFER_YES | 0x24 |
| CMD_BACK | 0x13 | 응답코드를 클라이언트로 전송한다. | CMD_BACK_YES | 0x14 |
| CMD_SOCKS_PROXY | 0x29 | 클라이언트를 프록시로 사용한다. | CMD_SOCKS_PROXY_YES | 0x30 |
| CMD_SINGLE_CMD | 0x25 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. 이후 백도어 프로세스를 종료한다. | CMD_SINGLE_CMD_YES | 0x26 |
| CMD_IN_SINGLE_CMD | 0x27 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_IN_SINGLE_CMD_YES | 0x28 |
| CMD_D_SINGLE_CMD | 0x2b | 주어진 명령어를 독립된 프로세스로 실행한다. | CMD_D_SINGLE_CMD_YES | 0x2c |
| CMD_D_OUT_SINGLE_CMD | 0x2d | 주어진 명령어를 독립된 프로세스로 실행한다. 이후 백도어 프로세스를 종료한다. | CMD_D_OUT_SINGLE_CMD_YES | 0x2e |
| CMD_NEW_SINGLE_CMD | 0x2f | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_NEW_SINGLE_CMD_YES | 0x30

| 명령코드 이름 | 명령코드 | 행위 | 응답코드 이름 | 응답코드 |
| --- | --- | --- | --- | --- |
| CMD_LOGIN | 0x11 | 응답코드를 클라이언트로 전송한다. | CMD_LOGIN_YES | 0x12 |
| CMD_FILE_UP | 0x15 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_UP_YES | 0x16 |
| CMD_FILE_DOWN | 0x18 | 지정된 파일을 클라이언트로 전송한다. | CMD_DOWN_YES | 0x19 |
| CMD_NEW_UPLOAD | 0xb3 | 클라이언트에서 전송하는 파일을 저장한다. | CMD_NEW_UPLOAD_YES | 0xb4 |
| CMD_NEW_DOWNLOAD | 0xb1 | 지정된 파일을 지정된 옵션(전송 속도, 분할 개수 ) 맞춰 클라이언트로 전송한다. | CMD_NEW_DOWNLOAD_YES | 0xb2 |
| CMD_SHELL | 0x1b | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_SHELL_YES | 0x1c |
| CMD_LISTEN_PROXY_TRANS | 0x3f | callback 프록시 서버로서 행위를 수행한다. | CMD_LISTEN_PROXY_TRANS_YES | 0x40 |
| CMD_LISTEN_TRANS | 0x3d | callback 서버로서 행위를 수행한다. | CMD_LISTEN_TRANS_YES | 0x3e |
| CMD_TRANSFER | 0x1f | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드를 전송하여 백도어를 실행시키도록 하고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. | CMD_TRANSFER_YES | 0x20 |
| CMD_PROXY_TRANSFER | 0x21 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드와 통신 설정 정보를 sha512 해시한 값을 보내고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 프록시 서버로서 동작한다. 이때 패킷의 9바이트를 "0000002C061E00000020"으로 채운다 | CMD_PROXY_TRANSFER_YES | 0x22 |
| CMD_SOCKS_PROXY_TRANSFER | 0x23 | 클라이언트에게 접속 정보를 전달 받은 새로운 백도어에 패스워드와 통신 설정 정보를 sha512 해시한 값을 보내고 이후 클라이언트에서 전송하는 패킷을 새로운 백도어로 전송하는 SOCKS5 프록시 서버로서 동작한다. 이때 패킷의 9바이트를 "0000002C061E00000020"으로 채운다 | CMD_SOCKS_PROXY_TRANSFER_YES | 0x24 |
| CMD_BACK | 0x13 | 응답코드를 클라이언트로 전송한다. | CMD_BACK_YES | 0x14 |
| CMD_SOCKS_PROXY | 0x29 | 클라이언트를 프록시로 사용한다. | CMD_SOCKS_PROXY_YES | 0x30 |
| CMD_SINGLE_CMD | 0x25 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. 이후 백도어 프로세스를 종료한다. | CMD_SINGLE_CMD_YES | 0x26 |
| CMD_IN_SINGLE_CMD | 0x27 | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_IN_SINGLE_CMD_YES | 0x28 |
| CMD_D_SINGLE_CMD | 0x2b | 주어진 명령어를 독립된 프로세스로 실행한다. | CMD_D_SINGLE_CMD_YES | 0x2c |
| CMD_D_OUT_SINGLE_CMD | 0x2d | 주어진 명령어를 독립된 프로세스로 실행한다. 이후 백도어 프로세스를 종료한다. | CMD_D_OUT_SINGLE_CMD_YES | 0x2e |
| CMD_NEW_SINGLE_CMD | 0x2f | 주어진 명령어를 실행한 결과를 클라이언트로 전송한다. | CMD_NEW_SINGLE_CMD_YES | 0x30

caption - バックドアコマンドコードの動作

1.2.2. syslogk rootkit

work/mnt/hgfs/Desktop/tomcat20250414_rootkit_linux234/tomcat20250414_rootkit_linux2345/main.cファイルは、2022年バージョンと同様に、Netfilterのフックポイントにコールバック関数を登録する。変更点は関数のフック方法、モジュールの隠蔽手法、そして新たに追加されたNetfilterフックポイントである。

2022年版のルートキットでは関数のフックに udis86 ライブラリを使用していたのに対し、2025年版では khook ライブラリを使用している。

좌)2022 루트킷 udis86 라이브러리, 우) 2025 루트킷 khook 라이브러리

caption - 左) 2022ルートキット udis86ライブラリ、右) 2025ルートキット khookライブラリ

フック対象の関数が3つだった2022年ルートキットとは異なり、計5つの関数をフックする。フックに関する情報は以下の表の通りである。

| 후킹 대상 함수 | 후킹 함수 | 행위 |
| --- | --- | --- |
| tcp4_seq_show | khook_tcp4_seq_show | 열린 포트 은닉 |
| proc_filldir | khook_proc_filldir | 프로세스 은닉 |
| filldir | khook_filldir | 디렉토리 은닉 |
| filldir64 | khook_filldir64 | 디렉토리 은닉 |
| proc_root_readdir | khook_proc_root_readdir | 디렉토리 은닉

| 후킹 대상 함수 | 후킹 함수 | 행위 |
| --- | --- | --- |
| tcp4_seq_show | khook_tcp4_seq_show | 열린 포트 은닉 |
| proc_filldir | khook_proc_filldir | 프로세스 은닉 |
| filldir | khook_filldir | 디렉토리 은닉 |
| filldir64 | khook_filldir64 | 디렉토리 은닉 |
| proc_root_readdir | khook_proc_root_readdir | 디렉토리 은닉

| 후킹 대상 함수 | 후킹 함수 | 행위 |
| --- | --- | --- |
| tcp4_seq_show | khook_tcp4_seq_show | 열린 포트 은닉 |
| proc_filldir | khook_proc_filldir | 프로세스 은닉 |
| filldir | khook_filldir | 디렉토리 은닉 |
| filldir64 | khook_filldir64 | 디렉토리 은닉 |
| proc_root_readdir | khook_proc_root_readdir | 디렉토리 은닉

caption - フック情報

Netfilterフックは、従来の NF_INET_LOCAL_IN、NF_INET_LOCAL_OUT の2つのフックポイントからさらに2つ追加された。

  • NF_INET_LOCAL_IN : ローカルホストにパケットを送信する前

  • NF_INET_LOCAL_OUT : ローカルホストから外部にパケットを送信する前

  • NF_INET_PRE_ROUTING : パケットがネットワークスタックに入った直後

  • NF_INET_POST_ROUTING : ルーティング発生後、ネットワークに送り出される直前

hkcap.c 후킹 함수 등록 로직

caption - hkcap.c フック関数登録ロジック

NF_INET_PRE_ROUTING 地点のコールバック関数は、大きく分けて2つの動作を行う。

1つ目は、2022年ルートキットの NF_INET_LOCAL_IN 地点と同様に、パケットの条件を検査してバックドアを実行することである。パケット条件が複雑だった2022年ルートキットとは異なり、NF_INET_PRE_ROUTING 地点のコールバック関数は TCP + SYN パケットを対象とし、id および seq 値が id_list、seq_list 内に存在するか確認する。

条件を満たしたパケットの window 値をマジックパケットの値と比較し、その値に応じた偽装プロトコルを設定してバックドアを実行する。マジックパケットの値に対応する偽装プロトコルは以下の表の通りである。

| 매직 패킷  | 위장 프로토콜 |
| --- | --- |
| 1022 | TCP |
| 2025 | HTTP |
| 29201 | HTTPS |
| 1130 | SSL |
| 2101 | SMTP

| 매직 패킷  | 위장 프로토콜 |
| --- | --- |
| 1022 | TCP |
| 2025 | HTTP |
| 29201 | HTTPS |
| 1130 | SSL |
| 2101 | SMTP

| 매직 패킷  | 위장 프로토콜 |
| --- | --- |
| 1022 | TCP |
| 2025 | HTTP |
| 29201 | HTTPS |
| 1130 | SSL |
| 2101 | SMTP

caption - 偽装プロトコル一覧

バックドアを実行する方法にも大きな違いがある。/bin/sh -c を使用していた2022年ルートキットとは異なり、call_usermodehelper 関数を用いてバックドアを実行する。このとき、ポート番号は 3000 〜 8000 の範囲からランダムに決定される。

hkcap.c kernel_run 함수

caption - hkcap.c kernel_run関数

2つ目は、バックドアと通信中のIPから送信されたパケットの dest(宛先)を、上記で生成されたランダムポートに変更することである。これにより、攻撃者はランダムに生成されたバックドアのポートを知らなくても通信が可能となる。

caption - パケットの dest の値を変更する hkcap.c コード

NF_INET_LOCAL_IN 地点のコールバック関数は、NF_IP_PRE_ROUTING で変更されたパケットの dest を、実際のバックドアが使用するポートに変更する。これは2段階に分けてポートを変更することにより、実際にバックドアが稼働しているポートの特定を妨害するための設計と見られる。

패킷의 dest 값을 백도어가 사용하는 포트로 변경하는 hkcap.c 코드

caption - パケットの dest の値をバックドアが使用するポートに変更する hkcap.c コード

NF_INET_LOCAL_OUT 地点のコールバック関数は、バックドアが送信したパケットの source 値がバックドアの listen ポートの場合、これを任意のポートに変更し、そうではない場合は実際に外部に送信するポートに書き換える。

NF_INET_POST_ROUTING 地点のコールバック関数は、source 値が任意のポートに変更されたパケットの source 値を、実際に外部へ送出するポートに書き換える。

caption - パケットの dest の値を攻撃者のポートに変更する hkcap.c コード

ルートキット自体の隠蔽に関しては、モジュールリストから自身を削除する処理に加え、sysfsからも削除する関数が追加されている。

모듈 은닉 hkmod.c 코드

caption - モジュール隠蔽を行う hkmod.c コード

sysfs関連の関数は、KoviD ルートキットの kv_hide_mod 関数をそのまま流用している。

KoviD 루트킷의 kv_hide_mod 함수

caption - KoviD ルートキットの kv_hide_mod 関数

隠蔽されたルートキットに特定の文字列を書き込むと再びモジュールが確認できるようになる点は同様だが、入力された文字列に応じて、Netfilterフック側で差し替えるポートの値を指定する機能が追加されている。

포트 값을 지정하는 hkcap.c 코드

caption - ポートの値を指定する hkcap.c コード

ルートキットにデータを書き込む際、モジュールを表示させるための特定の文字列を除くすべての文字列は、AES-CBC-256で復号した上でチェックを行う。AESキーおよびivは以下の通りである。

  • AESキー: d03deb92153a71458973aef3857d75b27e552cc63e6158a8339811873994de47

  • iv: efa3c987532cc0bdac533845ad8df5ea

ルートキットに書き込まれる文字列に応じた動作は以下の通りである。

| 문자열 이름 | 문자열 | 행위 |
| --- | --- | --- |
| ModuleDecodeKey | mrdyZwIh2Bh | 루트킷 은닉 해제 |
| PROC_CMD_TRANSREG | h9WJZ1 | 현재 백도어와 통신 중인 공격자 정보의 ip, 포트와 백도어의 포트를 문자열 이후에 주어진 값으로 변경한다. |
| PROC_CMD_TRANSCLR | 64OWE2 | 백도어 포트가 문자열 이후에 주어진 값과 같은 공격자 정보를 현재 연결된 통신 정보 리스트에서 삭제한다. |
| PROC_CMD_CONNSET | C8lnS3 | 백도어 포트가 문자열 이후에 주어진 값과 같은 공격자 정보를 주어진 ip, 포트 값으로 변경한다

| 문자열 이름 | 문자열 | 행위 |
| --- | --- | --- |
| ModuleDecodeKey | mrdyZwIh2Bh | 루트킷 은닉 해제 |
| PROC_CMD_TRANSREG | h9WJZ1 | 현재 백도어와 통신 중인 공격자 정보의 ip, 포트와 백도어의 포트를 문자열 이후에 주어진 값으로 변경한다. |
| PROC_CMD_TRANSCLR | 64OWE2 | 백도어 포트가 문자열 이후에 주어진 값과 같은 공격자 정보를 현재 연결된 통신 정보 리스트에서 삭제한다. |
| PROC_CMD_CONNSET | C8lnS3 | 백도어 포트가 문자열 이후에 주어진 값과 같은 공격자 정보를 주어진 ip, 포트 값으로 변경한다

| 문자열 이름 | 문자열 | 행위 |
| --- | --- | --- |
| ModuleDecodeKey | mrdyZwIh2Bh | 루트킷 은닉 해제 |
| PROC_CMD_TRANSREG | h9WJZ1 | 현재 백도어와 통신 중인 공격자 정보의 ip, 포트와 백도어의 포트를 문자열 이후에 주어진 값으로 변경한다. |
| PROC_CMD_TRANSCLR | 64OWE2 | 백도어 포트가 문자열 이후에 주어진 값과 같은 공격자 정보를 현재 연결된 통신 정보 리스트에서 삭제한다. |
| PROC_CMD_CONNSET | C8lnS3 | 백도어 포트가 문자열 이후에 주어진 값과 같은 공격자 정보를 주어진 ip, 포트 값으로 변경한다

caption - 文字列および動作

1.2.3. Backdoor Client

work/mnt/hgfs/Desktop/tomcat20250414_rootkit_linux234/tomcat20250414_rootkit_linux2345/work/tcat.cファイルは、バックドアクライアントのソースコードである。2022年版バックドアクライアントと比較すると、kernel cmd 機能が廃止された以外に大きな変化はなく、オプションも kc オプションが廃止され LLL オプションが追加された。すべてのオプションについては以下の表を参照されたい。

| Long Option | Short Option | 의미 |
| --- | --- | --- |
| HOST | H | 백도어 주소 |
| PORT | P | 백도어 포트 |
| password | p | 백도어 접속 시에 사용할 패스워드 (기본: ssecuremw) |
| callback | c | callback 모드 활성화 |
| single_command | s | 주어진 명령어를 실행하도록 전송 |
| daemon_command | d | 주어진 명령어를 새로운 프로세스에서 실행 하도록 전송 (백그라운드 실행) |
| proxy | x | 프록시 서버 사용 |
| socks_proxy | y | socks5 프록시 서버 사용 |
| socks_aim_hostname | i | 사용할 socks5 프록시 서버의 주소 |
| socks_aim_port | o | 사용할 socks5 프록시 서버의 포트 |
| socks_username | u | socks5 프록시 서버에 로그인할 유저이름 |
| socks_password | a | socks5 프록시 서버에 로그인할 패스워드 |
| knock_protocol | k | 백도어 초기 접속 시도에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSL, 3: HTTPS, 4: SMTP) |
| ethernet_interface | e | 사용할 이더넷 인터페이스 |
| cookie | 5 | HTTP 혹은 HTTPS를 사용할 때의 쿠키  |
| host | 6 | HTTP 혹은 HTTPS를 사용할 때의 selfdefined host  |
| main_protocol | m | 백도어 통신에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSL, 3: HTTPS, 4: SMTP) |
| LLL | L | 로그파일 경로

| Long Option | Short Option | 의미 |
| --- | --- | --- |
| HOST | H | 백도어 주소 |
| PORT | P | 백도어 포트 |
| password | p | 백도어 접속 시에 사용할 패스워드 (기본: ssecuremw) |
| callback | c | callback 모드 활성화 |
| single_command | s | 주어진 명령어를 실행하도록 전송 |
| daemon_command | d | 주어진 명령어를 새로운 프로세스에서 실행 하도록 전송 (백그라운드 실행) |
| proxy | x | 프록시 서버 사용 |
| socks_proxy | y | socks5 프록시 서버 사용 |
| socks_aim_hostname | i | 사용할 socks5 프록시 서버의 주소 |
| socks_aim_port | o | 사용할 socks5 프록시 서버의 포트 |
| socks_username | u | socks5 프록시 서버에 로그인할 유저이름 |
| socks_password | a | socks5 프록시 서버에 로그인할 패스워드 |
| knock_protocol | k | 백도어 초기 접속 시도에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSL, 3: HTTPS, 4: SMTP) |
| ethernet_interface | e | 사용할 이더넷 인터페이스 |
| cookie | 5 | HTTP 혹은 HTTPS를 사용할 때의 쿠키  |
| host | 6 | HTTP 혹은 HTTPS를 사용할 때의 selfdefined host  |
| main_protocol | m | 백도어 통신에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSL, 3: HTTPS, 4: SMTP) |
| LLL | L | 로그파일 경로

| Long Option | Short Option | 의미 |
| --- | --- | --- |
| HOST | H | 백도어 주소 |
| PORT | P | 백도어 포트 |
| password | p | 백도어 접속 시에 사용할 패스워드 (기본: ssecuremw) |
| callback | c | callback 모드 활성화 |
| single_command | s | 주어진 명령어를 실행하도록 전송 |
| daemon_command | d | 주어진 명령어를 새로운 프로세스에서 실행 하도록 전송 (백그라운드 실행) |
| proxy | x | 프록시 서버 사용 |
| socks_proxy | y | socks5 프록시 서버 사용 |
| socks_aim_hostname | i | 사용할 socks5 프록시 서버의 주소 |
| socks_aim_port | o | 사용할 socks5 프록시 서버의 포트 |
| socks_username | u | socks5 프록시 서버에 로그인할 유저이름 |
| socks_password | a | socks5 프록시 서버에 로그인할 패스워드 |
| knock_protocol | k | 백도어 초기 접속 시도에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSL, 3: HTTPS, 4: SMTP) |
| ethernet_interface | e | 사용할 이더넷 인터페이스 |
| cookie | 5 | HTTP 혹은 HTTPS를 사용할 때의 쿠키  |
| host | 6 | HTTP 혹은 HTTPS를 사용할 때의 selfdefined host  |
| main_protocol | m | 백도어 통신에 사용할 프로토콜 (0: TCP, 1: HTTP, 2: SSL, 3: HTTPS, 4: SMTP) |
| LLL | L | 로그파일 경로

caption - 引数オプション

前述の通り kernel cmd 機能が削除されたため、バックドアにコマンドを伝達する方式は以下の3つが存在する。

  • single cmd : 入力されたコマンドをバックドアで実行させるために送信する。

  • single daemon cmd : 入力されたコマンドをバックドアで新しいプロセスを生成して実行させるために送信する。

  • input loop : exit が入力されるまで無限にコマンドの入力を受け付け、コマンドに応じた異なる動作を実行する。

2022年のバックドアクライアントは単にコマンドのみを入力されて通信していたが、2025年のバックドアクライアントは各コマンドごとに個別のオプションを指定できるようになり、コマンド自体の数は減少したものの、実行可能な処理はより高度化・精緻化している。input loop においてコマンドに応じて実行される動作は以下の表の通りである。

| 명령어 | 행위 |
| --- | --- |
| shell | shell 명령어를 입력 받아 백도어에서 실행하도록 전송한다. |
| inc | 현재 연결된 모든 백도어의 연결 정보를 출력한다. |
| trans | 현재 연결된 백도어를 프록시 서버로 사용하여 새로운 백도어에 연결한다. |
| socks5 | 현재 연결된 백도어를 socks5 프록시 서버로 사용하여 새로운 백도어에 연결한다. |
| upload | 입력받은 경로의 단일 파일을 백도어에 업로드한다. |
| download | 입력받은 경로의 단일 파일을 백도어에서 다운로드한다. |
| nup | 입력받은 경로의 파일을 지정된 옵션에 맞춰 백도어에 업로드한다. |
| ndown | 입력받은 경로의 파일을 지정된 옵션 백도어에서 다운로드한다. |
| back | 현재 백도어 연결을 종료하고 프록시로 사용하던 백도어에 접속한다. |
| exit | 프로세스를 종료한다. |
| cookie | 쿠키 값을 설정하거나 현재 쿠키값을 출력한다. |
| host | 현재 연결된 백도어 정보를 출력한다

| 명령어 | 행위 |
| --- | --- |
| shell | shell 명령어를 입력 받아 백도어에서 실행하도록 전송한다. |
| inc | 현재 연결된 모든 백도어의 연결 정보를 출력한다. |
| trans | 현재 연결된 백도어를 프록시 서버로 사용하여 새로운 백도어에 연결한다. |
| socks5 | 현재 연결된 백도어를 socks5 프록시 서버로 사용하여 새로운 백도어에 연결한다. |
| upload | 입력받은 경로의 단일 파일을 백도어에 업로드한다. |
| download | 입력받은 경로의 단일 파일을 백도어에서 다운로드한다. |
| nup | 입력받은 경로의 파일을 지정된 옵션에 맞춰 백도어에 업로드한다. |
| ndown | 입력받은 경로의 파일을 지정된 옵션 백도어에서 다운로드한다. |
| back | 현재 백도어 연결을 종료하고 프록시로 사용하던 백도어에 접속한다. |
| exit | 프로세스를 종료한다. |
| cookie | 쿠키 값을 설정하거나 현재 쿠키값을 출력한다. |
| host | 현재 연결된 백도어 정보를 출력한다

| 명령어 | 행위 |
| --- | --- |
| shell | shell 명령어를 입력 받아 백도어에서 실행하도록 전송한다. |
| inc | 현재 연결된 모든 백도어의 연결 정보를 출력한다. |
| trans | 현재 연결된 백도어를 프록시 서버로 사용하여 새로운 백도어에 연결한다. |
| socks5 | 현재 연결된 백도어를 socks5 프록시 서버로 사용하여 새로운 백도어에 연결한다. |
| upload | 입력받은 경로의 단일 파일을 백도어에 업로드한다. |
| download | 입력받은 경로의 단일 파일을 백도어에서 다운로드한다. |
| nup | 입력받은 경로의 파일을 지정된 옵션에 맞춰 백도어에 업로드한다. |
| ndown | 입력받은 경로의 파일을 지정된 옵션 백도어에서 다운로드한다. |
| back | 현재 백도어 연결을 종료하고 프록시로 사용하던 백도어에 접속한다. |
| exit | 프로세스를 종료한다. |
| cookie | 쿠키 값을 설정하거나 현재 쿠키값을 출력한다. |
| host | 현재 연결된 백도어 정보를 출력한다

caption - バックドアクライアントコマンドの動作

現時点で tcat_new_send_file、tcat_new_recv_file のように、追加的に実装された主要なコマンド関数が定義されていないため、追加された機能の詳細な分析は行えなかった。

通信の都度パスワードの sha512 ハッシュ値を送信していた2022年のバックドアクライアントとは異なり、2025年のバックドアクライアントは最初の接続時のみパスワードの sha512 ハッシュ値を送信する。

バックドアとのすべての通信は、2022年版クライアントと同様に xor キー(1101link)で暗号化されて行われる。ただし、3つのプロトコルを指定可能であった2022年版とは異なり、2025年版クライアントでは以下の4つのプロトコルのうちいずれかに設定される。特に OLD HTTP が廃止され、HTTPS、SMTPが新しく追加された。

  • TCP

  • HTTP

  • HTTPS

  • SMTP

1.3. Cobalt Strike

1.3.1. Cobalt Strike Loader

Rustで作成されたローダーマルウェアであり、内蔵されたシェルコードを復号してメモリ上で実行する。Cobalt Strike Loaderのファイルパスは以下の通りである。

  • work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe

  • work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe

  • work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz

  • work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe

  • work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe

  • work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe

  • work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe

Windows APIを呼び出す際、djb2 ハッシュ値を算出することで動的なローディングを行い、シェルコードは AES-CBC-256 で復号する。その後、復号されたシェルコードをメモリ上で実行する。

Window API 로딩 함수

caption - Window API ローディング関数

すべての Cobalt Strike Loader で使用されている AESキーおよびivの一覧は以下の表の通りである。

| 파일 경로 | AES key | iv |
| --- | --- | --- |
| work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe | 0e9cae6dc34ae064b71604c221bca933f1ffcb63df4af7e6806fa94849d66924 | 33081eba056e1ad1c8e5618829b84030 |
| work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe | b5976580954f47b49a33a5c580610aeb77e8c2ef66e0ab337ddfb2d1851e7998 | 545644526a2a08dff2dd4e3a812be126 |
| work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz | ae49597d8c87d5cf23f485fa0fc138d06c13aa743405e3c9c44b3097ba008dcf | a61080d5ee883489ee8ce40f84569fc3 |
| work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe | 6f627869fe1f05add75dc039fce970fce9bb4531d5601f894877aab3bc9dfe89 | 5fb9604da0d50bce37e53c194ab074b0 |
| work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe | 62609005b9ace1387a1065e6a72b7f796e12fd7c3b34b30a94af5d9112287b3c | 5990b37f9e73a20c2d5605cba72399cc |
| work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe | 0e9cae6dc34ae064b71604c221bca933f1ffcb63df4af7e6806fa94849d66924 | 33081eba056e1ad1c8e5618829b84030 |
| work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe | b5976580954f47b49a33a5c580610aeb77e8c2ef66e0ab337ddfb2d1851e7998 | 545644526a2a08dff2dd4e3a812be126

| 파일 경로 | AES key | iv |
| --- | --- | --- |
| work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe | 0e9cae6dc34ae064b71604c221bca933f1ffcb63df4af7e6806fa94849d66924 | 33081eba056e1ad1c8e5618829b84030 |
| work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe | b5976580954f47b49a33a5c580610aeb77e8c2ef66e0ab337ddfb2d1851e7998 | 545644526a2a08dff2dd4e3a812be126 |
| work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz | ae49597d8c87d5cf23f485fa0fc138d06c13aa743405e3c9c44b3097ba008dcf | a61080d5ee883489ee8ce40f84569fc3 |
| work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe | 6f627869fe1f05add75dc039fce970fce9bb4531d5601f894877aab3bc9dfe89 | 5fb9604da0d50bce37e53c194ab074b0 |
| work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe | 62609005b9ace1387a1065e6a72b7f796e12fd7c3b34b30a94af5d9112287b3c | 5990b37f9e73a20c2d5605cba72399cc |
| work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe | 0e9cae6dc34ae064b71604c221bca933f1ffcb63df4af7e6806fa94849d66924 | 33081eba056e1ad1c8e5618829b84030 |
| work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe | b5976580954f47b49a33a5c580610aeb77e8c2ef66e0ab337ddfb2d1851e7998 | 545644526a2a08dff2dd4e3a812be126

| 파일 경로 | AES key | iv |
| --- | --- | --- |
| work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe | 0e9cae6dc34ae064b71604c221bca933f1ffcb63df4af7e6806fa94849d66924 | 33081eba056e1ad1c8e5618829b84030 |
| work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe | b5976580954f47b49a33a5c580610aeb77e8c2ef66e0ab337ddfb2d1851e7998 | 545644526a2a08dff2dd4e3a812be126 |
| work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz | ae49597d8c87d5cf23f485fa0fc138d06c13aa743405e3c9c44b3097ba008dcf | a61080d5ee883489ee8ce40f84569fc3 |
| work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe | 6f627869fe1f05add75dc039fce970fce9bb4531d5601f894877aab3bc9dfe89 | 5fb9604da0d50bce37e53c194ab074b0 |
| work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe | 62609005b9ace1387a1065e6a72b7f796e12fd7c3b34b30a94af5d9112287b3c | 5990b37f9e73a20c2d5605cba72399cc |
| work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe | 0e9cae6dc34ae064b71604c221bca933f1ffcb63df4af7e6806fa94849d66924 | 33081eba056e1ad1c8e5618829b84030 |
| work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe | b5976580954f47b49a33a5c580610aeb77e8c2ef66e0ab337ddfb2d1851e7998 | 545644526a2a08dff2dd4e3a812be126

caption - Cobalt Strike Loader AESキー、iv一覧

1.3.2. Shellcode

Cobalt Strike Loader によって実行されるシェルコードであり、Cobalt Strike Beaconを復号してメモリ上で実行する。ファイル形式で存在するシェルコードの実態パスは以下の通りである。

  • work/home/user/.cache/vmware/drag_and_drop/5wdgDr/payload.bin

  • work/home/user/.cache/vmware/drag_and_drop/6bX9mm/Black.x64.exe

  • work/home/user/.cache/vmware/drag_and_drop/NDBu65/payload.bin

  • work/home/user/.cache/vmware/drag_and_drop/yf91yD/payload.bin

  • work/home/user/.cache/vmware/drag_and_drop/zlLWeR/payload.bin

work/home/user/.cache/vmware/drag_and_drop/6bX9mm/Black.x64.exe は、他のシェルコードファイルと同様の動作を行うが、シェルコード単体ではなくコンパイル済みの実行形式ファイル(EXE)である。

Black.x64.exe DIE 결과

caption - Black.x64.exe DIE 解析結果

Cobalt Strike Loaderと同様に、djb2 ハッシュ値を算出して Windows APIを動的にロードした上で呼び出し、その後 Cobalt Strike Beaconを RC4 で復号してメモリ上で実行する。

RC4 ksa 함수

caption - RC4 ksa 関数

すべてのシェルコードで使用される RC4 キーは以下の表の通りである。ファイル形式として直接存在はしないが、Cobalt Strike Loader によってインメモリ展開・起動されるシェルコードもこれに含む。

| 파일 경로 | RC4 key |
| --- | --- |
| work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe | QGWYqgYFSXgkEvmll |
| work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe | bVeuvyKwejaykgTg |
| work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz | YPJngQShuxKNEcPw |
| work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe | QGWYqgYFSXgkEvmll |
| work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe | bVeuvyKwejaykgTg |
| work/home/user/.cache/vmware/drag_and_drop/5wdgDr/payload.bin | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/6bX9mm/Black.x64.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/NDBu65/payload.bin | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/yf91yD/payload.bin | bVeuvyKwejaykgTg |
| work/home/user/.cache/vmware/drag_and_drop/zlLWeR/payload.bin | xYHuKSOLEkSzrxVq

| 파일 경로 | RC4 key |
| --- | --- |
| work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe | QGWYqgYFSXgkEvmll |
| work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe | bVeuvyKwejaykgTg |
| work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz | YPJngQShuxKNEcPw |
| work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe | QGWYqgYFSXgkEvmll |
| work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe | bVeuvyKwejaykgTg |
| work/home/user/.cache/vmware/drag_and_drop/5wdgDr/payload.bin | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/6bX9mm/Black.x64.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/NDBu65/payload.bin | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/yf91yD/payload.bin | bVeuvyKwejaykgTg |
| work/home/user/.cache/vmware/drag_and_drop/zlLWeR/payload.bin | xYHuKSOLEkSzrxVq

| 파일 경로 | RC4 key |
| --- | --- |
| work/mnt/hgfs/Desktop/New folder (2)/DboRrmSS.exe | QGWYqgYFSXgkEvmll |
| work/mnt/hgfs/Desktop/New folder (2)/m01QzOfI.exe | bVeuvyKwejaykgTg |
| work/mnt/hgfs/Desktop/New folder (2)/voS9AyMZ.tar.gz | YPJngQShuxKNEcPw |
| work/home/user/.cache/vmware/drag_and_drop/0pkbW4/3Powwovv.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/gWMDML/GnAN3FhY.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/QqiN9h/DboRrmSS.exe | QGWYqgYFSXgkEvmll |
| work/home/user/.cache/vmware/drag_and_drop/rM0FG0/m01QzOfI.exe | bVeuvyKwejaykgTg |
| work/home/user/.cache/vmware/drag_and_drop/5wdgDr/payload.bin | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/6bX9mm/Black.x64.exe | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/NDBu65/payload.bin | xYHuKSOLEkSzrxVq |
| work/home/user/.cache/vmware/drag_and_drop/yf91yD/payload.bin | bVeuvyKwejaykgTg |
| work/home/user/.cache/vmware/drag_and_drop/zlLWeR/payload.bin | xYHuKSOLEkSzrxVq

caption - シェルコード RC4キー一覧

シェルコードによって起動される Cobalt Strike Beacon には、ETW関連の関数や AMSI.dllの APIをバイパス・パッチすることで、Windowsのセキュリティ機能を回避するロジックが存在する。しかし、暗号化された設定(Config)情報が不完全であったため、具体的な設定内容の特定には至らなかった。

1.3.3. Cobalt Strike Beacon

work/mnt/hgfs/Desktop/111/beaconディレクトリ内から Cobalt Strike Beaconのソースコードが確認された。Beaconの起動時、内包された設定(Config)情報は1バイトの xor キー(46)で復号される。復号された構成情報は複数のブロックで構成されており、各ブロックは以下のようなデータ構造を持つ。

struct Block {
  ushort index;
  ushort data_type;
  ushort data_size;
  union { // data
    typedef unsigned short s; // short
    typedef unsigned int i;  // int
    char d[]; // string
  } value

struct Block {
  ushort index;
  ushort data_type;
  ushort data_size;
  union { // data
    typedef unsigned short s; // short
    typedef unsigned int i;  // int
    char d[]; // string
  } value

struct Block {
  ushort index;
  ushort data_type;
  ushort data_size;
  union { // data
    typedef unsigned short s; // short
    typedef unsigned int i;  // int
    char d[]; // string
  } value

復号された設定情報はパースされてグローバル変数に格納され、必要な値が存在する際にインデックスを介してアクセス・利用される。CobaltStrikeParser を用いてパースした一部の設定情報は以下の通りである。

CobaltStrikeParser 결과

caption - CobaltStrikeParser パース結果

復号された構成情報には C&Cサーバーのアドレスとエンドポイントが含まれているが、実行フェーズにおいてこの値は直接使用されず、ソースコード上に直接記述されたハードコード値(IPやドメイン)が優先して使用される。

comm.cpp send_Metadata 함수

caption - comm.cpp send_Metadata 関数

設定情報の復号が完了すると、C&Cサーバーに転送するためのメタデータを構築する。主要なメタデータの情報は以下の表の通りである。

| 메타데이터 변수 이름 | 설명 |
| --- | --- |
| beacon_key | 무작위 16바이트(세션 ) |
| codepage | 65001 |
| oem | 65001 |
| beacon_id | 무작위 4바이트(에이전트 식별 id) |
| MajorVersion | MajorVersion(uname 결과) |
| dwBuildNumber | dwBuildNumber(uname 결과) |
| build | build(uname 결과) |
| machine | x86_64인 경우 2, 기본 0 |
| hostinfo | 아이피 주소 |
| ComputerName | 컴퓨터 이름 |
| UserName | 유저 이름 |
| ProcessName | 프로세스 이름

| 메타데이터 변수 이름 | 설명 |
| --- | --- |
| beacon_key | 무작위 16바이트(세션 ) |
| codepage | 65001 |
| oem | 65001 |
| beacon_id | 무작위 4바이트(에이전트 식별 id) |
| MajorVersion | MajorVersion(uname 결과) |
| dwBuildNumber | dwBuildNumber(uname 결과) |
| build | build(uname 결과) |
| machine | x86_64인 경우 2, 기본 0 |
| hostinfo | 아이피 주소 |
| ComputerName | 컴퓨터 이름 |
| UserName | 유저 이름 |
| ProcessName | 프로세스 이름

| 메타데이터 변수 이름 | 설명 |
| --- | --- |
| beacon_key | 무작위 16바이트(세션 ) |
| codepage | 65001 |
| oem | 65001 |
| beacon_id | 무작위 4바이트(에이전트 식별 id) |
| MajorVersion | MajorVersion(uname 결과) |
| dwBuildNumber | dwBuildNumber(uname 결과) |
| build | build(uname 결과) |
| machine | x86_64인 경우 2, 기본 0 |
| hostinfo | 아이피 주소 |
| ComputerName | 컴퓨터 이름 |
| UserName | 유저 이름 |
| ProcessName | 프로세스 이름

caption - メタデータ情報

メタデータの作成が完了すると C&Cサーバーへ送信し、その後5秒間隔でHTTPベースのポーリング通信を実施する。この際、命令コードの受信には GET、実行結果等の送信には POST メソッドが用いられる。受信したコマンドIDに応じて処理を実行し、その結果をC&Cサーバーへ返信する。全動作の一覧は以下の表の通りである。

| id | 함수 이름 | 상태 | 행위 |
| --- | --- | --- | --- |
| 5 | cd | 동작 | 작업 디렉토리를 주어진 경로로 변경한다 |
| 10 | upload | 비활성 | 파일 업로드 관련 코드가 주석 처리되어 있다. |
| 11 | download | 비활성 | 파일 다운로드 관련 코드가 주석 처리되어 있다. |
| 12 | execute | 미구현 | BeaconExecuteCommand 함수가 정의되지 않았고 관련 코드는 주석 처리되어 있다. |
| 14 | proxy_connect | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다. |
| 15 | proxy_write | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다. |
| 16 | proxy_close | 비활성 | 전역변수 gBeaconRportfwd의 state를 0으로 변경하는 코드가 주석 처리되어 있다. |
| 17 | proxy_listen | 미구현 | 정의되지 않은 함수를 사용하는 코드가 주석 처리 되어있다. |
| 39 | pwd | 동작 | 현재 작업 경로를 출력한다. |
| 53 | ls | 동작 | 주어진 경로의 파일 목록을 출력한다. |
| 100 | bof | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다

| id | 함수 이름 | 상태 | 행위 |
| --- | --- | --- | --- |
| 5 | cd | 동작 | 작업 디렉토리를 주어진 경로로 변경한다 |
| 10 | upload | 비활성 | 파일 업로드 관련 코드가 주석 처리되어 있다. |
| 11 | download | 비활성 | 파일 다운로드 관련 코드가 주석 처리되어 있다. |
| 12 | execute | 미구현 | BeaconExecuteCommand 함수가 정의되지 않았고 관련 코드는 주석 처리되어 있다. |
| 14 | proxy_connect | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다. |
| 15 | proxy_write | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다. |
| 16 | proxy_close | 비활성 | 전역변수 gBeaconRportfwd의 state를 0으로 변경하는 코드가 주석 처리되어 있다. |
| 17 | proxy_listen | 미구현 | 정의되지 않은 함수를 사용하는 코드가 주석 처리 되어있다. |
| 39 | pwd | 동작 | 현재 작업 경로를 출력한다. |
| 53 | ls | 동작 | 주어진 경로의 파일 목록을 출력한다. |
| 100 | bof | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다

| id | 함수 이름 | 상태 | 행위 |
| --- | --- | --- | --- |
| 5 | cd | 동작 | 작업 디렉토리를 주어진 경로로 변경한다 |
| 10 | upload | 비활성 | 파일 업로드 관련 코드가 주석 처리되어 있다. |
| 11 | download | 비활성 | 파일 다운로드 관련 코드가 주석 처리되어 있다. |
| 12 | execute | 미구현 | BeaconExecuteCommand 함수가 정의되지 않았고 관련 코드는 주석 처리되어 있다. |
| 14 | proxy_connect | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다. |
| 15 | proxy_write | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다. |
| 16 | proxy_close | 비활성 | 전역변수 gBeaconRportfwd의 state를 0으로 변경하는 코드가 주석 처리되어 있다. |
| 17 | proxy_listen | 미구현 | 정의되지 않은 함수를 사용하는 코드가 주석 처리 되어있다. |
| 39 | pwd | 동작 | 현재 작업 경로를 출력한다. |
| 53 | ls | 동작 | 주어진 경로의 파일 목록을 출력한다. |
| 100 | bof | 미구현 | 정의되지 않은 함수를 호출하는 코드가 주석 처리되어 있다

caption - Cobalt Strike Beacon の動作

全体の半分以上の機能が未実装または無効化されており、当該 Cobalt Strike Beacon は開発途中の作成中段階のものであったと推測される。

1.3.4. C# Loader

work/home/user/Desktop/0128ディレクトリ内には、他プロセスに対して Cobalt Strike Beaconをインジェクション(注入)する C#製ローダー(ok.dll)と、そのソースコード(ok.cs)、さらには同ローダーを読み込み実行するスクリップト(ok.sct)などが確認された。
関数名や引数に「test」という文字列が多用されていることから、テスト目的で検証用として使用されていたファイルと推測される。

ok.hta

hxxp://192.168[.]123.200/ok.sctにアクセスしてスクリプトをダウンロードし、Fuk関数を実行する。

ok.hta 파일 내용

caption - ok.hta ファイル内容

ダウンロード対象のスクリプトは、同一パス上に存在する ok.sct と想定される。

ok.sct

シリアル化(シリアライズ)されたC#オブジェクトをデシリアライズした上で、Work関数をコールする。このとき実行されるDLLは、ok.csをビルド・コンパイルした ok.dllである。ok.csファイル内でWork関数を確認することが可能である。

C# Loader 역직렬화 코드

caption - C# Loader デシリアライズコード

C#製ローダーは、conhost.exe プロセスに対して Cobalt Strike Beaconをインジェクションして起動する。Cobalt Strike Beaconは、zlib圧縮された上でBase64エンコードされており、静的文字列として保持されている。

Cobalt Strike Beacon 디코딩 함수

caption - Cobalt Strike Beacon デコード関数

CobaltStrikeParser を使用して調査した、C#ローダー内蔵の Cobalt Strike Beaconの構成定義パラメータの一部は以下の通りである。

C# Loader에 저장된 Cobalt Strike Beacon 설정 정보

caption - C# Loaderに保存された Cobalt Strike Beacon 設定(Config)情報

1.4. Ivanti Connect Secure

1.4.1. BRUSHFIRE

work/mnt/hgfs/Desktop/ivanti-new-exp-20241220.zipファイルを展開すると、Ivanti Connect SecureのRCE(遠隔コード実行)脆弱性を悪用してバックドアを展開・感染させる exp*.pyスクリプトファイルを確認できる。

Pythonスクリプト側で clientCapabilitiesパラメータの長さを256バイトを超えるようにセットする。これは、同箇所で発生するバッファオーバーフローを突いてリモートコード実行を引き起こす脆弱性「CVE-2025-0282(256バイトのバッファオーバーフロー)」を標的にしたものと判断される。

caption - ex*.py ロジック

「CVE-2025-0282」は、過去に中国主導のサイバー攻撃活動グループとして知られるUNC5221が初期侵入経路(ベクター)として悪用したことで知られる脆弱性である。

一連の悪用を想定した exp*.pyファイル群は、基本挙動に関する動きはすべて同様であり、ハッキング(エクスプロイト)時に指定する関数のオフセットアドレスや、アクセス先のサービスエンドポイントの文字列にのみ差分がある。コマンドライン実行時の引数に準じた exp*.pyの動きは以下の表の通りとなる。

| long option | short option | 행위 |
| --- | --- | --- |
| ip | i | vpn 서버 ip |
| port | p | vpn 서버 포트 |
| leak |  | 메모리 정보를 leak하여 config에 저장한다. |
| detect_version |  | 취약점이 존재하는 버전의 vpn인지 확인한다. |
| install |  | 백도어를 설치한다. |
| check |  | config에 저장된 정보가 올바른지 확인한다. |
| check_path |  | 취약점이 정상적으로 동작하는지 확인한다. |
| delay |  | timeout 시간을 지정한다. |
| check_backdoor |  | 백도어가 설치되어있는지 확인한다. |
| cmd |  | 주어진 명령어를 root권한으로 실행하도록 한다. |
| python |  | 주어진 파일을 per-static으로 실행하도록 한다. |
| perl |  | 주어진 파일을 per-static으로 실행하도록 한다. |
| lcmd |  | 주어진 명령어를 실행하도록 한다. |
| version |  | /home/ssl-vpn-VERSION 파일의 내용을 받아 출력한다. |
| config |  | dsls -B -R -S / 명령어의 실행 결과를 출력한다. |
| local_path |  | 파일을 업로드 업로드할 로컬 파일 경로 |
| upload |  | 파일을 업로드한다. |
| download |  | 파일을 다운로드한다. |
| clear_log |  | 해당 프로그램을 사용하며 남은 로그를 삭제한다. |
| clean |  | 해당 프로그램을 사용하며 남은 모든 흔적을 삭제한다. |
| verbose | v | 사용되지 않는다

| long option | short option | 행위 |
| --- | --- | --- |
| ip | i | vpn 서버 ip |
| port | p | vpn 서버 포트 |
| leak |  | 메모리 정보를 leak하여 config에 저장한다. |
| detect_version |  | 취약점이 존재하는 버전의 vpn인지 확인한다. |
| install |  | 백도어를 설치한다. |
| check |  | config에 저장된 정보가 올바른지 확인한다. |
| check_path |  | 취약점이 정상적으로 동작하는지 확인한다. |
| delay |  | timeout 시간을 지정한다. |
| check_backdoor |  | 백도어가 설치되어있는지 확인한다. |
| cmd |  | 주어진 명령어를 root권한으로 실행하도록 한다. |
| python |  | 주어진 파일을 per-static으로 실행하도록 한다. |
| perl |  | 주어진 파일을 per-static으로 실행하도록 한다. |
| lcmd |  | 주어진 명령어를 실행하도록 한다. |
| version |  | /home/ssl-vpn-VERSION 파일의 내용을 받아 출력한다. |
| config |  | dsls -B -R -S / 명령어의 실행 결과를 출력한다. |
| local_path |  | 파일을 업로드 업로드할 로컬 파일 경로 |
| upload |  | 파일을 업로드한다. |
| download |  | 파일을 다운로드한다. |
| clear_log |  | 해당 프로그램을 사용하며 남은 로그를 삭제한다. |
| clean |  | 해당 프로그램을 사용하며 남은 모든 흔적을 삭제한다. |
| verbose | v | 사용되지 않는다

| long option | short option | 행위 |
| --- | --- | --- |
| ip | i | vpn 서버 ip |
| port | p | vpn 서버 포트 |
| leak |  | 메모리 정보를 leak하여 config에 저장한다. |
| detect_version |  | 취약점이 존재하는 버전의 vpn인지 확인한다. |
| install |  | 백도어를 설치한다. |
| check |  | config에 저장된 정보가 올바른지 확인한다. |
| check_path |  | 취약점이 정상적으로 동작하는지 확인한다. |
| delay |  | timeout 시간을 지정한다. |
| check_backdoor |  | 백도어가 설치되어있는지 확인한다. |
| cmd |  | 주어진 명령어를 root권한으로 실행하도록 한다. |
| python |  | 주어진 파일을 per-static으로 실행하도록 한다. |
| perl |  | 주어진 파일을 per-static으로 실행하도록 한다. |
| lcmd |  | 주어진 명령어를 실행하도록 한다. |
| version |  | /home/ssl-vpn-VERSION 파일의 내용을 받아 출력한다. |
| config |  | dsls -B -R -S / 명령어의 실행 결과를 출력한다. |
| local_path |  | 파일을 업로드 업로드할 로컬 파일 경로 |
| upload |  | 파일을 업로드한다. |
| download |  | 파일을 다운로드한다. |
| clear_log |  | 해당 프로그램을 사용하며 남은 로그를 삭제한다. |
| clean |  | 해당 프로그램을 사용하며 남은 모든 흔적을 삭제한다. |
| verbose | v | 사용되지 않는다

caption - 引数オプション

実装されたバックドアは plugins/ssl_readファイルであり、RCEによって稼働された plugins/installスクリプトが、バックドア自体を「ssl_read」関数に割り込み(フック)させる。フックされた ssl_read関数は、SSL通信受信時に特定のマジックバイト(事前設定値)が挿入されたパケットを発見すると、exp*.py側からランダムに自動生成された4バイトのキーとxor(排他的論理和)を行い、ペイロードから直接シェルコードを取り出して稼働させる。

caption - ssl_read シェルコード実行ロジック

exp*.pyスクリプトは、設定したプロセス引数に応じて動的にシェルコードを構成し、標的サーバーへとアップロードする。この際、あらかじめシェルコード側に仕込まれた固定パターン(ダミーコード)をプログラム上で置換処理(replace等)を施し、ターゲットにとって正常に稼働するコードへと修正・加工する。pluginsディレクトリ下に組み込まれた、シェルコードアップロード用 exp*.pyのコマンド指定パターンと実行ステップは以下の通りである。

| 실행 인자 | 쉘코드 이름 | 쉘코드 행위 |
| --- | --- | --- |
| install | install | ssl_read 함수 주소를 ssl_read 쉘코드로 덮어쓴다. |
| install | leak_info | inject 쉘코드를 실행하기 위해 함수 메모리 주소를 전송한다. |
| install | inject | ssl_read 쉘코드를 leak된 메모리의 ssl_read에 덮어쓴다. |
| install | patch | inject에서 ssl_read를 메모리에 인젝션할 사용되는 strncpy 함수이다. |
| version, download | read_file_asm | 지정된 파일의 데이터를 읽어 4바이트 키로 암호화하여 전송한다. |
| python, perl, upload | write_file_asm | 지정된 경로에 암호화된 파일을 4바이트 키로 복호화하여 저장한다. |
| upload, lcmd, cmd, config | cmd_asm | 지정된 명령어를 실행하고 4바이트 키로 암호화하여 결과를 전송한다

| 실행 인자 | 쉘코드 이름 | 쉘코드 행위 |
| --- | --- | --- |
| install | install | ssl_read 함수 주소를 ssl_read 쉘코드로 덮어쓴다. |
| install | leak_info | inject 쉘코드를 실행하기 위해 함수 메모리 주소를 전송한다. |
| install | inject | ssl_read 쉘코드를 leak된 메모리의 ssl_read에 덮어쓴다. |
| install | patch | inject에서 ssl_read를 메모리에 인젝션할 사용되는 strncpy 함수이다. |
| version, download | read_file_asm | 지정된 파일의 데이터를 읽어 4바이트 키로 암호화하여 전송한다. |
| python, perl, upload | write_file_asm | 지정된 경로에 암호화된 파일을 4바이트 키로 복호화하여 저장한다. |
| upload, lcmd, cmd, config | cmd_asm | 지정된 명령어를 실행하고 4바이트 키로 암호화하여 결과를 전송한다

| 실행 인자 | 쉘코드 이름 | 쉘코드 행위 |
| --- | --- | --- |
| install | install | ssl_read 함수 주소를 ssl_read 쉘코드로 덮어쓴다. |
| install | leak_info | inject 쉘코드를 실행하기 위해 함수 메모리 주소를 전송한다. |
| install | inject | ssl_read 쉘코드를 leak된 메모리의 ssl_read에 덮어쓴다. |
| install | patch | inject에서 ssl_read를 메모리에 인젝션할 사용되는 strncpy 함수이다. |
| version, download | read_file_asm | 지정된 파일의 데이터를 읽어 4바이트 키로 암호화하여 전송한다. |
| python, perl, upload | write_file_asm | 지정된 경로에 암호화된 파일을 4바이트 키로 복호화하여 저장한다. |
| upload, lcmd, cmd, config | cmd_asm | 지정된 명령어를 실행하고 4바이트 키로 암호화하여 결과를 전송한다

caption - 実行引数およびシェルコードの挙動

plugins/ssl_readに仕込まれたバッグドアの役割・実装方式は、過去のUNC5221によるキャンペーンレポートで公表されているマルウェア「BRUSHFIRE」と同一のものであり、当該攻撃で観測された「BRUSHFIRE」の感染配置パスおよび「ssl_read」にフックを設定する配置の構成パスが完全に合致している。

1.4.2. SPAWN Family Client

work/mnt/hgfs/Desktop/New folder/203.234.192.200_client.zipファイル内には、トンネリング接続スクリプト、SSHクライアントツール、暗号認証用などの証明書ファイルがアーカイブされている。同内包ファイルの readme.txtには、client.py および controller.pyを使用し、特定IPアドレス 203.234[.]192.200にSSH通信にて接続へ至る手順・詳細が記載されている。この接続先IPは、韓国のハンギョレ新聞社のIPアドレスであることが特定されている。

readme.txt 파일 내용

caption - readme.txt ファイル内容

client.pyは、ネットワークのプロキシ制御・迂回トンネル接続経路として機能する「SOCKS5プロキシー」を起動する役割があり、指定オプションごとの用途は以下の通りである。

| 옵션 | 기능 | 기본값 |
| --- | --- | --- |
| -h | 로컬 바인드 주소 | 127.0.0.1 |
| -p | 로컬 바인드 포트 | 1080 |
| -H | 접속할 프록시 주소 | raddr |
| -P | 접속할 프록시 포트 | 443 |
| -v | 디버깅 로그 활성화 |  |
| --ca | ca 인증서 경로 |  |
| --cert | client 인증서 경로 |  |
| --key | client 개인키 경로

| 옵션 | 기능 | 기본값 |
| --- | --- | --- |
| -h | 로컬 바인드 주소 | 127.0.0.1 |
| -p | 로컬 바인드 포트 | 1080 |
| -H | 접속할 프록시 주소 | raddr |
| -P | 접속할 프록시 포트 | 443 |
| -v | 디버깅 로그 활성화 |  |
| --ca | ca 인증서 경로 |  |
| --cert | client 인증서 경로 |  |
| --key | client 개인키 경로

| 옵션 | 기능 | 기본값 |
| --- | --- | --- |
| -h | 로컬 바인드 주소 | 127.0.0.1 |
| -p | 로컬 바인드 포트 | 1080 |
| -H | 접속할 프록시 주소 | raddr |
| -P | 접속할 프록시 포트 | 443 |
| -v | 디버깅 로그 활성화 |  |
| --ca | ca 인증서 경로 |  |
| --cert | client 인증서 경로 |  |
| --key | client 개인키 경로

caption - オプション別機能

client.pyには、TLSにおけるクライアントハッシュに相当する client_helloメッセージ、および暗号キー合意を行う client_key_exchangeメッセージの構文・構造テンプレートがあらかじめソース内に直接用意されている。通信時の挙動は以下の仕組みで動く。

  • client_hello

    • テンプレートの並び順で[11–15バイト目]の範囲に、[15–43バイト目](ランダム生成の28バイト)をハッシュしたCRC32算出の値を動的に埋め込んでC&Cへパケット送信を行う。

    • sessionid変数部分にランダム構成された32バイトデータを確保し、それを含んだメッセージを構築してパケット投下する。

  • client_key_exchange

    • 暗号強度の高い premaster部分に256バイトのランダムバッファデータ、ハンドシェイク検証のための enc_handshake_msgに32バイトのランダムデータを設定し、これをパケットペイロードに挿入して転送を実行する。

client.pyに定義されている、初期(デフォルト)設定時の client_helloパケットの構造パターンは、UNC5221グループの侵害で確認されている「SPAWNMOLE」特有のマジックパケット(署名)パターンと完全に同一である。また、パケット構成時に client_hello側の15〜43バイト目にランダムで28バイトを発生させ、その値を元にしたCRC32計算値を11〜15バイト目に埋めてシリアライズする動作ロジックは、JPCertによる技術解析トピックで紹介・情報共有されている「SPAWNCHIMERA」の接続シーケンスパターンの特色とも一致する。

「SPAWNCHIMERA」や「SPAWNMOLE」は、同じくUNC5221が実行するIvanti製品へのハッキングにおいて利用された特徴的なインメモリアクティビティーツールであり、先述した「CVE-2025-0282」脆弱性を経て感染プロセスを起動していた背景を持っている。

controller.pyは、client.pyが確立したSOCKS5トンネル回線(プロキシ)を経由して、相手側SSHサーバー(ホスト)に相互接続し一連のC&C操作・制御を実施するための中継補助スクリプトである。

ssh 서버에 접속하는 controller.py

caption - sshサーバーと通信する controller.py

SPAWNMOLEはSSHの通信先待ち受けとなる「SPAWNSNAIL」とペアで稼働が確認されており、SPAWNCHIMERA自体もそれ単体でSSHのデーモンを立ち上げるポートオープン機構を有している。これらの点から、controller.pyは「SPAWNSNAIL」または「SPAWNCHIMERA」によって提供されるSSH待ち受けへのアクセス専用クライアントとして利用されていたものと見なされる。

1.4.3. ROOTROT Client

work/mnt/hgfs/Desktop/ivanti_control/main.pyは、引数でパラメータとして渡されたOSコマンドをベースに、Perlコードのスクリプト断片(スニペット)を毎回動的に作成し、結果をBase64変換で記述変換、そのデータを特定のクッキーフィールドに埋めてHTTP GETリクエストを送り出す司令ツールである。引数設定を介して作成されるPerlスクリプトパターンは以下の手順:

  • download {file_path}

my $s = "";
my $path = "{file_path}";
local *FH;open(*FH, "<". $path);
while (<FH>) { $s.= "$_";}
close(*FH);
print  "<!-- ".MIME::Base64::encode_base64($s,"")."-->"

my $s = "";
my $path = "{file_path}";
local *FH;open(*FH, "<". $path);
while (<FH>) { $s.= "$_";}
close(*FH);
print  "<!-- ".MIME::Base64::encode_base64($s,"")."-->"

my $s = "";
my $path = "{file_path}";
local *FH;open(*FH, "<". $path);
while (<FH>) { $s.= "$_";}
close(*FH);
print  "<!-- ".MIME::Base64::encode_base64($s,"")."-->"

  • {command}

    my $l ="";
    my $r = "";
    my $fd = popen(*PIPE,"{command}",\\'r\\') ;
    while (<PIPE>) {$l.= $_;};
    $r.= MIME::Base64::encode_base64($l,"");
    my $s = "<!-- ". pack("C*", unpack("C*", $r));$s.= "-->";
    print $s
    
    
    my $l ="";
    my $r = "";
    my $fd = popen(*PIPE,"{command}",\\'r\\') ;
    while (<PIPE>) {$l.= $_;};
    $r.= MIME::Base64::encode_base64($l,"");
    my $s = "<!-- ". pack("C*", unpack("C*", $r));$s.= "-->";
    print $s
    
    
    my $l ="";
    my $r = "";
    my $fd = popen(*PIPE,"{command}",\\'r\\') ;
    while (<PIPE>) {$l.= $_;};
    $r.= MIME::Base64::encode_base64($l,"");
    my $s = "<!-- ". pack("C*", unpack("C*", $r));$s.= "-->";
    print $s
    
    

上記の各Perlスクリプト断片は、ターゲット上でのコマンド実行結果をBase64で包み、それをHTMLのコメントアウト(コメントブロック <!-- -->)に隠蔽して出力・表示するステップが構成されている。レスポンスを受けた main.pyは、サーバーからの返信コンテンツの終端部分にある上記HTMLコメント情報を検索抽出し、Base64をデコードして実行結果の出力を戻す仕組みとなっている。

요청을 보내고 주석의 내용을 디코딩하는 함수

caption - リクエストを送信し、コメントタグの内容をデコードする関数

仕組みがPerlで書かれたWebシェルである点、クッキー内のBase64を読み解き evalで動的プログラムを実行している点、実行ログをBase64で丸めてコメントブロックとして吐き出している点などから、この main.py は過去に UNC5221によるAPT活動で検知されたWebシェル「ROOTROT」を遠隔操作するための攻撃者側クライアントツールであると推定される。

また、「ROOTROT」についても、前述のUNC5221によって実行されるIvanti Connect Secureの侵害プロセスと同様に悪用が観測されている。

1.5. フィッシング攻撃

1.5.1. Naver中間者(MitM)攻撃

work/mnt/hgfs/Desktop/New folder/vps2/Cipherishingディレクトリ配下に確認されたモジュールは、主に韓国ユーザーのNaver(ネイバー)ポータルサイトのアカウント窃取を狙った中間者攻撃(Man-in-the-Middle)を企図する構造ファイルである。特にこの、work/mnt/hgfs/Desktop/New folder/readme.txt内には、中国簡体字で書かれたシステムセットアップの案内・手法がメモされている。

readme.txt 파일 내용

caption - readme.txt ファイル内容

readme.txt ファイル記載のテキストの翻訳内容は以下の通りである。




work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/cipherginx.pyプログラムは、攻撃用のC&Cサーバー等でデーモンプロセスとして常駐稼働し、Naver純正サービスとアクセス標的ユーザーの間で暗号通信を中継・解読する「HTTPSリバースプロキシ」のように機能する。ターゲットユーザーが、サーバーのアドレスを中継してNaverポータルなどへログイン・サービス利用を行うと、認証ヘッダー、ログインセッションCookie、およびユーザーのアカウント・パスワード情報がテキストで抽出・記録され cookiesディレクトリに保存(ドロップ)される。生成される各ファイルルールは以下の仕様通り:

  • [nid_id].headers

  • [nid_id].cookie

  • accounts.txt

cipherginx.py accounts.txt 생성 로직

caption - cipherginx.py による accounts.txt 生成ロジック

work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/naverconfig.py内には、Naverサービスで入力されるログイン資格情報を窃取・インターセプトするための悪意あるJavaScriptプログラムが埋め込まれており、対象が偽のログインページなどに遷移した際、正規サイトのスクリプトツリー構成をインジェクションして偽情報を割り込ませる仕組みとなっている。

naverconfig.py 계정 정보 탈취 자바스크립트 코드

caption - naverconfig.py アカウント情報窃取JavaScriptコード

このようにして窃取したログイン情報を利用して自動的にアクセスを行い、ターゲット側のNaver Webメール受送信箱、アドレス帳(連絡先)、プロフィールなどの個人情報をバックグラウンドでクローリング・抽出する各機能スクリプトが同一配下に保存されている。ファイル名と用途概要は以下のリストの通り:

| 파일 경로 | 설명 |
| --- | --- |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/download.py | 이메일 수집, 주소록 덤프, 로그인 기록 수집 기능의 파일 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/gen_eml.py | download.py를 통해 수집한 이메일을 html에서 eml 파일로 패키징 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/manuel_download.py | 인자로 받은 nid에 대한 모든 이메일 수집 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/models.py | 중복 수집을 방지하기 위해 수집한 계정 정보와 mailSN을 his.db에 저장

| 파일 경로 | 설명 |
| --- | --- |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/download.py | 이메일 수집, 주소록 덤프, 로그인 기록 수집 기능의 파일 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/gen_eml.py | download.py를 통해 수집한 이메일을 html에서 eml 파일로 패키징 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/manuel_download.py | 인자로 받은 nid에 대한 모든 이메일 수집 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/models.py | 중복 수집을 방지하기 위해 수집한 계정 정보와 mailSN을 his.db에 저장

| 파일 경로 | 설명 |
| --- | --- |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/download.py | 이메일 수집, 주소록 덤프, 로그인 기록 수집 기능의 파일 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/gen_eml.py | download.py를 통해 수집한 이메일을 html에서 eml 파일로 패키징 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/manuel_download.py | 인자로 받은 nid에 대한 모든 이메일 수집 |
| work/mnt/hgfs/Desktop/New folder/vps2/Cipherishing/downloader/models.py | 중복 수집을 방지하기 위해 수집한 계정 정보와 mailSN을 his.db에 저장

caption - 各ファイル説明

1.5.2. Kakao(カカオ)ログイン認証フィッシング攻撃

vps/var/www/html/templates/kakao.htmlファイルは、韓国ポータルのカカオ(Kakao)サービスの認証ログインを模倣したフィッシング詐欺用HTMLテンプレートであり、vps/var/www/html/kakao-login.phpプログラムはカカオログイン後のポスト処理などを担当するサーバーサイト用ファイルである。この kakao-login.phpは、GETアクセスを検知した際に「ft」という名称のCookieの有無を評価し、これが検知されたケースにおいては「https://mail.daum.net」の正規ログインページ側へと即座にリダイレクト処理(遷移)させる。

GET 요청 처리 코드

caption - GETアクセス処理ロジック

Cookieに「ft」が含まれない場合のみ、フィッシング用のカカオログイン画面の表示を送り出し、ターゲットが情報をタイプしてログインボタンを押下すると、そのデータがフィッシングサイトへのPOSTリクエストとなって送信される。

POSTを受信すると、被害者が送信したアカウント認証データを log/password_log.txtにクリアテキストで追加保存し、以降のリダイレクト自動移行のためにCookieの「ft」値を「no」に書き換える。

POST 요청 처리 코드

caption - POSTアクセス処理ロジック

password_log.txt内にはすでに14件のアカウント・パスワード蓄積データが格納されていたが、そのうち実際の活動と見られる有意な情報1件を除いた他のログ値は、攻撃者自身が動作デバッグの際にタイプしたものと見られるテストデータ(例: 123123, ttttt222, qwqwqw111 等)であった。

1.5.3. フィッシングメール攻撃

vps/var/www/htmlディレクトリ配下には、ターゲットへの標的型メール(ルアーメール)の構成・生成から、侵害ユーザーの管理を行うためのアタックインフラ用Webサーバー関連プログラムが保存されている。

これらアタックパネル機能を持つプログラム群は、システムへのアクセス状況をログ保存する仕組みを持ち、後述する generator.phpファイルを除いては、すべて以下の定型フォーマットで各ログを蓄積する:

========[영문  이름] [일자], [연도], [:] [am/pm]====================
[appendix (옵션)]
[client ip]
[Header-Name]: [Header-Value] <- with_headers=true일 
[parameters]

========[영문  이름] [일자], [연도], [:] [am/pm]====================
[appendix (옵션)]
[client ip]
[Header-Name]: [Header-Value] <- with_headers=true일 
[parameters]

========[영문  이름] [일자], [연도], [:] [am/pm]====================
[appendix (옵션)]
[client ip]
[Header-Name]: [Header-Value] <- with_headers=true일 
[parameters]

ログファイルは、作成タイミングの日付・テキストに基づき、自動的に拡張子(.txt)の後ろに日時フォーマットが付与される規則で生成される。

generator.php

vps/var/www/html/generator.phpは、フィッシングに用いる電子メールを容易に量産するためのメールジェネレータープログラムである。ユーザーの「HnoplYTfPX」Cookieおよび接続要求方式(メソッド)に準じて動作を切り替える構成。詳細は以下の仕様を参照:

| HnoplYTfPX 쿠키 존재 여부 | 요청 메소드 | 기능 |
| --- | --- | --- |
| X | POST | passwd 파라미터 존재 “hkjOPQIBBgU”와 비교 , 일치하면 HnoplYTfPX 쿠키 설정  |
| O | GET | 피싱 이메일을 생성할 있는 관리자 출력 |
| O | POST | 피싱 이메일 생성 정보 출력

| HnoplYTfPX 쿠키 존재 여부 | 요청 메소드 | 기능 |
| --- | --- | --- |
| X | POST | passwd 파라미터 존재 “hkjOPQIBBgU”와 비교 , 일치하면 HnoplYTfPX 쿠키 설정  |
| O | GET | 피싱 이메일을 생성할 있는 관리자 출력 |
| O | POST | 피싱 이메일 생성 정보 출력

| HnoplYTfPX 쿠키 존재 여부 | 요청 메소드 | 기능 |
| --- | --- | --- |
| X | POST | passwd 파라미터 존재 “hkjOPQIBBgU”와 비교 , 일치하면 HnoplYTfPX 쿠키 설정  |
| O | GET | 피싱 이메일을 생성할 있는 관리자 출력 |
| O | POST | 피싱 이메일 생성 정보 출력

caption - 実行ロジック

メールジェネレーター構成時の入力フォーム指定要素は以下のリストの通り:

| name | label | tag name | value |
| --- | --- | --- | --- |
| to_email_address | to email_address | input type="text" |  |
| onlysniff | only sniff | input type="checkbox" |  |
| phishing_url | Phishing URL | input type="text" | https://nid.navermails.com/nidlogin.login?mode=form&url= |
| redirect_url | Redirect URL | input type="text" | https://bigfile.mail.naver.com/bigfileupload/download?fid=9leqBrJ4Wrdjb4kOM6kmK3Y/aAu/KIYZKAurKxUwFxgXKxbmKCYwFA2dFAg9axvlHruqMq0opx2wazUlMovlFzK/K4MlKqMlp6JoKx0CMq04 |
| content | html content %img% %link_url% | textarea  |  |
| saveForm |  | input type="submit" | Submit

| name | label | tag name | value |
| --- | --- | --- | --- |
| to_email_address | to email_address | input type="text" |  |
| onlysniff | only sniff | input type="checkbox" |  |
| phishing_url | Phishing URL | input type="text" | https://nid.navermails.com/nidlogin.login?mode=form&url= |
| redirect_url | Redirect URL | input type="text" | https://bigfile.mail.naver.com/bigfileupload/download?fid=9leqBrJ4Wrdjb4kOM6kmK3Y/aAu/KIYZKAurKxUwFxgXKxbmKCYwFA2dFAg9axvlHruqMq0opx2wazUlMovlFzK/K4MlKqMlp6JoKx0CMq04 |
| content | html content %img% %link_url% | textarea  |  |
| saveForm |  | input type="submit" | Submit

| name | label | tag name | value |
| --- | --- | --- | --- |
| to_email_address | to email_address | input type="text" |  |
| onlysniff | only sniff | input type="checkbox" |  |
| phishing_url | Phishing URL | input type="text" | https://nid.navermails.com/nidlogin.login?mode=form&url= |
| redirect_url | Redirect URL | input type="text" | https://bigfile.mail.naver.com/bigfileupload/download?fid=9leqBrJ4Wrdjb4kOM6kmK3Y/aAu/KIYZKAurKxUwFxgXKxbmKCYwFA2dFAg9axvlHruqMq0opx2wazUlMovlFzK/K4MlKqMlp6JoKx0CMq04 |
| content | html content %img% %link_url% | textarea  |  |
| saveForm |  | input type="submit" | Submit

caption - フィッシングメール作成フォームの入力項目

フィッシングメールの構築が完了した画面で攻撃者パネルに出力・表示されるステータスは以下の通りである。

| h2 | 내용 |
| --- | --- |
| dot image url: | {base_url}/request.php?i={b64encode(to_email_address)}&dot.png를 가리키는 img 태그 |
| redict url: | {base_url}/bigDataDownload/{b64encode(to_email_address)} |
| link url: | {phishing_url}{urlencode(redirect_url)}&login_id={b64encode(to_email_address)}{"&f=yes" if onlysniff else ""} |
| content: | content에서 img를 dot image url의 내용, link_url를 link url의 내용으로 대입한 결과

| h2 | 내용 |
| --- | --- |
| dot image url: | {base_url}/request.php?i={b64encode(to_email_address)}&dot.png를 가리키는 img 태그 |
| redict url: | {base_url}/bigDataDownload/{b64encode(to_email_address)} |
| link url: | {phishing_url}{urlencode(redirect_url)}&login_id={b64encode(to_email_address)}{"&f=yes" if onlysniff else ""} |
| content: | content에서 img를 dot image url의 내용, link_url를 link url의 내용으로 대입한 결과

| h2 | 내용 |
| --- | --- |
| dot image url: | {base_url}/request.php?i={b64encode(to_email_address)}&dot.png를 가리키는 img 태그 |
| redict url: | {base_url}/bigDataDownload/{b64encode(to_email_address)} |
| link url: | {phishing_url}{urlencode(redirect_url)}&login_id={b64encode(to_email_address)}{"&f=yes" if onlysniff else ""} |
| content: | content에서 img를 dot image url의 내용, link_url를 link url의 내용으로 대입한 결과

caption - 出力情報

HTML文を含めた内容が contentにロードされると見られ、メールが自動出力されると log/generator_log.txtに対して以下の構造ルールで履歴が生成される:

--------[영문  이름] [일자], [년도], [:] [am/pm]----------------
[to_emailaddress]_[b64encode(to_email_address)]
[link url]
[client ip]

--------[영문  이름] [일자], [년도], [:] [am/pm]----------------
[to_emailaddress]_[b64encode(to_email_address)]
[link url]
[client ip]

--------[영문  이름] [일자], [년도], [:] [am/pm]----------------
[to_emailaddress]_[b64encode(to_email_address)]
[link url]
[client ip]

また、送り付けられたターゲット側がこの偽装メールを自分のメールソフト・メーラー等で開封した際、リモート画像の自動読み込み設定等がオンになっている環境(HTMLメールの展開時など)であれば、それを足がかりに request.phpがロードをトリガーする(トラッキングウェブバグの仕組み)。

request.php

vps/var/www/html/request.phpは、標的側のユーザーがフィッシングメールを「開封・閲覧したか否か」を攻撃管理パネル側にレポート蓄積するためのプログラムである。読み込みをトリガーした接続ログは、自動的に log/request_log.txtに格納される。

URI末尾に「.png」が格納されている場合、検証を欺くかのようにサイズのない透過色画像をロードし、そのほかの接続時には対象端末の詳細(環境・ブラウザの種類など)を収集抽出するように設計されたトラッキング用JavaScriptコードを自動で差し込んで実行する。この時に読み込まれるプログラムと収集される機微情報は以下の通りとなる:

| path | info |
| --- | --- |
| payload/adobe.js | Adobe Reader 버전 확인(플러그인/ActiveX 탐지) |
| payload/flash.js | Adobe Flash 버전 확인 |
| payload/java.js.bak | JRE 버전 확인 |
| payload/plugin.js | 브라우저 플러그인 목록·버전 열람(navigator.plugins) |
| payload/xecure.js | XecureWeb 설치 여부·버전 확인 |
| payload/ie/drive.js.bak | 드라이브 문자 열람(파일시스템/WMIC ) |
| payload/ie/office.js | Microsoft Office 버전 조회(COM/ActiveX, Word.Application) |
| payload/ie/pdf.js | Adobe Reader 확인(ActiveX AcroPDF.PDF) |
| payload/ie/xecure.js | XecureWeb ActiveX 버전 확인 |
| payload/ie/xmldom.js | 프로그램/구성 정보 수집(MSXML/COM/레지스트리 활용 추정) |
| payload/non-ie/webrtc.js.bak | WebRTC로 로컬·공인 IP 수집(RTCPeerConnection ICE 후보)

| path | info |
| --- | --- |
| payload/adobe.js | Adobe Reader 버전 확인(플러그인/ActiveX 탐지) |
| payload/flash.js | Adobe Flash 버전 확인 |
| payload/java.js.bak | JRE 버전 확인 |
| payload/plugin.js | 브라우저 플러그인 목록·버전 열람(navigator.plugins) |
| payload/xecure.js | XecureWeb 설치 여부·버전 확인 |
| payload/ie/drive.js.bak | 드라이브 문자 열람(파일시스템/WMIC ) |
| payload/ie/office.js | Microsoft Office 버전 조회(COM/ActiveX, Word.Application) |
| payload/ie/pdf.js | Adobe Reader 확인(ActiveX AcroPDF.PDF) |
| payload/ie/xecure.js | XecureWeb ActiveX 버전 확인 |
| payload/ie/xmldom.js | 프로그램/구성 정보 수집(MSXML/COM/레지스트리 활용 추정) |
| payload/non-ie/webrtc.js.bak | WebRTC로 로컬·공인 IP 수집(RTCPeerConnection ICE 후보)

| path | info |
| --- | --- |
| payload/adobe.js | Adobe Reader 버전 확인(플러그인/ActiveX 탐지) |
| payload/flash.js | Adobe Flash 버전 확인 |
| payload/java.js.bak | JRE 버전 확인 |
| payload/plugin.js | 브라우저 플러그인 목록·버전 열람(navigator.plugins) |
| payload/xecure.js | XecureWeb 설치 여부·버전 확인 |
| payload/ie/drive.js.bak | 드라이브 문자 열람(파일시스템/WMIC ) |
| payload/ie/office.js | Microsoft Office 버전 조회(COM/ActiveX, Word.Application) |
| payload/ie/pdf.js | Adobe Reader 확인(ActiveX AcroPDF.PDF) |
| payload/ie/xecure.js | XecureWeb ActiveX 버전 확인 |
| payload/ie/xmldom.js | 프로그램/구성 정보 수집(MSXML/COM/레지스트리 활용 추정) |
| payload/non-ie/webrtc.js.bak | WebRTC로 로컬·공인 IP 수집(RTCPeerConnection ICE 후보)

caption - JavaScriptファイルおよび収集情報

キャプチャ・抽出された環境変数は results変数として構築され、最終的にバックグラウンドリクエストの形式で response.php?i={b64encode(email)}&{results}に自動的にコールバック(送信)される。

response.php

vps/var/www/html/response.phpは、トラッキング通信を受けた際にターゲット端末の接続元IP、端末ブラウザ識別文字列(User-Agent)、言語システムロケールなどを検証・識別し、あらかじめ作成されたフィルタリングルールを満たす場合のみ、該当のパラメーター「i」のBase64エンコードデータをデコードした上で log/response_log.txtに開封確認として最終出力する役割を持つ。評価に使われる接続チェックは以下のルールに基づいて行われる:

  1. 端末ロケール(locale)が、ホワイトリストに合致している場合

  2. IPアドレス、およびブラウザ識別文字列(User-Agent)がブラックリスト(Bot、除外対象リスト等)に合致しない場合

検証ロケール(locale)の許可ホワイトリストは以下の通り:

  • us

  • jp

  • ja

  • kr

  • ko

排除対象となるブラウザ識別文字列(User-Agent)ブラックリスト(主にセキュリティスキャンやBot等の巡回検知による偽陽性を省くため):

  • bot

  • spider

  • crawl

  • trend

  • symantec

  • virus

  • spam

  • secure

除外対象となるIPアドレスのブラックリスト範囲(セキュリティ検査事業者などの既知IPレンジなど):

| ip range | info | ip range | info |
| --- | --- | --- | --- |
| 103.246.39.0/24 | bluecoat | 222.122.128.0/24 | Korea Telecom KORNET |
| 104.132.0.0/14 | Google | 222.122.185.0/24 | Korea Telecom KORNET |
| 121.156.79.0/24 | Korea Telecom KORNET | 23.192.0.0/11 | Akamai |
| 125.141.228.0/24 |  | 52.84.0.0/14 | Amazon |
| 15.211.153.0/24 | HP | 52.88.0.0/13 | Amazon |
| 150.70.0.0/16 | Trend Micro | 54.192.0.0/16 | Amazon |
| 182.162.206.103 | with symantec client v1 | 59.12.199.252 |  |
| 182.162.206.39 | with symantec client v2 | 59.18.85.26 |  |
| 183.99.48.26 | 20170124 fake chrome | 61.98.76.115 |  |
| 199.19.248.0/21 | bluecoat 199.19.248.0/21 | 61.111.0.0/18 | LG DACOM KIDC |
| 200.63.47.32/32 | AboveNet Engineering  ipeng@zayo.com | 66.102.0.0/20 | Google |
| 210.121.169.0/24 |  | 66.249.64.0/19 | Google |
| 211.115.119.0/24 | jnjedu | 72.13.86.0/24 | ??echosting |
| 211.233.80.0/ | Ahnlab | 83.223.96.0/19 | Gyron |
| 211.55.19.0/24 | KT CUSTOMER | 89.145.108.0/22 | Gyron |
| 216.104.0.0/19 | Trend Micro | 89.148.104.0/21 | DUNAWEB_89.148.104.0/21 |
| 218.153.8.0/24 |  | 115.90.74.131 |  |
| 221.141.3.0/24 | SK Broadband Co Ltd | 211.249.40.0/24 | naver |
| 221.252.42.0/24 | NRI SecureTechnologies | 211.56.96.0/24 | kakao |
| 222.99.190.104 | 20170124 chrome alike testing

| ip range | info | ip range | info |
| --- | --- | --- | --- |
| 103.246.39.0/24 | bluecoat | 222.122.128.0/24 | Korea Telecom KORNET |
| 104.132.0.0/14 | Google | 222.122.185.0/24 | Korea Telecom KORNET |
| 121.156.79.0/24 | Korea Telecom KORNET | 23.192.0.0/11 | Akamai |
| 125.141.228.0/24 |  | 52.84.0.0/14 | Amazon |
| 15.211.153.0/24 | HP | 52.88.0.0/13 | Amazon |
| 150.70.0.0/16 | Trend Micro | 54.192.0.0/16 | Amazon |
| 182.162.206.103 | with symantec client v1 | 59.12.199.252 |  |
| 182.162.206.39 | with symantec client v2 | 59.18.85.26 |  |
| 183.99.48.26 | 20170124 fake chrome | 61.98.76.115 |  |
| 199.19.248.0/21 | bluecoat 199.19.248.0/21 | 61.111.0.0/18 | LG DACOM KIDC |
| 200.63.47.32/32 | AboveNet Engineering  ipeng@zayo.com | 66.102.0.0/20 | Google |
| 210.121.169.0/24 |  | 66.249.64.0/19 | Google |
| 211.115.119.0/24 | jnjedu | 72.13.86.0/24 | ??echosting |
| 211.233.80.0/ | Ahnlab | 83.223.96.0/19 | Gyron |
| 211.55.19.0/24 | KT CUSTOMER | 89.145.108.0/22 | Gyron |
| 216.104.0.0/19 | Trend Micro | 89.148.104.0/21 | DUNAWEB_89.148.104.0/21 |
| 218.153.8.0/24 |  | 115.90.74.131 |  |
| 221.141.3.0/24 | SK Broadband Co Ltd | 211.249.40.0/24 | naver |
| 221.252.42.0/24 | NRI SecureTechnologies | 211.56.96.0/24 | kakao |
| 222.99.190.104 | 20170124 chrome alike testing

| ip range | info | ip range | info |
| --- | --- | --- | --- |
| 103.246.39.0/24 | bluecoat | 222.122.128.0/24 | Korea Telecom KORNET |
| 104.132.0.0/14 | Google | 222.122.185.0/24 | Korea Telecom KORNET |
| 121.156.79.0/24 | Korea Telecom KORNET | 23.192.0.0/11 | Akamai |
| 125.141.228.0/24 |  | 52.84.0.0/14 | Amazon |
| 15.211.153.0/24 | HP | 52.88.0.0/13 | Amazon |
| 150.70.0.0/16 | Trend Micro | 54.192.0.0/16 | Amazon |
| 182.162.206.103 | with symantec client v1 | 59.12.199.252 |  |
| 182.162.206.39 | with symantec client v2 | 59.18.85.26 |  |
| 183.99.48.26 | 20170124 fake chrome | 61.98.76.115 |  |
| 199.19.248.0/21 | bluecoat 199.19.248.0/21 | 61.111.0.0/18 | LG DACOM KIDC |
| 200.63.47.32/32 | AboveNet Engineering  ipeng@zayo.com | 66.102.0.0/20 | Google |
| 210.121.169.0/24 |  | 66.249.64.0/19 | Google |
| 211.115.119.0/24 | jnjedu | 72.13.86.0/24 | ??echosting |
| 211.233.80.0/ | Ahnlab | 83.223.96.0/19 | Gyron |
| 211.55.19.0/24 | KT CUSTOMER | 89.145.108.0/22 | Gyron |
| 216.104.0.0/19 | Trend Micro | 89.148.104.0/21 | DUNAWEB_89.148.104.0/21 |
| 218.153.8.0/24 |  | 115.90.74.131 |  |
| 221.141.3.0/24 | SK Broadband Co Ltd | 211.249.40.0/24 | naver |
| 221.252.42.0/24 | NRI SecureTechnologies | 211.56.96.0/24 | kakao |
| 222.99.190.104 | 20170124 chrome alike testing

caption - ipブラックリスト

フィッシングで運用されたファイル構成、残されたログ、および実際に記録されている侵害検知ログと被害ログを統合・分析してまとめた結果は以下の表に図示する通り:

| 로그 파일 경로 | 관련 파일 | 실제 피해(추정)/로그 개수 |
| --- | --- | --- |
| log/generator_log.txt | generator.php | 220/315 |
| log/password_log.txt | kakao-login.php | 1/14 |
| log/request_log.txt | redirect.php, request.php | 42/536 |
| log/response_log.txt | response.php | 4/33

| 로그 파일 경로 | 관련 파일 | 실제 피해(추정)/로그 개수 |
| --- | --- | --- |
| log/generator_log.txt | generator.php | 220/315 |
| log/password_log.txt | kakao-login.php | 1/14 |
| log/request_log.txt | redirect.php, request.php | 42/536 |
| log/response_log.txt | response.php | 4/33

| 로그 파일 경로 | 관련 파일 | 실제 피해(추정)/로그 개수 |
| --- | --- | --- |
| log/generator_log.txt | generator.php | 220/315 |
| log/password_log.txt | kakao-login.php | 1/14 |
| log/request_log.txt | redirect.php, request.php | 42/536 |
| log/response_log.txt | response.php | 4/33

caption - 全ログおよび実際の被害ログ

1.6. Yonsei(延世)大学を標的とした電子メール資格情報窃取・転送スクリプト

vps/var/www/html/js/chks.jsファイルは、韓国の延世(ヨンセ)大学の公式Webメールの認証APIサービス・エンドポイント「https://mail.yonsei.ac.kr/common/json/agent.do」に対して裏でフッキング通信を試み、接続する教職員や学生のアカウント構成情報を抜き出した上で、ターゲットのメール設定内の「自動転送通知メールアドレス一覧」に、攻撃者自身が確保していると考えられるメール受取用メールボックス cimoon185@daum.net をユーザーに無断で埋め込んで自動転送されるよう追加変更する。

chks.js Forward 함수

caption - chks.js 内の Forward(転送設定)関数

このようにしてアカウントを掌握した後、メールボックス内の「ent_{user}」および「TOTAL」カテゴリ階層を探索し、送受信日付が「2017-10-01」以降となる比較的新しいメールデータをバックグラウンドで一括窃取し、その中身を攻撃者側の収集サーバーである https://service.navers.org/emuy.php?i={user}にアップロード送信(転出)する処理を行う。

2. 奪取されたデータの分析

本章では、攻撃者のVMware VMダンプファイルから確認された資料のうち、一般的には入手不可能ま​​たは困難なデータを「奪取資料」と定義し、これを整理した内容を扱う。

2.1. 外交部 AyersRock Mail

work/mnt/hgfs/Desktop/mofa.go.kr.7z ファイル内には、外交部内部で使用されたと推定される AyersRock Mail ソースコードが存在する。AyersRock Mailは、(株)ナラビジョン(Nara Vision)が2016年にKebi Mailをベースに開発したウェブメールソリューションである。

解凍した後に確認されたディレクトリ構造が kebi-batchkebi-corkebi-web-mail などのKebi系列モジュールで構成されており、各ディレクトリ配下にある app-*.xml ファイルの mail 要素の host 値から mofa.go.kr を確認できる。

app-*.xml mail 요소의 host 값

caption - app-*.xml mail 要素の host 値

mofa.go.kr/kebi-web-parent/mail/document/info.txt ファイルには、内部開発サーバー、DB、テストサーバーなどの複数のアカウント情報やメモが書き込まれている。

info.txt 파일 내용

caption - info.txt ファイルの内容

mofa.go.kr/kebi-web-parent/mail/document/worklist.txt ファイルには、作業日程と作業リストに関するメモが書き込まれている。

worklist.txt 파일 내용

caption - worklist.txt ファイルの内容

Gitリポジトリに関連する .gitignore.gitmodules ファイルが存在し、開発元が作成したと推定される info.txt、worklist.txt ファイルが存在することから、mofa.go.kr.7z は開発元のGitリポジトリから奪取したファイルを圧縮したものと見られる。ただし .git フォルダーは存在しないため、リポジトリのタイプは不明である。

2.2. シキュアキー(SecureKey) APPM & iRASS

2.2.1. APPM & iRASS サーバー管理プログラム

work/mnt/hgfs/Desktop/111/home/home パスに appm、irass ディレクトリが存在し、それぞれ APPM、iRASS サーバー管理が目的と推定されるプログラムが存在する。

APPM

work/mnt/hgfs/Desktop/111/home/home/appm ディレクトリ内には、appm_master バイナリと暗号化関連モジュールがある crypto ディレクトリが存在する。appm_masterは実行プロセスにおいて複数のバイナリと設定ファイルの存在を確認するが、公開された資料では多数のファイルが存在しない。主要な設定ファイルとしてはクラウド設定ファイル、復号された appmkey ファイルがあるが、存在しない。

공개된 파일 목록

caption - 公開されたファイルの一覧

iRASS

work/mnt/hgfs/Desktop/111/home/home/irass/bin ディレクトリ内には、irass_master バイナリを含め irass_db、irass_admin、irass_rdp などの複数のバイナリが存在する。すべて iRASS 関連のバイナリである。特に history.txt に「KB国民銀行」のキーワードが存在し、開発元が作成した問題およびパッチのメモファイルと推定される。

history.txt 파일 내용

caption - history.txt ファイルの内容

2.3. 行政安全部 GPKISecureWebX

2.3.1. GPKISecureWebX ソースコード

work/mnt/hgfs/Desktop/111/GPKISecureWebX ディレクトリ内には、ドリームセキュリティ(Dream Security)が開発した公認認証書関連セキュリティプログラムである GPKISecureWebX のソースコードが存在する。

Visual Studio Build Log ファイルである work/mnt/hgfs/Desktop/111/GPKISecureWebX/GPKISecureWebX.plg から「01_행자부 웹보안API (01_行自部ウェブセキュリティAPI)」が確認でき、「行自部」は現在「行政安全部」と呼ばれていることから、これが行政安全部 GPKISecureWebX に関連していることがわかる。

GPKISecureWebX.plg 파일 내용

caption - GPKISecureWebX.plg ファイルの内容

さらに、work/mnt/hgfs/Desktop/111/GPKISecureWebX ディレクトリを圧縮した work/mnt/hgfs/Desktop/111/1.rar ファイルが存在する。

2.3.2. GPKISecureWeb パッケージ

work/mnt/hgfs/Desktop/111/gpki.7z ファイル内に GPKI API、GPKISecureWebX、setup ディレクトリが存在する。GPKI標準APIに関するマニュアルと、複数のキー、認証書ファイルが存在する。

特に gpki/gpkisecureweb/log ディレクトリに、2017年12月から2020年4月までの認証書検証ログファイルが存在する。

로그 파일 일부 데이터

caption - ログファイルの一部のデータ

2.3.3. GPKISecureWeb 文書およびモジュールソースコード

work/mnt/hgfs/Desktop/111/2/01_행자부 웹보안API(ORG) - 권유미 인수 ディレクトリ内には、引き継ぎのための GPKISecureWeb 関連文書ファイルと、GPKIInstaller、GPKICertManager などの全体モジュールに関するソースコードが存在する。

確認された文書ファイルは以下の通りである。

  • work/mnt/hgfs/Desktop/111/2/01_행자부 웹보안API(ORG) - 권유미 인수/웹용 표준보안API_매뉴얼(유선).chm

  • work/mnt/hgfs/Desktop/111/2/01_행자부 웹보안API(ORG) - 권유미 인수/설계문서/SSA_AO_AD_WT_002_웹보안 프로토콜설계서_Ver1.0_.doc

  • work/mnt/hgfs/Desktop/111/2/01_행자부 웹보안API(ORG) - 권유미 인수/행자부 웹보안API 인수인계.doc

文書ファイルのプロパティを確認すると、かなり前に作成されたものであることがわかる。

doc 파일 문서 속성

caption - docファイルの文書プロパティ

work/mnt/hgfs/Desktop/111/2/01_행자부 웹보안API(ORG) - 권유미 인수/02_src/ReadMe.txt に全体モジュールに関する説明が存在する。

ReadMe.txt 파일 내용

caption - ReadMe.txt ファイルの内容

2.4. GPKI 認証書および秘密鍵ファイル

work/home/user/Downloads/cert/extracted-key-20200512 ディレクトリ内には、認証書と秘密鍵ファイルが2475個存在する。Subject から法制処、政府傘下機関および委員会などの様々な情報を確認できたが、攻撃者がこれをどのように収集したかは不明である。

/work/home/user/Downloads/cert/extracted-key-20200512 디렉토리 파일 목록

caption - work/home/user/Downloads/cert/extracted-key-20200512 ディレクトリのファイル一覧

2.5. オンナラ(On-Nara)ログイン自動化スクリプト

work/mnt/hgfs/Desktop/111/onnara_auto ディレクトリ内には、大韓民国政府で使用されている公務員用サービス「オンナラ」のログイン自動化スクリプトが存在する。client_main.py にオンナラサービスのアカウントID、組織ID、サブドメインがハードコーディングされている。

client_main.py main 함수

caption - client_main.py main 関数

ログイン方式は、入力された情報を基に work/mnt/hgfs/Desktop/111/onnara_auto/script に存在する onnaraSSO.jar ファイルを実行し、SSOトークン値を生成する。

onnara_sso.py generate_L1 함수

caption - onnara_sso.py generate_L1 関数

onnaraSSO.jar は入力オプションに応じて、ARIA または DES アルゴリズムで文字列を暗号化・復号する。実行形式は以下の通りである。

  • onnaraSSO.jar <文字列> <0:DES | 1:ARIA> <0:暗号化 | 1:復号>

また、ARIA と DES に使用されるキーはすべてハードコーディングされている。

HostとVMware間でファイルを移動した形跡から、攻撃者が onnara_sso_test.py ファイルに出力結果をコメントとして残していることが確認できる。

/work/home/user/.cache/vmware/drag_and_drop/Z3INst/onnara/onnara_auto/onnara_sso_test.py 파일 데이터

caption - work/home/user/.cache/vmware/drag_and_drop/Z3INst/onnara/onnara_auto/onnara_sso_test.py ファイルデータ

work/home/user/.cache/vmware/drag_and_drop/Z3INst/onnara ディレクトリ内には onnara9-onnara4-SSO-login.saz ファイルが存在し、これを解凍すると実際のHTTPリクエストとレスポンスを確認することができる。

caption - onnara9-onnara4-SSO-login.saz/raw/001_c.txt ファイルデータ

3. 攻撃者およびシステム情報

本章では、公開された資料のうち、攻撃者とシステム環境について分析した内容を扱う。

3.1. システム情報

3.1.1. deepin

VMware workstationにはdeepinがインストールされていた。deepinは中国国外で人気のあるディストリビューションではなく、中国企業主導で開発され、中国市場を中心に配布・支援されているディストリビューションである。

独自に検証可能なユーザー数や分布に関するデータは知られていないが、deepinは2022-12にユーザー数が3M以上であると報告し、2024-09には「5.4M以上のユーザーのうち、ほぼ3Mが中国国外のユーザーである」と報告した。しかし、外部の検証がないため、数値の現実性には疑問がある。

Linuxユーザーの特性上、テレメトリやアンケートなどのデータ収集に対する拒否感が強く、デスクトップでは商用ソフトウェアへの需要や市場が小さいため、市場調査も活発ではない。したがって、Linuxディストリビューションのユーザー数に関する統計資料を得ることは難しい。しかし、公開された資料によって、各ディストリビューションの大まかな人気を知ることができる。このうち、各ディストリビューションの検索数、Stack Overflow Developer Survey、および各ディストリビューションの統計資料をまとめた。

| Distribution | Google Trends (Topic, relative to Ubuntu) | Ahrefs Search Traffic | Semrush | Subreddit weekly visitors/members | Stack Overflow (personal/work) | self-reported users |
| --- | --- | --- | --- | --- | --- | --- |
| Ubuntu | 16 | 1.4M-n(ref:https://ahrefstop.com/websites/ubuntu.com) | 3.9M | 251K / 258K | 27.8% / 27.7% | 6M+(2023)-n(ref:https://discourse.ubuntu.com/t/ubuntu-desktop-charting-a-course-for-the-future/38092), 2762K(2017 popcon)-n(ref:https://web.archive.org/web/20171222174904/https://popcon.ubuntu.com/by_inst) |
| Debian | 5 | 297.1K-n(ref:https://ahrefstop.com/websites/debian.org) | 1.1M | 158K / 115K | 11.4% / 10.4% | 265K(2025)-n(ref:https://popcon.debian.org/) |
| Arch Linux | 3 | 342.0K-n(ref:https://ahrefstop.com/websites/archlinux.org) | 1.3M | 271K / 324K | 9.7% / 4.6% |  |
| Linux Mint | 2 | 492.4K-n(ref:https://ahrefstop.com/websites/linuxmint.com) | 1.3M | 172K / 150K |  | 98K(2025)-n(ref:https://blog.linuxmint.com/?p=4811) |
| Fedora | 2 | 222.0K-n(ref:https://ahrefstop.com/websites/fedoraproject.org) | 582.5K | 156K / 142K | 5.8% / 3.7% | 323.3K+(2025)-n(ref:https://discussion.fedoraproject.org/t/fedora-usage-stats/144635/16) |
| Red Hat | 2 | 1.5M-n(ref:https://ahrefstop.com/websites/redhat.com) | 2.5M | 22K / 49K | 1.8% / 5.7% |  |
| CentOS | 1 |  | 208.4K | 3.5K / 16K |  |  |
| NixOS | 1 |  | 142K | 46K / 44K | 3.4% / 1.8% |  |
| openSUSE | <1 |  | 136.7K | 28K / 41K |  | 269K(15.6), 186K(tumbleweed 2025)-n(ref:https://metrics.opensuse.org/d/osrt_access) |
| Gentoo | <1 |  | 27.5K | 22K / 33K |  |  |
| Rocky Linux | <1 |  | 75.7K | 4.2K / 10K |  |  |
| AlmaLinux | <1 |  | 38.1K | 3.7K / 7.3K |  |  |
| deepin | <

| Distribution | Google Trends (Topic, relative to Ubuntu) | Ahrefs Search Traffic | Semrush | Subreddit weekly visitors/members | Stack Overflow (personal/work) | self-reported users |
| --- | --- | --- | --- | --- | --- | --- |
| Ubuntu | 16 | 1.4M-n(ref:https://ahrefstop.com/websites/ubuntu.com) | 3.9M | 251K / 258K | 27.8% / 27.7% | 6M+(2023)-n(ref:https://discourse.ubuntu.com/t/ubuntu-desktop-charting-a-course-for-the-future/38092), 2762K(2017 popcon)-n(ref:https://web.archive.org/web/20171222174904/https://popcon.ubuntu.com/by_inst) |
| Debian | 5 | 297.1K-n(ref:https://ahrefstop.com/websites/debian.org) | 1.1M | 158K / 115K | 11.4% / 10.4% | 265K(2025)-n(ref:https://popcon.debian.org/) |
| Arch Linux | 3 | 342.0K-n(ref:https://ahrefstop.com/websites/archlinux.org) | 1.3M | 271K / 324K | 9.7% / 4.6% |  |
| Linux Mint | 2 | 492.4K-n(ref:https://ahrefstop.com/websites/linuxmint.com) | 1.3M | 172K / 150K |  | 98K(2025)-n(ref:https://blog.linuxmint.com/?p=4811) |
| Fedora | 2 | 222.0K-n(ref:https://ahrefstop.com/websites/fedoraproject.org) | 582.5K | 156K / 142K | 5.8% / 3.7% | 323.3K+(2025)-n(ref:https://discussion.fedoraproject.org/t/fedora-usage-stats/144635/16) |
| Red Hat | 2 | 1.5M-n(ref:https://ahrefstop.com/websites/redhat.com) | 2.5M | 22K / 49K | 1.8% / 5.7% |  |
| CentOS | 1 |  | 208.4K | 3.5K / 16K |  |  |
| NixOS | 1 |  | 142K | 46K / 44K | 3.4% / 1.8% |  |
| openSUSE | <1 |  | 136.7K | 28K / 41K |  | 269K(15.6), 186K(tumbleweed 2025)-n(ref:https://metrics.opensuse.org/d/osrt_access) |
| Gentoo | <1 |  | 27.5K | 22K / 33K |  |  |
| Rocky Linux | <1 |  | 75.7K | 4.2K / 10K |  |  |
| AlmaLinux | <1 |  | 38.1K | 3.7K / 7.3K |  |  |
| deepin | <

| Distribution | Google Trends (Topic, relative to Ubuntu) | Ahrefs Search Traffic | Semrush | Subreddit weekly visitors/members | Stack Overflow (personal/work) | self-reported users |
| --- | --- | --- | --- | --- | --- | --- |
| Ubuntu | 16 | 1.4M-n(ref:https://ahrefstop.com/websites/ubuntu.com) | 3.9M | 251K / 258K | 27.8% / 27.7% | 6M+(2023)-n(ref:https://discourse.ubuntu.com/t/ubuntu-desktop-charting-a-course-for-the-future/38092), 2762K(2017 popcon)-n(ref:https://web.archive.org/web/20171222174904/https://popcon.ubuntu.com/by_inst) |
| Debian | 5 | 297.1K-n(ref:https://ahrefstop.com/websites/debian.org) | 1.1M | 158K / 115K | 11.4% / 10.4% | 265K(2025)-n(ref:https://popcon.debian.org/) |
| Arch Linux | 3 | 342.0K-n(ref:https://ahrefstop.com/websites/archlinux.org) | 1.3M | 271K / 324K | 9.7% / 4.6% |  |
| Linux Mint | 2 | 492.4K-n(ref:https://ahrefstop.com/websites/linuxmint.com) | 1.3M | 172K / 150K |  | 98K(2025)-n(ref:https://blog.linuxmint.com/?p=4811) |
| Fedora | 2 | 222.0K-n(ref:https://ahrefstop.com/websites/fedoraproject.org) | 582.5K | 156K / 142K | 5.8% / 3.7% | 323.3K+(2025)-n(ref:https://discussion.fedoraproject.org/t/fedora-usage-stats/144635/16) |
| Red Hat | 2 | 1.5M-n(ref:https://ahrefstop.com/websites/redhat.com) | 2.5M | 22K / 49K | 1.8% / 5.7% |  |
| CentOS | 1 |  | 208.4K | 3.5K / 16K |  |  |
| NixOS | 1 |  | 142K | 46K / 44K | 3.4% / 1.8% |  |
| openSUSE | <1 |  | 136.7K | 28K / 41K |  | 269K(15.6), 186K(tumbleweed 2025)-n(ref:https://metrics.opensuse.org/d/osrt_access) |
| Gentoo | <1 |  | 27.5K | 22K / 33K |  |  |
| Rocky Linux | <1 |  | 75.7K | 4.2K / 10K |  |  |
| AlmaLinux | <1 |  | 38.1K | 3.7K / 7.3K |  |  |
| deepin | <

caption - ディストリビューションの統計資料

上記の指標を総合すると、deepinの中国国外のユーザー数は約300万人に達していないとみられる。独自の検証がないことを考慮すると、数千人規模にとどまる可能性が相対的に高い。また、中国国外でのこの評判は概して否定的である。さらに、以下のような内容も知られている。

  • ポリシー・規制:EULAおよび個人情報保護方針に、情報収集の権限と中華人民共和国法律への準拠が明記されている。

  • データ収集を巡る議論の履歴:過去にApp StoreでCNZZ Analyticsを使用して議論を呼び、DeepinはGoogle Analyticsと同様であると弁明した上で収集の中止を告知したが、その後Umeng+ Analyticsを再び追加した

  • セキュリティ:deepin desktop environmentは、繰り返されるセキュリティ上の問題やセキュリティ検証を回避しようとする手法が指摘され、2025-05にOpenSUSEから削除された

総合すると、中国国外でdeepinを選択または使用する確率は極めて低いと判断される。

3.1.2. IME

VMware workstationには、複数の中国語用IMEがインストール、設定された痕跡が確認された。deepinの特性上、中国市場に合わせた基本構成が含まれており、デフォルトパッケージとしてfcitx-pinyinfcitx-sunpinyinfcitx-table-wubiがインストールされている。インストール言語が英語の場合はデフォルトIMEが非活性化され、中国語の場合はwubishuangpinpinyinsunpinyinが活性化されることが確認された。

기본 설치된 fcitx 패키지 목록

caption - デフォルトでインストールされているfcitxパッケージ一覧

追加インストールの内訳は以下の通りである。

  • fcitxプラグイン:huayupywbpyrimeiflyimeの追加インストールの痕跡が確認され、活性化されている入力方式はwbpypinyinである。

  • ibusプラグイン:rimeの追加インストールおよび使用の痕跡が確認された。

  • SogouPYの追加設定の痕跡が存在する。

caption - work/home/user/.config/fcitx/profileEnabledIMList diff結果

3.1.3. etc

インストールおよびロケール情報

work/home/user/.config配下の設定のロケールが英語で表記されているため、英語版の環境からインストールされたとみられる。work/home/user/.config/locale.confファイルの内容は以下の通りである。

caption - work/home/user/.config/locale.conf ファイルの内容

しかし、work/var/log/{access.log*,vmware-network.*.log}ファイルのデータは韓国語で記録されている。

caption - work/var/log/vmware-network.1.log ファイルの内容

deepinの設定変更および痕跡

work/home/user/.config/deepin/dde-desktop/dde-desktop.confファイルにProtonVPN OpenVPNのプロファイルとみられるファイルが存在するが、ダンプには含まれていない。

work/home/user/.config/deepin/dde-desktop/dde-desktop.conf 파일 내용

caption - work/home/user/.config/deepin/dde-desktop/dde-desktop.conf ファイルの内容

work/home/user/.config/deepin/dde-file-manager/dde-file-manager.obtusely.jsonファイルに、個人情報を含むファイル名や、リモート・共有パスへのアクセス履歴が存在する。例は以下の通りである。

  • standard://downloads/cert/gpki_cert/*

  • file:///mnt/hgfs/share_data/GitHack/www.caa.org.tw/*

  • ftp:host=192.168.50.1:21212

work/home/user/.config/deepin/dcc-weather-plugin.confファイルでLocationWaterlooに設定されている。

work/home/user/.config/deepin/dcc-weather-plugin.conf 파일 내용

caption - work/home/user/.config/deepin/dcc-weather-plugin.conf ファイルの内容

この他に、その他のアプリケーションで確認された痕跡は以下の通りである。

  • work/home/user/.config/filezilla/recentservers.xml: ftp.fu-berlin.de

    • FileZillaを使用して公開FTPミラーに接続

  • work/home/user/.config/Foxit Software/Foxit Reader.conf: tmp/kclee/hiaei84@gmail.com.pst_pst_*.msg

    • ファイルパスから確認されたメールアドレス

3.2. ブラウザ情報

work/home/user配下の複数のブラウザプロファイルをすべて分析した結果、主に使用されていたプロファイルはwork/home/user/.config/google-chrome/Defaultであることが確認された。全体のブラウザプロファイルと、確認されたタイムスタンプの最小値・最大値は、以下の表の通りである。

| path | range |
| --- | --- |
| work/home/user/.config/BraveSoftware/Brave-Browser/Default | 2021-05-18 07:15:25.107 ~ 2021-05-18 07:25:22.279 |
| work/home/user/.config/browser/Default | 2021-11-15 17:54:39.645 ~ 2021-11-15 17:55:58.790 |
| work/home/user/.config/chromium/Default | 2019-05-17 05:52:04.363 ~ 2020-12-15 09:21:59.050 |
| work/home/user/.config/google-chrome/Default | 2020-12-02 01:56:37.844 ~ 2025-06-06 04:41:48.697 |
| work/home/user/.mozilla/firefox/06yb5px0.default-release | 2020-12-21T15:59:20.598000 ~ 2021-01-04T17:09:34.052025 |
| work/home/user/.mozilla/firefox/l8g1zjqb.default | 2020-12-21T15:59:20.146000 |
| work/home/user/.thunderbird/3qg2p785.default-default | 2021-04-13T11:39:36.277000 ~ 2021-04-13T12:22:31.051914(추정) |
| work/home/user/.thunderbird/3s68zwb6.default | 2020-12-21T11:44:07.857000 |
| work/home/user/.thunderbird/8inzqqf5.default-release | 2023-12-25T11:47:56.099000 ~ 2023-12-27T10:18:21.103(추정))

| path | range |
| --- | --- |
| work/home/user/.config/BraveSoftware/Brave-Browser/Default | 2021-05-18 07:15:25.107 ~ 2021-05-18 07:25:22.279 |
| work/home/user/.config/browser/Default | 2021-11-15 17:54:39.645 ~ 2021-11-15 17:55:58.790 |
| work/home/user/.config/chromium/Default | 2019-05-17 05:52:04.363 ~ 2020-12-15 09:21:59.050 |
| work/home/user/.config/google-chrome/Default | 2020-12-02 01:56:37.844 ~ 2025-06-06 04:41:48.697 |
| work/home/user/.mozilla/firefox/06yb5px0.default-release | 2020-12-21T15:59:20.598000 ~ 2021-01-04T17:09:34.052025 |
| work/home/user/.mozilla/firefox/l8g1zjqb.default | 2020-12-21T15:59:20.146000 |
| work/home/user/.thunderbird/3qg2p785.default-default | 2021-04-13T11:39:36.277000 ~ 2021-04-13T12:22:31.051914(추정) |
| work/home/user/.thunderbird/3s68zwb6.default | 2020-12-21T11:44:07.857000 |
| work/home/user/.thunderbird/8inzqqf5.default-release | 2023-12-25T11:47:56.099000 ~ 2023-12-27T10:18:21.103(추정))

| path | range |
| --- | --- |
| work/home/user/.config/BraveSoftware/Brave-Browser/Default | 2021-05-18 07:15:25.107 ~ 2021-05-18 07:25:22.279 |
| work/home/user/.config/browser/Default | 2021-11-15 17:54:39.645 ~ 2021-11-15 17:55:58.790 |
| work/home/user/.config/chromium/Default | 2019-05-17 05:52:04.363 ~ 2020-12-15 09:21:59.050 |
| work/home/user/.config/google-chrome/Default | 2020-12-02 01:56:37.844 ~ 2025-06-06 04:41:48.697 |
| work/home/user/.mozilla/firefox/06yb5px0.default-release | 2020-12-21T15:59:20.598000 ~ 2021-01-04T17:09:34.052025 |
| work/home/user/.mozilla/firefox/l8g1zjqb.default | 2020-12-21T15:59:20.146000 |
| work/home/user/.thunderbird/3qg2p785.default-default | 2021-04-13T11:39:36.277000 ~ 2021-04-13T12:22:31.051914(추정) |
| work/home/user/.thunderbird/3s68zwb6.default | 2020-12-21T11:44:07.857000 |
| work/home/user/.thunderbird/8inzqqf5.default-release | 2023-12-25T11:47:56.099000 ~ 2023-12-27T10:18:21.103(추정))

caption - 活動範囲

work/home/user/.config/google-chrome/Defaultプロファイルの最初の記録は2020-12-02 01:56:37.844であり、その後2025-03-18 04:04:20.873から2025-05-28 07:11:01.791までの訪問記録が存在する。その他のプロファイルは、作成されたのみか、短期間の使用痕跡にとどまった。

3.2.1. language

work/home/user/.config/google-chrome/Default/Preferencesの記録が、work/home/user/.config/google-chrome/Default/.com.google.Chrome.*に残っていた。以下の表を参照されたい。

| file name | profile.last_engagement_time |
| --- | --- |
| .com.google.Chrome.yf0Cko | 2020-04-23T10:28:01.605486 |
| .com.google.Chrome.wNxXMb | 2021-04-08T03:15:41.989060 |
| .com.google.Chrome.oM0Y0k | 2021-05-18T07:46:52.497051 |
| .com.google.Chrome.qTDg5z | 2025-04-10T16:13:27.859136 |
| .com.google.Chrome.m6662X | 2025-05-20T00:26:05.876747 |
| .com.google.Chrome.LajSSi | 2025-05-22T00:31:18.814059 |
| Preferences | 2025-05-28T07:11:01.793570

| file name | profile.last_engagement_time |
| --- | --- |
| .com.google.Chrome.yf0Cko | 2020-04-23T10:28:01.605486 |
| .com.google.Chrome.wNxXMb | 2021-04-08T03:15:41.989060 |
| .com.google.Chrome.oM0Y0k | 2021-05-18T07:46:52.497051 |
| .com.google.Chrome.qTDg5z | 2025-04-10T16:13:27.859136 |
| .com.google.Chrome.m6662X | 2025-05-20T00:26:05.876747 |
| .com.google.Chrome.LajSSi | 2025-05-22T00:31:18.814059 |
| Preferences | 2025-05-28T07:11:01.793570

| file name | profile.last_engagement_time |
| --- | --- |
| .com.google.Chrome.yf0Cko | 2020-04-23T10:28:01.605486 |
| .com.google.Chrome.wNxXMb | 2021-04-08T03:15:41.989060 |
| .com.google.Chrome.oM0Y0k | 2021-05-18T07:46:52.497051 |
| .com.google.Chrome.qTDg5z | 2025-04-10T16:13:27.859136 |
| .com.google.Chrome.m6662X | 2025-05-20T00:26:05.876747 |
| .com.google.Chrome.LajSSi | 2025-05-22T00:31:18.814059 |
| Preferences | 2025-05-28T07:11:01.793570

caption - last_engagement_time 情報

変更のタイムラインは以下の通りである。

  • 2020-04-23T10:28:01.605486 以前:intl.*_languagesに英語+中国語を設定、中国語の翻訳をブロック(translate_blocked_languages)。

  • 2021-04-08T03:15:41.989060 以前:韓国語を追加、韓国語の翻訳をブロック。

  • 2025-04-10T16:13:27.859136 以前:英語の翻訳ブロックを追加。

翻訳の承諾は主に英語と韓国語で発生しており、中国語の承諾履歴はない。翻訳結果の言語は中国語として記録された。

主に閲覧されたページは英語、中国語のページであり、2021-04-08までは韓国語のサイトにも訪問していた。2025-04-10以降は、韓国語のサイトへの訪問はほとんど存在しない。

3.2.2. history

work/home/user/.config/google-chrome/Defaultプロファイルは、業務および業務外の用途で使用されていた。脆弱性やマルウェアなど、業務に関連する記録の他、フォーラムなどを利用した記録が存在する。

Cobalt Strikeに関連する訪問記録は以下の表の通りである。

| first visit | url | title |
| --- | --- | --- |
| 2025-03-24 05:25:47.145 | https://github.com/TH3xACE/EDR-Test | GitHub - TH3xACE/EDR-Test: Automating EDR Testing with reference to MITRE ATTACK via Cobalt Strike [Purple Team]. |
| 2025-03-25 08:37:57.366 | https://github.com/georgesotiriadis/Chimera | GitHub - georgesotiriadis/Chimera: Automated DLL Sideloading Tool With EDR Evasion Capabilities |
| 2025-03-26 05:17:51.538 | https://github.com/kyleavery/AceLdr | GitHub - kyleavery/AceLdr: Cobalt Strike UDRL for memory scanner evasion. |
| 2025-04-07 01:13:41.868 | https://github.com/tomcarver16/BOF-DLL-Inject | GitHub - tomcarver16/BOF-DLL-Inject: Manual Map DLL injection implemented with Cobalt Strike's Beacon Object Files. |
| 2025-04-10 01:49:21.313 | https://github.com/Cobalt-Strike/ProxyDLLExample | GitHub - Cobalt-Strike/ProxyDLLExample: code for the Proxy DLL example blog post |
| 2025-04-16 04:49:48.157 | https://github.com/n3k7ar91/Cobalt-Strike-EDR-AV-bypass | GitHub - n3k7ar91/Cobalt-Strike-EDR-AV-bypass: Various resources to enhance Cobalt Strike's functionality and its ability to evade antivirus/EDR detection |
| 2025-04-29 02:04:12.124 | https://github.com/CrackerCat/CobaltStrike-KunKun | GitHub - CrackerCat/CobaltStrike-KunKun: 坤坤 CS 基于 CobaltStrike cat 4.5 二开项目 添加反沙箱、反测绘。集成常用后渗透插件,开箱即用(GitHub - CrackerCat/CobaltStrike-KunKun: KunKun CS is a CobaltStrike cat 4.5 based project that adds anti-sandbox and anti-mapping capabilities. It also integrates common post-exploitation plugins and is ready to use.) |
| 2025-04-29 02:04:46.756 | https://github.com/D13Xian/CobaltStrike-KunKun | GitHub - D13Xian/CobaltStrike-KunKun: 坤坤 CS 基于 CobaltStrike cat 4.5 二开项目 添加反沙箱、反测绘。集成常用后渗透插件,开箱即用(GitHub - D13Xian/CobaltStrike-KunKun: KunKun CS is a CobaltStrike cat 4.5 based project that adds anti-sandbox and anti-mapping capabilities. It also integrates common post-exploitation plugins and is ready to use.) |
| 2025-05-27 01:14:48.105 | https://github.com/RedefiningReality/Cobalt-Strike | GitHub - RedefiningReality/Cobalt-Strike: Various resources to enhance Cobalt Strike's functionality and its ability to evade antivirus/EDR detection |
| first visit | url | title |
| --- | --- | --- |
| 2025-03-24 05:25:47.145 | https://github.com/TH3xACE/EDR-Test | GitHub - TH3xACE/EDR-Test: Automating EDR Testing with reference to MITRE ATTACK via Cobalt Strike [Purple Team]. |
| 2025-03-25 08:37:57.366 | https://github.com/georgesotiriadis/Chimera | GitHub - georgesotiriadis/Chimera: Automated DLL Sideloading Tool With EDR Evasion Capabilities |
| 2025-03-26 05:17:51.538 | https://github.com/kyleavery/AceLdr | GitHub - kyleavery/AceLdr: Cobalt Strike UDRL for memory scanner evasion. |
| 2025-04-07 01:13:41.868 | https://github.com/tomcarver16/BOF-DLL-Inject | GitHub - tomcarver16/BOF-DLL-Inject: Manual Map DLL injection implemented with Cobalt Strike's Beacon Object Files. |
| 2025-04-10 01:49:21.313 | https://github.com/Cobalt-Strike/ProxyDLLExample | GitHub - Cobalt-Strike/ProxyDLLExample: code for the Proxy DLL example blog post |
| 2025-04-16 04:49:48.157 | https://github.com/n3k7ar91/Cobalt-Strike-EDR-AV-bypass | GitHub - n3k7ar91/Cobalt-Strike-EDR-AV-bypass: Various resources to enhance Cobalt Strike's functionality and its ability to evade antivirus/EDR detection |
| 2025-04-29 02:04:12.124 | https://github.com/CrackerCat/CobaltStrike-KunKun | GitHub - CrackerCat/CobaltStrike-KunKun: 坤坤 CS 基于 CobaltStrike cat 4.5 二开项目 添加反沙箱、反测绘。集成常用后渗透插件,开箱即用(GitHub - CrackerCat/CobaltStrike-KunKun: KunKun CS is a CobaltStrike cat 4.5 based project that adds anti-sandbox and anti-mapping capabilities. It also integrates common post-exploitation plugins and is ready to use.) |
| 2025-04-29 02:04:46.756 | https://github.com/D13Xian/CobaltStrike-KunKun | GitHub - D13Xian/CobaltStrike-KunKun: 坤坤 CS 基于 CobaltStrike cat 4.5 二开项目 添加反沙箱、反测绘。集成常用后渗透插件,开箱即用(GitHub - D13Xian/CobaltStrike-KunKun: KunKun CS is a CobaltStrike cat 4.5 based project that adds anti-sandbox and anti-mapping capabilities. It also integrates common post-exploitation plugins and is ready to use.) |
| 2025-05-27 01:14:48.105 | https://github.com/RedefiningReality/Cobalt-Strike | GitHub - RedefiningReality/Cobalt-Strike: Various resources to enhance Cobalt Strike's functionality and its ability to evade antivirus/EDR detection |
| first visit | url | title |
| --- | --- | --- |
| 2025-03-24 05:25:47.145 | https://github.com/TH3xACE/EDR-Test | GitHub - TH3xACE/EDR-Test: Automating EDR Testing with reference to MITRE ATTACK via Cobalt Strike [Purple Team]. |
| 2025-03-25 08:37:57.366 | https://github.com/georgesotiriadis/Chimera | GitHub - georgesotiriadis/Chimera: Automated DLL Sideloading Tool With EDR Evasion Capabilities |
| 2025-03-26 05:17:51.538 | https://github.com/kyleavery/AceLdr | GitHub - kyleavery/AceLdr: Cobalt Strike UDRL for memory scanner evasion. |
| 2025-04-07 01:13:41.868 | https://github.com/tomcarver16/BOF-DLL-Inject | GitHub - tomcarver16/BOF-DLL-Inject: Manual Map DLL injection implemented with Cobalt Strike's Beacon Object Files. |
| 2025-04-10 01:49:21.313 | https://github.com/Cobalt-Strike/ProxyDLLExample | GitHub - Cobalt-Strike/ProxyDLLExample: code for the Proxy DLL example blog post |
| 2025-04-16 04:49:48.157 | https://github.com/n3k7ar91/Cobalt-Strike-EDR-AV-bypass | GitHub - n3k7ar91/Cobalt-Strike-EDR-AV-bypass: Various resources to enhance Cobalt Strike's functionality and its ability to evade antivirus/EDR detection |
| 2025-04-29 02:04:12.124 | https://github.com/CrackerCat/CobaltStrike-KunKun | GitHub - CrackerCat/CobaltStrike-KunKun: 坤坤 CS 基于 CobaltStrike cat 4.5 二开项目 添加反沙箱、反测绘。集成常用后渗透插件,开箱即用(GitHub - CrackerCat/CobaltStrike-KunKun: KunKun CS is a CobaltStrike cat 4.5 based project that adds anti-sandbox and anti-mapping capabilities. It also integrates common post-exploitation plugins and is ready to use.) |
| 2025-04-29 02:04:46.756 | https://github.com/D13Xian/CobaltStrike-KunKun | GitHub - D13Xian/CobaltStrike-KunKun: 坤坤 CS 基于 CobaltStrike cat 4.5 二开项目 添加反沙箱、反测绘。集成常用后渗透插件,开箱即用(GitHub - D13Xian/CobaltStrike-KunKun: KunKun CS is a CobaltStrike cat 4.5 based project that adds anti-sandbox and anti-mapping capabilities. It also integrates common post-exploitation plugins and is ready to use.) |
| 2025-05-27 01:14:48.105 | https://github.com/RedefiningReality/Cobalt-Strike | GitHub - RedefiningReality/Cobalt-Strike: Various resources to enhance Cobalt Strike's functionality and its ability to evade antivirus/EDR detection |

caption - Cobalt Strike関連の訪問記録

rootkitに関連する訪問記録は以下の表の通りである。

| first visit | url | title |
| --- | --- | --- |
| 2025-03-24 00:57:27.819 | https://github.com/D4stiny/spectre | GitHub - D4stiny/spectre: A Windows kernel-mode rootkit that abuses legitimate communication channels to control a machine. |
| 2025-03-24 00:57:30.161 | https://github.com/TheMalwareGuardian/Bentico | GitHub - TheMalwareGuardian/Bentico: Windows Kernel Mode Rootkit |
| 2025-03-24 00:57:33.816 | https://github.com/eversinc33/Banshee | GitHub - eversinc33/Banshee: Experimental Windows x64 Kernel Rootkit with anti-rootkit evasion features. |
| 2025-03-24 00:57:35.016 | https://github.com/alal4465/Win_Rootkit | GitHub - alal4465/Win_Rootkit: A kernel-mode rootkit with remote control |
| 2025-03-24 01:27:30.574 | https://github.com/Nullxyzz/Windows-RootKit | GitHub - Nullxyzz/Windows-RootKit: Its not made by me, Its just a compilation of diffrent windows rootkits |
| 2025-03-24 01:39:51.900 | https://github.com/brosck/Frosty | GitHub - brosck/Frosty: 「🧊」Ring 3 Rootkit for Windows 10 |
| 2025-03-24 01:39:51.900 | https://github.com/MrEmpy/Frosty | GitHub - brosck/Frosty: 「🧊」Ring 3 Rootkit for Windows 10 |
| 2025-03-24 01:40:05.815 | https://github.com/Dol3v/Mark | GitHub - Dol3v/Mark: Windows kernel rootkit for the highschool's cyber track |
| 2025-03-24 01:40:07.609 | https://github.com/memN0ps/eagle-rs | GitHub - memN0ps/eagle-rs: Rusty Rootkit - Windows Kernel Rookit in Rust (Codename: Eagle) |
| 2025-03-24 01:40:32.416 | https://github.com/eLoopWoo/zwhawk | GitHub - eLoopWoo/zwhawk: A kernel rootkit with remote command and control interface for windows |
| 2025-03-24 01:46:34.018 | https://github.com/rakendrathapa/NetRootKit | GitHub - rakendrathapa/NetRootKit: Hide the TCP Connection |
| 2025-03-25 09:13:08.826 | https://github.com/Meowoverflow/Rootkits_Subverting_the_Windows_Kernel | GitHub - Meowoverflow/Rootkits_Subverting_the_Windows_Kernel: source code for the examples and topics from the book |
| 2025-03-25 09:16:46.105 | https://github.com/landhb/HideProcess | HideProcess,一个基本的直接内核对象操作 rootkit,它从 EPROCESS 列表中删除一个进程,并将其从任务管理器中隐藏起来(HideProcess, a basic direct kernel object manipulation rootkit that removes a process from the EPROCESS list and hides it from Task Manager) |
| 2025-04-03 02:36:21.421 | https://github.com/carloslack/KoviD | GitHub - carloslack/KoviD: Red-Team Linux kernel rootkit |
| 2025-04-03 02:48:13.341 | https://github.com/FunnyWolf | FunnyWolf (rootkit) · GitHub |
| 2025-04-10 04:37:45.062 | https://github.com/BeneficialCode/WinArk | GitHub - BeneficialCode/WinArk: Windows Anti-Rootkit Tool |
| 2025-04-10 05:10:11.795 | https://github.com/swwwolf/wdbgark | GitHub - swwwolf/wdbgark: WinDBG Anti-RootKit Extension |
| 2025-04-10 05:10:15.137 | https://github.com/eternalklaus/EternalAntirootkit | GitHub - eternalklaus/EternalAntirootkit: Anti-rootkit works as a Windows system driver. |
| 2025-04-10 05:10:43.093 | https://github.com/forentfraps/AntiRootkit | GitHub - forentfraps/AntiRootkit: Usermode rootkit scanner written in C x86 + x86_64 |
| 2025-04-10 06:45:19.255 | https://github.com/codetronik/KernelV | KernelV,Rootkit 和 Anti-rootkit,下载 KernelV 的源码*GitHub*帮酷 |
| 2025-04-17 01:03:23.699 | https://github.com/XaFF-XaFF/Black-Angel-Rootkit | GitHub - XaFF-XaFF/Black-Angel-Rootkit: Black Angel is a Windows 11/10 x64 kernel mode rootkit. Rootkit can be loaded with enabled DSE while maintaining its full functionality. |
| 2025-04-17 01:03:46.823 | https://github.com/XaFF-XaFF/Cronos-Rootkit | GitHub - XaFF-XaFF/Cronos-Rootkit: Cronos is Windows 10/11 x64 ring 0 rootkit. Cronos is able to hide processes, protect and elevate them with token manipulation. |
| 2025-04-17 01:08:20.491 | https://github.com/Idov31/Nidhogg | GitHub - Idov31/Nidhogg: Nidhogg is an all-in-one simple to use windows kernel rootkit. |
| 2025-04-17 01:08:22.483 | https://github.com/ColeHouston/Sunder | GitHub - ColeHouston/Sunder: Windows rootkit designed to work with BYOVD exploits |
| 2025-04-17 01:08:24.541 | https://github.com/ispoleet/malware | GitHub - ispoleet/malware: Some of my old malware |
| 2025-04-17 01:10:21.217 | https://github.com/joaoviictorti/shadow-rs | GitHub - joaoviictorti/shadow-rs: Windows Kernel Rootkit in Rust |
| 2025-04-17 01:14:23.331 | https://github.com/ZeroMemoryEx/Chaos-Rootkit | GitHub - ZeroMemoryEx/Chaos-Rootkit: Now You See Me, Now You Don't |
| 2025-04-17 01:14:28.746 | https://github.com/crvvdev/MasterHide | GitHub - crvvdev/MasterHide: A x64 Windows Rootkit using SSDT or Hypervisor hook |
| 2025-04-17 01:14:40.074 | https://github.com/ZeroMemoryEx/URootkit | URootkit,简单的用户模式 Rootkit,下载 URootkit 的源码*GitHub*帮酷(GitHub - kumar/URootkit: Simple user-mode rootkit. Download the source code for URootkit.) |
| 2025-04-17 01:14:58.455 | https://github.com/assarbad/Nidhogg | GitHub - assarbad/Nidhogg: Nidhogg is an all-in-one simple to use rootkit for red teams. |
| 2025-04-17 01:15:13.143 | https://github.com/adamhlt/Basic-Rootkit | GitHub - adamhlt/Basic-Rootkit: POC Ring3 Windows Rootkit (x86 / x64) - Hide processes and files |
| 2025-04-17 01:15:17.263 | https://github.com/huoji120/numen | GitHub - huoji120/numen: 简单安排一下 autochk.sys 这个 rootkit |
| 2025-04-27 05:33:00.318 | https://github.com/NoviceLive/research-rootkit | GitHub - NoviceLive/research-rootkit: LibZeroEvil & the Research Rootkit project. |
| 2025-04-27 05:40:00.848 | https://github.com/T1erno/ALPART | GitHub - T1erno/ALPART: Automatic Linux Persistence And Rootkit Tool |
| 2025-04-27 05:40:02.549 | https://github.com/MatthiasCr/LKM-Rootkit | GitHub - MatthiasCr/LKM-Rootkit: Malware to gain persistence on a victims machine. |
| 2025-04-27 05:45:31.565 | https://github.com/pythonmandev/rootkit_persistence- | rootkit*persistence-,专注于 Linux rootkit 的代码集合,下载 rootkit_persistence-的源码\_GitHub*帮酷(GitHub - rootkit_persistence/rootkit_persistence-: A collection of code focused on Linux rootkits. Download the source code.) |
| 2025-04-27 05:45:34.469 | https://github.com/DualHorizon/blackpill | GitHub - DualHorizon/blackpill: A Linux kernel rootkit in Rust using a custom made type-2 hypervisor, eBPF XDP and TC programs |
| 2025-04-27 05:51:12.876 | https://github.com/MatheuZSecurity/Rootkit | Rootkit,专注于 Linux rootkit 的代码集合,下载 Rootkit 的源码*GitHub*帮酷 |
| 2025-04-27 05:54:57.312 | https://github.com/killvxk/Rootkit-MatheuZSecurity | GitHub - killvxk/Rootkit-MatheuZSecurity: Collection of codes focused on Linux rootkits |
| 2025-05-20 05:11:12.591 | https://github.com/f0rb1dd3n/Reptile | GitHub - f0rb1dd3n/Reptile: LKM Linux rootkit |
| first visit | url | title |
| --- | --- | --- |
| 2025-03-24 00:57:27.819 | https://github.com/D4stiny/spectre | GitHub - D4stiny/spectre: A Windows kernel-mode rootkit that abuses legitimate communication channels to control a machine. |
| 2025-03-24 00:57:30.161 | https://github.com/TheMalwareGuardian/Bentico | GitHub - TheMalwareGuardian/Bentico: Windows Kernel Mode Rootkit |
| 2025-03-24 00:57:33.816 | https://github.com/eversinc33/Banshee | GitHub - eversinc33/Banshee: Experimental Windows x64 Kernel Rootkit with anti-rootkit evasion features. |
| 2025-03-24 00:57:35.016 | https://github.com/alal4465/Win_Rootkit | GitHub - alal4465/Win_Rootkit: A kernel-mode rootkit with remote control |
| 2025-03-24 01:27:30.574 | https://github.com/Nullxyzz/Windows-RootKit | GitHub - Nullxyzz/Windows-RootKit: Its not made by me, Its just a compilation of diffrent windows rootkits |
| 2025-03-24 01:39:51.900 | https://github.com/brosck/Frosty | GitHub - brosck/Frosty: 「🧊」Ring 3 Rootkit for Windows 10 |
| 2025-03-24 01:39:51.900 | https://github.com/MrEmpy/Frosty | GitHub - brosck/Frosty: 「🧊」Ring 3 Rootkit for Windows 10 |
| 2025-03-24 01:40:05.815 | https://github.com/Dol3v/Mark | GitHub - Dol3v/Mark: Windows kernel rootkit for the highschool's cyber track |
| 2025-03-24 01:40:07.609 | https://github.com/memN0ps/eagle-rs | GitHub - memN0ps/eagle-rs: Rusty Rootkit - Windows Kernel Rookit in Rust (Codename: Eagle) |
| 2025-03-24 01:40:32.416 | https://github.com/eLoopWoo/zwhawk | GitHub - eLoopWoo/zwhawk: A kernel rootkit with remote command and control interface for windows |
| 2025-03-24 01:46:34.018 | https://github.com/rakendrathapa/NetRootKit | GitHub - rakendrathapa/NetRootKit: Hide the TCP Connection |
| 2025-03-25 09:13:08.826 | https://github.com/Meowoverflow/Rootkits_Subverting_the_Windows_Kernel | GitHub - Meowoverflow/Rootkits_Subverting_the_Windows_Kernel: source code for the examples and topics from the book |
| 2025-03-25 09:16:46.105 | https://github.com/landhb/HideProcess | HideProcess,一个基本的直接内核对象操作 rootkit,它从 EPROCESS 列表中删除一个进程,并将其从任务管理器中隐藏起来(HideProcess, a basic direct kernel object manipulation rootkit that removes a process from the EPROCESS list and hides it from Task Manager) |
| 2025-04-03 02:36:21.421 | https://github.com/carloslack/KoviD | GitHub - carloslack/KoviD: Red-Team Linux kernel rootkit |
| 2025-04-03 02:48:13.341 | https://github.com/FunnyWolf | FunnyWolf (rootkit) · GitHub |
| 2025-04-10 04:37:45.062 | https://github.com/BeneficialCode/WinArk | GitHub - BeneficialCode/WinArk: Windows Anti-Rootkit Tool |
| 2025-04-10 05:10:11.795 | https://github.com/swwwolf/wdbgark | GitHub - swwwolf/wdbgark: WinDBG Anti-RootKit Extension |
| 2025-04-10 05:10:15.137 | https://github.com/eternalklaus/EternalAntirootkit | GitHub - eternalklaus/EternalAntirootkit: Anti-rootkit works as a Windows system driver. |
| 2025-04-10 05:10:43.093 | https://github.com/forentfraps/AntiRootkit | GitHub - forentfraps/AntiRootkit: Usermode rootkit scanner written in C x86 + x86_64 |
| 2025-04-10 06:45:19.255 | https://github.com/codetronik/KernelV | KernelV,Rootkit 和 Anti-rootkit,下载 KernelV 的源码*GitHub*帮酷 |
| 2025-04-17 01:03:23.699 | https://github.com/XaFF-XaFF/Black-Angel-Rootkit | GitHub - XaFF-XaFF/Black-Angel-Rootkit: Black Angel is a Windows 11/10 x64 kernel mode rootkit. Rootkit can be loaded with enabled DSE while maintaining its full functionality. |
| 2025-04-17 01:03:46.823 | https://github.com/XaFF-XaFF/Cronos-Rootkit | GitHub - XaFF-XaFF/Cronos-Rootkit: Cronos is Windows 10/11 x64 ring 0 rootkit. Cronos is able to hide processes, protect and elevate them with token manipulation. |
| 2025-04-17 01:08:20.491 | https://github.com/Idov31/Nidhogg | GitHub - Idov31/Nidhogg: Nidhogg is an all-in-one simple to use windows kernel rootkit. |
| 2025-04-17 01:08:22.483 | https://github.com/ColeHouston/Sunder | GitHub - ColeHouston/Sunder: Windows rootkit designed to work with BYOVD exploits |
| 2025-04-17 01:08:24.541 | https://github.com/ispoleet/malware | GitHub - ispoleet/malware: Some of my old malware |
| 2025-04-17 01:10:21.217 | https://github.com/joaoviictorti/shadow-rs | GitHub - joaoviictorti/shadow-rs: Windows Kernel Rootkit in Rust |
| 2025-04-17 01:14:23.331 | https://github.com/ZeroMemoryEx/Chaos-Rootkit | GitHub - ZeroMemoryEx/Chaos-Rootkit: Now You See Me, Now You Don't |
| 2025-04-17 01:14:28.746 | https://github.com/crvvdev/MasterHide | GitHub - crvvdev/MasterHide: A x64 Windows Rootkit using SSDT or Hypervisor hook |
| 2025-04-17 01:14:40.074 | https://github.com/ZeroMemoryEx/URootkit | URootkit,简单的用户模式 Rootkit,下载 URootkit 的源码*GitHub*帮酷(GitHub - kumar/URootkit: Simple user-mode rootkit. Download the source code for URootkit.) |
| 2025-04-17 01:14:58.455 | https://github.com/assarbad/Nidhogg | GitHub - assarbad/Nidhogg: Nidhogg is an all-in-one simple to use rootkit for red teams. |
| 2025-04-17 01:15:13.143 | https://github.com/adamhlt/Basic-Rootkit | GitHub - adamhlt/Basic-Rootkit: POC Ring3 Windows Rootkit (x86 / x64) - Hide processes and files |
| 2025-04-17 01:15:17.263 | https://github.com/huoji120/numen | GitHub - huoji120/numen: 简单安排一下 autochk.sys 这个 rootkit |
| 2025-04-27 05:33:00.318 | https://github.com/NoviceLive/research-rootkit | GitHub - NoviceLive/research-rootkit: LibZeroEvil & the Research Rootkit project. |
| 2025-04-27 05:40:00.848 | https://github.com/T1erno/ALPART | GitHub - T1erno/ALPART: Automatic Linux Persistence And Rootkit Tool |
| 2025-04-27 05:40:02.549 | https://github.com/MatthiasCr/LKM-Rootkit | GitHub - MatthiasCr/LKM-Rootkit: Malware to gain persistence on a victims machine. |
| 2025-04-27 05:45:31.565 | https://github.com/pythonmandev/rootkit_persistence- | rootkit*persistence-,专注于 Linux rootkit 的代码集合,下载 rootkit_persistence-的源码\_GitHub*帮酷(GitHub - rootkit_persistence/rootkit_persistence-: A collection of code focused on Linux rootkits. Download the source code.) |
| 2025-04-27 05:45:34.469 | https://github.com/DualHorizon/blackpill | GitHub - DualHorizon/blackpill: A Linux kernel rootkit in Rust using a custom made type-2 hypervisor, eBPF XDP and TC programs |
| 2025-04-27 05:51:12.876 | https://github.com/MatheuZSecurity/Rootkit | Rootkit,专注于 Linux rootkit 的代码集合,下载 Rootkit 的源码*GitHub*帮酷 |
| 2025-04-27 05:54:57.312 | https://github.com/killvxk/Rootkit-MatheuZSecurity | GitHub - killvxk/Rootkit-MatheuZSecurity: Collection of codes focused on Linux rootkits |
| 2025-05-20 05:11:12.591 | https://github.com/f0rb1dd3n/Reptile | GitHub - f0rb1dd3n/Reptile: LKM Linux rootkit |
| first visit | url | title |
| --- | --- | --- |
| 2025-03-24 00:57:27.819 | https://github.com/D4stiny/spectre | GitHub - D4stiny/spectre: A Windows kernel-mode rootkit that abuses legitimate communication channels to control a machine. |
| 2025-03-24 00:57:30.161 | https://github.com/TheMalwareGuardian/Bentico | GitHub - TheMalwareGuardian/Bentico: Windows Kernel Mode Rootkit |
| 2025-03-24 00:57:33.816 | https://github.com/eversinc33/Banshee | GitHub - eversinc33/Banshee: Experimental Windows x64 Kernel Rootkit with anti-rootkit evasion features. |
| 2025-03-24 00:57:35.016 | https://github.com/alal4465/Win_Rootkit | GitHub - alal4465/Win_Rootkit: A kernel-mode rootkit with remote control |
| 2025-03-24 01:27:30.574 | https://github.com/Nullxyzz/Windows-RootKit | GitHub - Nullxyzz/Windows-RootKit: Its not made by me, Its just a compilation of diffrent windows rootkits |
| 2025-03-24 01:39:51.900 | https://github.com/brosck/Frosty | GitHub - brosck/Frosty: 「🧊」Ring 3 Rootkit for Windows 10 |
| 2025-03-24 01:39:51.900 | https://github.com/MrEmpy/Frosty | GitHub - brosck/Frosty: 「🧊」Ring 3 Rootkit for Windows 10 |
| 2025-03-24 01:40:05.815 | https://github.com/Dol3v/Mark | GitHub - Dol3v/Mark: Windows kernel rootkit for the highschool's cyber track |
| 2025-03-24 01:40:07.609 | https://github.com/memN0ps/eagle-rs | GitHub - memN0ps/eagle-rs: Rusty Rootkit - Windows Kernel Rookit in Rust (Codename: Eagle) |
| 2025-03-24 01:40:32.416 | https://github.com/eLoopWoo/zwhawk | GitHub - eLoopWoo/zwhawk: A kernel rootkit with remote command and control interface for windows |
| 2025-03-24 01:46:34.018 | https://github.com/rakendrathapa/NetRootKit | GitHub - rakendrathapa/NetRootKit: Hide the TCP Connection |
| 2025-03-25 09:13:08.826 | https://github.com/Meowoverflow/Rootkits_Subverting_the_Windows_Kernel | GitHub - Meowoverflow/Rootkits_Subverting_the_Windows_Kernel: source code for the examples and topics from the book |
| 2025-03-25 09:16:46.105 | https://github.com/landhb/HideProcess | HideProcess,一个基本的直接内核对象操作 rootkit,它从 EPROCESS 列表中删除一个进程,并将其从任务管理器中隐藏起来(HideProcess, a basic direct kernel object manipulation rootkit that removes a process from the EPROCESS list and hides it from Task Manager) |
| 2025-04-03 02:36:21.421 | https://github.com/carloslack/KoviD | GitHub - carloslack/KoviD: Red-Team Linux kernel rootkit |
| 2025-04-03 02:48:13.341 | https://github.com/FunnyWolf | FunnyWolf (rootkit) · GitHub |
| 2025-04-10 04:37:45.062 | https://github.com/BeneficialCode/WinArk | GitHub - BeneficialCode/WinArk: Windows Anti-Rootkit Tool |
| 2025-04-10 05:10:11.795 | https://github.com/swwwolf/wdbgark | GitHub - swwwolf/wdbgark: WinDBG Anti-RootKit Extension |
| 2025-04-10 05:10:15.137 | https://github.com/eternalklaus/EternalAntirootkit | GitHub - eternalklaus/EternalAntirootkit: Anti-rootkit works as a Windows system driver. |
| 2025-04-10 05:10:43.093 | https://github.com/forentfraps/AntiRootkit | GitHub - forentfraps/AntiRootkit: Usermode rootkit scanner written in C x86 + x86_64 |
| 2025-04-10 06:45:19.255 | https://github.com/codetronik/KernelV | KernelV,Rootkit 和 Anti-rootkit,下载 KernelV 的源码*GitHub*帮酷 |
| 2025-04-17 01:03:23.699 | https://github.com/XaFF-XaFF/Black-Angel-Rootkit | GitHub - XaFF-XaFF/Black-Angel-Rootkit: Black Angel is a Windows 11/10 x64 kernel mode rootkit. Rootkit can be loaded with enabled DSE while maintaining its full functionality. |
| 2025-04-17 01:03:46.823 | https://github.com/XaFF-XaFF/Cronos-Rootkit | GitHub - XaFF-XaFF/Cronos-Rootkit: Cronos is Windows 10/11 x64 ring 0 rootkit. Cronos is able to hide processes, protect and elevate them with token manipulation. |
| 2025-04-17 01:08:20.491 | https://github.com/Idov31/Nidhogg | GitHub - Idov31/Nidhogg: Nidhogg is an all-in-one simple to use windows kernel rootkit. |
| 2025-04-17 01:08:22.483 | https://github.com/ColeHouston/Sunder | GitHub - ColeHouston/Sunder: Windows rootkit designed to work with BYOVD exploits |
| 2025-04-17 01:08:24.541 | https://github.com/ispoleet/malware | GitHub - ispoleet/malware: Some of my old malware |
| 2025-04-17 01:10:21.217 | https://github.com/joaoviictorti/shadow-rs | GitHub - joaoviictorti/shadow-rs: Windows Kernel Rootkit in Rust |
| 2025-04-17 01:14:23.331 | https://github.com/ZeroMemoryEx/Chaos-Rootkit | GitHub - ZeroMemoryEx/Chaos-Rootkit: Now You See Me, Now You Don't |
| 2025-04-17 01:14:28.746 | https://github.com/crvvdev/MasterHide | GitHub - crvvdev/MasterHide: A x64 Windows Rootkit using SSDT or Hypervisor hook |
| 2025-04-17 01:14:40.074 | https://github.com/ZeroMemoryEx/URootkit | URootkit,简单的用户模式 Rootkit,下载 URootkit 的源码*GitHub*帮酷(GitHub - kumar/URootkit: Simple user-mode rootkit. Download the source code for URootkit.) |
| 2025-04-17 01:14:58.455 | https://github.com/assarbad/Nidhogg | GitHub - assarbad/Nidhogg: Nidhogg is an all-in-one simple to use rootkit for red teams. |
| 2025-04-17 01:15:13.143 | https://github.com/adamhlt/Basic-Rootkit | GitHub - adamhlt/Basic-Rootkit: POC Ring3 Windows Rootkit (x86 / x64) - Hide processes and files |
| 2025-04-17 01:15:17.263 | https://github.com/huoji120/numen | GitHub - huoji120/numen: 简单安排一下 autochk.sys 这个 rootkit |
| 2025-04-27 05:33:00.318 | https://github.com/NoviceLive/research-rootkit | GitHub - NoviceLive/research-rootkit: LibZeroEvil & the Research Rootkit project. |
| 2025-04-27 05:40:00.848 | https://github.com/T1erno/ALPART | GitHub - T1erno/ALPART: Automatic Linux Persistence And Rootkit Tool |
| 2025-04-27 05:40:02.549 | https://github.com/MatthiasCr/LKM-Rootkit | GitHub - MatthiasCr/LKM-Rootkit: Malware to gain persistence on a victims machine. |
| 2025-04-27 05:45:31.565 | https://github.com/pythonmandev/rootkit_persistence- | rootkit*persistence-,专注于 Linux rootkit 的代码集合,下载 rootkit_persistence-的源码\_GitHub*帮酷(GitHub - rootkit_persistence/rootkit_persistence-: A collection of code focused on Linux rootkits. Download the source code.) |
| 2025-04-27 05:45:34.469 | https://github.com/DualHorizon/blackpill | GitHub - DualHorizon/blackpill: A Linux kernel rootkit in Rust using a custom made type-2 hypervisor, eBPF XDP and TC programs |
| 2025-04-27 05:51:12.876 | https://github.com/MatheuZSecurity/Rootkit | Rootkit,专注于 Linux rootkit 的代码集合,下载 Rootkit 的源码*GitHub*帮酷 |
| 2025-04-27 05:54:57.312 | https://github.com/killvxk/Rootkit-MatheuZSecurity | GitHub - killvxk/Rootkit-MatheuZSecurity: Collection of codes focused on Linux rootkits |
| 2025-05-20 05:11:12.591 | https://github.com/f0rb1dd3n/Reptile | GitHub - f0rb1dd3n/Reptile: LKM Linux rootkit |

caption - rootkit関連の訪問記録

translate.google.*の訪問記録から、主に英語、韓国語のドキュメント、docs、LLMの応答などを中国語に翻訳していた。翻訳内容の一部は以下の通りである。

History에서 확인한 Google Translate

caption - Historyから確認されたGoogle翻訳

| timestamp | text |
| --- | --- |
| 2025-03-27 04:52:46.942 | 문서는 대한민국의 안전보장상 중요한      내용과 개인정보가 수록되어 있으므로 아래      \\n     사항을 준수해야  |
| 2025-04-03 00:54:16.358 | CryptoMalloc\\nEncrypt your RAM!!! Allows you to encrypt the "physical" memory of any process.\\n\\nHow does it work?\\nCryptoMalloc as the name suggest overloads the libc standard malloc function and replaces it with the one that will map process\\' virtual memory to a managed ANONYMOUS mapping. ... |
| 2025-04-22 08:35:59.740 | 무선용 비대칭키 암호 메시지를 처리한다.\\nParameters:\\nbaCert - 키분배용 인증서\\nbaPriKey - 키분배용 개인키\\nbaWapEnvData - 무선용 비대칭키 암호 멧시지\\nReturns:\\n성공시 0 리턴 (baReturnArray를 이용하여 원본 메시지를 획득한다.)\\n실패시 에러코드(양수) 리턴 (sReturnString, sDetailErrorString을 이용하여 에러 원인을 확인한다.) |
| 2025-04-25 06:09:41.951 | // They didn\\'t like posix basename so they defined another function with the\\n// same name and if you include libgen.h it #defines basename to something\\n// else (where they implemented the real basename), and that define breaks\\n// the table entry for the basename command. They didn\\'t make a new function\\n// with a different name for their new behavior because gnu.\\n//\\n// Solution: don\\'t use their broken header, provide an inline to redirect the\\n// correct name to the broken name.\\n |
| 2025-05-07 07:24:38.343 | \\nSVIP用户使用地址点击我\\nlibev dup2 in thread\\n欢迎使用 公益站! 站长合作邮箱:wxgpt@qq.com\\n\\nOkay, let\\'s break down the implications of using dup2 in a thread while libev is running, especially if the file descriptors involved are being managed by libev.\\n\\nThe Core Problem:\\n\\nUsing dup2 on file descriptors (FDs) that are currently being watched by a libev event loop, ... |
| timestamp | text |
| --- | --- |
| 2025-03-27 04:52:46.942 | 문서는 대한민국의 안전보장상 중요한      내용과 개인정보가 수록되어 있으므로 아래      \\n     사항을 준수해야  |
| 2025-04-03 00:54:16.358 | CryptoMalloc\\nEncrypt your RAM!!! Allows you to encrypt the "physical" memory of any process.\\n\\nHow does it work?\\nCryptoMalloc as the name suggest overloads the libc standard malloc function and replaces it with the one that will map process\\' virtual memory to a managed ANONYMOUS mapping. ... |
| 2025-04-22 08:35:59.740 | 무선용 비대칭키 암호 메시지를 처리한다.\\nParameters:\\nbaCert - 키분배용 인증서\\nbaPriKey - 키분배용 개인키\\nbaWapEnvData - 무선용 비대칭키 암호 멧시지\\nReturns:\\n성공시 0 리턴 (baReturnArray를 이용하여 원본 메시지를 획득한다.)\\n실패시 에러코드(양수) 리턴 (sReturnString, sDetailErrorString을 이용하여 에러 원인을 확인한다.) |
| 2025-04-25 06:09:41.951 | // They didn\\'t like posix basename so they defined another function with the\\n// same name and if you include libgen.h it #defines basename to something\\n// else (where they implemented the real basename), and that define breaks\\n// the table entry for the basename command. They didn\\'t make a new function\\n// with a different name for their new behavior because gnu.\\n//\\n// Solution: don\\'t use their broken header, provide an inline to redirect the\\n// correct name to the broken name.\\n |
| 2025-05-07 07:24:38.343 | \\nSVIP用户使用地址点击我\\nlibev dup2 in thread\\n欢迎使用 公益站! 站长合作邮箱:wxgpt@qq.com\\n\\nOkay, let\\'s break down the implications of using dup2 in a thread while libev is running, especially if the file descriptors involved are being managed by libev.\\n\\nThe Core Problem:\\n\\nUsing dup2 on file descriptors (FDs) that are currently being watched by a libev event loop, ... |
| timestamp | text |
| --- | --- |
| 2025-03-27 04:52:46.942 | 문서는 대한민국의 안전보장상 중요한      내용과 개인정보가 수록되어 있으므로 아래      \\n     사항을 준수해야  |
| 2025-04-03 00:54:16.358 | CryptoMalloc\\nEncrypt your RAM!!! Allows you to encrypt the "physical" memory of any process.\\n\\nHow does it work?\\nCryptoMalloc as the name suggest overloads the libc standard malloc function and replaces it with the one that will map process\\' virtual memory to a managed ANONYMOUS mapping. ... |
| 2025-04-22 08:35:59.740 | 무선용 비대칭키 암호 메시지를 처리한다.\\nParameters:\\nbaCert - 키분배용 인증서\\nbaPriKey - 키분배용 개인키\\nbaWapEnvData - 무선용 비대칭키 암호 멧시지\\nReturns:\\n성공시 0 리턴 (baReturnArray를 이용하여 원본 메시지를 획득한다.)\\n실패시 에러코드(양수) 리턴 (sReturnString, sDetailErrorString을 이용하여 에러 원인을 확인한다.) |
| 2025-04-25 06:09:41.951 | // They didn\\'t like posix basename so they defined another function with the\\n// same name and if you include libgen.h it #defines basename to something\\n// else (where they implemented the real basename), and that define breaks\\n// the table entry for the basename command. They didn\\'t make a new function\\n// with a different name for their new behavior because gnu.\\n//\\n// Solution: don\\'t use their broken header, provide an inline to redirect the\\n// correct name to the broken name.\\n |
| 2025-05-07 07:24:38.343 | \\nSVIP用户使用地址点击我\\nlibev dup2 in thread\\n欢迎使用 公益站! 站长合作邮箱:wxgpt@qq.com\\n\\nOkay, let\\'s break down the implications of using dup2 in a thread while libev is running, especially if the file descriptors involved are being managed by libev.\\n\\nThe Core Problem:\\n\\nUsing dup2 on file descriptors (FDs) that are currently being watched by a libev event loop, ... |

caption - 翻訳対象データ

訪問記録の中で、特に中国のフォーラムである「acfun.cn/v/list63/index.htm」を頻繁に訪れていた。acfun.cnの最初の記録は2021-05-19 03:08:57.000であり、訪問記録が存在する2025-03-18 08:00:42.949〜2025-05-28 07:11:01.791の間に232回、全体のacfun.cn訪問記録は1,440回である。

検索エンジンの使用頻度は以下の表の通りである。google、duckduckgoに次いでbaiduを最も多く使用していたことがわかる。

| search engine | count |
| --- | --- |
| google.com/search | 7,332 |
| duckduckgo.com | 1,301 |
| baidu.com | 319 |
| yandex.com | 189

| search engine | count |
| --- | --- |
| google.com/search | 7,332 |
| duckduckgo.com | 1,301 |
| baidu.com | 319 |
| yandex.com | 189

| search engine | count |
| --- | --- |
| google.com/search | 7,332 |
| duckduckgo.com | 1,301 |
| baidu.com | 319 |
| yandex.com | 189

caption - 検索エンジンの使用頻度

3.2.3. geolocation

work/home/user/.config/google-chrome/Local Stateの記録が、work/home/user/.config/google-chrome/.com.google.Chrome.*に残っていた。これらの記録によると、ChromeのVariationsServiceが位置をtwhkjksgの順に変更していった。詳細情報は以下の表を参照されたい。なお、variations_permanent_consistency_countryvariations_countryはソースコードを分析して機能を確認した。

  • variations_permanent_consistency_country: https://source.chromium.org/chromium/chromium/src/+/main:components/variations/pref_names.cc;l=59

  • variations_country: https://source.chromium.org/chromium/chromium/src/+/main:components/variations/pref_names.cc;l=22

| file name | profile.info_cache.Default.active_time | variations_permanent_consistency_country | variations_country |
| --- | --- | --- | --- |
| .com.google.Chrome.zDDdld | 2020-04-23T19:11:59.112960 | tw | tw |
| .com.google.Chrome.a24195 | 2021-04-08T12:12:44.552911 | hk | jp |
| .com.google.Chrome.JqmFGk | 2021-05-18T15:50:41.894885 | jp | jp |
| Local State(Brave) | 2021-05-18T16:13:29.503903 | JP | JP |
| .com.google.Chrome.6G7Z9m | 2025-04-18T10:32:30.958497 | sg | sg |
| .com.google.Chrome.iNphSA | 2025-05-20T09:25:28.242808 | sg | sg |
| Local State | 2025-05-28T15:48:54.071609 | sg | sg

| file name | profile.info_cache.Default.active_time | variations_permanent_consistency_country | variations_country |
| --- | --- | --- | --- |
| .com.google.Chrome.zDDdld | 2020-04-23T19:11:59.112960 | tw | tw |
| .com.google.Chrome.a24195 | 2021-04-08T12:12:44.552911 | hk | jp |
| .com.google.Chrome.JqmFGk | 2021-05-18T15:50:41.894885 | jp | jp |
| Local State(Brave) | 2021-05-18T16:13:29.503903 | JP | JP |
| .com.google.Chrome.6G7Z9m | 2025-04-18T10:32:30.958497 | sg | sg |
| .com.google.Chrome.iNphSA | 2025-05-20T09:25:28.242808 | sg | sg |
| Local State | 2025-05-28T15:48:54.071609 | sg | sg

| file name | profile.info_cache.Default.active_time | variations_permanent_consistency_country | variations_country |
| --- | --- | --- | --- |
| .com.google.Chrome.zDDdld | 2020-04-23T19:11:59.112960 | tw | tw |
| .com.google.Chrome.a24195 | 2021-04-08T12:12:44.552911 | hk | jp |
| .com.google.Chrome.JqmFGk | 2021-05-18T15:50:41.894885 | jp | jp |
| Local State(Brave) | 2021-05-18T16:13:29.503903 | JP | JP |
| .com.google.Chrome.6G7Z9m | 2025-04-18T10:32:30.958497 | sg | sg |
| .com.google.Chrome.iNphSA | 2025-05-20T09:25:28.242808 | sg | sg |
| Local State | 2025-05-28T15:48:54.071609 | sg | sg

caption - geolocation 情報

work/home/user/.mozilla/firefox/06yb5px0.default-release/saved-telemetry-pings/配下のevent pingの検索エンジン地域設定は、すべてHKである。

| file name | creationDate | environment.settings.userPrefs.browser.search.region |
| --- | --- | --- |
| 07c6c260-34a1-4c05-bc9c-881f7c0ee5bc | 2021-01-04T10:44:37.490Z | HK |
| 3c594245-10b0-40a4-b968-0eec2d771c94 | 2021-01-04T11:44:37.491Z | HK

| file name | creationDate | environment.settings.userPrefs.browser.search.region |
| --- | --- | --- |
| 07c6c260-34a1-4c05-bc9c-881f7c0ee5bc | 2021-01-04T10:44:37.490Z | HK |
| 3c594245-10b0-40a4-b968-0eec2d771c94 | 2021-01-04T11:44:37.491Z | HK

| file name | creationDate | environment.settings.userPrefs.browser.search.region |
| --- | --- | --- |
| 07c6c260-34a1-4c05-bc9c-881f7c0ee5bc | 2021-01-04T10:44:37.490Z | HK |
| 3c594245-10b0-40a4-b968-0eec2d771c94 | 2021-01-04T11:44:37.491Z | HK

caption - 検索エンジンの地域設定情報

work/home/user/.thunderbird/*/.default-default/saved-telemetry-pingsディレクトリ内のファイルから、payload.info.timezoneOffsetを確認できる。

| profile directory name | file name | creationDate | payload.info.timezoneOffset |
| --- | --- | --- | --- |
| 3qg2p785.default-default | eb9839a3-f01f-442b-b25e-a7392c658222 | 2021-04-13T16:00:00.031Z | 480(UTC+8) |
| 3qg2p785.default-default | 0fc51ffd-81a2-4cf0-8c8b-faba548e05ce | 2021-04-14T01:26:25.515Z | 480(UTC+8) |
| 3qg2p785.default-default | 67d8e780-b911-4a9a-9eb7-8b4090252a50 | 2021-04-14T01:26:25.531Z | 480(UTC+8) |
| 3qg2p785.default-default | d5de4158-1043-4b29-8b09-18ce575719be | 2021-04-14T01:26:25.533Z | 480(UTC+8) |
| 3qg2p785.default-default | 3a5102ff-1111-493b-9ab3-2189ad6edc78 | 2022-03-30T22:22:15.828Z | 480(UTC+8) |
| 3qg2p785.default-default | 075cdd5b-1ac0-4dd1-b5bb-f887d8d5db47 | 2022-03-30T22:22:15.829Z | 480(UTC+8) |
| 3qg2p785.default-default | bae0b2e3-3dec-4015-986d-e8d7d97e9ac7 | 2022-03-30T22:22:19.397Z | 480(UTC+8) |
| 3qg2p785.default-default | 91500b5d-b577-45f5-8ec8-5d9b0a5cd1bc | 2022-03-30T22:22:19.398Z | 480(UTC+8) |
| 8inzqqf5.default-release | 1e69b935-10ca-4643-9e15-5b85d23aac29 | 2023-12-27T10:18:21.103Z | 540(UTC+9) |
| 8inzqqf5.default-release  | 380a9109-2b49-486b-ad0f-23fe852f6bef | 2023-12-27T10:18:21.101Z | 540(UTC+9)

| profile directory name | file name | creationDate | payload.info.timezoneOffset |
| --- | --- | --- | --- |
| 3qg2p785.default-default | eb9839a3-f01f-442b-b25e-a7392c658222 | 2021-04-13T16:00:00.031Z | 480(UTC+8) |
| 3qg2p785.default-default | 0fc51ffd-81a2-4cf0-8c8b-faba548e05ce | 2021-04-14T01:26:25.515Z | 480(UTC+8) |
| 3qg2p785.default-default | 67d8e780-b911-4a9a-9eb7-8b4090252a50 | 2021-04-14T01:26:25.531Z | 480(UTC+8) |
| 3qg2p785.default-default | d5de4158-1043-4b29-8b09-18ce575719be | 2021-04-14T01:26:25.533Z | 480(UTC+8) |
| 3qg2p785.default-default | 3a5102ff-1111-493b-9ab3-2189ad6edc78 | 2022-03-30T22:22:15.828Z | 480(UTC+8) |
| 3qg2p785.default-default | 075cdd5b-1ac0-4dd1-b5bb-f887d8d5db47 | 2022-03-30T22:22:15.829Z | 480(UTC+8) |
| 3qg2p785.default-default | bae0b2e3-3dec-4015-986d-e8d7d97e9ac7 | 2022-03-30T22:22:19.397Z | 480(UTC+8) |
| 3qg2p785.default-default | 91500b5d-b577-45f5-8ec8-5d9b0a5cd1bc | 2022-03-30T22:22:19.398Z | 480(UTC+8) |
| 8inzqqf5.default-release | 1e69b935-10ca-4643-9e15-5b85d23aac29 | 2023-12-27T10:18:21.103Z | 540(UTC+9) |
| 8inzqqf5.default-release  | 380a9109-2b49-486b-ad0f-23fe852f6bef | 2023-12-27T10:18:21.101Z | 540(UTC+9)

| profile directory name | file name | creationDate | payload.info.timezoneOffset |
| --- | --- | --- | --- |
| 3qg2p785.default-default | eb9839a3-f01f-442b-b25e-a7392c658222 | 2021-04-13T16:00:00.031Z | 480(UTC+8) |
| 3qg2p785.default-default | 0fc51ffd-81a2-4cf0-8c8b-faba548e05ce | 2021-04-14T01:26:25.515Z | 480(UTC+8) |
| 3qg2p785.default-default | 67d8e780-b911-4a9a-9eb7-8b4090252a50 | 2021-04-14T01:26:25.531Z | 480(UTC+8) |
| 3qg2p785.default-default | d5de4158-1043-4b29-8b09-18ce575719be | 2021-04-14T01:26:25.533Z | 480(UTC+8) |
| 3qg2p785.default-default | 3a5102ff-1111-493b-9ab3-2189ad6edc78 | 2022-03-30T22:22:15.828Z | 480(UTC+8) |
| 3qg2p785.default-default | 075cdd5b-1ac0-4dd1-b5bb-f887d8d5db47 | 2022-03-30T22:22:15.829Z | 480(UTC+8) |
| 3qg2p785.default-default | bae0b2e3-3dec-4015-986d-e8d7d97e9ac7 | 2022-03-30T22:22:19.397Z | 480(UTC+8) |
| 3qg2p785.default-default | 91500b5d-b577-45f5-8ec8-5d9b0a5cd1bc | 2022-03-30T22:22:19.398Z | 480(UTC+8) |
| 8inzqqf5.default-release | 1e69b935-10ca-4643-9e15-5b85d23aac29 | 2023-12-27T10:18:21.103Z | 540(UTC+9) |
| 8inzqqf5.default-release  | 380a9109-2b49-486b-ad0f-23fe852f6bef | 2023-12-27T10:18:21.101Z | 540(UTC+9)

caption - payload.info.timezoneOffset 情報

work/home/user/.thunderbird/*/.default-default/saved-telemetry-pingsディレクトリ内のtimes.jsonprefs.jsファイルから、それぞれfirstUseとcalendar.timezone.localを確認した。

| profile directory name | firstUse | calendar.timezone.local  |
| --- | --- | --- |
| 3qg2p785.default-default | 2021-04-13T11:39:36.277000 | Asia/Shanghai |
| 8inzqqf5.default-release | 2023-12-25T11:47:56.099000 | Asia/Seoul

| profile directory name | firstUse | calendar.timezone.local  |
| --- | --- | --- |
| 3qg2p785.default-default | 2021-04-13T11:39:36.277000 | Asia/Shanghai |
| 8inzqqf5.default-release | 2023-12-25T11:47:56.099000 | Asia/Seoul

| profile directory name | firstUse | calendar.timezone.local  |
| --- | --- | --- |
| 3qg2p785.default-default | 2021-04-13T11:39:36.277000 | Asia/Shanghai |
| 8inzqqf5.default-release | 2023-12-25T11:47:56.099000 | Asia/Seoul

caption - calendar.timezone.local 情報

ブラウザの閲覧履歴におけるGoogleの地域検出結果が、上記のデータを裏付けている。

  • 2020-12-21T16:07:17.035527において、google.comのtitleが「Google 搜尋」である。

    • work/home/user/.mozilla/firefox/06yb5px0.default-release

  • 2021-05-18 07:15:25.120において、google.comのtitleが「Google 検索」である。

    • work/home/user/.config/BraveSoftware/Brave-Browser/Default

  • 2021-05-18 07:15:30.999において、google.comで「translate」を検索した後、検索結果を介してtranslate.google.co.jpに接続した。

    • work/home/user/.config/BraveSoftware/Brave-Browser/Default

  • 2025-03-19 01:15:18.988において、google.comのtitleが「Google 搜索」である。

    • work/home/user/.config/google-chrome/Default

  • 2025-03-27 04:52:26.132において、google.comで「translate」を検索した後、検索結果を介してtranslate.google.com.sgに接続した。

    • work/home/user/.config/google-chrome/Default

  • 2025-05-13 05:08:47.578において、google.comで「google search」を検索した後、検索結果を介してgoogle.com.sgに接続した。

    • work/home/user/.config/google-chrome/Default

また、github.comのtitleも中国語にローカライズされているケースが多かった。

3.2.4. etc

work/home/user/.config/baidunetdiskパスに、BaiduNetdiskクライアントの設定ファイルが存在する。BaiduNetdiskは、百度網盤(中国系クラウド)ストレージ・同期クライアントである。

baidunetdisk 디렉토리

caption - baidunetdiskディレクトリ

work/home/user/.thunderbird/8inzqqf5.default-release/encrypted-openpgp-passphrase.txtファイルが存在することから、攻撃者は該当のプロファイルでopenpgpを使用していた。

work/home/user/.config/google-chrome/Default/Local Storageに、amiunique.orgの情報が保存されていることがわかる。amiunique.orgは、ブラウザフィンガープリントの統計的頻度を確認できるサイトである。この情報を確認した際に、確認された情報が保存される。

{
  "value": {
    "fpHashed": "05b8bbd2fdc2823aceba4857bba8d604b57decce",
    "cookieId": "24c16ac1-9407-4801-8a56-5381af2ec10f",
    "attributes": {
      "0": [
        {
          "attribute": "userAgent-js",
          "value": "Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6301.219 Safari/537.36",
          "cpt": 32,
          "ratio": 8.588111745289488e-6,
          "versions": {}
        },
        {
          "attribute": "platform",
          "value": "Win32",
          "cpt": 1381816,
          "ratio": 0.3708496943571543,
          "versions": {}
        },
        // ...
        {
          "attribute": "timezone",
          "value": "-540",
          "cpt": 27431,
          "ratio": 0.007361890415157373,
          "versions": {}
        },
        {
          "attribute": "languages-js",
          "value": "en-US,en,ko,zh-CN",
          "cpt": 6,
          "ratio": 1.610270952241779e-6,
          "versions": {}
        },
        // ...
        {
          "attribute": "screen_width",
          "value": "1920",
          "cpt": 760879,
          "ratio": 0.20420355864512876,
          "versions": {}
        },
        {
          "attribute": "screen_height",
          "value": "1200",
          "cpt": 50011,
          "ratio": 0.013421876765427268,
          "versions": {}
        },
        {
          "attribute": "screen_depth",
          "value": "24",
          "cpt": 3124471,
          "ratio": 0.8385408154036372,
          "versions": {}
        },
        {
          "attribute": "screen_availTop",
          "value": "0",
          "cpt": 2994567,
          "ratio": 0.8036773757736345,
          "versions": {}
        },
        {
          "attribute": "screen_availLeft",
          "value": "41",
          "cpt": 191,
          "ratio": 5.126029197969663e-5,
          "versions": {}
        },
        {
          "attribute": "screen_availHeight",
          "value": "1200",
          "cpt": 15086,
          "ratio": 0.004048757930919913,
          "versions": {}
        },
        {
          "attribute": "screen_availWidth",
          "value": "1879",
          "cpt": 312,
          "ratio": 8.373408951657251e-5,
          "versions": {}
        },
        // ...
        {
          "attribute": "battery",
          "value": { "charging": "true", "chargingTime": "0", "level": "1" },
          "cpt": 576035,
          "ratio": 0.15459540466243218,
          "versions": {}
        },
        // ...
        {
          "attribute": "x-forwarded-proto",
          "value": "https",
          "cpt": 3717236,
          "ratio": 0.9976261922379036,
          "versions": {}
        },
        {
          "attribute": "x-real-ip",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        },
        {
          "attribute": "x-forwarded-for",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        },
        // ...
        {
          "attribute": "client-ip",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        }
      ]
      // ...
    }
    // ...
  },
  "expiry"

{
  "value": {
    "fpHashed": "05b8bbd2fdc2823aceba4857bba8d604b57decce",
    "cookieId": "24c16ac1-9407-4801-8a56-5381af2ec10f",
    "attributes": {
      "0": [
        {
          "attribute": "userAgent-js",
          "value": "Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6301.219 Safari/537.36",
          "cpt": 32,
          "ratio": 8.588111745289488e-6,
          "versions": {}
        },
        {
          "attribute": "platform",
          "value": "Win32",
          "cpt": 1381816,
          "ratio": 0.3708496943571543,
          "versions": {}
        },
        // ...
        {
          "attribute": "timezone",
          "value": "-540",
          "cpt": 27431,
          "ratio": 0.007361890415157373,
          "versions": {}
        },
        {
          "attribute": "languages-js",
          "value": "en-US,en,ko,zh-CN",
          "cpt": 6,
          "ratio": 1.610270952241779e-6,
          "versions": {}
        },
        // ...
        {
          "attribute": "screen_width",
          "value": "1920",
          "cpt": 760879,
          "ratio": 0.20420355864512876,
          "versions": {}
        },
        {
          "attribute": "screen_height",
          "value": "1200",
          "cpt": 50011,
          "ratio": 0.013421876765427268,
          "versions": {}
        },
        {
          "attribute": "screen_depth",
          "value": "24",
          "cpt": 3124471,
          "ratio": 0.8385408154036372,
          "versions": {}
        },
        {
          "attribute": "screen_availTop",
          "value": "0",
          "cpt": 2994567,
          "ratio": 0.8036773757736345,
          "versions": {}
        },
        {
          "attribute": "screen_availLeft",
          "value": "41",
          "cpt": 191,
          "ratio": 5.126029197969663e-5,
          "versions": {}
        },
        {
          "attribute": "screen_availHeight",
          "value": "1200",
          "cpt": 15086,
          "ratio": 0.004048757930919913,
          "versions": {}
        },
        {
          "attribute": "screen_availWidth",
          "value": "1879",
          "cpt": 312,
          "ratio": 8.373408951657251e-5,
          "versions": {}
        },
        // ...
        {
          "attribute": "battery",
          "value": { "charging": "true", "chargingTime": "0", "level": "1" },
          "cpt": 576035,
          "ratio": 0.15459540466243218,
          "versions": {}
        },
        // ...
        {
          "attribute": "x-forwarded-proto",
          "value": "https",
          "cpt": 3717236,
          "ratio": 0.9976261922379036,
          "versions": {}
        },
        {
          "attribute": "x-real-ip",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        },
        {
          "attribute": "x-forwarded-for",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        },
        // ...
        {
          "attribute": "client-ip",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        }
      ]
      // ...
    }
    // ...
  },
  "expiry"

{
  "value": {
    "fpHashed": "05b8bbd2fdc2823aceba4857bba8d604b57decce",
    "cookieId": "24c16ac1-9407-4801-8a56-5381af2ec10f",
    "attributes": {
      "0": [
        {
          "attribute": "userAgent-js",
          "value": "Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6301.219 Safari/537.36",
          "cpt": 32,
          "ratio": 8.588111745289488e-6,
          "versions": {}
        },
        {
          "attribute": "platform",
          "value": "Win32",
          "cpt": 1381816,
          "ratio": 0.3708496943571543,
          "versions": {}
        },
        // ...
        {
          "attribute": "timezone",
          "value": "-540",
          "cpt": 27431,
          "ratio": 0.007361890415157373,
          "versions": {}
        },
        {
          "attribute": "languages-js",
          "value": "en-US,en,ko,zh-CN",
          "cpt": 6,
          "ratio": 1.610270952241779e-6,
          "versions": {}
        },
        // ...
        {
          "attribute": "screen_width",
          "value": "1920",
          "cpt": 760879,
          "ratio": 0.20420355864512876,
          "versions": {}
        },
        {
          "attribute": "screen_height",
          "value": "1200",
          "cpt": 50011,
          "ratio": 0.013421876765427268,
          "versions": {}
        },
        {
          "attribute": "screen_depth",
          "value": "24",
          "cpt": 3124471,
          "ratio": 0.8385408154036372,
          "versions": {}
        },
        {
          "attribute": "screen_availTop",
          "value": "0",
          "cpt": 2994567,
          "ratio": 0.8036773757736345,
          "versions": {}
        },
        {
          "attribute": "screen_availLeft",
          "value": "41",
          "cpt": 191,
          "ratio": 5.126029197969663e-5,
          "versions": {}
        },
        {
          "attribute": "screen_availHeight",
          "value": "1200",
          "cpt": 15086,
          "ratio": 0.004048757930919913,
          "versions": {}
        },
        {
          "attribute": "screen_availWidth",
          "value": "1879",
          "cpt": 312,
          "ratio": 8.373408951657251e-5,
          "versions": {}
        },
        // ...
        {
          "attribute": "battery",
          "value": { "charging": "true", "chargingTime": "0", "level": "1" },
          "cpt": 576035,
          "ratio": 0.15459540466243218,
          "versions": {}
        },
        // ...
        {
          "attribute": "x-forwarded-proto",
          "value": "https",
          "cpt": 3717236,
          "ratio": 0.9976261922379036,
          "versions": {}
        },
        {
          "attribute": "x-real-ip",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        },
        {
          "attribute": "x-forwarded-for",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        },
        // ...
        {
          "attribute": "client-ip",
          "value": "156.59.13.154",
          "cpt": 1,
          "ratio": 2.683784920402965e-7,
          "versions": {}
        }
      ]
      // ...
    }
    // ...
  },
  "expiry"

work/home/user/.thunderbird/8inzqqf5.default-release/session.jsonに、開いているウィンドウやタブの情報が記録されている。ファイルパスを解釈した結果は以下の通りである。

  • tos安装文件: tos installation file

  • メールのファイル名形式はmaildirのmailbox形式とみられ、S=はファイルのサイズを表している。




すべての情報を総合すると、以下の通りである。

  1. 攻撃者は、一般的に中国国外では使用されないdeepinを使用している。

  2. IMEのインストール痕跡から、複数の中国語用IMEのみがインストールされていることを確認した。

  3. ブラウザの閲覧履歴から、中国のフォーラムに頻繁にアクセスしていることを確認した。

  4. 翻訳履歴から、韓国語→中国語、英語→中国語へ翻訳していることを確認した。中国語から他国の言語に翻訳した履歴は存在しない。

システム情報から一部の韓国語が確認されたが、韓国人であるとは考えにくく、攻撃者は中国語に堪能であり、中国のエコシステム(サービスおよびソフトウェア)に精通しているとみられる。

4. 過去の国内侵害事故との関連性

4.1. インシデントの概要

当社は2022年に国内金融機関のインシデント調査を実施した。事案は、ローン申請プロセスで顧客情報を入力した後、数日以内に他社から営業の連絡があり、これを異常の兆候と認識した顧客企業が当社にインシデント調査を依頼したものである。

初期のサーバーフォレンジックプロセスでは、マルウェアの実行ファイルは確認されなかったが、ネットワークパケットダンプにおいてSMTP(25/tcp)セッションのペイロードから、HTTPヘッダー・ボディデータと暗号化されたデータが共に含まれているパケットが確認された。パターンベースのキー推測を通じて暗号化キーを特定し、復号に成功した結果、マルウェアの実行コマンドを確認することができた。実際に復号されたデータは以下の通りである。

  • /etc/[マルウェアパス]/[インフォスティーラーローダー] -jar /etc/[マルウェアパス]/[インフォスティーラー] -url [DB情報] -u [アカウントID] -p [アカウントパスワード] -q[日付および時刻データ] -qSTA MBDE -d "0:0, 1:1, 2:1, 5:0" -de "0"

    exit

マルウェアパスからインフォスティーラーとバックドアを確認し、その後、ディスクイメージの分析によってルートキットの存在を確認した。

4.2. マルウェアの関連性

攻撃に使用されたインフォスティーラーは、攻撃者が望む情報の窃取に合わせてカスタマイズされて作成されており、バックドアとルートキットは、その構成および動作方式が攻撃者のVMwareダンプファイル内の tomcat20220420_rootkit ディレクトリで確認されたバックドア、ルートキットと一致している。

좌) master.c(백도어) main 함수 소스코드, 우) 침해사고 당시 확보한 백도어 main 함수 의사코드

caption - 左) master.c(バックドア) main関数のソースコード、右) インシデント当時に確保したバックドア main関数の擬似コード

좌) main.c(루트킷) init 함수 소스코드, 우) 침해사고 당시 확보한 루트킷 init_module 함수 의사코드

caption - 左) main.c(ルートキット) init関数のソースコード、右) インシデント当時に確保したルートキット init_module関数の擬似コード

ビルド時に config.sh ファイルに従って決定されるマジックパケット、ルートキット名、バックドア名などは異なるが、通信データのxorキー、AESキー、ivなど、多様な変数値が同一である。

4.3. 属性分析の根拠

当時は、バックドアとルートキットに関する公開された情報がなく、ルートキットがadore-ngを参考にして作成されたという点以外には特定されなかった。また、コードの類似性や攻撃手法だけでは、特定の攻撃グループに帰属させるには限界があった。しかし、「戦術・技術・手順(TTP)」に基づき、北朝鮮のAPTグループではないという結論に至った。主な根拠は以下の通りである。

  • 北朝鮮のAPTグループは主に情報収集や資産窃取の傾向が強いのに対し、本事案は外部サーバーから内部サーバーまで侵入してDBサーバーの顧客情報を窃取し、ボイスフィッシング(電話詐欺)に活用された形跡が中心であった。

  • 初期侵入もスピアフィッシングやソーシャルエンジニアリング攻撃の手法ではなく、Webサービスの脆弱性が利用された。これは、北朝鮮のAPTグループが主に使用する初期侵入手法とは異なる。また、Webサービスの脆弱性を利用して乗っ取ったとしても、C&Cサーバーとして活用しながら内部侵入まで行うケースは稀である。

  • 公開された情報の中で、Linuxシステムを対象に北朝鮮のAPTグループがルートキットとバックドアを攻撃に活用した履歴は存在しない。

その後、2022年6月にavastがこのルートキットをsyslogkと命名してレポートを公開したが、実環境(In the Wild)で確認されたという事実が明らかにされたのみで、依然として攻撃の背後に関する根拠は不足していた。

しかし、今回公開された攻撃者のVMwareダンプから バックドアとルートキットのオリジナルソースコード が確認され、これは攻撃の背後を推定できる有力な根拠であると判断される。また、ディレクトリ名もインシデント発生時期と類似している。

したがって、APT Down - The North Korea Files レポートで言及された攻撃者が、2022年の国内金融機関インシデントの背後にある有力な攻撃主体であると判断される。

最後に、バックドアとルートキットは2025年版が存在することから、該当のマルウェア群は2022年から攻撃者によって継続的に使用されており、2022年の国内金融機関への攻撃事例以外にも、攻撃者が追加の攻撃を実行してきた可能性が高いと考えられる。これに伴い、感染の有無の確認方法を "7. syslogk rootkit 感染の有無の確認方法" で説明する。

5. 攻撃の背後関係の推定および根拠

APT Down - The North Korea Files レポートの攻撃背後は、中国の UNC5221 グループに関連していると推定される。判断の根拠については、以下で詳しく説明する。

5.1. Kimsuky

5.1.1. Operation Covert Stalker

攻撃者の VMware VM ダンプファイルのうち、Naver フィッシングに関連するファイル(work/mnt/hgfs/Desktop/New folder/default-ssl.conf)から、nid-security[.]com ドメインを確認した。該当ドメインは2024年11月5日に、AhnLab Operation Covert Stalker レポートで言及された IP(27.255[.]80.170)と接続された履歴があり、この IP は work/mnt/hgfs/Desktop/New folder/readme.txt で確認できる。

readme.txt を見ると、IP(27.255[.]80.170)を「フィッシング VPS アドレス」に変更するように指示されていることがわかる。フィッシング VPS サーバーの IP を設定するファイル(work/mnt/hgfs/Desktop/New folder/forward.conf)では、IP が 45.133[.]194.88 に設定されている。

現在、nid-security[.]com ドメインの IP は 45.133[.]194.126 であり、work/mnt/hgfs/Desktop/New folder/forward.con で確認した IP(45.133[.]194.88)と同じ帯域である。このようにインフラ上の接点は存在するが、これ以外に攻撃者が Kimsuky と関連していると判断するには根拠が不十分であると考えられる。

5.1.2. GPKI Stolen Certificates

攻撃者の VMware VM ダンプファイルから、多数の GPKI 証明書が確認された。Kimsuky の Troll Stealer に GPKI 証明書の窃取機能が存在することは知られているが、攻撃者がどのように GPKI 証明書を窃取したのかは確認されておらず、Troll Stealer に関連するファイルも発見されなかった。したがって、Kimsuky との関連性を判断するには根拠が不十分であると考えられる。

5.1.3. Similar Targets

攻撃者の VMware VM ダンプファイルのうち、Naver フィッシングに関連するファイル(/work/mnt/hgfs/Desktop/New folder/htdocs/generator.php)から、nid[.]navermails[.]com ドメインが確認された。該当ドメインはAhnLab が作成したレポートで Naver フィッシングドメインとして言及されているが、Kimsuky に関する内容は確認できない。したがって、根拠が不十分であると判断した。

5.2. APT41 & UNC3886

5.2.1. reptile rootkit 関連性

reptile rootkit は、APT41 および UNC3886 グループが攻撃に使用した履歴が報告されている。

攻撃者の VMware ダンプファイルから確認された syslogk(tomcat*_rootkit)と reptile rootkit の共通点は以下の通りである。

  1. 関数フッキングのために使用するライブラリが同一である。

  2. 一部の関数が類似したコードを持つ。

  3. port knocking 方式でバックドアを実行する。

syslogk と reptile rootkit の間に関連性は存在するものの、reptile rootkit はオープンソースであり、該当指標のみで特定の APT との関連性を判断するには無理があると判断した。実際に syslogk は、reptile rootkit のほかに KoviD rootkit のソースコードがそのまま使用されるなど、多様なオープンソースを参考にした痕跡が存在する。したがって、APT41 や UNC3886 に帰属させるには根拠が不十分であると考えられる。

5.2.3. TinyShell 関連性

UNC3886 グループが攻撃によく使用することで知られる TinyShell のソースコードが、work/mnt/hgfs/share_data/backdoor/20220812/SSS ディレクトリに存在する。しかし、Google から報告された UNC3886 の TinyShell ベースのバックドアは AES、HMAC、または RC4 で暗号化された通信を行うのに対し、確認された TinyShell ソースコードは AES と SHA1 で暗号化された通信を行う。通信方法に差異があるため、関連性の根拠としては不十分であると判断した。

5.3. UNC5221

5.3.1. CVE-2025-0282, BRUSHFIRE

CVE-2025-0282 は Ivanti Connect Secure の RCE 脆弱性であり、UNC5221 がこれを悪用してマルウェアを配布した状況が報告されている。攻撃者のファイルのうち、該当脆弱性を悪用してマルウェアを配布する Python スクリプトが6件確認されており、スクリップトから流布されるマルウェアは、UNC5221 の攻撃で確認された BrushFire であると識別された。

同一の脆弱性の悪用と同一マルウェアの流布状況を根拠に、UNC5221 との関連性が高いと判断した。

5.3.2. SPAWN Family

SPAWN Family は UNC5221 が頻繁に使用するマルウェアであり、work/mnt/hgfs/Desktop/New folder/203.234.192.200_client.zip ファイル内で SPAWN Family との関連指標が確認された。関連性が確認された SPAWN Family は、SPAWNMOLE、SPAWNSNAIL、SPAWNCHIMERA である。

client.py に定義された client_hello の値は、その後のコードで自動的に変更されるが、初期値はUNC5221 の攻撃で使用された SPAWNMOLE のマジックパケットと一致する。変更された client_hello の値は、JPCERT のブログで報告された SpawnChimera のマジックパケットの説明と類似していることが確認された。SPAWNCHIMERA マルウェアは、SPAWNMOLE と同様に UNC5221 の攻撃に使用されたマルウェアであり、前述した CVE-2025-0282 脆弱性を介して流布されたマルウェアである。

controller.py は、client.py が駆動した SOCKS5 プロキシを介して SSH サーバーに接続するクライアントスクリプトであり、SPAWNSNAIL または SPAWNCHIMERA のクライアントプログラムであると推定される。

したがって、SPAWN Family 系列のマルウェアが多数確認され、client_hello の初期値および変更値がそれぞれ SPAWNMOLE、SPAWNCHIMERA のマジックパケットの特性と一致・類似している点、また SPAWN Family 系列のマルウェアが UNC5221 の代表的なマルウェアであるという点を根拠に、UNC5221 との関連性が高いと判断した。

5.3.3. ROOTROT

ROOTROT は、UNC5221 の攻撃で確認されたウェブシェルである。Perl で記述されており、クッキーの特定の値を base64 でデコードして eval で実行する。実行結果は HTML コメントの形式で HTTP レスポンスの最後に挿入され、ユーザーに送信される。攻撃者のファイルのうち、ROOTROT のクライアントと推定されるスクリプト /work/mnt/hgfs/Desktop/ivanti_control/main.py が確認された。該当スクリプトは、与えられたコマンドを実行するように作成された Perl スクリプトを base64 でエンコードした後、DSPSALPREF クッキー値として設定して HTTP GET リクエストを送信する。これは、クッキー値をデコードして Perl で実行する ROOTROT のクライアントの動作と正確に一致する。

また、main.py は GET レスポンスの最後のコメントを base64 でデコードしてユーザーに表示するが、これはウェブシェルの実行結果をユーザーに伝達するための ROOTROT クライアントの標準的な動作と一致する。これらの情報を根拠に、UNC5221 との関連性が高いと判断した。

しかし、UNC5221 関連のマルウェアを、攻撃者が共有されて使用した可能性もある。

  1. マルウェアおよび攻撃ガイド

    • Naver の中間者攻撃に関連するディレクトリから、中国語で攻撃環境の構築について記述された readme.txt ファイルが確認された。

    • work/mnt/hgfs/share_data/backdoor ディレクトリ配下にも、中国語で作成された readme.txt ファイルと、1.ko 图文编译 .doc (1.ko 画像とテキストの編集 .doc)、技术说明书 - 22.docx (技術説明書 - 22.docx) ファイルが存在する。

    • しかし、未完成である Cobalt Strike Beacon や、攻撃者が過去から使用していたと見られる syslogk root は、機能テストとデバッグのためのロジックは存在するものの、使用方法がどこにも記述されていない。

  2. "contact" キーワードが言及されたマルウェア

    • CVE-2025-0282 exploit 関連マルウェアの detect_version で This version may exist vul, Please contact us to check. という文字列を出力していることがわかり、exp1_admin.py でもこれを確認できる。これは、攻撃者が CVE-2025-0282 exploit 関連のファイルを、他の攻撃者や組織内の誰かから共有された可能性を示唆している。

最終的に、公開された資料を分析した結果、攻撃者は中国語に慣れており、中国のソフトウェア、コミュニティ、サービスなどを利用している。攻撃者が使用している OS もまた、中国で開発され、中国人以外にはあまり使用されていないことで知られる deepin OS であることを根拠に、攻撃者は中国人であると推定される。また、UNC5221 の攻撃に関連するマルウェアが多数確認された点から、UNC5221 グループと関連があるものと見られる。

6. おわりに

本レポートは、APT Down - The North Korea Files レポートと公開された資料に基づいて、詳細に分析した内容をまとめたものである。

特に公開された資料は、一般的には入手が困難な攻撃者の実際の環境(VMware VM、VPS)のダンプファイルであり、攻撃者の活動を具体的に調査できるため、大きな助けとなった。

公開された資料には、韓国国内を標的にした攻撃の痕跡と、奪取されたと見られる韓国の企業および機関に関連する資料が多数存在する。特に攻撃者のVMおよびホストで確認された tomcat*_rootkit ディレクトリのルートキット、バックドアのソースコードは、2022年の韓国国内の金融機関におけるインシデントで採取されたマルウェアのソースコードであることを確認した。

ルートキットとバックドアは、2022年バージョンの他に2025年バージョンも存在しており、これは攻撃者が継続的にアップグレードしながら長期にわたって使用してきたことを示唆している。2022年にルートキットとバックドアを使用した実際の攻撃事例が確認されているが、該当するマルウェアは検知が困難であり、感染の兆候を容易に知ることができないため、少なくとも2022年から2025年までに識別されていない追加の攻撃事例が存在するものと見られる。これに伴い、感染のチェック手順を「7. syslogk rootkit 感染有無のチェック方法」にまとめた。

攻撃者は北朝鮮の攻撃グループ Kimsuy ではなく、UNC5221 グループと関連がある中国人であると判断した。攻撃の背後を確信できる根拠は確認されていないが、多数のマルウェア関連ファイルから中国語が確認され、中国のソフトウェア、コミュニティ、サービスを利用した形跡があり、韓国語や英語を中国語に翻訳して確認した痕跡が存在する。そして、Ivanti Connect Secure 関連のマルウェアは、UNC5221 グループが攻撃に使用したマルウェアと一致する部分が多数確認された。

総合的に見て、攻撃者は過去から現在まで韓国国内を継続的に標的にして攻撃を仕掛けてきており、公開資料からはルートキット、1-Day エクスプロイトの悪用、そして韓国国内の企業・機関の資料も多数確認された。これらの状況を総合すると、攻撃者は高度な攻撃能力とマルウェア開発能力を保有しており、実際に攻撃に成功して資料の奪取にまで至った状況が確認された。

7. syslogk rootkit 感染有無のチェック方法

2022年のインシデント調査当時、syslogkルートキットは/etc/init.dに存在するスクリプトによって、ブート時に自動的にロードされていた。公開された資料を通じて、syslogkルートキットをビルドする際に、インストールスクリプト(install.sh)と削除スクリプト(del.sh)、そしてローダースクリプト(shservice)が生成されることが確認できた。このとき生成されるファイルのうち、ローダースクリプト(shservice)は、2022年のインシデント調査当時に確認したスクリプトと同一である。

shservice

caption - shserviceファイルの内容

したがって、config.shで生成されたスクリプトとマルウェアをベースに、感染の有無を調査する。

すべてのスクリプトは管理者アカウントまたは権限で実行する必要があり、2022、2025ルートキットバックドアの両方を検知できる。

7.1. /etc/init.dの隠しファイル調査

/etc/init.dにルートキットをブートするたびにロードするスクリプトが存在するか確認する。スクリプトのファイル名はルートキットによって隠蔽される文字列であるため、一般的には検知できない。

したがって、/etc/init.dディレクトリが占めるディスク容量と、ディレクトリ内で確認可能なすべてのファイルが占めるディスク容量の総和を比較し、隠しファイルが存在するかどうかを確認するスクリプトを作成した。スクリプトは付録 A. check-initd.shとして添付した。

7.2. syslogk rootkitの調査

ローダースクリプトに記述された文字列をベースに、syslogkルートキットのパスを抽出する。dumpを使用して/etc/init.dディレクトリのダンプファイルを生成するため、dumpがインストールされている必要がある。

  • sudo apt install dump

ダンプファイルから一致する文字列が見つかると、ルートキットのパスを抽出し、モジュールをアンロードするために隠蔽を解除するデータを探索する。データが見つかると、ルートキットに書き込んで隠蔽を解除し、アンロードする。

スクリプトは付録 B. find-syslogk.py付録 C. find-syslogk.shとして添付した。

  • 付録 B. find-syslogk.pyはPythonバージョン3.7以上を使用する。

dumpは一部のシステムで動作しないことがあるため、付録 A. check-initd.shで隠しファイルの存在を確認した場合は、ディスクイメージをダンプするか、分析ツールにマウントして調査することをお勧めする。

7.3. Backdoor実行有無の調査

バックドアプロセスは、/proc/*/cmdlineパスに対するグロブ列挙の結果をベースに、直接アクセスした際の結果を比較することで検知する。実行中のプロセスの最大pidを基準にスキャンするため、実行中のプロセスが多いシステムでは負荷が増加する可能性がある。

スクリプトは付録 D. check-backdoor.py付録 E. check-backdoor.shとして添付した。

  • 付録 D. check-backdoor.pyはPythonバージョン3.0以上を使用する。

8. 付録

付録 A. check-initd.sh

initd_disk=$(du /etc/init.d/ 2>/dev/null | awk '{print $1}')
initd_files=$(du /etc/init.d/* 2>/dev/null | awk '{print $1}' | awk '{total_size += $1;} END {print total_size;}')

echo "init.d disk usage: ${initd_disk}"
echo "init.d files disk usage sum: ${initd_files}"

if [ "$initd_disk" -ne "$initd_files" ]; then
    echo "/etc/init.d/ might have a hidden module"
fi
initd_disk=$(du /etc/init.d/ 2>/dev/null | awk '{print $1}')
initd_files=$(du /etc/init.d/* 2>/dev/null | awk '{print $1}' | awk '{total_size += $1;} END {print total_size;}')

echo "init.d disk usage: ${initd_disk}"
echo "init.d files disk usage sum: ${initd_files}"

if [ "$initd_disk" -ne "$initd_files" ]; then
    echo "/etc/init.d/ might have a hidden module"
fi
initd_disk=$(du /etc/init.d/ 2>/dev/null | awk '{print $1}')
initd_files=$(du /etc/init.d/* 2>/dev/null | awk '{print $1}' | awk '{total_size += $1;} END {print total_size;}')

echo "init.d disk usage: ${initd_disk}"
echo "init.d files disk usage sum: ${initd_files}"

if [ "$initd_disk" -ne "$initd_files" ]; then
    echo "/etc/init.d/ might have a hidden module"
fi

付録 B. find-syslogk.py

import subprocess
import os


if os.getuid() != 0:
  print("[-] run as root")
  exit()

def clean():
    if os.path.exists("./dump_object"):
        os.remove("./dump_object")
    if os.path.exists("./modules_list"):
        os.remove("./modules_list")
    if os.path.exists("./dumpfile"):
        os.remove("./dumpfile")
    exit()

def checkpath(filepath):
    filename = os.path.basename(filepath)
    dirlist = os.listdir(os.path.dirname(filepath))
    if filename in dirlist and os.path.exists(filepath):
        return True
    elif os.path.exists(filepath):
        return False
    else:
        print(f"{filepath} not found")
        clean()

subprocess.run(["/bin/sh", "-c", "dump -0f ./dumpfile /etc/init.d/"], text=True)

dump = open("./dumpfile", "rb").read()

shservice = b'#!/bin/bash\n#\ncase "$1" in\n\'start\')\n\t/sbin/insmod '

idx = dump.find(shservice)

suspicious_path = ''
if idx != -1:
    for i in dump[idx + 50:]:
        if i == 10:
            break
        suspicious_path += chr(i)

    print(f"[+] found suspicious module path : {suspicious_path}")
else:
    print("[-] suspicious module path not found")
    clean()

if suspicious_path != '':
    filename = os.path.basename(suspicious_path)
if checkpath(filename) != False:
    print('[-] suspicious module file is not hidden')
    clean()

subprocess.run(["/bin/sh", "-c", f"objdump -s -F -j .rodata.str1.1 {suspicious_path} > dump_object"], text=True)
subprocess.run(["/bin/sh", "-c", f"cp {suspicious_path} ./"], text=True)
subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)

old_modules_list = open('./modules_list', 'r').read().split('\n')
a = len("Starting at file offset: ")
f = open("dump_object", "r").read()
b = f.find("Starting at file offset: ")
offset = f[b+a:]
c = offset.find(')')
offset = int(offset[2:c], 16)
length = int(f.split('\n')[-2].split(' ')[1], 16) + 16
f = open(f'./{filename}', 'rb').read()
stringlist = f[offset:offset+length].strip(b'\x00').split(b'\x00')
modulenameidx = 1
magicidx = 13
procnameidx = 47
print(f"[+] possible module name: {stringlist[modulenameidx]}")
print(f"[+] possible magic: {stringlist[magicidx]}")
print(f"[+] possible proc name: {stringlist[procnameidx]}")

procpath = ''
procpathlist = []
tmppath = os.path.join('/proc', stringlist[procnameidx].decode())
if os.path.exists(tmppath):
    procpath = tmppath
    print(f"[+] found procpath {procpath}")
else:
    for string in stringlist:
        tmppath = os.path.join('/proc', string.decode())
        if os.path.exists(tmppath):
            procpathlist.append(tmppath)
    if len(procpathlist) > 1:
        print(f"[-] expected one path got {len(procpath)} : {procpath}")
        clean()
    elif len(procpathlist) == 0:
        print("[-] could not find process path")
        clean()
    else:
        procpath = procpathlist[0]

subprocess.run(["/bin/sh", "-c", f"echo -n {stringlist[magicidx].decode()} > {procpath}"], text=True)
subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)

modules_list = open("./modules_list", "r").read().split("\n")
for modules in modules_list:
    if modules not in old_modules_list:
        print(f"[+] found rootkit module: {modules}")
        subprocess.run(["/bin/sh", "-c", f"/sbin/rmmod {modules}.ko"], text=True)
        print("[+] rmmod complete")
        clean()

for string in stringlist:
    if b'%' not in string and b'/' not in string:
        subprocess.run(["/bin/sh", "-c", f"echo -n {string.decode()} > {procpath}"], text=True)
        subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)
        modules_list = open("./modules_list", "r").read().split("\n")
        for modules in modules_list:
            if modules not in old_modules_list:
                print(f"[+] found rootkit module: {modules}")
                subprocess.run(["/bin/sh", "-c", f"/sbin/rmmod {modules}.ko"], text=True)
                print("[+] rmmod complete")
                clean()

clean()
import subprocess
import os


if os.getuid() != 0:
  print("[-] run as root")
  exit()

def clean():
    if os.path.exists("./dump_object"):
        os.remove("./dump_object")
    if os.path.exists("./modules_list"):
        os.remove("./modules_list")
    if os.path.exists("./dumpfile"):
        os.remove("./dumpfile")
    exit()

def checkpath(filepath):
    filename = os.path.basename(filepath)
    dirlist = os.listdir(os.path.dirname(filepath))
    if filename in dirlist and os.path.exists(filepath):
        return True
    elif os.path.exists(filepath):
        return False
    else:
        print(f"{filepath} not found")
        clean()

subprocess.run(["/bin/sh", "-c", "dump -0f ./dumpfile /etc/init.d/"], text=True)

dump = open("./dumpfile", "rb").read()

shservice = b'#!/bin/bash\n#\ncase "$1" in\n\'start\')\n\t/sbin/insmod '

idx = dump.find(shservice)

suspicious_path = ''
if idx != -1:
    for i in dump[idx + 50:]:
        if i == 10:
            break
        suspicious_path += chr(i)

    print(f"[+] found suspicious module path : {suspicious_path}")
else:
    print("[-] suspicious module path not found")
    clean()

if suspicious_path != '':
    filename = os.path.basename(suspicious_path)
if checkpath(filename) != False:
    print('[-] suspicious module file is not hidden')
    clean()

subprocess.run(["/bin/sh", "-c", f"objdump -s -F -j .rodata.str1.1 {suspicious_path} > dump_object"], text=True)
subprocess.run(["/bin/sh", "-c", f"cp {suspicious_path} ./"], text=True)
subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)

old_modules_list = open('./modules_list', 'r').read().split('\n')
a = len("Starting at file offset: ")
f = open("dump_object", "r").read()
b = f.find("Starting at file offset: ")
offset = f[b+a:]
c = offset.find(')')
offset = int(offset[2:c], 16)
length = int(f.split('\n')[-2].split(' ')[1], 16) + 16
f = open(f'./{filename}', 'rb').read()
stringlist = f[offset:offset+length].strip(b'\x00').split(b'\x00')
modulenameidx = 1
magicidx = 13
procnameidx = 47
print(f"[+] possible module name: {stringlist[modulenameidx]}")
print(f"[+] possible magic: {stringlist[magicidx]}")
print(f"[+] possible proc name: {stringlist[procnameidx]}")

procpath = ''
procpathlist = []
tmppath = os.path.join('/proc', stringlist[procnameidx].decode())
if os.path.exists(tmppath):
    procpath = tmppath
    print(f"[+] found procpath {procpath}")
else:
    for string in stringlist:
        tmppath = os.path.join('/proc', string.decode())
        if os.path.exists(tmppath):
            procpathlist.append(tmppath)
    if len(procpathlist) > 1:
        print(f"[-] expected one path got {len(procpath)} : {procpath}")
        clean()
    elif len(procpathlist) == 0:
        print("[-] could not find process path")
        clean()
    else:
        procpath = procpathlist[0]

subprocess.run(["/bin/sh", "-c", f"echo -n {stringlist[magicidx].decode()} > {procpath}"], text=True)
subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)

modules_list = open("./modules_list", "r").read().split("\n")
for modules in modules_list:
    if modules not in old_modules_list:
        print(f"[+] found rootkit module: {modules}")
        subprocess.run(["/bin/sh", "-c", f"/sbin/rmmod {modules}.ko"], text=True)
        print("[+] rmmod complete")
        clean()

for string in stringlist:
    if b'%' not in string and b'/' not in string:
        subprocess.run(["/bin/sh", "-c", f"echo -n {string.decode()} > {procpath}"], text=True)
        subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)
        modules_list = open("./modules_list", "r").read().split("\n")
        for modules in modules_list:
            if modules not in old_modules_list:
                print(f"[+] found rootkit module: {modules}")
                subprocess.run(["/bin/sh", "-c", f"/sbin/rmmod {modules}.ko"], text=True)
                print("[+] rmmod complete")
                clean()

clean()
import subprocess
import os


if os.getuid() != 0:
  print("[-] run as root")
  exit()

def clean():
    if os.path.exists("./dump_object"):
        os.remove("./dump_object")
    if os.path.exists("./modules_list"):
        os.remove("./modules_list")
    if os.path.exists("./dumpfile"):
        os.remove("./dumpfile")
    exit()

def checkpath(filepath):
    filename = os.path.basename(filepath)
    dirlist = os.listdir(os.path.dirname(filepath))
    if filename in dirlist and os.path.exists(filepath):
        return True
    elif os.path.exists(filepath):
        return False
    else:
        print(f"{filepath} not found")
        clean()

subprocess.run(["/bin/sh", "-c", "dump -0f ./dumpfile /etc/init.d/"], text=True)

dump = open("./dumpfile", "rb").read()

shservice = b'#!/bin/bash\n#\ncase "$1" in\n\'start\')\n\t/sbin/insmod '

idx = dump.find(shservice)

suspicious_path = ''
if idx != -1:
    for i in dump[idx + 50:]:
        if i == 10:
            break
        suspicious_path += chr(i)

    print(f"[+] found suspicious module path : {suspicious_path}")
else:
    print("[-] suspicious module path not found")
    clean()

if suspicious_path != '':
    filename = os.path.basename(suspicious_path)
if checkpath(filename) != False:
    print('[-] suspicious module file is not hidden')
    clean()

subprocess.run(["/bin/sh", "-c", f"objdump -s -F -j .rodata.str1.1 {suspicious_path} > dump_object"], text=True)
subprocess.run(["/bin/sh", "-c", f"cp {suspicious_path} ./"], text=True)
subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)

old_modules_list = open('./modules_list', 'r').read().split('\n')
a = len("Starting at file offset: ")
f = open("dump_object", "r").read()
b = f.find("Starting at file offset: ")
offset = f[b+a:]
c = offset.find(')')
offset = int(offset[2:c], 16)
length = int(f.split('\n')[-2].split(' ')[1], 16) + 16
f = open(f'./{filename}', 'rb').read()
stringlist = f[offset:offset+length].strip(b'\x00').split(b'\x00')
modulenameidx = 1
magicidx = 13
procnameidx = 47
print(f"[+] possible module name: {stringlist[modulenameidx]}")
print(f"[+] possible magic: {stringlist[magicidx]}")
print(f"[+] possible proc name: {stringlist[procnameidx]}")

procpath = ''
procpathlist = []
tmppath = os.path.join('/proc', stringlist[procnameidx].decode())
if os.path.exists(tmppath):
    procpath = tmppath
    print(f"[+] found procpath {procpath}")
else:
    for string in stringlist:
        tmppath = os.path.join('/proc', string.decode())
        if os.path.exists(tmppath):
            procpathlist.append(tmppath)
    if len(procpathlist) > 1:
        print(f"[-] expected one path got {len(procpath)} : {procpath}")
        clean()
    elif len(procpathlist) == 0:
        print("[-] could not find process path")
        clean()
    else:
        procpath = procpathlist[0]

subprocess.run(["/bin/sh", "-c", f"echo -n {stringlist[magicidx].decode()} > {procpath}"], text=True)
subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)

modules_list = open("./modules_list", "r").read().split("\n")
for modules in modules_list:
    if modules not in old_modules_list:
        print(f"[+] found rootkit module: {modules}")
        subprocess.run(["/bin/sh", "-c", f"/sbin/rmmod {modules}.ko"], text=True)
        print("[+] rmmod complete")
        clean()

for string in stringlist:
    if b'%' not in string and b'/' not in string:
        subprocess.run(["/bin/sh", "-c", f"echo -n {string.decode()} > {procpath}"], text=True)
        subprocess.run(["/bin/sh", "-c", "lsmod | awk '{ print $1 }' > ./modules_list"], text=True)
        modules_list = open("./modules_list", "r").read().split("\n")
        for modules in modules_list:
            if modules not in old_modules_list:
                print(f"[+] found rootkit module: {modules}")
                subprocess.run(["/bin/sh", "-c", f"/sbin/rmmod {modules}.ko"], text=True)
                print("[+] rmmod complete")
                clean()

clean()

付録 C. find-syslogk.sh

#!/bin/bash
set -euo pipefail

split() {
    local -n output_array="${1}"
    local separator="${2}"
    local input_string="${3}"
    
    output_array=()
    
    local temp_string="${input_string}"

    while [[ "${temp_string}" == *"${separator}"* ]]; do
        output_array+=("${temp_string%%"${separator}"*}")
        temp_string="${temp_string#*"${separator}"}"
    done

    output_array+=("$temp_string")
}

find_matches() {
    local -n output_array="${1}"
    local match_string="${2}"
    local -n input_array="${3}"
    
    output_array=()

    for element in "${input_array[@]}"; do
        if [[ "${element}" == *"${match_string}"* ]]; then
            output_array+=("${element}")
        fi
    done
}

main() {
    if [[ ${EUID} -ne 0 ]]; then
        echo "run as root"
        exit 1
    fi

    local output_separator
    printf -v output_separator "\nOUTPUT_SEPARATOR\n"

    # find `shservice`s
    declare -a strings_array
    split strings_array "${output_separator}" "$(dump -0f - /etc/init.d/ | strings -w -s "${output_separator}")"

    local pattern
    printf -v pattern "#!/bin/bash\n#\ncase \"\$1\" in\n'start')\n\t/sbin/insmod"
    declare -a matching_scripts
    find_matches matching_scripts "${pattern}" strings_array
    echo "found ${#matching_scripts[@]} matching scripts"

    # find module paths
    declare -a paths
    paths=()
    for script in "${matching_scripts[@]}"; do
        local regex="insmod[[:space:]]+([^[:space:]]+)"
        if [[ "${script}" =~ ${regex} ]]; then
            local path="${BASH_REMATCH[1]}"
            paths+=("${path}")

        else
            printf "could not parse: %s\n" "${script}"
        fi
    done
    echo "found ${#paths[@]} paths"
    echo "paths = " "${paths[@]}"
    
    # unload modules
    for path in "${paths[@]}"; do
        # find proc dir
        declare -a ko_strings
        split ko_strings "${output_separator}" "$(strings -w -s "${output_separator}" "${path}")"

        declare -a proc_paths=()
        for string in "${ko_strings[@]}"; do
            if [[ -e "/proc/${string}" ]]; then
                proc_paths+=("${string}")
            fi
        done

        if [[ ${#proc_paths[@]} -ne 1 ]]; then
            printf "%s\n" "expected one path, got ${#proc_paths[@]}: " "${proc_paths[@]}" ", skipping"
            continue
        fi
        local proc_path=${proc_paths[0]}

        # save modules list
        mapfile -t a < <(lsmod | awk '{print $1}')

        # unhide module
        for string in "${ko_strings[@]}"; do
            printf "%s" "${string}" > "/proc/${proc_path}"
        done
        echo "unhid module"

        # compare modules list
        mapfile -t b < <(lsmod | awk '{print $1}')
        
        for bb in "${b[@]}"; do
            if [[ -n "${bb}" && ! "${a[*]}" = *"${bb}"* ]]; then
                echo "found rootkit module: $bb"
                /sbin/rmmod "$bb.ko" && echo "rmmod complete"
            fi
        done
    done

}

main "${@}"
#!/bin/bash
set -euo pipefail

split() {
    local -n output_array="${1}"
    local separator="${2}"
    local input_string="${3}"
    
    output_array=()
    
    local temp_string="${input_string}"

    while [[ "${temp_string}" == *"${separator}"* ]]; do
        output_array+=("${temp_string%%"${separator}"*}")
        temp_string="${temp_string#*"${separator}"}"
    done

    output_array+=("$temp_string")
}

find_matches() {
    local -n output_array="${1}"
    local match_string="${2}"
    local -n input_array="${3}"
    
    output_array=()

    for element in "${input_array[@]}"; do
        if [[ "${element}" == *"${match_string}"* ]]; then
            output_array+=("${element}")
        fi
    done
}

main() {
    if [[ ${EUID} -ne 0 ]]; then
        echo "run as root"
        exit 1
    fi

    local output_separator
    printf -v output_separator "\nOUTPUT_SEPARATOR\n"

    # find `shservice`s
    declare -a strings_array
    split strings_array "${output_separator}" "$(dump -0f - /etc/init.d/ | strings -w -s "${output_separator}")"

    local pattern
    printf -v pattern "#!/bin/bash\n#\ncase \"\$1\" in\n'start')\n\t/sbin/insmod"
    declare -a matching_scripts
    find_matches matching_scripts "${pattern}" strings_array
    echo "found ${#matching_scripts[@]} matching scripts"

    # find module paths
    declare -a paths
    paths=()
    for script in "${matching_scripts[@]}"; do
        local regex="insmod[[:space:]]+([^[:space:]]+)"
        if [[ "${script}" =~ ${regex} ]]; then
            local path="${BASH_REMATCH[1]}"
            paths+=("${path}")

        else
            printf "could not parse: %s\n" "${script}"
        fi
    done
    echo "found ${#paths[@]} paths"
    echo "paths = " "${paths[@]}"
    
    # unload modules
    for path in "${paths[@]}"; do
        # find proc dir
        declare -a ko_strings
        split ko_strings "${output_separator}" "$(strings -w -s "${output_separator}" "${path}")"

        declare -a proc_paths=()
        for string in "${ko_strings[@]}"; do
            if [[ -e "/proc/${string}" ]]; then
                proc_paths+=("${string}")
            fi
        done

        if [[ ${#proc_paths[@]} -ne 1 ]]; then
            printf "%s\n" "expected one path, got ${#proc_paths[@]}: " "${proc_paths[@]}" ", skipping"
            continue
        fi
        local proc_path=${proc_paths[0]}

        # save modules list
        mapfile -t a < <(lsmod | awk '{print $1}')

        # unhide module
        for string in "${ko_strings[@]}"; do
            printf "%s" "${string}" > "/proc/${proc_path}"
        done
        echo "unhid module"

        # compare modules list
        mapfile -t b < <(lsmod | awk '{print $1}')
        
        for bb in "${b[@]}"; do
            if [[ -n "${bb}" && ! "${a[*]}" = *"${bb}"* ]]; then
                echo "found rootkit module: $bb"
                /sbin/rmmod "$bb.ko" && echo "rmmod complete"
            fi
        done
    done

}

main "${@}"
#!/bin/bash
set -euo pipefail

split() {
    local -n output_array="${1}"
    local separator="${2}"
    local input_string="${3}"
    
    output_array=()
    
    local temp_string="${input_string}"

    while [[ "${temp_string}" == *"${separator}"* ]]; do
        output_array+=("${temp_string%%"${separator}"*}")
        temp_string="${temp_string#*"${separator}"}"
    done

    output_array+=("$temp_string")
}

find_matches() {
    local -n output_array="${1}"
    local match_string="${2}"
    local -n input_array="${3}"
    
    output_array=()

    for element in "${input_array[@]}"; do
        if [[ "${element}" == *"${match_string}"* ]]; then
            output_array+=("${element}")
        fi
    done
}

main() {
    if [[ ${EUID} -ne 0 ]]; then
        echo "run as root"
        exit 1
    fi

    local output_separator
    printf -v output_separator "\nOUTPUT_SEPARATOR\n"

    # find `shservice`s
    declare -a strings_array
    split strings_array "${output_separator}" "$(dump -0f - /etc/init.d/ | strings -w -s "${output_separator}")"

    local pattern
    printf -v pattern "#!/bin/bash\n#\ncase \"\$1\" in\n'start')\n\t/sbin/insmod"
    declare -a matching_scripts
    find_matches matching_scripts "${pattern}" strings_array
    echo "found ${#matching_scripts[@]} matching scripts"

    # find module paths
    declare -a paths
    paths=()
    for script in "${matching_scripts[@]}"; do
        local regex="insmod[[:space:]]+([^[:space:]]+)"
        if [[ "${script}" =~ ${regex} ]]; then
            local path="${BASH_REMATCH[1]}"
            paths+=("${path}")

        else
            printf "could not parse: %s\n" "${script}"
        fi
    done
    echo "found ${#paths[@]} paths"
    echo "paths = " "${paths[@]}"
    
    # unload modules
    for path in "${paths[@]}"; do
        # find proc dir
        declare -a ko_strings
        split ko_strings "${output_separator}" "$(strings -w -s "${output_separator}" "${path}")"

        declare -a proc_paths=()
        for string in "${ko_strings[@]}"; do
            if [[ -e "/proc/${string}" ]]; then
                proc_paths+=("${string}")
            fi
        done

        if [[ ${#proc_paths[@]} -ne 1 ]]; then
            printf "%s\n" "expected one path, got ${#proc_paths[@]}: " "${proc_paths[@]}" ", skipping"
            continue
        fi
        local proc_path=${proc_paths[0]}

        # save modules list
        mapfile -t a < <(lsmod | awk '{print $1}')

        # unhide module
        for string in "${ko_strings[@]}"; do
            printf "%s" "${string}" > "/proc/${proc_path}"
        done
        echo "unhid module"

        # compare modules list
        mapfile -t b < <(lsmod | awk '{print $1}')
        
        for bb in "${b[@]}"; do
            if [[ -n "${bb}" && ! "${a[*]}" = *"${bb}"* ]]; then
                echo "found rootkit module: $bb"
                /sbin/rmmod "$bb.ko" && echo "rmmod complete"
            fi
        done
    done

}

main "${@}"

付録 D. check-backdoor.py

import os
import glob

if os.getuid() != 0:
  print("run as root")
  exit()

processes = glob.glob("/proc/*/cmdline")
pids = []

def checkpath(checkpath):
    dirlist = os.listdir(os.path.dirname(checkpath))
    if os.path.basename(checkpath) in dirlist and os.path.exists(checkpath):
        return 1
    elif os.path.exists(checkpath):
        return 0
    else:
       return -1

for p in processes:
  pid = p.split("/")[2]

  if not "self" in pid:
    pids.append(int(pid))

max_pid = max(pids) + 10000

for pid in range(1, max_pid):
  fname = "/proc/%u/cmdline"%(pid)

  try:
    open(fname,"rb").close()
  except IOError:
    continue

  if not pid in pids:
    cmdline = open(fname, "rb").read()
    execpath = cmdline.split(b'\x00')[0]
    if b'//' in execpath or (execpath.startswith(b'/') and checkpath(execpath) == 0):
      print("found backdoor process")
      print("pid: {}".format(pid))
      print("backdoor path: {}".format(execpath.decode()))
      print("process cmdline: {}".format(cmdline))
import os
import glob

if os.getuid() != 0:
  print("run as root")
  exit()

processes = glob.glob("/proc/*/cmdline")
pids = []

def checkpath(checkpath):
    dirlist = os.listdir(os.path.dirname(checkpath))
    if os.path.basename(checkpath) in dirlist and os.path.exists(checkpath):
        return 1
    elif os.path.exists(checkpath):
        return 0
    else:
       return -1

for p in processes:
  pid = p.split("/")[2]

  if not "self" in pid:
    pids.append(int(pid))

max_pid = max(pids) + 10000

for pid in range(1, max_pid):
  fname = "/proc/%u/cmdline"%(pid)

  try:
    open(fname,"rb").close()
  except IOError:
    continue

  if not pid in pids:
    cmdline = open(fname, "rb").read()
    execpath = cmdline.split(b'\x00')[0]
    if b'//' in execpath or (execpath.startswith(b'/') and checkpath(execpath) == 0):
      print("found backdoor process")
      print("pid: {}".format(pid))
      print("backdoor path: {}".format(execpath.decode()))
      print("process cmdline: {}".format(cmdline))
import os
import glob

if os.getuid() != 0:
  print("run as root")
  exit()

processes = glob.glob("/proc/*/cmdline")
pids = []

def checkpath(checkpath):
    dirlist = os.listdir(os.path.dirname(checkpath))
    if os.path.basename(checkpath) in dirlist and os.path.exists(checkpath):
        return 1
    elif os.path.exists(checkpath):
        return 0
    else:
       return -1

for p in processes:
  pid = p.split("/")[2]

  if not "self" in pid:
    pids.append(int(pid))

max_pid = max(pids) + 10000

for pid in range(1, max_pid):
  fname = "/proc/%u/cmdline"%(pid)

  try:
    open(fname,"rb").close()
  except IOError:
    continue

  if not pid in pids:
    cmdline = open(fname, "rb").read()
    execpath = cmdline.split(b'\x00')[0]
    if b'//' in execpath or (execpath.startswith(b'/') and checkpath(execpath) == 0):
      print("found backdoor process")
      print("pid: {}".format(pid))
      print("backdoor path: {}".format(execpath.decode()))
      print("process cmdline: {}".format(cmdline))

付録 E. check-backdoor.sh

#!/bin/bash
set -euo pipefail

if [[ $EUID -ne 0 ]]; then
  echo "run as root"
  exit 1
fi

checkpath() {
  local path="$1"
  local dir=$(dirname "$path")
  local base=$(basename "$path")

  if [[ -e "$path" ]]; then
    if (cd "$dir" 2>/dev/null && for f in * .*; do [[ "$f" == "$base" ]] && exit 0; done; exit 1); then
      echo 1
    else
      echo 0
    fi
  else
    echo -1
  fi
}

pids=()
for f in /proc/[0-9]*/cmdline; do
  pid=$(echo "$f" | cut -d/ -f3)
  [[ "$pid" == "self" ]] && continue
  pids+=("$pid")
done

max_pid=$(printf "%s\n" "${pids[@]}" | sort -n | tail -n1)
max_pid=$((max_pid + 10000))

for ((pid=1; pid<=max_pid; pid++)); do
  fname="/proc/$pid/cmdline"
  if [[ ! -r "$fname" ]]; then
    continue
  fi

  if ! printf "%s\n" "${pids[@]}" | grep -qx "$pid"; then
    cmdline=$(tr '\000' ' ' < "$fname")
    execpath=$(tr '\000' '\n' < "$fname" | head -n1)

    if [[ "$execpath" == *"//"* ]]; then
      echo "found backdoor process"
      echo "pid: $pid"
      echo "backdoor path: $execpath"
      echo "process cmdline: $cmdline"
    elif [[ "$execpath" == /* ]]; then
      result=$(checkpath "$execpath")
      if [[ "$result" == 0 ]]; then
        echo "found backdoor process"
        echo "pid: $pid"
        echo "backdoor path: $execpath"
        echo "process cmdline: $cmdline"
      fi
    fi
  fi
done
#!/bin/bash
set -euo pipefail

if [[ $EUID -ne 0 ]]; then
  echo "run as root"
  exit 1
fi

checkpath() {
  local path="$1"
  local dir=$(dirname "$path")
  local base=$(basename "$path")

  if [[ -e "$path" ]]; then
    if (cd "$dir" 2>/dev/null && for f in * .*; do [[ "$f" == "$base" ]] && exit 0; done; exit 1); then
      echo 1
    else
      echo 0
    fi
  else
    echo -1
  fi
}

pids=()
for f in /proc/[0-9]*/cmdline; do
  pid=$(echo "$f" | cut -d/ -f3)
  [[ "$pid" == "self" ]] && continue
  pids+=("$pid")
done

max_pid=$(printf "%s\n" "${pids[@]}" | sort -n | tail -n1)
max_pid=$((max_pid + 10000))

for ((pid=1; pid<=max_pid; pid++)); do
  fname="/proc/$pid/cmdline"
  if [[ ! -r "$fname" ]]; then
    continue
  fi

  if ! printf "%s\n" "${pids[@]}" | grep -qx "$pid"; then
    cmdline=$(tr '\000' ' ' < "$fname")
    execpath=$(tr '\000' '\n' < "$fname" | head -n1)

    if [[ "$execpath" == *"//"* ]]; then
      echo "found backdoor process"
      echo "pid: $pid"
      echo "backdoor path: $execpath"
      echo "process cmdline: $cmdline"
    elif [[ "$execpath" == /* ]]; then
      result=$(checkpath "$execpath")
      if [[ "$result" == 0 ]]; then
        echo "found backdoor process"
        echo "pid: $pid"
        echo "backdoor path: $execpath"
        echo "process cmdline: $cmdline"
      fi
    fi
  fi
done
#!/bin/bash
set -euo pipefail

if [[ $EUID -ne 0 ]]; then
  echo "run as root"
  exit 1
fi

checkpath() {
  local path="$1"
  local dir=$(dirname "$path")
  local base=$(basename "$path")

  if [[ -e "$path" ]]; then
    if (cd "$dir" 2>/dev/null && for f in * .*; do [[ "$f" == "$base" ]] && exit 0; done; exit 1); then
      echo 1
    else
      echo 0
    fi
  else
    echo -1
  fi
}

pids=()
for f in /proc/[0-9]*/cmdline; do
  pid=$(echo "$f" | cut -d/ -f3)
  [[ "$pid" == "self" ]] && continue
  pids+=("$pid")
done

max_pid=$(printf "%s\n" "${pids[@]}" | sort -n | tail -n1)
max_pid=$((max_pid + 10000))

for ((pid=1; pid<=max_pid; pid++)); do
  fname="/proc/$pid/cmdline"
  if [[ ! -r "$fname" ]]; then
    continue
  fi

  if ! printf "%s\n" "${pids[@]}" | grep -qx "$pid"; then
    cmdline=$(tr '\000' ' ' < "$fname")
    execpath=$(tr '\000' '\n' < "$fname" | head -n1)

    if [[ "$execpath" == *"//"* ]]; then
      echo "found backdoor process"
      echo "pid: $pid"
      echo "backdoor path: $execpath"
      echo "process cmdline: $cmdline"
    elif [[ "$execpath" == /* ]]; then
      result=$(checkpath "$execpath")
      if [[ "$result" == 0 ]]; then
        echo "found backdoor process"
        echo "pid: $pid"
        echo "backdoor path: $execpath"
        echo "process cmdline: $cmdline"
      fi
    fi
  fi
done

エンキホワイトハット

エンキホワイトハット

ENKI ホワイトハット
ENKI ホワイトハット

オフェンシブセキュリティの専門企業として、攻撃者の視点から次元の異なるセキュリティを提示します。

オフェンシブセキュリティの専門企業として、攻撃者の視点から次元の異なるセキュリティを提示します。

隙のないセキュリティ設計の始まり、NO.1ホワイトハッカーのノウハウから

インシデント発生前、
今すぐ備えましょう

隙のないセキュリティ設計の始まり、
No.1ホワイトハッカーのノウハウから

インシデント発生前、
今すぐ備えましょう

隙のないセキュリティ設計の始まり、
No.1ホワイトハッカーのノウハウから

インシデント発生前、
今すぐ備えましょう

購読する

コンテンツが役に立ったら?
エンキーレターを購読しましょう!

Copyright © 2025. ENKI WhiteHat Co., Ltd. All rights reserved.

Copyright © 2025. ENKI WhiteHat Co., Ltd. All rights reserved.

Copyright © 2025. ENKI WhiteHat Co., Ltd. All rights reserved.