【CTF】堆栈攻击总结

Structure

从高到低(来自ctf-wiki):

 

从低到高:

Low addr

-------------------------------------------------------

New stack

Data

Cannary

Saved registers

Return address

Old stack

 

Old chunk

New chunk

Prev size|Prev Data(Reuse)

Size(AMP)

Data|Fd

  |Bk

Top chunk

-------------------------------------------------------

High addr

 

ANON_MAIN_ARENA,记录当前 chunk 是否不属于主线程,1表示不属于,0表示属于

MIS_MAPPED,记录当前 chunk 是否是由 mmap 分配的。

PPREV_INUSE,记录前一个 chunk 块是否被分配。

 

Top chunk:当所有的bin都无法满足用户请求的大小时,分割top chunk产生新chunk

初始情况下,我们可以将 unsorted chunk 作为 top chunk

thread 不同的是,main_arena 并不在申请的 heap 中,而是一个全局变量,在 libc.so 的数据段。

Command

ls -ali 查看inode

exec 1>&2 恢复重定向

Shell

命令:onegadgetsystemexecve

跳板:_malloc_hook_free_hookgotropreturn addr,(虚表,堆喷)

Libc

泄露:unsorted bin UAF(超大chunk调用mmap,读取gotleak需要先构造puts/printf),任意读->DynELF,构造printf读取栈上的__libc_start_main返回地址

Printf

可控格式化参数,利用%n%p进行任意读/

 

Stack

栈中shellcoderet gadget->jmp rsp跳板

bssshellcode:控制ret addr指向bss

ret->shellcodeonegadget(需要先leak libc)/backdoor

rop(ret=pop rip)->栈溢出/栈迁移->指向rop

Rop

先通过gadget控制寄存器,再调用依赖寄存器传参的函数/onegadget

 

Gadget1pop Value到寄存器,ret=pop rip

Value1

Value2

Gadget2pop Value到寄存器,ret=pop rip

Value3

Funcexecve依赖寄存器/system@plt依赖寄存器和栈/onegadget依赖寄存器或无依赖)

