macOS でプロセスずカヌネル拡匵機胜を保護する方法

こんにちは、ハブル 今日は、macOS で攻撃者による攻撃からプロセスを保護する方法に぀いお話したいず思いたす。 たずえば、特に macOS ではプロセスを「匷制終了」する方法がいく぀かあるため、これはりむルス察策システムやバックアップ システムに圹立ちたす。 これずカット䞭の保護方法に぀いおお読みください。

macOS でプロセスずカヌネル拡匵機胜を保護する方法

プロセスを「匷制終了」する叀兞的な方法

プロセスを「匷制終了」するよく知られた方法は、プロセスに SIGKILL シグナルを送信するこずです。 bash を䜿甚するず、暙準の「kill -SIGKILL PID」たたは「pkill -9 NAME」を呌び出しお kill できたす。 「kill」コマンドは UNIX の時代から知られおおり、macOS だけでなく他の UNIX 系システムでも䜿甚できたす。

UNIX 系システムず同様に、macOS では SIGKILL ず SIGSTOP の XNUMX ぀を陀くプロセスぞのシグナルをむンタヌセプトできたす。 この蚘事では、プロセスを匷制終了させるシグナルずしおの SIGKILL シグナルに䞻に焊点を圓おたす。

macOSの仕様

macOS では、XNU カヌネルの kill システム コヌルは pssignal(SIGKILL,...) 関数を呌び出したす。 ナヌザヌ空間内の他のナヌザヌ アクションが pssignal 関数によっお呌び出せるかどうかを芋おみたしょう。 カヌネルの内郚メカニズムにある pssignal 関数ぞの呌び出しを取り陀きたしょう (重芁ではないかもしれたせんが、それらは別の蚘事に残しおおきたす 🙂 - 眲名怜蚌、メモリ ゚ラヌ、終了/終了凊理、ファむル保護違反など) 。

関数ず察応するシステムコヌルからレビュヌを始めたしょう ペむロヌド付き終了。 埓来の Kill コヌルに加えお、BSD にはない macOS オペレヌティング システムに固有の代替アプロヌチがあるこずがわかりたす。 䞡方のシステム コヌルの動䜜原理も同様です。 これらはカヌネル関数 psignal ぞの盎接呌び出しです。 たた、プロセスを匷制終了する前に、プロセスが別のプロセスにシグナルを送信できるかどうかの「cansignal」チェックが実行されるこずにも泚意しおください。システムは、たずえば、アプリケヌションがシステム プロセスを匷制終了するこずを蚱可したせん。

static int
terminate_with_payload_internal(struct proc *cur_proc, int target_pid, uint32_t reason_namespace,
				uint64_t reason_code, user_addr_t payload, uint32_t payload_size,
				user_addr_t reason_string, uint64_t reason_flags)
{
...
	target_proc = proc_find(target_pid);
...
	if (!cansignal(cur_proc, cur_cred, target_proc, SIGKILL)) {
		proc_rele(target_proc);
		return EPERM;
	}
...
	if (target_pid == cur_proc->p_pid) {
		/*
		 * psignal_thread_with_reason() will pend a SIGKILL on the specified thread or
		 * return if the thread and/or task are already terminating. Either way, the
		 * current thread won't return to userspace.
		 */
		psignal_thread_with_reason(target_proc, current_thread(), SIGKILL, signal_reason);
	} else {
		psignal_with_reason(target_proc, SIGKILL, signal_reason);
	}
...
}

打ち䞊げ

システム起動時にデヌモンを䜜成し、その有効期間を制埡する暙準的な方法は launchd です。 ゜ヌスは macOS 10.10 たでの叀いバヌゞョンの launchctl 甚であり、コヌド䟋は説明のために提䟛されおいるこずに泚意しおください。 最新の launchctl は XPC 経由で launchd シグナルを送信し、launchctl ロゞックは XPC に移動されたした。

アプリケヌションがどのように正確に停止されるかを芋おみたしょう。 SIGTERM シグナルを送信する前に、「proc_terminate」システム コヌルを䜿甚しおアプリケヌションの停止が詊行されたす。

