安全矩阵

 找回密码
 立即注册
搜索
查看: 2336|回复: 0

pwn入门之栈入门

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-9-23 20:33:48 | 显示全部楼层 |阅读模式
原文链接:pwn入门之栈入门

pwn作为ctf比赛中的一个分支,可以说是难以入门。pwn确实难,但难点主要在于对它的学习——知识点不系统,或是太高深,对新手不友好。事实上,我们只要多做多练,相互对照理解就可以快速入门了。
此文希望能够为新手提供相对来说最详细、最基础、最简单的解题过程及利用过程,并附上部分详解的链接,可以边对照边学习边理解。
pwn分为堆、栈两个部分,其中栈是基础,也是最简单的。故以栈为例进行入门讲解。
栈利用的知识点分支中,最常见的为“格式化字符串漏洞”以及“栈溢出漏洞”。
PS:想要入门,大佬写的文章是必须阅读一些的。本文中涉及到的文章会附上链接。大佬会写的很详细,但本文仅作为pwn入门,所以本人会将自己的理解讲出来,作为入门而言,够用。
1
工具及其使用
工具介绍
首先讲讲用到的环境。我们一般情况下都是使用vmware安装一个ubuntu16.04版的环境,在其中运行自己写的利用脚本(即exp)。
pwn通过分析一个程序来寻找漏洞点进行利用,该题的目的在于通过程序中可以利用的点来获取靶机(在ctf中会以ip以及端口的方式给出)上的权限(称之为shell)。该过程中必不可少的一环就是静态分析。
而静态分析所需要利用的到的工具便是ida:
吾爱破解ida:

https://www.52pojie.cn/thread-675251-1-1.html
使用过程介绍
pwn程序目前大部分为linux上的ELF格式的可执行程序,该程序同操作系统相匹配,分成了32位和64位两种。
所以在做题之前可以先进行一下测试,看一下是哪一种,步骤为下(以攻防世界的CGfsb为例):

两个命令:一个file [pwn_name]查看文件信息,(file+空格+文件名)可看出是32位程序。
第二个checksec [pwn_name]查看该文件开启的保护,亦可以查看文件是32位程序(Arch后面的)。其中各保护代表的含义:
【1】RELRO:RELRO会有Partial RELRO和FULL RELRO,如果开启FULL RELRO,意味着我们无法修改got表。
【2】Stack:如果栈中开启Canary found,那么就不能用直接用溢出的方法覆盖栈中返回地址,而且要通过改写指针与局部变量、leak canary、overwrite canary的方法来绕过。
【3】NX:NX enabled如果这个保护开启就是意味着栈中数据没有执行权限,以前的经常用的callesp或者jmp esp的方法就不能使用,但是可以利用rop这种方法绕过。
【4】PIE:PIE enabled如果程序开启这个地址随机化选项就意味着程序每次运行的时候地址都会变化,而如果没有开PIE的话那么No PIE (0x400000),括号内的数据就是程序的基地址。
【5】FORTIFY:FORTIFY_SOURCE机制对格式化字符串有两个限制,一是包含%n的格式化字符串不能位于程序内存中的可写地址;二是当使用位置参数时,必须使用范围内的所有参数。所以如果要使用%7$x,你必须同时使用1,2,3,4,5和6。该5条总结来源于:

https://blog.csdn.net/niexinming/article/details/78814422
仅入门可先看本文即可。
之所以先看程序是几位的,是因为静态分析工具ida(也可动态分析,入门仅需了解静态)也分为32位和64位两种。使用时需要一一对应。
虽然64位的ida也可打开32位的ida,但无法进行反汇编查看伪代码(32位ida打开64位的同理),ida下载后自带两种打开方式:

使用步骤(以该题为例):
上面已知该程序为32位,所以打开ida.exe并用ida打开该程序:


打开后界面如下:左边是函数区,右边是汇编代码区。单击右侧区域后,空格可切换函数汇编和程序汇编两种格式。


按Tab键或F5,可查看反汇编的伪代码,效果如下(部分程序被加工过,如函数不能反汇编,或反汇编出的内容比较杂乱,就需要自己处理了。入门的话暂时接触不到):

好了,有了伪码,就可以分析漏洞,寻找可利用点了。
pwntools工具,安装过程网上各种各样,初次安装又会遇到各种各样的问题,所以需要自己有耐心,一点点通过搜索解决。具体过程在后文中边使用边讲解。
2
如何区分堆题和栈题
pwn分支的题目,分为堆题和栈题,作为新手入门而言,基本都是自栈开始。
所以,需要在开始前对所做题是堆题还是栈题进行简单判断,再快速判断该题自己是否可以做,要不然很可能浪费时间。
其中堆、栈(也有人称栈为堆栈,名称不同意义相同)区别有六点,分别是:1.在申请方式上;2.申请后系统的响应上;3.申请大小的限制;4.分配空间的效率上;5.堆和栈中的存储内容;6.存取效率的比较。
我们可以在申请方式上对堆、栈进行区分:
栈:它由编译器自动管理,无需我们手工控制。
堆(heap): 申请和释放由程序员控制,并指明大小。在C中使用malloc函数。
如下:

