私はCTFウォーゲームをプレイしていて、unfortunatleyがこのレベルで立ち往生しているので、「クラウドソーシング」したいと思います。完全な答えは必要ありませんが、ヒントで十分です。
どうやって:
シェルを実行したい:
execl("/bin/sh", "sh", NULL);
何か案は?
バイナリはsetuidであり、/ levels/level08.passは上位レベルでのみ読み取り可能です。
-r-sr-x--- 1 level9 level8 7696 Apr 25 2015 level08
-r-------- 1 level8 level8 412 Apr 25 2015 level08.c
-r-------- 1 level9 level9 13 Apr 25 2015 level08.pass
Level08.cのプログラムコードは次のとおりです。
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char buf[64];
FILE *fp;
signal(SIGABRT, SIG_DFL);
fp = fopen("/levels/level08.pass", "r");
fgets(buf, 64, fp);
buf[strcspn(buf, "\n")] = '\0';
if(strcmp(buf, argv[1]))
raise(SIGABRT);
execl("/bin/sh", "sh", NULL);
}
strcmp
呼び出しとその原因は正しいです。信号をいくらか抑制するための2番目の直感は正しいものです。
signal(SIGABRT, SIG_DFL); … raise(SIGABRT);
raise
を呼び出すと、デフォルトのアクションがプログラムの強制終了であるシグナルが発生します。 signal
への以前の呼び出しにより、デフォルトのアクションが確実に実行されます。プロセスがそれ自体をシグナルするとき、これは同期的です:raise
関数が戻る前にシグナルが受信されます。
キャッチは何ですか? 無視されることに加えて、シグナルはブロックされることができます。無視されたシグナルが配信されても、何も起こりません。ブロックされたシグナルが受信されると、カーネルはそれをメモするだけで配信しません。そのため、プログラムは正常に動作し続けます。信号のブロックが解除されると、信号が配信され、配信時に信号に関連付けられたアクションが実行されます。
ブロックされた信号がexecve
の境界を越えています。 POSIXの根拠であるexecve
にそれに関する注記があります。シグナルブロッキングを管理する関数は sigprocmask
です。
したがって、SIGABRTをブロックしてプログラムを呼び出すと、パスワードチェックがバイパスされます。
Perl -MPOSIX -we 'sigprocmask(SIG_BLOCK, POSIX::SigSet->new(SIGABRT), POSIX::SigSet->new()); exec "level08", ""'
- Strcmpを0にする
Strcmpが正しく実装されていると仮定すると、これは、argv[1]
がlevel08.pass
の内容と63バイトまたは最初の改行/ nullバイトまで正確に一致する必要があることを意味します。
ファイルの内容がわからないため、argv[1]
を操作して、正しい文字列を含むメモリ内のアドレス(たとえば、buf
)をポイントするのが唯一のオプションです。 argc
チェックがないため、これについて不思議に思いましたが、argv
の処理方法は不可能です。
- どういうわけか信号を抑制する
プロセス信号マスクを見てください。
この段落には the docs の重要なポイントがいくつかあります。
現在ブロックされている信号の集まりを信号マスクと呼びます。各プロセスには独自のシグナルマスクがあります。新しいプロセスを作成すると(「プロセスの作成」を参照)、親のマスクを継承します。信号マスクを変更することにより、完全に柔軟に信号をブロックまたはブロック解除できます。
/levels/level08.pass
の最初の行を入力すると、シェルを実行するようです。たぶん、タイミング攻撃を使用してstrcmp
の終了場所を確認し、それを使用して一度に1文字を推測することができます。