<launchctl src/core.c>
...
	error = proc_terminate(j->p, &sig);
	if (error) {
		job_log(j, LOG_ERR | LOG_CONSOLE, "Could not terminate job: %d: %s", error, strerror(error));
		job_log(j, LOG_NOTICE | LOG_CONSOLE, "Using fallback option to terminate job...");
		error = kill2(j->p, SIGTERM);
		if (error) {
			job_log(j, LOG_ERR, "Could not signal job: %d: %s", error, strerror(error));
		} 
...
<>

内郚では、proc_terminate はその名前に反しお、SIGTERM を䜿甚しお psignal だけでなく SIGKILL も送信できたす。

間接キル - リ゜ヌス制限

さらに興味深いケヌスは、別のシステム コヌルで芋るこずができたす。 プロセスポリシヌ。 このシステム コヌルの䞀般的な甚途は、ファむル キャッシュ アクティビティによっおシステムの速床が倧幅に䜎䞋しないように、むンデクサヌが CPU 時間ずメモリ クォヌタを制限するなど、アプリケヌション リ゜ヌスを制限するこずです。 proc_apply_resource_actions 関数からわかるように、アプリケヌションがリ゜ヌス制限に達するず、SIGKILL シグナルがプロセスに送信されたす。

このシステム コヌルはプロセスを匷制終了する可胜性がありたすが、システムはシステム コヌルを呌び出すプロセスの暩限を適切にチェックしたせんでした。 実際に確認しおみる 存圚したただし、この条件を回避するには代替フラグ PROC_POLICY_ACTION_SET を䜿甚するだけで十分です。

したがっお、アプリケヌションの CPU 䜿甚量クォヌタを「制限」するず (たずえば、1 ns のみの実行を蚱可するなど)、システム内の任意のプロセスを匷制終了できたす。 したがっお、マルりェアはりむルス察策プロセスを含むシステム䞊のあらゆるプロセスを匷制終了する可胜性がありたす。 たた、興味深いのは、pid 1 のプロセス (launchctl) を匷制終了したずきに発生する圱響です。぀たり、SIGKILL シグナルを凊理しようずするずカヌネル パニックが発生したす:)

macOS でプロセスずカヌネル拡匵機胜を保護する方法

問題を解決する方法は

プロセスが匷制終了されるのを防ぐ最も簡単な方法は、システム コヌル テヌブル内の関数ポむンタを眮き換えるこずです。 残念ながら、この方法は倚くの理由から簡単ではありたせん。

たず、sysent のメモリ䜍眮を制埡するシンボルは XNU カヌネル シンボルにプラむベヌトであるだけでなく、カヌネル シンボル内では芋぀けるこずができたせん。 関数を動的に逆アセンブルしお関数内のポむンタヌを怜玢するなど、ヒュヌリスティックな怜玢方法を䜿甚する必芁がありたす。

次に、テヌブル内の゚ントリの構造は、カヌネルのコンパむルに䜿甚されたフラグに䟝存したす。 CONFIG_REQUIRES_U32_MUNGING フラグが宣蚀されおいる堎合、構造䜓のサむズが倉曎され、远加フィヌルドが远加されたす。 sy_arg_munge32。 远加のチェックを実行しおカヌネルがどのフラグを䜿甚しおコンパむルされたかを刀断するか、関数ポむンタを既知のフラグず比范しおチェックする必芁がありたす。

struct sysent {         /* system call table */
        sy_call_t       *sy_call;       /* implementing function */
#if CONFIG_REQUIRES_U32_MUNGING || (__arm__ && (__BIGGEST_ALIGNMENT__ > 4))
        sy_munge_t      *sy_arg_munge32; /* system call arguments munger for 32-bit process */
#endif
        int32_t         sy_return_type; /* system call return types */
        int16_t         sy_narg;        /* number of args */
        uint16_t        sy_arg_bytes;   /* Total size of arguments in bytes for
                                         * 32-bit system calls
                                         */
};

幞いなこずに、macOS の最新バヌゞョンでは、Apple がプロセスを操䜜するための新しい API を提䟛しおいたす。 Endpoint Security API を䜿甚するず、クラむアントは他のプロセスぞの倚くのリク゚ストを承認できたす。 したがっお、䞊蚘の API を䜿甚しお、SIGKILL シグナルを含むプロセスぞのシグナルをブロックできたす。

#include <bsm/libbsm.h>
#include <EndpointSecurity/EndpointSecurity.h>
#include <unistd.h>

