web-dev-qa-db-ja.com

stdinはROPチェーンの後で次の入力を待機しません

私はCTFの脆弱性を悪用しようとしていましたが、fgets()にstdinを再度開いて2番目のステージのROPチェーンを配置できません。私はpwntoolを使用していますが、問題はよりソ​​ケット指向です。

上記の状況を再現しました。テストプログラムで。コンセプトはシンプルです。 ripをオーバーフローした後、入力を与える/変更するためにmainに戻ります。この問題は、pwntools/bashなどが2番目のstdinを閉じるときに発生します。

ローカルで私は次のようなことをして問題を克服できます:

cat <(cat myinput.txt) - | ./test.txt

しかし、どうすればソケットでこれを行うことができますか? pwntools.interactiveを使用すると問題は解決しますが、印刷できない文字を送信したいと思います。これが私の./testプログラムのスクリーンショットの例です。

int main (){
    char mastr[150];
    printf("lol\n");

    fflush(stdout);
    fflush(stdin);
    fgets(&mastr,600,stdin);
    puts(mastr);

    return 0;
}

Pwntools.interactive() enter image description here

Pwntools.interactive()なし enter image description here

ソケットと標準入力の背後にある理論は何ですか?私はそれを検索するための正しい用語を本当に知りません。

2
ItsYou

fgets()に関するメモ:これは、デバッガーをステップ実行するときに観察したものです。パイプで連結されたデータがfgets()に送信されると、ヒープ上でバッファリングされ、スタックに1つずつ転送されます。

  1. 比較命令は、カウンターとRSIレジスターでfgets()関数に渡された値でtrueを返します。これは、プログラマーが指定した文字列の長さです。

  2. または0x0aバイト( "\ n")の受信

これらのいずれかが満たされると、関数が終了し、STDINが破棄されます。つまり、バッファされたI/Oであり、OSとの通信に使用されるファイル記述子への実際のI/Oではありません。

とにかく、Pythonでbashを使用しているようにstdinを管理するには、サブプロセスのPopen関数を使用してサービスに直接接続できますが、問題があります。 Pythonは、デフォルトでSIGPIPEをブロックします。これにより、書き込みのためにfdを開いたままにできなくなります。signalsモジュールを使用してこれを克服し、SIGPIPを許可するサブプロセス構成をオーバーライドするパッチ関数を作成できます。

信号のインポート
 def restore_signals():
 signal =( 'SIGPIPE'、 'SIGXFZ'、 'SIGXFSZ')
 for sig in signals:
 if hasattr( signal、sig):
 signal.signal(getattr(signal、sig)、signal.SIG_DFL)

次に、_preexec_fn_ argを介してSIGPIPパッチを渡して、サブプロセスシェルを介して(パイプなしで)プログラムを直接呼び出します。また、使用したいシェルとしてbashを指定しましたが、必須ではありません。

myproc = Popen( "./ test"、
 stdin = PIPE、
 stdout = PIPE、
 Shell = True、
 executable = '/ bin/bash' 、
 preexec_fn = ** restore_signals()**)

これにより、パイプが開いたままになり、手動で閉じる必要があります。私はエクスプロイト開発者の観点からは、それはあなたにとって本当に問題ではないと思います。

データを送信するには、.communicate()メソッドを使用しないでください。次のように、シェルコードバイトを送信する接続を手動で管理する必要があります。

#最初に受信バッファーをクリアして準備する
 myproc.stdout.flush()
#シェルコードを送信します。ここで、shellcodeはシェルコードを保持する変数です
 myproc.stdin.write(shellcode + "\ n")

Enterをシミュレートする "\ n"に注意して、fgets()がバッファを終了することを認識してください。

次に、出力を読み取るために、出力(バナーなど)が改行バイト(0x0a/'\ n')で終わることがわかっている場合を除いて、.readline()メソッドを使用することはおそらくありません。 .read()メソッドを使用して手動で管理する必要があります。

.read()で重要なのは、返されるデータをある程度理解する必要があることです。したがって、メモリリークや文字列などが返されることを予期していて、サイズがわかっている場合は、それを指定して、追加のデータを待つ間にスクリプトがブロックされないようにする必要があります。読み取り時のデフォルトのバッファーサイズを思い出せませんが、そのバッファーがいっぱいになるまで戻りません。 .readline()は0x0aバイトで戻ります。したがって、10バイトの文字列が必要な場合:

 #シェルコード送信の1秒後にスリープして、プロセスに時間を与えます
 time.sleep(1)
 output = myproc.stdout.read(10)

複数の行を送信する場合は、毎回stdoutバッファーをflush()する必要があることに注意してください。そうしないと、応答は以前に受信したデータから10バイトを読み取ります。

必要に応じて繰り返します。お役に立てば幸いです。

1
cyberjitz