web-dev-qa-db-ja.com

バッファオーバーフローはgdbで機能しますが、gdbなしでは機能しません

CentOS 6.4 32ビットを使用していますが、プログラムでバッファオーバーフローを引き起こそうとしています。 GDB内で動作します。出力は次のとおりです。

[root@localhost bufferoverflow]# gdb stack
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6_4.1)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/bufferoverflow/stack...done.
(gdb) r
Starting program: /root/bufferoverflow/stack
process 6003 is executing new program: /bin/bash
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.el6_4.2.i686
sh-4.1#

ただし、プログラムスタックを単独で実行すると、セグメンテーションエラーが発生します。これはなぜでしょうか?

38
thaweatherman

デバッグプロセスにnon-determinismを導入する要因を適切に考慮しないと、エクスプロイト開発は深刻な頭痛の種になります。特に、デバッガのスタックアドレスは、通常の実行中にアドレスと一致しない場合があります。このアーティファクトは、オペレーティングシステムローダーが環境変数とプログラム引数の両方を配置するために発生しますbeforeスタックの先頭:

Process layout

脆弱なプログラムは引数を取らないため、環境変数が原因である可能性があります。シェルとデバッガーの両方の呼び出しで同じであることを確認してください。このために、呼び出しをenvでラップできます。

_env - /path/to/stack
_

そして、デバッガーでは:

_env - gdb /path/to/stack
($) show env
LINES=24
COLUMNS=80
_

上記の例では、gdbによって設定される2つの環境変数があり、さらに無効にできます。

_unset env LINES
unset env COLUMNS
_

これで、_show env_は空のリストを返すはずです。この時点で、デバッグプロセスを開始して、ジャンプ先の絶対スタックアドレス(例、_0xbffffa8b_)を見つけ、それをエクスプロイトにハードコーディングできます。

さらに微妙だが重要な詳細:_./stack_と_/path/to/stack_の呼び出しには違いがあります:_argv[0]_はプログラムを呼び出したとおりに保持するため、呼び出し文字列が等しいことを確認する必要があります。上記の例で_/path/to/stack_と_./stack_だけでなく_gdb stack_を使用したのはそのためです。

メモリの安全性の脆弱性を悪用することを学ぶときは、以下のラッパープログラムを使用することをお勧めします。

_$ invoke stack         # just call the executable
$ invoke -d stack      # run the executable in GDB
_

スクリプトは次のとおりです。

_#!/bin/sh

while getopts "dte:h?" opt ; do
  case "$opt" in
    h|\?)
      printf "usage: %s -e KEY=VALUE prog [args...]\n" $(basename $0)
      exit 0
      ;;
    t)
      tty=1
      gdb=1
      ;;
    d)
      gdb=1
      ;;
    e)
      env=$OPTARG
      ;;
  esac
done

shift $(expr $OPTIND - 1)
prog=$(readlink -f $1)
shift
if [ -n "$gdb" ] ; then
  if [ -n "$tty" ]; then
    touch /tmp/gdb-debug-pty
    exec env - $env TERM=screen PWD=$PWD gdb -tty /tmp/gdb-debug-pty --args $prog "$@"
  else
    exec env - $env TERM=screen PWD=$PWD gdb --args $prog "$@"
  fi
else
  exec env - $env TERM=screen PWD=$PWD $prog "$@"
fi
_
94
mavam

Gdbでコードを実行するときのスタックフレームポインターのアドレスは、通常の実行とは異なります。そのため、gdbモードでは戻りアドレスが破損する可能性がありますが、通常モードで実行している場合は正しくない場合があります。その主な理由は、環境変数が2つの状況間で異なるためです。

これは単なるデモであるため、被害者のコードを変更し、バッファーのアドレスを出力できます。次に、戻りアドレスをoffset + bufferのアドレスに変更します。

ただし、実際には、悪意のあるコードの前に NOPスレッド を追加して、リターンアドレスを推測する必要があります。また、推測が間違っている可能性があるため、正しいアドレスを取得するために複数回推測する場合があります。

