# cpp primer pluss write up

这是一个简单的多线程资源利用的 bug

# 静态逆向

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  char v10; // [rsp+Bh] [rbp-25h] BYREF
  int v11; // [rsp+Ch] [rbp-24h] BYREF
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v13; // [rsp+28h] [rbp-8h]
  v13 = __readfsqword(0x28u);
  init_func();
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "WELCOME SI LIBRARY");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  Library::Library((Library *)&v10);
  v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Please enter your choice");
  std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
  while ( 1 )
  {
    menu();
    std::istream::operator>>(&std::cin, &v11);
    switch ( v11 )
    {
      case 1:
        if ( flag <= 0 )
        {
          v6 = std::operator<<<std::char_traits<char>>(&std::cout, "Please input bookname");
          std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
          read(0, buf, 0x14uLL);
          Library::borrowbook((Library *)&v10, buf);
          memset(buf, 0, 0x14uLL);
        }
        else
        {
          v5 = std::operator<<<std::char_traits<char>>(&std::cout, "Please return book first");
          std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
        }
        break;
      case 2:
        if ( flag <= 0 )
        {
          v7 = std::operator<<<std::char_traits<char>>(&std::cout, "There is no book need to return");
        }
        else
        {
          Library::returnbook((Library *)&v10);
          flag = 0;
          v7 = std::operator<<<std::char_traits<char>>(&std::cout, "Return success!");
        }
        std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
        break;
      case 3:
        if ( flag <= 0 )
        {
          v8 = std::operator<<<std::char_traits<char>>(&std::cout, "There is no book need to read");
          std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
        }
        else
        {
          vuln();
        }
        break;
      case 4:
        show();
        break;
      case 5:
        exit(0);
      default:
        continue;
    }
  }
}

在 main 主函数内,提供四个选项

1.borrow a book

2.return a book

3.read a book

4.show book name

unsigned __int64 vuln(void)
{
  pthread_t newthread; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]
  v2 = __readfsqword(0x28u);
  std::string::operator=(&bookname[abi:cxx11], "C++_primer_plus");
  pthread_create(&newthread, 0LL, readbooks, 0LL);
  return __readfsqword(0x28u) ^ v2;
}

在 read a book 的时候,会忽略我们自己读取的 bookname , 而是重新设定 bookname 为 “c++_primer_plus”.

我们进 readbooks 函数实现去看看

unsigned __int64 __fastcall readbooks(void *a1)
{
  const char *v1; // rax
  FILE *stream; // [rsp+8h] [rbp-88h]
  char dest[32]; // [rsp+10h] [rbp-80h] BYREF
  char ptr[88]; // [rsp+30h] [rbp-60h] BYREF
  unsigned __int64 v6; // [rsp+88h] [rbp-8h]
  v6 = __readfsqword(0x28u);
  sleep(1u);
  v1 = (const char *)std::string::c_str(&bookname[abi:cxx11]);
  strcpy(dest, v1);
  stream = fopen(dest, "r");
  if ( !stream )
    exit(0);
  fread(ptr, 1uLL, 0x50uLL, stream);
  puts(ptr);
  return __readfsqword(0x28u) ^ v6;
}

fopen 时使用的文件名是从 bookname [abi:cxx11] 这个全局变量中获取的,而且主线程创建子线程后,主线程依旧可以操作。

由于子线程 readbooks 函数内,sleep (1), 所以,这就给主线程 1 秒的时间,可以修改全局变量 bookname [abi:cxx11]。..

# 攻击

主界面输入 3,在一秒的等待时间内,输入 2 ,return book, 之后输入 1 ,borrow a book, 此时输入文件名,注意,这里的 flag 要带上 \x00 结尾,因为程序输入的时候不会自动追加,如果不加上就会变成 "flagprimer_plus",foen 失败。