4

私は学校の課題に取り組んでいますが、完全に困惑しています。教授と TA は何の役にも立たず、彼らが学生に提供するすべての答えは、「探し続ければ答えはそこにあります」というバリエーションにすぎません。このコードを使用してシェルを作成しようとしています:

#include <stdio.h>
#include <stdlib.h>

const char code[] =
"\x31\xc0"
"\x50"
"\x68""//sh"
"\x68""/bin"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x99"
"\xb0\x0b"
"\xcd\x80"
;

int main(int argc, char **argv)
{
printf("running...\n");

char buf[sizeof(code)];
strcpy(buf, code);
((void(*)( ))buf)( );
}

code[]オンラインで見つかった他の例(このサイトを含む)と、教授が提供した追加のpdfの例に置き換えようとしました。これらはどれも役に立ちませんでした。gdb を使用して逆アセンブルし、自分code[]で構築しようとしましたが、それも失敗しました。価値のあることとして、通常のユーザーでは、私のアプリケーションは回線上で segfault((void(*)( ))buf)( );し、同じ回線上の root ユーザーでは終了する (segfault の通知なし) と言えます。

この割り当てを他にどこで行うべきかわかりません。この簡単な最初のステップを理解するまで、後のバッファ オーバーフロー タスクに取り組むことはできません。どんな助けでも歓迎です。

編集:言及するのを忘れていました.OSX 10.8.2とVirtualBox経由のUbuntu VMの両方でこれを試しました。OSXでは動作しないと思いますが、必死でした。ha Ubuntu の場合、次のことを行うように求められました。

sudo #sysctl -w kernel.randomize_va_space=0

sudo apt-get install zsh cd/bin sudo rm sh sudo ln -s /bin/zsh /bin/sh

これらのコマンドは、アドレス空間のランダム化を無効にし、zsh をインストールして、/bin/sh にリンクする必要があります。VM でこれらのタスクをすべてエラーなしで完了しました

4

2 に答える 2

14

コードは次のように逆アセンブルされます。

00000000  31C0              xor eax,eax
00000002  50                push eax
00000003  682F2F7368        push dword 0x68732f2f
00000008  682F62696E        push dword 0x6e69622f
0000000D  89E3              mov ebx,esp
0000000F  50                push eax
00000010  53                push ebx
00000011  89E1              mov ecx,esp
00000013  99                cdq
00000014  B00B              mov al,0xb
00000016  CD80              int 0x80

の厚意によるndisasm。これらの手順を順を追って説明し、途中でスタック フレームを分析してみましょう。

xor eax,eaxeaxオペランドとそれ自体の XOR 演算の結果は常にゼロになるため、レジスタをゼロにします。push eax次に、値をスタックにプッシュします。したがって、スタックは現在、多かれ少なかれ次のようになっています (espコードの先頭にあるの値に関連して示されているオフセットは、現在指しているespスタック セルを意味します)。esp

       +----------+
     0 | 00000000 |
esp -4 | xxxxxxxx |
       +----------+

次に、push dwordいくつかの即時値をスタックにプッシュする 2 つの命令があります。これらを実行すると、次のようになります。

       +----------+
     0 | 00000000 |
    -4 | 68732f2f |
    -8 | 6e69622f |
esp -12| xxxxxxxx |
       +----------+

esp現在、スタックにプッシュされた 2 番目の即値の最後のバイトを指しています。プッシュされた値を ASCII として解釈してみましょう。現在の の値から順番に開始した場合に、スタックから読み取られる順序になりますesp。のバイト シーケンスを取得します。2f62696e2f2f7368これは ASCII では に等しくなり/bin//shます。さらに、シーケンスは 0 で終わるため、有効な C 文字列です。

espこれが、 の現在の値がレジスタに保存される主な理由ebxです。実行される実行可能ファイルへのパスが含まれています。POSIX は単純に複数のスラッシュを無視し、それらを 1 つのスラッシュとして扱うため、二重スラッシュは OS にとって問題ではありません。

次に、 と の現在の値をスタックeaxebxプッシュします。eaxにはゼロが含まebxれており、C-string へのポインターが含まれていることがわかっています"/bin//sh"。現在、スタックは次のようになっています。

           +----------+
         0 | 00000000 |
        -4 | 68732f2f |
        -8 | 6e69622f |
    ebx -12| 00000000 |
        -16| (ebxVal) |
ecx esp -20| xxxxxxxx |
           +----------+

レジスタの値をスタックにプッシュした後、現在のポインタespが に保存されecxます。

cdqこの場合、非常に巧妙なトリックを実行する命令です。現在の値をレジスタペアに符号拡張しeaxますedx:eax。したがって、この場合、ゼロedxの符号拡張はゼロであるため、 の値をゼロにします。もちろん、 で値をクリアすることもできますedxxor edx, edx、その命令は 2 バイトでエンコードされており、1 バイトcdqしか使用しません。

次の命令は、値0xb(11) を の下位バイトレジスタに入れeaxます。前のケースと同様に、 を実行することもできますがmov eax, 0xb、即値は完全な 32 ビット値としてエンコードする必要があるため、5 バイトの命令になります。

int 0x80Linux でシステム コール インボーカーを呼び出します。システムコールの数eax(現在は に等しい0xbため、sys_execve関数が呼び出されます) と、 、 、 、 、および の追加ebxecx引数edxesi必要ediですebp

それでは、そのシステム コールのプロトタイプを見てみましょう。

int execve(const char *filename, char *const argv[], char *const envp[]);

したがって、filename引数は に配置されますebx- それは を指し/bin//shます。argvに配置される はecx、実行される実行可能ファイルの引数の配列であり、NULL値で終了する必要があります。Intel アーキテクチャでNULLは、 は に等しく0、それをecx指します: へのポインタ/bin//sh、そしてNULL値です。envp、つまりNULL、環境値の配列を指します。これchar*は、形式の値として表現する必要がありますkey=value

実行が成功execveすると、現在のプロセス イメージが、指定された引数で実行された、指定された実行可能ファイルのイメージに置き換えられます。この場合、/bin/shは (存在する場合) の引数で実行されます/bin//sh

これが機能しない理由については、Michael がおそらく正しかったでしょう。最近の Linux カーネルはデータ ページを実行不可としてマークしており、それらを実行しようとするとセグメンテーション フォールトが発生します。

于 2013-02-12T21:36:53.127 に答える