0

オンラインのソフトウェア セキュリティ コースを受講しています。シェルコードで実験しようとしています。脆弱なサーバー、インジェクション プログラム、アセンブリに変換する (おそらく壊れている) シェルコードを作成し、それを Python スクリプトで削除しました。次に、すべてをシェル スクリプトでコンパイルして実行します。shellcodeバイナリを適切に作成していないことは確かですが、すべてのファイルを含めています。

脆弱な.c

#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>


int main(void) {
    int sd, sdc, addrsize, ret, i;
    struct sockaddr_in addr;
    char exec[1024], buf[128];
    void (*fn)(void);
    const short family = AF_INET;

    fn = (void (*)(void))exec;
    addrsize = sizeof(struct sockaddr_in);
    sd = socket(family, SOCK_STREAM, IPPROTO_TCP);
    if (sd < 0) {
        perror("socket");
        return 1;
    }
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8000);
    if (bind(sd, (struct sockaddr *) &addr, 
            sizeof(addr)) < 0) {
        perror("bind");
        return 2;
    }
    listen(sd, 1);
    sdc = accept(sd, (struct sockaddr *)&addr, &addrsize);
    i = 0;
    do {
        i += ret = read(sdc, buf, 128);
        memcpy(exec + i, buf, ret);
    } while (ret > 0);
    close(sd);
    fn();
    return 0;
}

inject.c

#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
    int sd, fd, ret, buflen = 128;
    struct sockaddr_in addr;
    FILE *f;
    const char fname[] = "shellcode";
    char buf[buflen];
    const short family = AF_INET;
    const char host[] = "127.0.0.1";

    f = fopen(fname, "r");
    fd = fileno(f);

    sd = socket(family, SOCK_STREAM, IPPROTO_TCP);
    if (sd < 0) {
        perror("socket");
        return 1;
    }
    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = family;
    addr.sin_port = htons(8000);
    inet_pton(family, host, &(addr.sin_addr.s_addr));
    ret = connect(sd, (struct sockaddr*)&addr, sizeof(struct sockaddr));
    if (ret < 0) {
        perror("connect");
        return 2;
    }
    do {
        ret = read(fd, buf, buflen);
        if (write(sd, buf, ret) != ret)
            perror("write");
    } while (ret > 0);
    close(fd);
    close(sd);
    return 0;
}

shellcode.c

void shellcode(void) {
    char sz[6] = { 'h', 'e', 'l', 'l', 'o', '\0' };
    write(1, sz, 6);
}

stripshellcode.py

copy = False
outf = open("shellcode-stripped.s", "w")
for line in open("shellcode.s").read().split("\n"):
    if copy:
        outf.write(line + "\n")
    if "shellcode:" in line:
        copy = True
    elif "ret" in line and copy:
        copy = False
outf.close()

make.sh

gcc -S shellcode.c && \
python stripshellcode.py && \
gcc -c shellcode-stripped.s -o shellcode && \
gcc inject.c -o inject && \
gcc vulnerable.c -o vulnerable && \
./vulnerable & ./inject

出力

$ sh make.sh
make.sh: line 6: 13905 Segmentation fault      ./vulnerable

この実験プロセスのどこが間違っているのでしょうか?

編集:

「クローズドシステム」とは、これが仮想マシンで実行されていることを意味し(バインドアドレスを確認してください)、また、簡潔さと容易さのためにいくつかの変数が依存する方法でハードコードされていることも意味します。

4

2 に答える 2

1

シェルコード ファイルはオブジェクト ファイルであるため、これは機能しません。これは、リンカーの使用を目的とした中間ファイル形式です。それを一連の命令として扱うのは明らかに間違っています。オブジェクトファイルには、とりわけコードが含まれています。ただし、このコードは不完全です。コードが書き込みなどのシンボルを参照する場合、アドレスの代わりにプレースホルダーが挿入されます。複数のオブジェクト ファイルとライブラリをつなぎ合わせてメモリ内にコードを配置し、シンボルにアドレスを効果的に割り当て、最後にプレースホルダにパッチを適用するのは、リンカの仕事です。

実行可能ファイルが生成されたとしても、最新の実行可能形式ではローダーを実行する必要があるため、機能しません。基本的に、ローダーは実行可能ファイルに保存されているメタデータを読み取り、ファイルからいくつかのセクションをメモリにマップし、調整して、最後にメタデータで定義されたエントリ ポイントに制御を移します。

シェルコードを作成するには、いくつかの実用的なアプローチがあると思います。

  1. アセンブリ シーケンスを手動でエンコードします。

  2. ツールを使用してエンコードを行います。

  3. オブジェクト ファイルの形式を調べてコードを抽出します。objdump で実行できると思います。

  4. ローダーの実行を必要としない形式で実行可能ファイルを生成するようにリンカーに依頼します。

他の回答を読んで、実際には失敗が以前に発生していることに気付きました.execバッファーはスタックに割り当てられ、スタックメモリ保護属性は明示的に実行を拒否します。この保護は、Linux の execstack ユーティリティなどでオフにすることができます。

于 2015-06-12T22:22:10.357 に答える