一般情况下,出现这种malloc函数有一半可能是堆题,可以找找资料看该题是否自己还没掌握的堆题。如果同时出现malloc函数,且函数中开辟空间的大小还是个变量,那这道题八成是堆题了。
而如果没有malloc函数,那基本上就是栈题了。如下:

3
栈题主流fms和栈溢出漏洞
fms同样分为32位fms和64位fms,做题方法略有不同,本人有一篇细讲64位的:

https://www.anquanke.com/post/id/194458
个人认为先有解题经验,再去看各种理论文章,可以一一对应,快速理解。
本人文章已详细讲解64位fms,故这里仅以攻防世界的CGfsb为例,讲解一下入门新手应如何解题:
格式化字符串漏洞特点是printf函数没有使用格式化字符,仅使用一个指针,如下图红色方框所示(出现这种类型的printf且还可以通过别的方法控制该函数中的变量 如此出的&s,那么绝对存在格式化字符串漏洞可利用)

利用过程:
首先要固定格式(红色方框中的r可以自行命名,但命名过后,后面就需要使用相同的变量名。这也是为什么网络中各位大佬写的exp中有的是r有的是sh等等)。

接下来,需要达到漏洞点(不管哪种题,都是在找到漏洞点后,先写出绕过代码,绕过程序的不可利用部分,达到了利用点)。比如该题,我们可利用点为 printf(&s)。
其中&s为可控变量,所以我们只需达到可以对s输入内容的地方,就可以通过控制变量来利用fms漏洞了。该题中无用的地方是红色方框中的输入:

所以绕过方法就是随便发送(即输入,read是读入函数)一些内容即可(不同的绕过有不同的方式,比如加入需要输入特定字符才能绕过,那就需要就题而论了),如下:
​​

绕过以后,就进入了可利用点,即对变量s输入的地方,编写利用代码如下:

其中,payload2之所以如此构造,是因为fms漏洞中,%n会将一个地址中的值进行修改,修改的数字取决于在%n前面一共打印了几个字符。
然后p32()是打包函数,将一个数值(一般是对一个内存地址进行打包),32位中一个地址是4个字节(拓展:u32()是解包, p64()是64位程序中的打包函数,u64()是解包)。
而该处p32()中的地址是变量pwnme的地址,也是我们想要修改的变量。要将其修改为8,然后一个地址是四个字节,所以再加四个a,共八个字节,即可通过%n将这个地址(即该pwnme变量)中的值修改为8(在伪代码中双击变量名即可跳转到下图界面):

然后又因为格式化字符串存在一个偏移问题,所以需要先找出该字符串的偏移才可,这也是为什么是%10$n而不是%n。此处的偏移是10,$前的10就是偏移量。
什么是偏移量?偏移量就是,我们的%n等格式化字符都是修改对应位置的值,所以我们要找到偏移,使%n对应到我们的地址。如下图:

其中以四个a为参照,a的十六进制为61,a后面的格式化字符%x依次输入对应位置(1、2、3、4...)的内存中的值的十六进制。
依次计数可知,第十个%x中的值的十六进制对应4个a,即我们格式化字符串开始的位置。
之所以以四个a为参照,是因为在32位程序中,是以四个字节为一组的。所以一个偏移也对应四个字节。计算可得偏移为10。
在我64位fms格式化漏洞文章中有一个我自己写的函数,直接调用可以自动算出偏移。链接:

https://www.anquanke.com/post/id/194458

好了,exp已写完,接下来开始进行测试。测试的话不一定要到写完之后才可以进行,可以边写边测试,每完成一小块exp即可进行一次测试,看一下是否符合自己的预期。
最后执行完成的exp获取权限。获取flag(由于该题作为入门题,直接执行了cat flag命令,所以不需要权限)。执行exp的命令为 python exp.py(因为我们的exp是python脚本,exp.py的名字即文件名可自行修改)。

至此,一个简单的格式化字符串漏洞就利用成功了。有了一次简单的利用,就可以参考比较详细的格式化字符串漏洞的讲解,边对照边学习,进而更快学习,更好入门。建议先将格式化字符串漏洞研究懂,然后就可以由点及面的更好的学习了。
于此附上几个格式化字符串漏洞的详细讲解,如下:
发表于i春秋的一个详解,主讲32位:

https://www.cnblogs.com/ichunqiu/p/9329387.html
wiki上的一个对fms的详解即利用,有栈有堆,建议先学完栈后在学堆,同样是以32位为主:

https://wiki.x10sec.org/pwn/fmtstr/fmtstr_example/
本人写的64位利用详解:

