getshell本质与深入探究
目标
在做CTF-PWN方向题目时,总体流程就是分析漏洞程序、然后写利用脚本、攻击本地环境、攻击靶机环境得到flag,但是在攻击本地时为什么会获得本地的shell,同时攻击远程靶机时又为什么会获得远程靶机的shell呢?同样当我们攻击一个IOT设备时,会发现使用CTF-PWN中的方法不能够得到一个交互式的shell,今天就来一探究竟
基础
首先了解几个概念
pts与linux终端
pts(pseudo-terminal slave)是pty的实现方法,与ptmx(pseudo-terminal master)配合使用实现pty
来源http://www.readfree.net/htm/200908/4786353.html
linux下一切皆文件,同样也会将终端作为特殊的设备文件存储在/dev/pts目录下,同样可以在终端中输入tty,查看当前的pts
,它代表着这个终端。
我们在这个pts中运行程序输出不会输出到别的终端,同样在这个终端中启动的进程都是这个pts的子进程,它是老大。
文件描述符
文件描述符是IO重定向中的重要概念。文件描述符使用数字表示,它指明了数据的流向特征。
软件设计认为,程序应该有一个数据来源、数据出口和报告错误的地方。在Linux系统中,它们分别使用描述符0、1、2来表示,这3个描述符默认的目标文件(设备)分别是/dev/stdin、/dev/stdout、/dev/stderr,它们分别是各个终端字符设备的软链接。
来源 https://www.junmajinlong.com/shell/fd_duplicate/
标准输入,标准输出,标准错误对应了0,1,2,这个终端下每一个进程都会打开这三个文件描述符,且将其指向这个终端所以能在终端看到进程的输入与输出]
CTF中的PWN
execve函数执行后会覆盖原进程
system函数=fork()+execve()+wait()
在使用编写好的脚本攻击本地环境时我们能够拿到本地的shell,通常我们拿到shell是因为控制程序执行了execve或者system函数,这两个函数都会继承父进程的描述符
#include <stdio.h>
void main()
{
int fd;
fd=open("./flag.txt",0);
system("/bin/sh");
}
可以看到在执行system函数后依然可以读到flag.txt文件,所以system函数fork的进程会继承父进程的文件描述符(包括0,1,2),当我们本地用system函数pwn掉一个程序,可以看到交互式的输出(这里execve同理)
在CTF题目部署通常使用xinetd或者socat,两者都会将将程序的输入输出绑定到端口上,产生一次连接启动一个程序进程,然后将连接的socket映射到进程的0,1,2文件描述符,所以当我们pwn掉这个程序如果socket没有断开,那么会继承父进程的文件描述符,就可以看到有交互式的服务端上的shell
当CTFer使用nc或者pwntool工具连接远端服务器的ip:port时,就会产生一个socket,socket会绑定到漏洞程序进程的0,1,2,当我们进行输入就相当于在写0文件描述符了。
IOT中的PWN
但是回到现实中的PWN,在一些IOT设备中通常一些服务,不像CTF中会有Xinetd映射好输入与输出,所以当我们劫持程序流程走到system函数后执行/bin/sh是无法获得交互式shell的,产生连接的socket并没有映射到进程的012,导致虽然执行了system("/bin/sh")也只是在设备中开了一个sh,所以这里就可以用到反弹shell的一系列操作