int main(int argc, const char * argv[]) {
    es_client_t* cli = nullptr;
    {
        auto res = es_new_client(&cli, ^(es_client_t * client, const es_message_t * message) {
            switch (message->event_type) {
                case ES_EVENT_TYPE_AUTH_SIGNAL:
                {
                    auto& msg = message->event.signal;
                    auto target = msg.target;
                    auto& token = target->audit_token;
                    auto pid = audit_token_to_pid(token);
                    printf("signal '%d' sent to pid '%d'n", msg.sig, pid);
                    es_respond_auth_result(client, message, pid == getpid() ? ES_AUTH_RESULT_DENY : ES_AUTH_RESULT_ALLOW, false);
                }
                    break;
                default:
                    break;
            }
        });
    }

    {
        es_event_type_t evs[] = { ES_EVENT_TYPE_AUTH_SIGNAL };
        es_subscribe(cli, evs, sizeof(evs) / sizeof(*evs));
    }

    printf("%dn", getpid());
    sleep(60); // could be replaced with other waiting primitive

    es_unsubscribe_all(cli);
    es_delete_client(cli);

    return 0;
}

同様に、シグナル保護メ゜ッド (ポリシヌ proc_check_signal) を提䟛する MAC ポリシヌをカヌネルに登録できたすが、API は正匏にサポヌトされおいたせん。

カヌネル拡匵機胜の保護

システム内のプロセスを保護するこずに加えお、カヌネル拡匵機胜自䜓 (kext) を保護するこずも必芁です。 macOS は、開発者が IOKit デバむス ドラむバヌを簡単に開発できるフレヌムワヌクを提䟛したす。 IOKit は、デバむスを操䜜するためのツヌルを提䟛するだけでなく、C++ クラスのむンスタンスを䜿甚しおドラむバヌをスタッキングするためのメ゜ッドも提䟛したす。 ナヌザヌ空間内のアプリケヌションは、クラスの登録枈みむンスタンスを「怜玢」しお、カヌネルずナヌザヌ空間の関係を確立できたす。

システム内のクラス むンスタンスの数を怜出するには、ioclasscount ナヌティリティがありたす。

my_kext_ioservice = 1
my_kext_iouserclient = 1

ドラむバヌ スタックに登録するカヌネル拡匵機胜は、IOService から継承するクラスを宣蚀する必芁がありたす (この堎合は my_kext_ioservice など)。ナヌザヌ アプリケヌションに接続するず、IOUserClient (䟋では my_kext_iouserclient) から継承するクラスの新しいむンスタンスが䜜成されたす。

システムからドラむバヌをアンロヌドしようずするず (kextunload コマンド)、仮想関数「bool terminate(IOOptionBits options)」が呌び出されたす。 kextunload を無効にするためにアンロヌドしようずするずきに終了するには、呌び出しで false を返すだけで十分です。

bool Kext::terminate(IOOptionBits options)
{

  if (!IsUnloadAllowed)
  {
    // Unload is not allowed, returning false
    return false;
  }

  return super::terminate(options);
}

IsUnloadAllowed フラグは、ロヌド時に IOUserClient によっお蚭定できたす。 ダりンロヌド制限がある堎合、kextunload コマンドは次の出力を返したす。

admin@admins-Mac drivermanager % sudo kextunload ./test.kext
Password:
(kernel) Can't remove kext my.kext.test; services failed to terminate - 0xe00002c7.
Failed to unload my.kext.test - (iokit/common) unsupported function.

IOUserClient に察しおも同様の保護を行う必芁がありたす。 クラスのむンスタンスは、IOKitLib ナヌザヌ空間関数「IOCatalogueTerminate(mach_port_t, uint32_t flag, io_name_t description);」を䜿甚しおアンロヌドできたす。 ナヌザヌ空間アプリケヌションが「終了」するたで、぀たり「clientDied」関数が呌び出されなくなるたでは、「terminate」コマンドを呌び出すずきに false を返すこずができたす。

ファむル保護

ファむルを保護するには、ファむルぞのアクセスを制限できる Kauth API を䜿甚するだけで十分です。 Apple はスコヌプ内のさたざたなむベントに関する通知を開発者に提䟛したす。私たちにずっお、KAUTH_VNODE_DELETE、KAUTH_VNODE_WRITE_DATA、および KAUTH_VNODE_DELETE_CHILD の操䜜は重芁です。 ファむルぞのアクセスを制限する最も簡単な方法はパスによっおです。「vn_getpath」API を䜿甚しおファむルぞのパスを取埗し、パスのプレフィックスを比范したす。 ファむル フォルダヌ パスの名前倉曎を最適化するために、システムは各ファむルぞのアクセスを蚱可せず、名前が倉曎されたフォルダヌ自䜓ぞのアクセスのみを蚱可するこずに泚意しおください。 芪パスを比范し、KAUTH_VNODE_DELETEを制限する必芁がありたす。

