0x02实现一个反弹shell
这个比较简单,就是用套接字,tcp三次握手,然后用subprocess模块实现命令执行
注意客户端和服务端脚本中的端口要一致,不然会连接失败,而且端口号一定要为数值型
脚本利用演示
把server_shell.py脚本放进靶机kali64,并执行就可以了(攻击了靶机之后可以把这个脚本设置为开机自动启用)--进入等待连接攻击机状态
在攻击机win7上执行客户端脚本client.py后
靶机上的脚本server_shell.py会接收到连接信息,就会输出一条被连接的信息(这个信息其实是可以被隐去的,这里是为了帮助我们更好的分析脚本的执行流程而写进去的)
这时候我们把靶机的终端关闭,然后在攻击机上输入一条命令 ls,可以收到回显,并且靶机上并没有显示有任何的异常,这个得到反弹shell的方式还是非常隐秘的
脚本代码的实现和分析
Client.py
客户端:创建socket 连接 通信循环
Server_shell.py
服务端:创建socket 绑定 监听 两循环(等待客户端连接循环 和 通信循环)
拓:
简单实现netcat功能的脚本
简单说说脚本里的模块和函数
Sys在这里的话主要是用到sys.argv--接收命令行参数,sys.stdin.read()--读取标准输入端中的数据,有点类似raw_input()但是又有区别,raw_input()遇到回车就会返回读取的数据,但是sys.stdin.read()会等到用户输入EOF符(即ctrl+d)才会开始读取数据
Threading主要是用于多线程编程的,提高运行速度和效率等等
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)--子进程执行args中的命令,并将其输出形成字符串返回
Socket的话就是跟套接字有关的模块,创建套接字对象,连接,绑定,监听,发送数据,接收数据这些等等(这个应该大家都比较熟悉,不多说)
Getopt主要是用来定义一些使用说明和使用方法的,同时处理和接收一下命令行传进来的参数
Getopt.getopt(args,options[,long_options])
用于解析命令行选项和参数列表,args是要解析的参数列表。
Options前缀为一个连字符(-),用来做剪短的选项(如”-x”)
Long_options前缀为两个连字符(--) 用来做长选项,是个列表
opts, args = getopt.getopt(sys.argv[1:], "hle:t:p:cu:",["help", "listen", "execute", "target", "port", "command", "upload"])
对于这一句,可以再举一个例子,例如这里如果在命令行执行这一句
python replaceNetcat.py -t 192.168.0.1 -p 5555 -l -e=\”cat/etc/passwd\”
那么得到的opts和args参数分别是
opts=[(‘-t’,’192.168.0.1’),(‘-p’,’5555’),(‘-l’,’’),(‘-e’,’\”cat/etc/passwd\”’)]
args=[‘replaceNetcat.py’,’192.168.0.1’,’5555’]
这个概念如果没接触过可能一时半会也还不能理解,这里我就不再深入说下去了,还不理解的话大家可以自行百度一下吧~~
通过脚本的利用分析一下这个脚本的代码设计流程
(因为这里的socket通信是很多脚本的基础中的基础,所以我这里说的有点详细,如果已经能熟练使用了的话可以不用去看这一部分)
(下面的分析过程建议大家对着脚本来看)
靶机 kali 10.10.10.128
攻击机kali 10.10.10.134
先在靶机上执行
其实这个脚本是一个服务端和客户端合并起来的脚本
先说下这个脚本中服务端的实现,主要是server_loop()和 client_sender()这两个函数
其中server_loop()函数就相当于上面提到的server_shell.py脚本,就是进行端口连接的监听
然后等待客户端连接,一旦有客户端连接上了,就会创建一个新的线程,执行client_sender()函数,client_sender()函数的作用其实就是实现跟客户端的通信。
这里执行python replaceNetcat.py -l -p 9999 -c
会先执行main函数,然后由于参数-l -p 9999 -c会把listen设置为true,port=9999,command = True,然后会进入if listen:执行server_loop()函数进入监听状态
此时在攻击机上执行python replaceNetcat.py -t 10.10.10.128 -p 9999
这是脚本运行会进行一些设置target=10.10.10.128 port=9999 然后进入if not listen and len(target) and port > 0:
# 从命令行读取内存数据
# 这里将阻塞,所以不再向标准输入发送数据时发送CTRL-D
buffer = sys.stdin.read()
client_sender(buffer)
注意!!此时脚本停留在了if里面阻塞住了,这个时候客户端(也就是攻击机)还没有跟服务端(靶机)连接上(因为我的分享是编程方面的知识而不是脚本的利用,所以这里我们一定要重点分析好进行每一步操作的时候脚本到底干了些什么,一定要弄清楚脚本到底具体执行到哪里,不能含糊)
所以这时候我们按下CTRL+D它就会返回一个shell
演示一下 在攻击机上按下CTRL+D 返回了一个shell并且有提示符<BHP:#>输出
有提示符<BHP:#>输出这是因为按下CTRL+D 后脚本将继续执行进入client_sender(buffer)函数,注意client.connect((target, port))当客户端执行到这里的时候就要等待服务端的响应才会继续执行下去了
我们分析一下此时服务端都干了些什么,先放出部分代码
while True:
client_socket, addr = server.accept()
# 分拆一个线程处理新的客户端
client_thread = threading.Thread(target=client_handler, args=(client_socket,))
client_thread.start()
此时服务端会处理客户端的连接请求,执行client_socket, addr = server.accept()接收客户端的连接并且返回一个client_socket和 addr(tips:accept()方法返回连接的客户端套接字以及其IP地址和端口的元组 addr )然后服务端就创建一个新的线程执行client_handler函数,在client_handler函数中进入了if command:然后执行client_socket.send("<BHP:#>"),服务端在这里向客户端发送了一个字符串"<BHP:#>"然后服务端会继续执行下去,直到停留在这一句了cmd_buffer += client_socket.recv(1024),等待客户端的输入然后接收客户端的输入
在服务端执行client_socket.send("<BHP:#>")之后,客户端的client.connect((target, port))就执行完毕了,并且一直执行到了data = client.recv(4096)接收服务端传过来的"<BHP:#>",并且执行print response把得到的信息打印出来,这里就是攻击机的终端上有提示符<BHP:#>输出的原因了 此时已经完成了服务端和客户端的连接正式进入通信阶段
这时候又轮到客户端出场了,这时客户端执行ls -la并按下回车键,会得到服务端执行命令后返回的数据
在客户端执行执行print response把得到的信息"<BHP:#>"打印出来之后,客户端的脚本就停留在了buffer = raw_input(""),然后当服务端输入ls -la,并按下回车的时候,就会执行client.send(buffer)向服务端发送数据,
然后服务端处理
if command:
while True:
# 跳出一个窗口
client_socket.send("<BHP:#>")
cmd_buffer = ""
while "\n" not in cmd_buffer:
cmd_buffer += client_socket.recv(1024)
# 返回命令输出
response = run_command(cmd_buffer)
# 返回响应数据
client_socket.send(response)
当客户端向服务端发送数据的时候,服务端的cmd_buffer += client_socket.recv(1024)这句会被激活,然后继续执行 run_command(cmd_buffer)和client_socket.send(response),执行客户端发送过来的命令并且把结果发回给客户端,然后客户端接收到结果之后又继续发送命令,就这样一直循环下去,知道按下ctrl+c结束脚本的运行
进一步浓缩一下
服务端:创建socket 绑定 监听 两循环(等待客户端连接循环 和 通信循环)
客户端:创建socket 连接 通信循环
除此之外这个脚本还有上传文件的功能,核心实现也是通过文件内容的读取和写入,显示在客户端读取文件的内容,然后把文件的内容像上面传递命令那样传递过去给服务端,然后服务端在把这些内容写到一个文件里面去,从而实现了文件的上传功能。