これがあなたのお役に立てば幸いです。

7
York

端末とgdbで同じスタックを使用してプログラムを実行する簡単な方法を次に示します。

まず、スタック保護なしでプログラムがコンパイルされていることを確認してください。

gcc -m32 -fno-stack-protector -z execstack -o shelltest shelltest.c -g

そして、ASLRは無効になっています。

echo 0 > /proc/sys/kernel/randomize_va_space

注:私のマシンのデフォルト値は2でしたが、これを変更する前に注意してください。

次に、そのようにプログラムを実行します(それぞれ端末とgdb):

env -i PWD="/root/Documents/MSec" Shell="/bin/bash" SHLVL=0 /root/Documents/MSec/shelltest
env -i PWD="/root/Documents/MSec" Shell="/bin/bash" SHLVL=0 gdb /root/Documents/MSec/shelltest

gdb内で、unsetLINESおよびCOLUMNSを確認してください。

注: テストプログラム で遊んで、これらの環境変数を取得しました。

これらの2回の実行により、スタックの最上部への同一のポインターが得られるため、リモートでホストされているバイナリを悪用しようとする場合、リモートスクリプトシェナンガンは必要ありません。

5
Aralox

バッファオーバーフローがgdbおよびsegfaultsの下で機能する理由は、gdbがアドレススペースレイアウトのランダム化を無効にするためです。これはgdbバージョン7でデフォルトで有効になったと思います。

これを確認するには、次のコマンドを実行します。

show disable-randomization

そしてそれを設定します

set disable-randomization on

または

set disable-randomization off
5
LogicG8

ここで受け入れられている解決策を試しましたが、うまくいきません(私にとって)。 gdbが環境変数を追加し、そのためにスタックアドレスが一致しないことは知っていましたが、その変数を削除しても、gdbなしでは悪用できません(受け入れられたソリューションに投稿されたスクリプトも試しました)。

しかし、Webで検索すると、自分に合った他のスクリプトが見つかりました。 https://github.com/hellman/fixenv/blob/master/r.sh

使用方法は、受け入れられたソリューションのスクリプトと基本的に同じです。

  • r.sh gdb ./program [args] gdbでプログラムを実行する
  • r.sh ./program [args] gdbなしでプログラムを実行する

そして、このスクリプトは私のために機能します。

2
RdlP

CentOS 6.4 32ビットを使用していますが、プログラムでバッファオーバーフローを引き起こそうとしていますが、プログラムスタックを単独で実行すると、セグメンテーションエラーが発生します。

また、FORTIFY_SOURCEが結果に影響していないことを確認する必要があります。 FORTIFY_SOURCEは「より安全な」関数呼び出しを挿入していくつかのタイプのバッファオーバーフローを防ぐため、SEGフォールトはFORTIFY_SOURCEのように聞こえます。コンパイラーが宛先バッファーのサイズを推測できる場合、サイズがチェックされ、違反(つまり、セグフォールト)でabort()が呼び出されます。

テストのためにFORTIFY_SOURCEをオフにするには、-U_FORTIFY_SOURCEまたは-D_FORTIFY_SOURCE=0

1
jww

Gdbの外部で発生しないgdbの主な機能の1つは、ゼロメモリです。おそらくコードのどこかで、メモリを初期化しておらず、ガベージ値を取得しています。 Gdbは、これらのタイプのエラーを隠して、割り当てたすべてのメモリを自動的にクリアします。

たとえば、次はgdbで機能しますが、gdbでは機能しません。

int main(){
    int **temp = (int**)malloc(2*sizeof(int*)); //temp[0] and temp[1] are NULL in gdb, but not outside
    if (temp[0] != NULL){
        *temp[0] = 1; //segfault outside of gdb
    }
    return 0;
}

Valgrindでプログラムを実行して、この問題を検出できるかどうかを確認してください。

0
chacham15