漏洞开发疑问:为何execve("/bin/sh")无法生成交互式Shell?
这个问题在漏洞开发初期太常见了,我刚入门的时候也踩过一模一样的坑!核心原因其实和终端会话(TTY)的关联有关,咱们一步步拆解清楚:
1. 管道环境导致的TTY缺失
你现在是通过管道把payload喂给目标程序的:
python -c "print 'A'*62 + '\x35\x56\x55\x56' + 'PAYLOAD'" | ./vuln
这种情况下,./vuln的标准输入(stdin)、标准输出(stdout)、标准错误(stderr)都是和管道绑定的,而不是和真实的终端(TTY)关联。
/bin/sh启动时会做一个关键检查:
- 如果stdin是TTY(比如你直接在终端敲
/bin/sh),它会进入交互模式,显示提示符并等待输入; - 如果stdin不是TTY(比如管道、文件重定向场景),它会默认以非交互模式运行——不会显示提示符,只会读取stdin的命令执行,执行完就等待EOF或者退出。
所以不是你的execve("/bin/sh") shellcode没跑起来,而是它成功启动了但没法和你交互,看起来像没生效而已。
2. 绑定/反向Shell为什么能正常工作?
你用的linux/x86/shell_bind_tcp payload,在最后调用execve("/bin/sh")之前,已经做了一个关键操作:把网络套接字的文件描述符复制到了0、1、2(对应stdin、stdout、stderr)。
看payload的核心逻辑(伪代码简化版):
int sockfd = socket(AF_INET, SOCK_STREAM, 0); bind(sockfd, ...); listen(sockfd, 1); int clientfd = accept(sockfd, ...); // 把客户端套接字重定向到标准IO dup2(clientfd, 0); dup2(clientfd, 1); dup2(clientfd, 2); execve("/bin/sh", NULL, NULL);
当你用netcat连接到目标端口时,netcat会提供一个类终端的交互环境,此时/bin/sh的IO都和这个套接字绑定,相当于有了一个“虚拟TTY”,所以能正常显示提示符、接收你的命令。
3. 验证shell是否真的启动了
可以用strace工具确认execve是否成功调用:
strace -e execve ./vuln < <(python -c "print 'A'*62 + '\x35\x56\x55\x56' + 'YOUR_EXECVE_SHELLCODE'")
如果输出里出现execve("/bin/sh", ["sh"], [/* 14 vars */]) = 0,说明shell已经成功启动了,只是缺交互环境而已。
另外也可以尝试给管道追加命令,比如:
python -c "print 'A'*62 + '\x35\x56\x55\x56' + 'YOUR_EXECVE_SHELLCODE' + 'echo test\n'" | ./vuln
如果能看到test的输出,说明shell确实在执行你发送的命令。
4. 解决办法:给shell一个TTY
方法一:用伪终端工具模拟TTY
用script命令给目标程序提供一个伪终端,这是最通用的方法:
script -q /dev/null -c "./vuln" < <(python -c "print 'A'*62 + '\x35\x56\x55\x56' + 'YOUR_EXECVE_SHELLCODE'")
或者用(python -c "..."; cat) | ./vuln,让cat保持管道打开,等待你输入命令:
(python -c "print 'A'*62 + '\x35\x56\x55\x56' + 'YOUR_EXECVE_SHELLCODE'"; cat) | ./vuln
这时候你输入命令就能得到实时的交互反馈了。
方法二:修改shellcode,强制sh进入交互模式
把execve的参数改成/bin/sh -i,让sh强制以交互模式启动。比如把shellcode里的字符串从"/bin//sh"改成"/bin/sh -i"(注意调整字节长度和栈布局,保证栈对齐)。
方法三:不用管道,直接用终端输入
把payload保存到文件,然后用终端直接喂给程序(适合短payload,长字符串手动输入不现实):
printf "$(cat payload.txt)" | ./vuln
不过这个方法局限性较大,不如前两种通用。
内容的提问来源于stack exchange,提问作者Eplox

