1

さて、次のコードが与えられます:

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "callstack.h"
#include "tweetIt.h"
#include "badguy2.c"

static char *correctPassword = "ceriaslyserious";
char *message = NULL;

int validateSanity(char *password) {
    for(int i=0;i<strlen(password);i++)
        if(!isalpha(password[i]))
            return 0;
    unsigned int magic = 0x12345678;
    return badguy(password);
}

int validate(char *password) {
    printf("--Validating something\n", password);
    if (strlen(password) > 128) return 0;

    char *passwordCopy = malloc(strlen(password) + 1);
    strcpy(passwordCopy, password);
    return validateSanity(passwordCopy);
}

int check(char *password, char *expectedPassword) {
    return (strcmp(password, expectedPassword) == 0);
}

int main() {
    char *password = "wrongpassword";
    unsigned int magic = 0xABCDE;
    char *expectedPassword = correctPassword;
    if (!validate(password)) {
        printf("--Invalid password!\n");
        return 1;
    }
    if (check(password, expectedPassword)) {
        if (message == NULL) {
            printf("--No message!\n");
            return 1;
        } else {
            tweetIt(message, strlen(message));
            printf("--Message sent.\n");
        }
    } else {
        printf("--Incorrect password!\n");
    }
    return 0;
}

関数を使用して、だましmainてツイートを送信することになっていますbadguypasswordbadguyでは、 inの宣言とにmain渡される引数の違いである前の問題からのオフセットがありますbadguy。このオフセットを使用して、メインのアドレスを検索し、の値を操作するように指示されているcorrectPasswordためpasswordpasswordパスワードcorrectPasswordチェックが発生したときに、それは正当であると考えられます。このオフセットを使用してアドレスを見つけ、そこから続行する方法を理解するのに問題があります。

4

1 に答える 1

2

まず、コンパイラの動作を適切に制御できることを確認してください。つまり、呼び出し規約を理解し、それらが尊重されていることを確認しください(最適化されたり、変更されたりしていないこと)。これは通常、堅牢な方法が考案されるまで、少なくともより制御された条件下でのテストでは、最適化設定をオフにするために要約されます。などの変数には特に注意しexpectedPasswordてください。最適化される可能性が高いためです(expectedPasswordスタック内に作成されることはなく、同等のものに置き換えられ、正しいパスワードへのスタック参照correctPasswordまったくない状態になる可能性があります)。

第二に、それは;"wrongpassword"よりも短いことに注意してください。"ceriaslyserious"言い換えれば、私がそれをまっすぐにした場合、そこにコピーするためにpasswordCopy(そのサイズは"wrongpassword"プラス1の長さです)が指すバッファーに割り込もうとする"ceriaslyserious"と、セグメンテーション違反が発生する可能性があります。それでも、特にスタックフレームからのオフセットがすでにある場合は、コールスタック内のアドレスを追跡するのは比較的簡単です(上記を参照expectedPasswordmain()

制御された状況下でのx8632ビットターゲットを考慮すると、expectedPassword8バイト下に存在しますpassword(最適化されていない場合はpassword4、magic最適化されていない場合は4)。あなたが言ったようにパラメータへのオフセットがあるので、そのパラメータのアドレスからオフセットを引いてから8を足すだけで十分です。結果のポインタは、パスワードを含む静的領域をpassword指すはずです。expectedPassword繰り返しになりますが、環境を再確認してください。x64のスタックレイアウトの説明については、これを確認してください(32ビットの場合のレイアウトも同様です)。

最後に、expectedPasswordが呼び出しスタックに存在しない場合、correctPasswordはグローバル静的であるため、データセグメントに存在し、メソッドを使用できなくなります。この状況で目標を達成するには、よりインテリジェントなアルゴリズムを使用してデータセグメントを注意深くスキャンする必要があります。check()ただし、プログラムテキストでの戻り値のテストを見つけてsに置き換える方がおそらく簡単ですnop(テキストセグメントへの書き込みを許可するためにページのアクセス許可を適切に操作した後)。

問題が発生した場合は、結果のアセンブリコードを調べるのが最善の方法です。GCCを使用している場合gcc -Sは、アセンブルの直前にコンパイルを停止します(つまり、アセンブリソースコードファイルを出力として生成します)。objdump -dも役立つ可能性があります。gdb命令間を移動したり、フレームの分解を表示したり、レジスタの内容を表示したりできます。ドキュメントを確認してください。

これらの演習は、一般的なプログラムでセキュリティ違反がどのように発生するかを理解し、防御プログラミングに関するいくつかの基本的な概念を提供するのに特に役立ちます。

于 2012-10-16T04:40:09.117 に答える