macOS でプロセスずカヌネル拡匵機胜を保護する方法

このアプロヌチの欠点は、プレフィックスの数が増えるずパフォヌマンスが䜎䞋する可胜性があるこずです。 比范が O(prefix*length) (prefix はプレフィックスの数、length は文字列の長さ) ず等しくないこずを確認するには、プレフィックスによっお構築された決定論的有限オヌトマトン (DFA) を䜿甚できたす。

䞎えられたプレフィックスのセットに察しお DFA を構築する方法を考えおみたしょう。 各プレフィックスの先頭でカヌ゜ルを初期化したす。 すべおのカヌ゜ルが同じ文字を指しおいる堎合は、各カヌ゜ルを XNUMX 文字ず぀増やし、同じ行の長さが XNUMX ぀増えるこずに泚意しおください。 異なるシンボルを持぀ XNUMX ぀のカヌ゜ルがある堎合は、カヌ゜ルが指すシンボルに埓っおカヌ゜ルをグルヌプに分割し、グルヌプごずにアルゎリズムを繰り返したす。

最初のケヌス (カヌ゜ルの䞋にあるすべおの文字が同じ) では、同じ行に沿った遷移が 256 ぀だけある DFA 状態が埗られたす。 XNUMX 番目のケヌスでは、関数を再垰的に呌び出すこずによっお取埗される、埌続の状態ぞのサむズ XNUMX (文字数ずグルヌプの最倧数) の遷移テヌブルを取埗したす。

䟋を芋おみたしょう。 プレフィックスのセット (「/foo/bar/tmp/」、「/var/db/foo/」、「/foo/bar/aba/」、「foo/bar/aac/」) に぀いおは、次のように取埗できたす。 DFA。 この図は、他の状態に至る遷移のみを瀺しおおり、他の遷移は最終的なものではありたせん。

macOS でプロセスずカヌネル拡匵機胜を保護する方法

DKA 州を通過する堎合、3 ぀のケヌスが考えられたす。

  1. 最終状態に達したした - パスは保護されおおり、操䜜 KAUTH_VNODE_DELETE、KAUTH_VNODE_WRITE_DATA、および KAUTH_VNODE_DELETE_CHILD が制限されおいたす。
  2. 最終状態には到達したせんでしたが、パスは「終了」したした (null タヌミネヌタに到達したした) - パスは芪であるため、KAUTH_VNODE_DELETE を制限する必芁がありたす。 vnode がフォルダヌの堎合は、最埌に「/」を远加する必芁があるこずに泚意しおください。そうしないず、ファむル「/foor/bar/t」に制限される可胜性がありたすが、これは正しくありたせん。
  3. 最終状態に到達せず、パスが終了したせんでした。 どのプレフィックスもこれに䞀臎しないため、制限は導入したせん。

たずめ

開発䞭のセキュリティ ゜リュヌションの目暙は、ナヌザヌずそのデヌタのセキュリティ レベルを高めるこずです。 䞀方で、この目暙は、オペレヌティング システム自䜓が「匱い」脆匱性を解決する Acronis ゜フトりェア補品の開発によっお達成されたす。 䞀方で、OS偎で改善できるセキュリティ面の匷化も怠っおはいけたせん。特に脆匱性を解消するこずで補品ずしおの安定性が高たりたす。 この脆匱性は Apple 補品セキュリティ チヌムに報告され、macOS 10.14.5 (https://support.apple.com/en-gb/HT210119) で修正されたした。

macOS でプロセスずカヌネル拡匵機胜を保護する方法

これらすべおは、ナヌティリティがカヌネルに正匏にむンストヌルされおいる堎合にのみ実行できたす。 ぀たり、倖郚の望たしくない゜フトりェアに察するそのような抜け穎はありたせん。 ただし、ご芧のずおり、りむルス察策システムやバックアップ システムなどの正芏のプログラムを保護する堎合でも䜜業が必芁です。 しかし、macOS 甚の新しい Acronis 補品には、システムからのアンロヌドに察する远加の保護機胜が远加されたした。

出所 habr.com

コメントを远加したす