11-21CTF
11-21CTF
山林川泽[GDOUCTF 2023]EASY PWN
shift+F12字符串检索
发现flag.txt
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
进入check函数
1 | int check() |
只需要满足v5不为零就好了
题目中存在漏洞函数get
exp
1 | from pwn import* |
ps:扣1送flag
[2021 鹤城杯]babyof
非常经典的ret2libc
check一下
1 | Arch: amd64-64-little |
开启了NX保护(堆栈不可执行)
ida分析
shift+F12寻找
非常的经典
没有任何后门函数
开启了NX保护,无法应用ret2shellcode,这里我们采用一种最新的攻击手法ret2libc
首先我们应该先知道C语言的标准库(libc库)里面包含了很多好用的函数
包括system()
我们还应该了解一下动态链接库和静态链接库
动态链接库(DLL,Dynamic Link Library)
- 定义
- 动态链接库是包含可由多个程序同时使用的代码和数据的库。它在程序运行时被加载到内存中,多个应用程序可以共享同一个动态链接库中的代码,从而节省内存和磁盘空间。
- 特点
- 内存共享:多个应用程序可以同时使用内存中同一个动态链接库的副本,减少了内存的占用。
- 易于更新:如果动态链接库需要更新(例如修复漏洞或添加功能),只需要替换库文件本身,而不需要重新编译使用该库的所有应用程序。
- 加载时间:在程序运行时才加载动态链接库,可能会导致程序启动时间稍微变长。
- 使用场景
- 在Windows操作系统中,许多系统功能都是通过动态链接库实现的,例如 kernel32.dll 、 user32.dll 等。应用程序可以调用这些库中的函数来实现诸如文件操作、图形界面绘制等功能。
- 在软件开发中,当多个项目需要共享一些通用功能时,可以将这些功能封装成动态链接库,方便各个项目调用。
静态链接库(Static Link Library)
- 定义
- 静态链接库是在程序编译时将库中的代码和数据复制到可执行文件中的库。每个使用静态链接库的程序都会在自己的可执行文件中包含一份库的代码。
- 特点
- 独立性:可执行文件不依赖于外部的库文件,因为库的代码已经嵌入到可执行文件中。这使得程序在分发和运行时更加简单,不用担心库文件丢失或版本不兼容的问题。
- 文件大小:由于每个可执行文件都包含了库的代码,会导致可执行文件的体积变大。
- 编译时间:在编译程序时需要将库的代码链接到可执行文件中,可能会使编译时间变长。
- 使用场景
- 当需要确保程序在不同环境下都能独立运行,且对可执行文件大小不是特别敏感时,可以选择静态链接库。例如,一些嵌入式系统开发中,可能会使用静态链接库来保证程序的稳定性和独立性。
两者比较
- 内存占用
- 动态链接库在内存中可以被多个程序共享,节省内存。静态链接库会使每个可执行文件都包含一份库代码,可能造成内存浪费。
- 可维护性和更新
- 动态链接库便于更新和维护,只需要替换库文件。静态链接库如果要更新功能,需要重新编译所有使用该库的程序。
- 程序分发
- 动态链接库需要确保库文件与可执行文件一起分发,并且版本要兼容。静态链接库只需要分发可执行文件即可。
阅读完上述内容,有两个问题
1.我们为什么要用动态链接库?
2.我们平时写的代码调用的是动态链接库还是静态链接库?
ok,言归正传
1 | int sub_400632() |
ida中发现sub函数
存在溢出点
64位操作系统下,参数的传递是这样的
前六个参数通过RDI,RSI,RDX,RCX,R8,R9六个寄存器来传递
剩余的参数采用栈传递
而32位操作系统直接采用栈传递
介绍一个pwn手必备工具———ROPgadget
这个工具可以让我们找到gadget的地址,分析二进制文件结构,还可以辅助漏洞利用开发
当然后门两个太高大上,我们只用第一种用途
这里还要再介绍一下Ret指令,这条指令的汇编语句是pop eip,eip是指令指针寄存器,用于储存下一条要执行的指令地址,因此我们将ret的内容覆盖成我们想要的地址,它就能带我们去任何地方(看我的,任意门!)
因此我们需要做的工作如下
1.找到system()函数和/bin/sh字符串在libc中的地址。
2.劫持程序的执行流程,让程序执行system(“/bin/sh”)
第二步不难,只要精巧合理地构造溢出,把main函数的返回地址覆盖为system()函数的地址,并合理实现传参即可,可我们怎么进行第一步呢?毕竟ida中可没有动态链接库的地址
关键在于如何找到system()函数和”/bin/sh”字符串的地址。这两个关键地址都在libc库中,这就是这类题型被叫做ret2libc的原因。那么如何寻找libc中的system()函数和”/bin/sh”字符串呢?这里需要用到以下公式:
函数的真实地址 = 基地址 + 偏移地址
偏移地址:libc是Linux新系统下的C函数库,其中就会有system()函数、”/bin/sh”字符串,而libc库中存放的就是这些函数的偏移地址。换句话说,只要确定了libc库的版本,就可以确定其中system()函数、”/bin/sh”字符串的偏移地址。解题核心在于如何确定libc版本,本文介绍过程将忽略这个问题,打本地直接确定为本地的libc版本即可。
基地址:每次运行程序加载函数时,函数的基地址都会发生改变。这是一种地址随机化的保护机制,导致函数的真实地址每次运行都是不一样的。然而,哪怕每次运行时函数的真实地址一直在变,最后三位确始终相同。可以根据这最后三位是什么确定这个函数的偏移地址,从而反向推断出libc的版本,此处需要用到工具LibcSearcher库。那么如何求基地址呢?如果我们可以知道一个函数的真实地址,用公式:
这次运行程序的基地址 = 这次运行得到的某个函数func的真实地址 - 函数func的偏移地址
即可求出这次运行的基地址。
那我们如何找到某个函数func的真实地址呢?
1.首先寻找一个函数的真实地址,以puts为例。构造合理的payload1,劫持程序的执行流程,使得程序执行puts(puts@got)打印得到puts函数的真实地址,并重新回到main函数开始的位置。
2.找到puts函数的真实地址后,根据其最后三位,可以判断出libc库的版本。
3.根据libc库的版本可以很容易的确定puts函数的偏移地址。
4.计算基地址。基地址 = puts函数的真实地址 - puts函数的偏移地址。
5.根据libc函数的版本,很容易确定system函数和”/bin/sh”字符串在libc库中的偏移地址。
6.根据 真实地址 = 基地址 + 偏移地址 计算出system函数和”/bin/sh”字符串的真实地址。
7.再次构造合理的payload2,劫持程序的执行流程,劫持到system(“/bin/sh”)的真实地址,从而拿到shell
延迟绑定机制
只有动态库libc中的函数在被调用时,才会进行地址解析和重定位工作,也就是说,只有函数发生调用之后,我们才能够通过got表读取到libc中的函数, 在可执行二进制程序调用函数A时,会先找到函数A对应的PLT表,PLT表中第一行指令则是找到函数A对应的GOT表。此时由于是程序第一次调用A,GOT表还未更新 ,会先去公共PLT进行一番操作查找函数A的位置,找到A的位置后再更新A的GOT表,并调用函数A。当第二次执行函数A时,发生的流程就很简单了, A的GOT表完成更新,可以直接在GOT表中找到其在内存中的位置并直接调用。
1 | from pwn import * |
[SWPUCTF 2021 新生赛]whitegive_pwn
泄露了get和puts地址
题目给出了libc版本为libc.so.6
ret2libc直接打就好
1 | rom pwn import * |