プロセスの特権を昇格させてroot
アクセスを取得するために悪用される可能性のあるスタックバッファオーバーフローの脆弱性を説明するUbuntu-12.10セキュリティエクスプロイト_CVE-2013-1763
_について読んでいます。
ハードコードされたエクスプロイトは次のリンクにあります: https://github.com/spinlockirqsave/examples/blob/master/hacker/ubuntu_cred/main.c
同じことに関していくつか質問があります。
sdiag_family=0x37
_はどのように決定されましたか?x()
はどのように実行されますか?sdiag_family=0x37
_と_mmap_start=0x1a000
_の値の間に関係はありますか?影響を受けるコード (カーネル3.7.0から、115行目から132行目)を見ると:
_static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
int err;
struct sock_diag_req *req = nlmsg_data(nlh);
const struct sock_diag_handler *hndl;
if (nlmsg_len(nlh) < sizeof(*req))
return -EINVAL;
hndl = sock_diag_lock_handler(req->sdiag_family);
if (hndl == NULL)
err = -ENOENT;
else
err = hndl->dump(skb, nlh);
sock_diag_unlock_handler(hndl);
return err;
}
_
sock_diag_lock_handler()
関数には、_req->sdiag_family
_値をインデックスとして使用してアクセスします。インデックスはユーザーランドから取得されます。呼び出される関数コードは次のとおりです。
_static const inline struct sock_diag_handler *sock_diag_lock_handler(int family)
{
if (sock_diag_handlers[family] == NULL)
request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
NETLINK_SOCK_DIAG, family);
mutex_lock(&sock_diag_table_mutex);
return sock_diag_handlers[family];
}
_
したがって、提供されたインデックスは、配列の長さをチェックせずに、_sock_diag_handlers[]
_という配列に直接アクセスするために使用されていることがわかります。配列には40個の要素が含まれていますが、エクスプロイトのインデックスは0x37、つまり55です。したがって、コードは配列の終わりを16エントリ超えたバイトを読み取ります(つまり、各エントリはポインタであり、エクスプロイトは明らかに64ビットマシン)。
戻り値は、呼び出し元(__sock_diag_rcv_msg()
)によって構造体へのポインターとして使用されます。構造体には、特に、関数ポインターであるdump()
と呼ばれるフィールドとコードが含まれます。 ポインタをたどることによってその関数を呼び出します。
このエクスプロイトは、カーネルメモリスペース内に、配列のオフセット55(配列の一部ではなく、たまたまそこにある他のデータ)に適切なポインタが存在することに依存しています。そのポインタは、カーネル空間内の良性で正常な構造を指しています。これは、まったく異なる何かを意味します。しかし、障害のあるコードは、その構造を_struct sock_diag_handler
_として解釈し、fault
フィールドと見なされるものを読み取ります。たまたま値0x1A000が含まれています。したがって、コードはそのアドレスに「ジャンプ」します。
エクスプロイトに残っているのは、0x1A000アドレスに実行可能コードがあることを確認することだけです。そのアドレスはユーザースペースの一部です。ユーザーランドエクスプロイトは、mmap()
呼び出しでそのスペースを予約し、その中にコードを入れることができます。ここでは、現在実行中のプロセスを「ルート」にするコードを示します。
理解しておくべき重要な点は、「ユーザースペース」と「カーネルスペース」が共存しているということです。プロセスが実行するとき、メモリをページのコレクションとして「認識」し、その一部は実際のRAMにマップされます。 「カーネルスペース」は、これらのページのサブセットです。マッピングは [〜#〜] mmu [〜#〜] で行われ、アクセス権を強制します。実行するコードに「カーネル特権」ではなく「ユーザー特権」がある場合。ここではルートと非ルートについては説明していません)、「カーネルスペース」としてマークされているページにアクセスしようとすると、例外(segfault)がトリガーされます。プロセスがシステムコール(例:send()
呼び出し)を実行すると、プロセスはカーネルスペースにジャンプし、一時的にカーネル特権を取得します。その時点で、すべてのカーネルスペースに到達できるようになります。ただし、ページのマッピングは変更されていません。ユーザーのランディングページはまだそこにあり、重要なことに、ユーザー特権はカーネルページにアクセスするのに十分ではありませんが、カーネル特権はユーザーページにアクセスするのに十分すぎるほどです。したがって、カーネル特権を持つコードが、たまたまユーザースペースにあるコードにジャンプするのを妨げるものは何もありません。これがここで起こることです。
したがって、エクスプロイト制御フローは次のとおりです。
send()
の実行につながるいくつかのパラメーターを使用して__sock_diag_rcv_msg()
システムコールを実行します。mmap()
呼び出しを使用してエクスプロイトによって構築されたものです)、カーネル特権で実行されます。__sock_diag_rcv_msg()
に戻ります。これは、手続き全体に非常に満足しており、戻ります。最終的に、システムコールは終了し、ユーザーランドプロセスはユーザー権限で制御を取り戻します。0x37と0x1A000は、エクスプロイトが調整された特定のカーネルバージョンの場合に「うまく機能する」値です。攻撃者は、オーバーフローした配列の128バイト後のカーネル空間に、カーネルへのポインタがあることに気づきました。 -疑似dump
フィールドに、ポインタとして解釈され、通常のユーザーランドmmap()
に到達するのに十分低いアドレスを指す値が含まれているスペース構造。ここで説明しているデータ(オーバーフローした配列とそれに続くRAM内の構造)は、「定数データ」セグメントの一部です。これらは、カーネルのコンパイル時に入力された通常の定数データ構造です。したがって、新しいカーネルがコンパイルされると変更される可能性がありますが、影響を受けるカーネルのすべてのインスタンスはその点で同一になります。これが、エクスプロイトがカーネルバージョン(ここでは、64でUbuntu 12.10に付属しているもの)に固有である理由です。ビットモード)が、そのカーネルを使用するすべてのシステムで動作します。
影響を受ける他のカーネルバージョン、または異なるオプションを持つ他のコンパイルでは、_sdiag_family
_と_mmap_start
_に異なる値が必要になる場合がありますが、適切な構造の条件は難しくなく、他の多くの適切な組み合わせがある可能性があります。 (または、少なくともそこにあった、カーネルはその点で修正され、配列の終わりを超えてアクセスを行わないため、回避されます問題全体。)