web-dev-qa-db-ja.com

C-単純なバッファーオーバーフローの悪用、異なるタイプの呼び出し関数でEIPはどのように上書きされますか?

一般的な背景:私は、次のようなstrcpy()関数呼び出しを利用するCでBoFの例を実装しようとするエコーサーバーを作成しました。

// .... including the corresponding libraries depending on Host environment

#include <stdio.h>                                              // crossed platform headers
#include <string.h>                                             // string header, for strcpy
#include <stdlib.h>                                             // stardard lib, for exit()


void echo(void);                                 // infinite loop echo serving client
char* vuln_func(char* recieved);                                // BoF vulnerable function that uses strcpy from string.h


int main(void)
{
  ... setting up echo server
  echo();

  return 0;
}

void echo(void)
{
  char *send_str;                                                 // declaring the pointer to the string recieved from vulnFunc
  char recv_str[1024];                                            // variable declaration to store input string from client

  // infinite loop to echo serve clients
  while(1)

  {
    ... recieving string from client and storing it starting at location recv_str
    // calling vuln_func and inputting the received string from client

    send_str = vuln_func(recv_str);                       

    ...echoing the send_str back to the client

  }


}

char* vuln_func( char* recieved)
{
  char* sending_str = (char*)calloc(100, sizeof(char));                               

   char buf[100];                                                                 

  int i;                                                                          // declaring counter for loop
  strcpy(buf, recieved);
  for (i = 0; i < strlen(buf); i++)                                              // for loop to populate return string with the send
  {
    sending_str[i] = buf[i];
  }

  return sending_str;                                                             
}

Problem:したがって、サーバーに十分な大きさの入力文字列を入力すると、サーバーはオフセット41414141(必要なもの)でアクセス違反を生成します。ただし、違反の時点で逆アセンブラ/デバッガ(イミュニティデバッガを使用した)でexeを逆アセンブルすると、ESPもEIPもAsの文字列で上書きされませんでした(たとえば))。 EBPレジスタは、Asのオーバーフローした大きな文字列で上書きされました。

Solution:だから私はグーグルして、同じ種類の同様のサンプルコード(BoF vulnを備えたEcho Server)を見て、次のようなvuln_funcで異なることを試しました:

void vuln_func( char* recieved)
{

   char buf[100];                                                                 

  strcpy(buf, recieved);

}

または:

int vuln_func( char* recieved)
{

  char buf[100];                                                                 

  strcpy(buf, recieved);

  return 1;

}

エコー関数は次のようになります。

void echo(void)
{

  char recv_str[1024];                                            // variable declaration to store input string from client

  // infinite loop to echo serve clients
  while(1)

  {
    ... recieving string from client and storing it starting at location recv_str
    // calling vuln_func with string received from client

    vuln_func(recv_str);                       

    ...echoing the recv_str back to the client

  }


}

したがって、ehoがsend_strの代わりにrecv_strをエコーバックして、vhon_func呼び出しの目的全体を無効にしますが、このBoFの脆弱性を実証するために、ここに残します。イミュニティデバッガーで確認した後、ESPとEIPレジスターの両方が文字列( "A * 128")で正常に上書きされます。問題が修正されました!!素晴らしい

質問:なぜ問題はそのように振る舞ったのですか?

  1. 文字列へのポインター(アドレス)を呼び出し元の関数に返すと、EIPが上書きされなくなりますが、voidまたはintを返すと、EIPレジスターが期待どおりに上書きされます。
  2. EIPレジスタは2つのscanriosにその値をどのように格納しますか
    上記の(1つのvuln_funcがchar *を返し、他のvuln_funcが
    戻るvoid)?
  3. このように動作する問題はスタックアライメントと関係がありますか?strcpyがmain()で呼び出され、他のSE投稿/回答から調べた場合、(EIPが上書きされる)問題が発生することを知っています

ご覧のとおり、問題は解決されましたが、なぜ機能したのかわかりません。説明するか、書き込み方向を指定してください。多くの称賛と事前に感謝します。

2
Rennitbaby

私の推測では、他のローカル変数(iまたはending_str)を上書きしたため、時期尚早のクラッシュが発生しました。

何が起こっているかを確認するには、生成されたコードを調べる必要があります。設定に応じて、コンパイラーは、呼び出された関数をすべてインライン化するなど、コードに対して興味深いことを行うことができるため、戻りアドレスの上書きが大幅に遅くなります。

3
manduca

回答が研究の方向性とより多くのテストを提供したため、@ manducaがベストアンサーに投票しました。そして、いくつかのテストの後、私は尋ねられた私の質問に対する完全な答えを結論付けることができました:

  1. 関数の戻り値の型は問題とは関係がなく、単に間違った仮定でした
  2. 問題の2つのシナリオのそれぞれについて、EIPは次の命令のメモリアドレスを格納します。スタックフレームの作成の開始以降、スタックにプッシュされたアドレス(戻りアドレス)を取得するのは、関数呼び出し/戻りステートメントの最後の場合のみです。したがって、bufを上書きするとすぐに例外が発生し、命令の次の行が関数の終了(voidを返す)またはreturnステートメントでない場合、EIPはスタックから更新されません。次の命令のメモリ位置に格納します(前の命令のメモリ位置からインクリメントして推測しています)。そしてそれが、EIPが10MBの "A"の文字列によって上書きされなかった理由です。一方、ソリューションでは、returnステートメントまたはvuln_func関数呼び出しの最後の直前にstrcpy関数呼び出しがあったため、スタックがバッファオーバーフローで爆発している間にEIP​​が更新され、ソリューションがEIPを「なので
  3. いいえ、この問題はスタックアライメントとは関係がないと思います。これは、スタックサイズ(64ビットシステムではデフォルトで16バイト)にいくつかのパディングを追加するだけで、オーバーフローで文字列操作を行おうとした場合にも考慮されます。意図的にEIPを変更しようとすると、正直に言って、この問題はmain()にのみ存在するように感じるので、main()の悪用を回避することが解決策になる可能性があります。
2
Rennitbaby