PWN基础

学习资料为从0到1CTFer成长之路(N1book)

什么是PWN

​ 在ctf中,pwn主要通过利用程序中漏洞造成内存破坏以获取远程计算机的shell,从而获得flag。Pwn题目比较常见的形式是把一个用C或C++编写的程序运行在目标服务器上,参赛者通过网络与服务器进行交互。

如何学习Pwn

  • 一定的逆向工程基础

  • 编程语言与编程原理

    通常,pwn题目会用C/C++语言编写,而我们在编写攻击脚本时,会用到Python,所以Python基础也是必备的。同时可能还会有其他语言编写的题目(但本人的能力暂时是不会遇到了)。所以广泛涉猎一些编程语言是很重要的。

    对于逆向来说,更好更快的反编译是一个难题。无论是手工反汇编还是利用工具,编译原理的知识总要懂些。

  • 汇编语言

    汇编语言可以说是核心了,只要进入了二进制领域,汇编语言就是那道门槛,是绕不过去的。因为只有理解底层的CPU的运行原理才能利用漏洞并且理解为何存在这种漏洞。

  • 操作系统

    操作系统为计算机的核心软件,是Pwn的攻击目标,我们必须了解程序如何被执行以及如何完成各种各样的工作,而且,有时候的利用漏洞的技巧也需要利用一些操作系统的特性

  • 数据结构和算法

​ 一定要注意不能急于求成,在前几天的比赛中我意识到了在学习中的一些纰漏,我从头开始写也是因为之前的学习中太急于学习各种技巧,反而最后却不懂了,所以决定从头开始学习基础,基础比学习技巧更重要,切勿“浮沙筑高台”

Linux基础知识

​ 目前大多数ctf平台的PWN题目的环境都是Linux平台,掌握一些Linux系统的基础知识是非常有必要的

系统与函数调用

​ 32位Linux程序遵循栈平衡的原则。espebp作为栈指针和帧指针寄存器,eax作为返回值。函数参数从右到左依次入栈,函数参数有调用者清除。

​ 而64位Linux系统程序使用fast call的调用方式进行传参。函数的前六个参数会依次使用rdi,rsi,rdx,rcx,r8,r9寄存器进行传递,如果还有多余的参数,那么与32位一样使用栈进行传递。

​ 在32位Linux操作系统中,调用系统调用需要执行int 0x80软中断指令。因此,eax中保存系统调用号,系统调用的参数依次保存在ebx,ecx,edx,esi,edi,ebp寄存器中。调用的返回结果保存在eax中。

​ 其实系统调用可以看成一种特殊的函数调用,只是使用int 0x80指令代替了call。call中的函数地址变成了存放在eax中的系统调用号,并且参数传递改为了寄存器传参。

​ 相较于32位系统,64位系统调用指令变为了syscall,传参寄存器变为rdi,rsi,rdx,r10,r8,r9,并且系统调用对应的系统调用号发生了变化。至于对应的调用号以及参数可以在网上查询。

ELF文件结构

​ Linux系统下的可执行文件格式为ELF(Executable and linkable Format),类似Windows的PE格式。ELF格式比较简单,我们需要了解的是ELF头,Section(节),Segment(段)的概念

  • ELF头必须在文件开头,表示这是ELF文件及其基本信息。ELF头包括ELF的magiccode、程序运行的计算机架构、程序入口等内容,可以通过**“readelf -h”**命令读取其内容,一般可用于找一些程序的入口。
  • ELF文件有多个节组成,其中存放各种数据。描述节的信息的数据统一存放在节头表中。ELF中的节用来存放各种各样不同的数据,主要包括:
    1. .text节——存放一个程序运行所需的所有代码。
    2. .rdata节——存放程序使用到的不可修改的静态数据,如字符串等。
    3. .data节——存放程序可修改的数据,如C语言中已经初始化的全局变量等。
    4. .bss节——用于存放程序的可修改数据,与.data节不同的是,这些数据没有被初始化,所以没有占用ELF空间。虽然节头表中存在.bss节,但是文件中并没有对应的数据。在程序开始执行后,系统才会申请一块空内存来作为实际的.bss节。
    5. .plt和.got节——程序调用动态链接库(SO文件)中函数时,需要这两个节配合,以获取被调用的函数的地址。

​ 由于ELF格式的可扩展性,甚至在编译链接程序时还可以创建自定义的节区。ELF中其实可以包括很多与程序执行的无关的内容,如程序版本、Hash或者一些符号调试信息等。但是操作系统执行ELF程序时并不会解析ELF中的这些信息,需要解析的是ELF头和程序头表(Program Head Table)。解析ELF文件头的目的是确定程序的指令集架构、ABI版本等系统是否支持信息,以及读取程序入口。然后,Linux解析程序头表来确定需要加载的程序段。程序头表其实是一个**程序头(Program Head)**结构体数组,其中的每项都包含这个段的描述信息。操作系统执行程序时需要按照程序头表中的指定的段信息来将ELF文件中的指定内容加载到内存的指定位置。所以,每个程序头的内容主要包括段类型、其在ELF文件中的地址、加载到内存中的哪个地址、段长度、内存读写属性等。