Fake return addr(对于依赖栈的函数需要伪造栈结构,call=push rip

Arg1

Arg2

 

栈溢出:通过栈溢出,从return addr处开始构造rop链,retrsp指向rop

栈迁移:修改备份的帧指针使其指向已经构造好的rop第一次leavepop rbp,第二次leavemov rsp,rbpretrsp指向rop

Ret2dl_resolve(解析libc任意函数地址)

Stage1:migrate

Rop1:padding+read(base)+migrate(base)

Rop2:write(str)+str

 

Stage2:migrate+dl_resolve

Rop1:padding+read(base)+migrate(base)

Rop2:plt[0](write_index@plt)(str)+str

 

Stage3:migrate+dl_resolve+fake_index

Rop1:padding+read(base)+migrate(base)

Rop2:plt[0](fake_index->fake_reloc)(str)+fake_reloc->write_symbol@dynsym+str

 

Stage4:migrate+dl_resolve+fake_index+fake_symbol

Rop1:padding+read(base)+migrate(base)

Rop2:plt[0](fake_index->fake_reloc)(str)+fake_reloc->fake_symbol+fake_symbol->write_str@dynstr+str

 

Stage5:migrate+dl_resolve+fake_index+fake_symbol+fake_str(fake_str=write)

Rop1:padding+read(base)+migrate(base)

Rop2:plt[0](fake_index->fake_reloc)(str)+fake_reloc->fake_symbol+fake_symbol->fake_str+fake_str+str

 

Stage6:migrate+dl_resolve+fake_index+fake_symbol+fake_str(fake_str=system)

Rop1:padding+read(base)+migrate(base)

Rop2:plt[0](fake_index->fake_reloc)(str)+fake_reloc->fake_symbol+fake_symbol->fake_str+fake_str+str

 

总结:plt[0]->dl_resolve->index->reloc->symbol->str

plt0作用:执行dl_resolve来解析函数的地址并将地址填写到got,再执行got指向的函数
fake_reloc = flat([write_got, r_info])
write_got可以用原来的got,也在bss段随便找一个位置,只要对应位置可写就可以

注:ctf-wiki用的是原来的gotROP_LEVEL5用的是bss+0x200

Heap

任意分配:

Fastbin Double Free是指 fastbin chunk 可以被多次释放,因此可以在 fastbin 链表中存在多次。这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块,相当于多个指针指向同一个堆块。如果更进一步修改 fd 指针,则能够实现任意地址分配 chunk

House of Spirit在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。

 

Lib>2.26:第一次malloc在申请目标chunk之前,会先申请一个大小为0x251tcache_perthread_struct输入缓冲区和输出缓冲区(后两个可以通过setbuf关闭)

 

chunk 分配位置:

栈:控制返回地址等关键数据。

libc:使用字节错位来绕过 size 域的检验,实现直接分配 fastbin _malloc_hook的位置来控制程序流程。

 

fd从而任意地址分配chunk,从而达到任意读写(unsorted bin UAF泄露libc):

堆有编辑溢出:直接溢出改fd

堆有新建溢出:先new进行内存布局,再用freenew溢出改fd

堆无溢出:double free,第一次申请改fd

堆有溢出且程序有指针列表:Unlink修改指针列表,从而达到任意读写

 

Heap攻击流程(堆溢出&没指针):

①悬挂指针

UAFunsorted bin->泄露libc

Double freefast bin->任意地址分配chunk->③可控指针(有约束)

②堆溢出

存在全局指针

Unlink->③可控指针(有约束)

修改fd

任意地址分配chunk->③可控指针(有约束)

提前计算堆地址构造第二个指针->①悬挂指针

③可控指针

已泄露libc

修改_malloc_hook(字节错位)->④获得shell

修改got->④获得shell

未泄露libc

读取got->泄露libc

修改关键数据

修改指针->③可控指针(无约束)

为绕过校验提供条件->②堆溢出

④获得shell

-------------------------------------------

指针流Heap攻击:

程序中自带ptrgot=&libcret=&libc

 

malloc(sz) 申请ptr,并修改*ptr

free(ptr) 依据*ptr

unsorted bin,修改*ptr=&libc

fast binfd(堆溢出/悬挂指针:直接改或者间接用double free改),同时*fd满足一定条件,申请到ptr=fd

unlink伪造chunk(堆溢出),同时存在_ptr指向伪造chunk,修改_ptr=&_ptr-3

④先free掉再用malloc,申请到同一个ptr(应对mallocedit绑定的情况)

⑤先free掉本来不存在的chunk再用malloc,申请到同一个ptr(首先要有可控指针)

read(ptr) 输出*ptr

edit(ptr) 修改*ptr

 

绕过检测:'\0'0,负数,有无符号比较,整数溢出,数组溢出(off by one

-------------------------------------------

Ⅰ泄露libc

Ⅱ获得shell

Main arena

#define NBINS 128

static struct malloc_state main_arena;

struct malloc_state {

mutex_t mutex;/* Serialize access.  */

int flags;/* Flags (formerly in max_fast).  */

mfastbinptr      fastbins[NFASTBINS];/* Fastbins */

/* Base of the topmost chunk -- not otherwise kept in a bin */

mchunkptr        top;

/* The remainder from the most recent split of a small request */

mchunkptr        last_remainder;

mchunkptr        bins[NBINS * 2 - 2];/* Normal bins packed as described above */

unsigned int     binmap[BINMAPSIZE];/* Bitmap of bins */

struct malloc_state *next;/* Linked list */

/* Memory allocated from the system in this arena.  */

INTERNAL_SIZE_T system_mem;

INTERNAL_SIZE_T max_system_mem;

};

typedef struct malloc_chunk *mfastbinptr;

mfastbinptr fastbinsY[ NFASTBINS ];

 

Chunk分类:fast binsunsorted binssmall binslarge bins

main_arena.bins存储各个表头chunkfdbk(双向链表),包含unsorted bins(1)small bins(2~63)large bins(64~127)

fastbinsY指向各个fast bins(单向链表)

 

main_arena.bins结构:

bins[0]=bin1.fd bins[1]=bin1.bk

bins[2]=bin2.fdbins[3]=bin2.bk

后边的bin以此类推

Unsorted bin UAF

linux中使用free()进行内存释放时,不大于 max_fast (默认值为 64B)的 chunk 被释放后,首先会被放到 fast bins中,大于max_fastchunk或者fast bins 中的空闲 chunk 合并后会被放入unsorted bin中(参考glibc内存管理ptmalloc源码分析一文)

而在fastbin为空时,unsortbinfdbk指向自身main_arena(双向链表的表头位于main_arena.bins,该地址的相对偏移值存放在libc.so中,可以通过use after free后打印出main_arena的实际地址,结合偏移值从而得到libc的加载地址。

 

def offset_bin_main_arena(idx):

word_bytes = context.word_size / 8

offset = 4  # lock

offset += 4  # flags

offset += word_bytes * 10  # offset fastbin

offset += word_bytes * 2  # top,last_remainder

offset += idx * 2 * word_bytes  # idx

offset -= word_bytes * 2  # bin overlap(前面的prev_sizesize

return offset

 

unsortedbin_offset_main_arena = offset_bin_main_arena(0)

main_arena_addr = unsortedbin_addr - unsortedbin_offset_main_arena

libc_base = main_arena_addr - main_arena_offset偏移值写在libc源码中

Einherjar堆溢出

堆溢出

修改prev_sizeP标志位->任意分配

Force堆溢出,任意大小malloc

堆溢出

修改top_chunksize->任意分配

Orangefree产生unsorted bin

申请大于top chunkchunk,会申请新的top chunk,原来的top chunk会被放入unsorted bin

Unlink(堆溢出)

堆溢出

修改prev_sizesizeP位、fdbk->修改某个指向chunk的指针指向其上面的地方->修改周围敏感数据及指针自身->任意写

 

P Q地址相邻,在PData区填写构造假chunk F,包含prev_size size fd bknext chunk's prev_size(由于地址复用size=next chunk's prev_size+1),并覆写Qprev_sizesizeP

从而使Free Q时让管理器认为F已经释放,于是把F Q合并,并将F从原来的链表unlink,又由于Pfdbk是精心构造的,导致unlink的时候能够修改某个原本指向PData区(即F)的指针ptr重新指向&ptr-3,从而修改ptr周围的数据

 

修改前:ptr=&P.Data

修改后:ptr=&ptr-3

Oreo可控指针

Str溢出造成指针可控->

任意读->泄露

任意释放->任意分配->任意写

 

控制next指针指向got表项,泄露libc base

控制next指针指向bss释放假chunkSpirit从而申请包含msg指针的chunk,使msg指针指向got

控制got表项指向system,获得shell

 

str溢出->ptr->leak libc

str溢出->ptr->释放目标chunk->分配目标chunk->ptr->got->获得shell

Search Engine悬挂指针

悬挂指针->\0绕过检测->指针复用

unsorted bin UAF->泄露

fast bin UAF->程序无修改功能->Double free->任意写

 

利用 unsorted bin 地址泄漏 libc 基地址

利用 double free 构造 fastbin 循环链表

分配 chunk malloc_hook 附近,修改malloc_hook one_gadget

 

申请unsorted chunk(uc)->free->ptr没清零且校验可用\0绕过->leak libc

 

申请a b c,均含'd'

list:c->b->a->uc(已清零)->NULL

delete 'd'

y(c)y(b)y(a)

0x70bin:a->b->c->NULL

 

list:c(已清零)->b(已清零)->a(已清零)->uc(已清零)->NULL

delete '\0'

(cfdNULL,所以初始字节为\0,没通过*i->sentence_ptr检验,不会提示这也就是c的作用,否则b在这里就不会有删除的提示,就无法double free)

y(b)n(a)n(uc)

0x70bin:b->a->b->a->...(申请a是为了绕过相邻检测)

 

分配b并使fd指向_malloc_hook->分配a->分配b->分配chunk_malloc_hook->_malloc_hook指向one_gadget->获得shell

Wheelofrobots悬挂指针

悬挂指针->off by one绕过检测->double free->修改读入限制

堆溢出->unlink->可控指针->泄露libc->got->获得shell

Roc826's_Note(悬挂指针)

堆内容没有初始化->泄露libc

悬挂指针->double free->修改got->获得shell

Babyheap堆溢出

堆溢出->

修改fd(和size->任意分配->

任意读(程序删指针,需要构造两个指针unsorted bin UAF->泄露

任意写

 

利用 unsorted bin 地址泄漏 libc 基地址。

利用 fastbin attack chunk 分配到 malloc_hook 附近。

 

利用4K0x1000)对齐可以1/16爆破相邻地址

 

申请0x10大小的a@0x00 b@0x20 c@0x40 d@0x600x80大小的e@0x80

a b c用来构造指向efdd用来修改esize,从而申请到e的第二个指针)

List:1(a) 2(b) 3(c) 4(d) 5(e)

释放3 2

Bin:b->c->null

填充astr溢出->控制bfd指向e

填充dstr溢出->控制esize0x21

分配2(b)->分配3(e)

 

List:1(a) 2(b) 3(e) 4(d) 5(e)

释放5->读取3->UAF泄露libc

 

同样的方法申请chunk_malloc_hook->指向onegadget->malloc触发->获得shell

Stkof(堆溢出)

堆溢出->unlink->修改指针周围敏感数据(指针列表)->指针可控->任意写

 

Unlink修改指针global[2]=global-1

修改global[0]指向free@got->修改free@got指向puts@plt->构造leak

修改global[1]指向puts@got->调用free实则是puts->泄露libc

修改global[2]指向atoi@got->修改atoi@got指向system->获得shell

 

note2堆溢出

堆溢出->unlink->修改指针周围敏感数据(指针列表)->可控指针->任意写

 

新建nodeSize0导致size-1溢出无穷大,从而产生堆溢出

 

申请note的大小分别为0x80(a)0(b)0x80(c),完成内存布局

这里因为只有新建node时有漏洞,而编辑node时没有漏洞,所以需要先free掉,再利用new写进内容(由于bin的机制,freenewchunk是同一个)

布局假chunk需要5word,而b中只有2word的空间,所以需要申请一个a来布局

a中布局假chunk,利用b溢出覆写cfree c触发unlinkptr[0]=ptr-3

 

修改ptr[0]指向atoi@got->查看内容->泄露libc

修改atoi@got指向system->获得shell

Annevi_Note(堆溢出)

堆内容没有初始化->泄露libc

堆溢出->Unlink->可控指针->got->获得shell

Annevi_Note2(堆溢出)

程序关闭标准输入输出,需要修改stdoutstderr,修改低两字节(4K对齐,1/16概率)

堆溢出->Unlink->可控指针

修改stdoutstderr->泄露libc

还原stdoutstdin

修改__free_hooksystem->获得shell

细节:用可控指针修改指针列表时还没有泄露libc,所以修改的时候保留一个指针指向指针列表,等到泄露libc知道__free_hook位置后,再用这个保留的指针修改指针列表,从而修改__free_hooksystem

E99p1ant_Note(堆溢出)

堆内容没有初始化->泄露libc(main_arena)

负数越界

show(-7)->泄露指针列表位置

show(-23)->泄露libc(_IO_2_1_stdout_)

堆溢出->Unlink->可控指针->free_hook_addr->获得shell

 

new 0,1

edit 0 (p64(0)+p64(0x90)+p64(list_addr-0x18)+p64(list_addr-0x10)).ljust(0x90,'\x00')+p64(0x90)+'\x90'(<-off by one@1)

free 1->unlink

list[0]=list_addr-0x18

 

edit 0 p64(0)*3+p64(free_hook_addr)+'\n'

list[0]=free_hook_addr

 

edit 0 system_addr

new 2 '/bin/sh\0'

del 2 ->获得shell

 

ROP_LEVEL5ROP

Ret2dl_resolve

解析并调用system

ROP_LEVEL2ROP

栈迁移+ORW

seccomp:orw

 

rop@bss布局:

read stdin->file@bss

open file

read file

puts file

 

ret前栈布局

data

s->rop

r->leave<-rsp rbp=rop

 

执行leave;ret

rsp=rbp

rip=[rsp]=[rop]

 

执行rop

read <-'./flag'

open './flag'->4

read 4->buf

puts buf->flag

上的坏死ROP

任意读和任意写

Ret2main修改ret两个低字节(4K对齐,1/16概率),然后进行多次循环

gotprintf->泄露canarylibc

有无符号数比较->绕过读入限制->rop

Fyslinux文件系统)

利用Linux文件系统的inode来定位

 

参考资料:

https://www.cnblogs.com/alisecurity/p/5486458.html

https://paper.seebug.org/1109/#14-unsafe_unlink

https://wiki.x10sec.org/pwn/heap/unlink/#_5

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_overview-zh/

猜你喜欢

转载自www.cnblogs.com/algonote/p/12677344.html