python木马程序设计_基于HTTP的Python特洛伊木马程序,用于远程系统取证和特权转移
python木馬程序設(shè)計
總覽 (Overview)
In this article, we will be building a python based trojan that does the following:
在本文中,我們將構(gòu)建一個執(zhí)行以下操作的基于python的木馬:
And we begin with a simple assumption: the target executes some code that is beneficial to it. It might be anything like a python package serving to do some task the user thinks is worth doing.
我們從一個簡單的假設(shè)開始:目標執(zhí)行一些對其有利的代碼。 它可能類似于python包,用于執(zhí)行用戶認為值得做的某些任務(wù)。
Source code: Github
源代碼: Github
介紹 (Introduction)
Trojans are powerful because they look nice and are one of the foremost candidates of evading suspicion. Once run, they get about their malicious intent while looking perfectly fine to the attacker. More so, since targets (especially developers) are usually not suspicious of grabbing open-source/packages code and running it. It might be a good entry point for our exploit.
特洛伊木馬之所以強大,是因為它們看起來不錯,并且是逃避懷疑的首要候選者之一。 一旦運行,他們就可以了解惡意意圖,同時對攻擊者看起來還不錯。 更重要的是,由于目標(尤其是開發(fā)人員) 通常并不懷疑獲取開源代碼/程序包代碼并運行它。 對于我們的漏洞利用來說,這可能是一個很好的切入點。
好的代碼 (The ‘good’ code)
The good code is simple. It does what the target intends it to do. It might range across a variety of things and span a whole package; the bigger the codebase, the subtler it is to spot activity. We’ll skip that part and write a simple code that prints something.
好的代碼很簡單。 它完成了目標打算執(zhí)行的操作。 它可能涉及多種事物,并且涉及整個軟件包。 代碼庫越大,發(fā)現(xiàn)活動就越微妙。 我們將跳過這一部分,并編寫一個簡單的代碼來打印一些內(nèi)容。
The good code with somewhat bad intents.好的代碼,意圖有些不好。To the target, this script should do what it is meant to (as in printing a simple line in our case) and exit peacefully. Apart from it, the main stuff here is the other stuff. The script builds a directory (in the normal case, you would want the working directory to be somewhere hidden. I’ll skip that for conciseness and obviousness of doing so) downloaded`, switches to it, and makes a cURL request (read more about cURL here) to some server at http://192.168.43.38:9000/downloader.py and downloads the content returned to a python script downloader.py . It then fires the command python3 downloader.py and peacefully exits. Since Popen was used, the child process (running downloader.py) disassociates from the parent (good.py)on the parent’s exit and associates with the init. So effectively, it becomes a separate process. The function run_command() is the python equivalent of a shell. It runs the specified command and returns the output from STDOUT or what you would have received had you used a shell.
對于目標,此腳本應(yīng)按其意圖(例如,在本例中為打印簡單行)執(zhí)行操作,然后和平退出。 除此之外,這里的主要內(nèi)容是其他內(nèi)容。 該腳本構(gòu)建一個目錄(通常情況下,您希望工作目錄位于某個隱藏的位置。為簡潔起見,我將跳過該目錄),然后downloaded并切換到該目錄并發(fā)出cURL請求(了解更多信息)。有關(guān)cURL的信息 )到位于http://192.168.43.38:9000/downloader.py某個服務(wù)器,然后將返回的內(nèi)容下載到python腳本downloader.py 。 然后它觸發(fā)命令python3 downloader.py并和平退出。 由于Popen使用,子進程(運行downloader.py )從父(解離good.py )在父母的出口和同伙與init 。 如此有效,它成為一個單獨的過程。 函數(shù)run_command()與shell的python等價。 它運行指定的命令并返回STDOUT的輸出,或者返回您使用Shell會收到的輸出。
Now is the time to configure this http://192.168.43.38:9000.
現(xiàn)在是時候配置此http://192.168.43.38:9000.
服務(wù)器端 (Server end)
The idea is to build a server that automatically pushes code to the remote end. It will be later used to update code in real-time, transfer files, commands, and a lot more. All we need to do is to configure a HTTP server capable of handling POST and GET.
這個想法是建立一個自動將代碼推送到遠程端的服務(wù)器。 稍后將用于實時更新代碼,傳輸文件,命令等。 我們需要做的就是配置一個能夠處理POST和GET的HTTP服務(wù)器。
basic HTTP server skeleton in setup_server.pysetup_server.py中的基本HTTP服務(wù)器框架A HTTPServer in python runs on two pieces of information: where to put it up and what to do on interaction. The former part is handled by ('192.168.43.38', 9000) which serves to bind the server to port 9000 of the machine and192.168.43.38 is the local IP. The latter part is handled by a separate class extending BaseHTTPRequestHandler that defines functionality for POST and GET. The _set_response() function serves to send mandatory HTTP header information (header, code 200 representing success, and end header representing the end of header). Since we shall be dealing with text data, setting the Content-type to text/html is fine. Now to add the two main functions.
python中的HTTPServer在兩條信息上運行:放置在何處以及如何進行交互。 前一部分由('192.168.43.38', 9000)處理,該部分用于將服務(wù)器綁定到計算機的端口9000 ,而192.168.43.38是本地IP。 后一部分由單獨的擴展BaseHTTPRequestHandler類處理,該類定義了POST和GET功能。 _set_response()函數(shù)用于發(fā)送強制性的HTTP標頭信息(標頭,表示成功的代碼200和標頭結(jié)尾的結(jié)束標頭)。 由于我們將處理文本數(shù)據(jù),因此將Content-type設(shè)置為text/html很好。 現(xiàn)在添加兩個主要功能。
GET.pyGET.pyA GET request, amongst other things, contains a path to get. In this case, it would be of the form filepath in http://192.168.43.38:9000/filepath . It might be empty (analogous to the home page of a website you visit) or it contains something (analogous to further pages you visit from the home page). In our case, we wish to return the list of files available in the server when a GET is made to our home, i.e. http://192.168.43.38:9000/ and return the contents of the file when a specific file is requested, as in http://192.168.43.38:9000/downloader.py . The technique is to distinguish the two cases, open the file in the latter case, craft a response, and wfile.write() that sends the response.
除其他事項外, GET請求包含獲取路徑。 在這種情況下,其格式為http://192.168.43.38:9000/filepath中的filepath 。 這可能是空的(類似于你訪問一個網(wǎng)站的主頁 ),或者它包含的東西(類似于您從主頁訪問進一步頁)。 在我們的情況下,我們希望在執(zhí)行GET返回服務(wù)器中可用文件的列表,例如 http://192.168.43.38:9000/并在請求特定文件時返回文件的內(nèi)容,就像http://192.168.43.38:9000/downloader.py 。 該技術(shù)是區(qū)分這兩種情況,在后一種情況下打開文件, wfile.write()響應(yīng),以及發(fā)送響應(yīng)的wfile.write() 。
A POST request contains data that must be handled.
POST請求包含必須處理的數(shù)據(jù)。
POST.pyPOST.pyThis gets the length from the HTTP header, extracts the data using rfile.read() , and stores to a file. The split using SPLIT is just a convenient way of handling newlines (for me! I am just never able to preserve newlines in data sent over a network. To a file, it looks like a huge single line data. To overcome this, I replace \n at the target end with a SPLIT which is then replaced back at the server end). Finally, we send a POST OK using wfile.write().
這從HTTP標頭獲取長度,使用rfile.read()提取數(shù)據(jù),然后存儲到文件中。 使用SPLIT拆分只是處理換行符的一種便捷方式(對我來說!我永遠無法在通過網(wǎng)絡(luò)發(fā)送的數(shù)據(jù)中保留換行符。對于文件,它看起來像是巨大的單行數(shù)據(jù)。要解決此問題,我替換了\n在目標端帶有SPLIT ,然后在服務(wù)器端被替換回)。 最后,我們使用wfile.write().發(fā)送POST OK wfile.write().
Now we move on to crafting other stuff. First, downloader.py
現(xiàn)在我們繼續(xù)制作其他東西。 首先, downloader.py
第一階段執(zhí)行 (Stage I execution)
Our good code serves to load a simple downloader.py to a specified directory and execute it. Now it is mainly up to downloader.py to handle everything else. As a starter, we want a way for downloader.py to handle these things:
我們的優(yōu)質(zhì)代碼可用于將簡單的downloader.py加載到指定目錄并執(zhí)行。 現(xiàn)在主要由downloader.py處理其他所有內(nèi)容。 首先,我們希望downloader.py能夠處理這些事情:
Know when code for downloader.py itself has been updated.
知道何時downloader.py本身的代碼已更新。
Evade all signals possible (not even a simple kill PID should terminate it).
避開所有可能的信號(甚至沒有簡單的終止kill PID都應(yīng)終止它)。
Schedule itself (related to cron and not covered here) and build kernel persistence.
調(diào)度自身(與cron相關(guān),在此不介紹)并構(gòu)建內(nèi)核持久性。
For the first two, the following code is sufficient.
對于前兩個,下面的代碼就足夠了。
downloader.pydownloader.pyThere might be several ways to check status. I settled with creating a special file status.txt having binary digits on two separate lines. The first line has a 0/1 that denotes whether the files on the server have been updated (or if the attacker wants to rerun certain programs), and the second line has a 0/1 that denotes whether downloader.py has itself to be updated. Quite intuitively, we download the status.txt and analyse the flags. If we obtain a 1 in the first line, we move on to fetching and downloading files (since updating downloader.py too involves the same operations, the second flag is not checked here). When data is retrieved, we delete status.txt since it is no longer needed.
可能有幾種檢查狀態(tài)的方法。 我決定創(chuàng)建一個特殊文件status.txt該文件在兩行中分別使用二進制數(shù)字。 第一行的0/1表示服務(wù)器上的文件是否已更新(或者攻擊者是否想重新運行某些程序),第二行的0/1表示downloader.py是否本身已被更新。更新。 非常直觀地,我們下載status.txt并分析標志。 如果在第一行中獲得1,則繼續(xù)獲取和下載文件(由于更新downloader.py也涉及相同的操作,因此此處未選中第二個標志)。 檢索數(shù)據(jù)后,由于不再需要status.txt ,因此我們將其刪除。
The third requirement is dealt with simply like the following.
第三個要求可以像下面這樣簡單地處理。
Handling signals and termination處理信號和終止Any of the following signals received causes a re-downloading of the downloader.py and a re-run as a new process. Only SIGKILL which can’t be ignored kills the process.
收到以下任何信號都會導(dǎo)致downloader.py的重新downloader.py并作為新進程重新運行。 只有不容忽視的SIGKILL才能殺死該過程。
The fourth and the main part goes as such.
第四也是主要部分。
Fetching files正在擷取檔案Recall from the discussion on GET that a request of the form http://192.168.43.38:9000 returns a list of files hosted in the server. We obtain that list and run a loop (ignoring status.txt as it has already been downloaded and analysed). Should we obtain downloader.py (i.e. it is available on the server) and data[1] == 1 (it needs to be updated), we fire refresh_downloader() that refreshes the downloader.py by re-downloading it, running a new process, and exiting the current process. If this is not the case, we simply create a list of files to be downloaded that are handled in the next snippet.
回想一下關(guān)于GET的討論, http://192.168.43.38:9000格式的請求將返回服務(wù)器中托管的文件列表。 我們獲取該列表并運行一個循環(huán)(忽略status.txt ,因為已經(jīng)下載并分析了它)。 我們應(yīng)該得到downloader.py (即它是在服務(wù)器上可用)和data[1] == 1 (它需要更新),我們火refresh_downloader()是刷新downloader.py通過重新下載它,跑新流程,并退出當(dāng)前流程。 如果不是這種情況,我們只需創(chuàng)建要下載的文件列表,這些文件將在下一個代碼段中處理。
Download files下載檔案Simply create cURL requests to the respective URL and download the files. Once downloaded, run_scripts() runs all files in the working directory except downloader.py (that results in a fork bomb as long as status.txt has a 1 in its first line: the flag indicating to keep downloading and refreshing files) and setup_server.py (that contains the script of the server as discussed before). [ You shouldn’t keep setup_server.py on the server itself but then I was lazy enough not to fix that :)].
只需創(chuàng)建對相應(yīng)URL的cURL請求并下載文件即可。 下載完成后, run_scripts()運行除了在工作目錄中的所有文件downloader.py (即導(dǎo)致fork炸彈,只要status.txt在其第一行1:標志,指示繼續(xù)下載和刷新文件)和setup_server.py (包含前面討論的服務(wù)器腳本)。 [您不應(yīng)該將setup_server.py保留在服務(wù)器本身上,但是那時我很懶惰,無法修復(fù)它:)]。
Here’s the complete code for downloader.py
這是downloader.py的完整代碼
Complete downloader.py. The commented line ‘command_list.append(“sudo”) is added for privilege transfer later.完整的downloader.py。 添加了注釋行'command_list.append(“ sudo”),以便稍后進行特權(quán)轉(zhuǎn)移。That’s pretty much it!
差不多了!
樣品運行 (Sample run)
Initial setup on the target side.目標側(cè)的初始設(shè)置。 Initial setup on the attacker side攻擊者側(cè)的初始設(shè)置 Running good.py. Notice the ps -A output on the left.運行g(shù)ood.py。 注意左側(cè)的ps -A輸出。 Notice ps -A output after good.py exits.注意good.py退出后的ps -A輸出。 downloader.py performing GET requests to the server for status.txtdownloader.py執(zhí)行GET請求到服務(wù)器獲取status.txtstatus.txt till now is set as:
到目前為止, status.txt設(shè)置為:
A simple two-line text file having 0 and 0.一個簡單的兩行文本文件,具有0和0。Now update some part of the downloader.py. I added a simple print statement before the check_status() call in the while loop. Then set the status.txt as 1 and 1. Wait for GET request. Here’s the result:
現(xiàn)在更新downloader.py.某些部分downloader.py. 我在while循環(huán)的check_status()調(diào)用之前添加了一個簡單的print語句。 然后將status.txt設(shè)置為1和1。等待GET請求。 結(jié)果如下:
As the status was changed to 1 and 1, notice on the left how GET requests were made for the downloader.py. Afterwards, normal requests for status.txt returned.當(dāng)狀態(tài)更改為1和1時,請注意左側(cè)如何對downloader.py進行GET請求。 之后,返回對status.txt的常規(guī)請求。 Printing列印 Notice the change in PID of the downloader.py注意downloader.py的PID更改第二階段執(zhí)行 (Stage II execution)
Now that a general framework for pushing remote code is up and running, now is the time for writing few exploits. You can be creative with it. I wasn’t and came up with two ideas:
現(xiàn)在已經(jīng)建立并運行了用于推送遠程代碼的通用框架,現(xiàn)在該編寫一些漏洞利用了。 您可以用它來發(fā)揮創(chuàng)造力。 我不是,并且提出了兩個想法:
We predefine commands.txt to be the standard for the attacker’s storage of commands need to be executed on the target machine. Likewise, directory.txt to be the set of directories to be queried.
我們預(yù)先定義了commands.txt作為攻擊者需要在目標計算機上執(zhí)行的命令存儲標準。 同樣, directory.txt是要查詢的目錄集。
We build the first exploit.
我們建立了第一個漏洞利用程序。
command_executor.pycommand_executor.pyQuite straightforward implementation in opening commands.txt and reading commands line by line, executing them, crafting a response, and sending it back to the server.
在打開commands.txt并逐行讀取命令,執(zhí)行命令,編寫響應(yīng)并將其發(fā)送回服務(wù)器方面,這是非常簡單的實現(xiàn)。
The second exploit goes as follows.
第二個漏洞利用如下。
directory_enlister.pydirectory_enlister.pyThis exploit opens the file directory.txt and reads the directories to target. It then fires enlist_directory() which lists separately the files and the subdirectories. For each file, it reads ( cat filename) and crafts a response. For each subdirectory, it adds the complete path to the main list directory_list which in turn makes sure a turn comes when this subdirectory appended is also queried for further files and subdirectories within it.
該漏洞利用打開文件directory.txt并讀取目標目錄。 然后,它觸發(fā)enlist_directory() ,該列表分別列出文件和子目錄。 對于每個文件,它都會讀取( cat filename )并生成響應(yīng)。 對于每個子目錄,它會將完整路徑添加到主列表directory_list ,這又可以確保在附加此子目錄的同時也查詢其中的其他文件和子目錄時,該目錄將出現(xiàn)。
That’s it! Craft commands.txt and directories.txt and go ahead execute them.
而已! 編寫commands.txt和directories.txt ,然后繼續(xù)執(zhí)行它們。
Sample (still quite sensitive information on the target)樣本(關(guān)于目標仍然非常敏感的信息)樣品運行 (Sample run)
While the server up and running, move both text files and both exploits to the server and change status.txt to 1 and 0 (implying update in files but downloader.py need not be updated, which in turn implies update and run other files which consequently runs our exploits).
在服務(wù)器啟動并運行時,將兩個文本文件和兩個漏洞利用都移動到服務(wù)器,并將status.txt更改為1和0(這意味著更新文件,但無需更新downloader.py ,這又意味著更新并運行其他文件)因此運行我們的漏洞利用)。
Based on the target’s connectivity as well as on the time.sleep(30) in the second exploit, it takes a while to transfer data. But once it does, you realise what you just obtained.
根據(jù)目標的連接以及第二次利用中的time.sleep(30) ,傳輸數(shù)據(jù)需要一段時間。 但是一旦完成,您就會意識到自己剛剛獲得了什么。
Note for status change how GET requests are made有關(guān)狀態(tài)更改的說明,如何進行GET請求Several files were downloaded. I moved them to different directories and analysed them. Here’s a snapshot.
已下載了幾個文件。 我將它們移到了不同??的目錄并進行了分析。 這是快照。
‘Contents of’ was added by directory_enlister.py when it read contents of files and stored them to send back to the server. These are the list of files having data in respective *.txt files.當(dāng)目錄_enlister.py讀取文件內(nèi)容并將其存儲并發(fā)送回服務(wù)器時,它由directory_enlister.py添加了。 這些是在各個* .txt文件中具有數(shù)據(jù)的文件列表。 A sample. Contents of .bash_profile, ifconfig, as well as a compressed gz file一個樣品。 .bash_profile,ifconfig和壓縮的gz文件的內(nèi)容 Some files whose data is POSTed to the server一些數(shù)據(jù)已過帳到服務(wù)器的文件 Files related to Mailbox of the target與目標郵箱相關(guān)的文件 Some mail data. Note non-encrypted mail data may have much information about the target.一些郵件數(shù)據(jù)。 請注意,未加密的郵件數(shù)據(jù)可能包含有關(guān)目標的很多信息。Here’s some browsing data.
這里是一些瀏覽數(shù)據(jù)。
Top sites on SafariSafari上的熱門網(wǎng)站 Recently closed tabs最近關(guān)閉的標簽頁 List of Safari files available for analysis可供分析的Safari文件列表特權(quán)轉(zhuǎn)移 (Privilege transfer)
The most interesting tasks require root access. While there are complex mechanisms for privilege escalation, we have a slight advantage here: child of a sudo process has root access.
最有趣的任務(wù)需要root訪問。 盡管特權(quán)提升有復(fù)雜的機制,但我們在這里有一點優(yōu)勢: sudo進程的子級具有root訪問權(quán)限。
All you need is to convince the target to run good.py as root. This should not be difficult; users are now and then giving root access to code that just doesn’t run without root access (like any Scapy code).
您所需good.py就是說服目標用戶以root身份運行g(shù)ood.py 這應(yīng)該不難; 用戶現(xiàn)在會授予root權(quán)限以訪問那些沒有root訪問權(quán)限就無法運行的代碼(例如任何Scapy代碼)。
Let’s build a simple sniffer that won’t run without root access. Before anything further, change all COMMAND from python3 filename.py to sudo python3 filename.py anywhere Popen() comes up (also in the good.py when it Popen the downloader).
讓我們構(gòu)建一個簡單的嗅探器,該嗅探器必須具有root用戶訪問權(quán)限才能運行。 任何進一步之前,更改所有COMMAND從python3 filename.py到sudo python3 filename.py任何地方Popen()來了(也是在good.py時Popen下載器)。
sniffer.py嗅探器We will upload this through the server, it gets downloaded. Now let the target run good.py . It prompts from root access. Since the code has meaning and utility to the target, the target will most probably give root access. Now begins your privilege transfer where root access gets transferred to all children. We shall verify this if the sniffer runs.
我們將通過服務(wù)器上傳此文件,然后將其下載。 現(xiàn)在讓目標運行g(shù)ood.py 它從根訪問提示。 由于代碼對目標具有意義和實用性,因此目標很可能會授予root訪問權(quán)限。 現(xiàn)在開始您的特權(quán)轉(zhuǎn)移,其中root用戶訪問權(quán)將轉(zhuǎn)移到所有子項。 如果嗅探器運行,我們將對此進行驗證。
Scapy sniffer runs without further promptingScapy嗅探器無需進一步提示即可運行Scapy requires root access, and here we just ran a scapy program from root access given to good.py . It transferred from good.py to downloader.py which transferred to all the child process it spawned. Therefore, the sniffer was able to run just fine. Moreover, terminating processes also requires root permissions.
Scapy需要root用戶訪問權(quán)限,在這里我們只是從root good.py給good.py運行了一個Scapy程序。 它從good.py轉(zhuǎn)移到downloader.py ,后者又轉(zhuǎn)移到它產(chǎn)生的所有子進程。 因此,嗅探器能夠正常運行。 此外,終止進程還需要root權(quán)限。
Root access to terminate directory_enlister.py process根目錄訪問以終止directory_enlister.py進程結(jié)論 (Conclusion)
The only thing left is to build functionality to self-hide. It is a broad topic and studying rootkits might be a good way to start thinking about that.
剩下的唯一事情就是構(gòu)建自我隱藏的功能。 這是一個廣泛的話題,研究rootkit可能是開始考慮這一問題的好方法。
Have a good day!
祝你有美好的一天!
翻譯自: https://medium.com/bugbountywriteup/python-http-based-trojan-for-remote-system-forensics-and-privilege-transfer-ae128891b4de
python木馬程序設(shè)計
總結(jié)
以上是生活随笔為你收集整理的python木马程序设计_基于HTTP的Python特洛伊木马程序,用于远程系统取证和特权转移的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SPSS常用的几种统计分析
- 下一篇: 计算机基础知识经典问答题,计算机基础知识