​ 比如,ELF中存放代码的段内存读写属性是可读可执行,存放数据的段则是可读可写或只读等。注意,有些段可能在ELF文件中没有对应的数据内容,如未初始化的静态内存,为了压缩ELF文件,只会在程序头表中存在一个字段,由操作系统进行内存申请和置零的操作。操作系统也不会关心每个段的具体内容,只需按照要求加载各段,并将PC指针指向程序入口。

​ 可能会有人对段和节之间的关系,其实二者只是解释数据的两种形式。就像一个人有多个身份,ELF同时使用段和节两种格式描述一段数据,只是侧重点不同。操作系统不需要关心数据具体功能,只需要知道哪一块数据应该被加载到哪一段内存以及读写属性即可。

Linux下的保护机制(漏洞缓解措施)

  1. NX

    NX保护在Windows中被称为DEP,是通过现代操作系统的内存保护单元(Memory Protect Unit,MPU)机制对程序内按页的的粒度进行权限设置,其基本规则为可写权限与可执行权限互斥。因此,在开启NX保护的程序中不能直接用shellcode执行任意代码。所有可以被修改写入shellcode的内存都不可被执行,所有可以被执行的代码数据都是不可被修改的。
    GCC默认是开启NX保护的,关闭方法是在编译时加入**“-z execstack”**参数

  2. Stack Canary

    Stack Canary保护是专门针对栈溢出攻击涉设计的一种保护机制。由于栈溢出攻击的主要目标是通过溢出覆盖函数栈高位的返回地址,因此其思路是在函数开始执行前,即返回地址前写入一个字长的随机数据,在函数返回前校验该值是否被改变,如果被改变,则认为是发生了栈溢出。程序会停止运行

  3. ASLR(Address Space Layout Randomization)

    ASLR的目的是将程序的堆栈地址和动态链接库的加载地址进行一定的随机化,这些地址之间是不可读写执行的未映射内存,降低攻击者对程序内存结构的了解。这样,即使攻击者布置了shellcode并可以控制跳转,由于内存地址结构未知,依然无法执行shellcode

    ASLR是系统等级的保护机制,关闭方式是修改/proc/sys/kernel/randomize_va_space文件的内容为0

  4. PIE

    与ALSR保护非常相似,PIE保护的目的是让可执行程序ELF的地址进行随机化加载,从而使得程序的内存结构对攻击者完全未知,进一步提高程序的安全性。

    GCC编译时开启PIE的方法为添加参数“-fpic -pie”。较新版本GCC默认开启PIE,可以设置“-no-pie”来关闭。

  5. Full Relro

    Full Relro保护与Linux下的Lazy Binding机制有关,其主要作用是禁止.GOT.PLT表和其他一些先关内存的读写,从而阻止攻击者通过写.GOT.PLT表来进行攻击利用的手段。

    GCC开启Full Relro的方法是添加参数“-z relro”。

GOT和PLT的作用

​ ELF文件通常存在.GOT.PLT和.PLT这两个特殊节,ELF编译时无法知道libc等动态链接库的加载地址。如果一个程序想要调用动态链接库中的函数,就必须使用.GOT.PLT和.PLT配合完成调用

​ 假如,在汇编语言中有一段指令为call _printf,但其实并不是跳转到实际的**_printf函数的位置。因为在编译时程序并不能确定printf函数的地址,所以这个call指令实际上通过相对跳转,跳转到了PLT表中的_printf项。ELF中所有用到的外部动态链接库函数都会有对应的PLT**项目

.PLT表还是一段代码,作用是从内存中取出一个地址然后跳转。取出的地址便是**_printf的实际地址,而存放这个_printf函数实际地址的地方就是.GOT.PLT表**。

.GOT.PLT表其实是一个函数指针数组,数组中保存着ELF中所有用到的外部函数的地址。.GOT.PLT表的初始化工作则由操作系统来完成。

​ 当然,由于Linux非常特殊的Lazy Binding机制。在没有开启Full Relro的ELF中,.GOT.PLT表的初始化是在第一次调用该函数的过程中完成的。也就是说,某个函数必须被调用过,.GOT.PLT表中才会存放函数的真实地址。有关Lazy Binding机制就不在这说了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d8LFQnI6-1679917203781)(PWN基础/image-20230310165209050.png)]

​ 总的来说,程序第一次运行时**.GOT.PLT表中并没有函数地址,所以会直接从.PLT表到执行,在这之后会将地址存到.GOT.PLT表**中以便以后调用,这也是之后在泄露地址、获取libc地址时经常会用到的控制程序执行流的一个知识。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lxxxt_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值