2015-10-23 5 views
49

を悪用、私はしばしば、このようなポインタを参照してください。説明はrootシェルを取得するためのいくつかの悪用では、コード

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++; 
} 
+3

バイナリの値「8191」は「1111111111111」であり、「long」のタイプは32ビットです。私はあなたに確かな答えを与えると思います、 '* p'ポインタがどのように使用されているか見る必要があります。 '&'演算子はおそらく何らかの種類のビットマスクです。 –

+0

@TimBiegeleisenご返信ありがとうございます。私はそれを編集しました。 – HuangJie

答えて

54

コードは、現在のスタックフレームへのポインタを取得するために、ローカル変数iのアドレスを取得します。次に、アドレスを8Kページに揃えます(つまり、x & ~8191の場合:8191は2^13-1です。つまり、~8191はすべて低位13ビットを除くすべてのものです。したがって、数値とANDを取ると低位13ビットがクリアされます)つまり、数値を2^13の最も近い下位倍数、つまり8K境界に整列させます)。

次に、このアドレスをとり、それをポインターへのポインターとして解釈し、そこからポインティングされたアドレスを読み込みます。詳細については、Understanding the getting of task_struct pointer from process kernel stackを参照してください。

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

Cf. struct cred

8

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

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

結果として、pは8192バイトサイズのメモリブロックの先頭へのポインタになります。しかし、コードは間違っています。 pがINT_MAXより大きい場合(符号なし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を読み込むほうが速いかもしれません。

+1

ISOの正当な正確さのために、両方の場所で 'ptrdiff_t'の代わりに' uintptr_t'を使います。しかし、すべてのLinux ABIは、すべてのTに対して 'sizeof(unsigned long)== sizeof(T *)'を保証しています。したがって、Linuxカーネルエクスプロイトのコンテキストでコードを修正する*最小限の変更は単純に '&〜8191UL 'の代わりに'&〜8191'を使います。 – zwol

+0

'int'の'〜8191'が '&' -expressionで使用され、左側に 'unsigned long'があると、符号拡張が適用されます。したがって、高いビットはマスクから剪断されることはありません。これは、uint64_t x = -1;が64ビットすべてを1に設定するのと同じ理由です。 – mortehu

+1

@mortehu:私はそのフラグメントを他の場所で使用して焼いてしまいました。 intがunsigned longよりもビット数が少ない場合、変換は間違ったことになります。 – Joshua

関連する問題