web-dev-qa-db-ja.com

エクスプロイトコードのポインタの説明

ルートシェルを取得するためのいくつかのエクスプロイトでは、次のようなポインタがよく見られます。

int i;
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

誰かがこのポインタを少し説明できますか? 8191はカーネルスタックのサイズだと思います。 pカーネルスタックの一番下を指しますか?ポインタpの使用方法は次のとおりです。

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
for (i = 0; i < 1024-13; i++) { 
    if (p[0] == uid && p[1] == uid && 
        p[2] == uid && p[3] == uid && 
        p[4] == gid && p[5] == gid && 
        p[6] == gid && p[7] == gid) { 
            p[0] = p[1] = p[2] = p[3] = 0; 
            p[4] = p[5] = p[6] = p[7] = 0; 
            p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); 
            p[0] = p[1] = p[2] = ~0; 
            break; 
        } 
    p++; 
} 
49
HuangJie

このコードは、ローカル変数iのアドレスを取得して、現在のスタックフレームへのポインターを取得します。次に、アドレスを8Kページに揃えます(つまり、x & ~8191で行うことです。8191は2 ^ 13 -1です。つまり、~8191は下位13ビットを除いてすべて1であるため、ANDを付けて数値は下位13ビットをクリアします。つまり、数値を2 ^ 13の最も近い下位倍数に揃えます。つまり、8K境界に揃えます。

次に、このアドレスを受け取り、それをポインタへのポインタとして解釈し、そこからポイントされたアドレスをロードします。詳細については、 プロセスカーネルスタックからのtask_structポインタの取得について を参照してください。

その後、そのアドレスの後のどこかに格納されている特定の構造を見つけようとします。次の1024-13unsignedsを調べて、現在のプロセス情報が(おそらく)格納されているメモリ内の場所を見つけようとします。 :現在のUIDとGIDの複数のコピーを保持しているメモリの一部を見つけると、それを見つけたと見なします。その場合、現在のプロセスがUIDとGID 0を取得するように変更し、プロセスをルートで実行します(さらに、すべて1を次の機能フラグに格納します)。

Cf. struct cred

54
Mormegil

ここに追加するものが本当にあるので、さらに別の回答を投稿します。

unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

その結果、pはメモリの8192バイトサイズのブロックの先頭へのポインタになります。ただし、コードが間違っています。 pがINT_MAXを超えている場合(unsigned longではなくunsignedにキャストされる可能性があるか、キャストされる)、上位ビットはマスクによってせん断されます。正しいコードは次のとおりです。

unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);

またはuintptr_tを使用する:

unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);

コードを機能させるには、整数にキャストしてポインタに戻す必要があります。ただし、intサイズのポインターを保証するには、ptrdiff_tを使用する必要があります(符号付きと符号なしの演算はビット演算でまったく同じように動作することを思い出してください)。なぜ彼らは16進定数でそれらを書かないのかについては、誰が気にします。このようなことをする人は、2の力を心から知っています。 8191を読み取ってから0x1FFFを読み取る方が速い場合があります。

8
Joshua