https://www.anquanke.com/post/id/194458
4
简单栈溢出入门
进行必要的检测步骤,查看开启了哪些保护,以及用ida打开查看伪码:

双击左侧main查看其伪码就一个函数,双击该进入查看其内容:


可以看到一个read函数,而输入的字节数位0x100,但是其大小为0x88个字节数,明显可输入字符大于变量buf的长度,所以存在溢出(溢出就是你可输入字符的字符数比它给定的变量字符数的大小要大,大的越多越好)。
由上可知,溢出漏洞的特点就是:1.有一个可输入函数;2.该函数输入的变量的原定大小要小于你实际可输入的大小。满足这两个条件,那么是溢出漏洞的可能性,就八九不离十了。
入门级原理如下:
首先要知道,之所以可以溢出利用,是因为我们通过溢出可以控制程序在该函数运行完后,接下来要调用的函数,即return到哪里。我们双击read函数中的buf变量可看到00000088buf,往下面一直拉可看到一个00000000 s和00000004 r,如下图。
其中buf即我们输入内容的变量,s是标记上一个函数(即使由哪个函数跳转而来的),r是标记下一个函数,即该函数执行完后,要跳转到哪里去继续执行,而buf的大小为0x88我们可输入大小为0x100,所以我们可以已知输入到r的位置,来控制程序运行完后要执行哪个函数。

利用过程如下:
首先,固定框架如下:

绕过无用代码:通过ida可知,该程序直接就是输出一段用来提示的字符串“Input:”后就可以直接输入了,所以该处不需要绕过不可利用的代码,如下:

接下来构建payload,通过溢出漏洞来进行利用:

其中'a'*0x88就是为了将buf给填满,然后就可以对s和r进行修改了。在入门阶段s我们暂时用不到,且影响不是太大,所以可以随便填充。
此处以4个b进行填充(因为是32位程序所以地址是四个字节,我们要填充就要用四个字节的字符来填充,内容任意)。
后面的就是填充r即return——我们要执行的函数,又因函数都是有参数的,所以后面还要加上相应的参数。
我们再次明确一下我们做pwn题时的目的,就是获取目标靶机的权限。而获取权限有两个函数,最常见也是最常用的就是system,然后是偏僻少用的 execve 函数,作为入门,我们此处使用system函数。
这个函数,就是程序中原来用来执行输出字符串命令的函数。因为程序中有该函数,所以我们可以直接利用(至于没有的话,我们就需要先泄露libc然后寻找system在libc中的偏移,进而获取其地址。此处仅讲直接利用,作为入门理解原理,参照大神们的细讲相互对照学习够用了)。

在我们使用程序中原有的函数时,我们可以利用plt表(每个程序都需要用到的两个表,plt表和got表。got表是用来链接libc中的函数的,plt表是用来链接程序中已有的函数的,作为入门可以暂时这么理解,待掌握后就需要自行搜索大神们的文章正确掌握了)。
​​
利用如下:

图中首先将该程序以ELF文件格式进行解析并将解析后的结果给变量file,接下来在解析后的结果file中使用plt表,并查找其中的system函数的地址赋值给sys_plt变量,最后将该函数的地址用p32()打包为payload做准备。
好了,这样将r覆盖后,我们就可以控制在该函数运行完之后跳转到system函数了。接下来想要获取权限,我们就要执行system('/bin/sh') ,即函数我们已经跳转到system了,还缺个参数:

我们可以先通过ida的这个功能搜索程序中已有的字符串,看一下是否已有 '/bin/sh'。该字符串可作为参数利用。可以看到存在如下:

双击查看地址:

找到 “/bin/sh”的地址为0x0804a024,用p32()打包如下:

这样就可以了吗?我们来测试一下:

可以发现命令ls并未执行,也并没有获取到系统权限,那是因为在32位程序栈中,参数距离调用函数中间还隔了四个字节(具体的暂时不做了解)。所以只需要随便再填充四个字符就可以获取权限了,如下:


可以看到命令ls成功执行。
此处给出几个溢出漏洞利用的详解,如下:
此题详解:

https://www.cnblogs.com/chrysanthemum/p/11768058.html
栈溢出详解:
  1. https://www.secpulse.com/archives/70608.html
  2. https://segmentfault.com/a/1190000020325723?utm_source=tag-newest
  3. https://www.jianshu.com/p/6a1235d99176
复制代码
5
结语
pwn其实并不难,我们要注意以下几点:
第一:我们要明确我们的目的,是通过利用程序中的原有片段来获取目标靶机的权限;
第二:想要利用已有片段就需要知道该片段在内存中的地址;
第三:不管哪个漏洞,其实都存在其原理,只要我们理解了原理就可以进行利用;
第四:在刚入门时我们学习,是一个一个漏洞点分开利用单个学习,但在我们掌握后就是对多个漏洞点同时利用,相互作用进而获得靶机权限。
感谢阅读。继续加油!


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-28 21:22 , Processed in 0.016769 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表