內網滲透有它就夠了,手把手教你破解CobaltStrike3.12
1 - 概述
CobaltStrike是一款內網滲透的商業遠控軟體,支援自定義指令碼擴充套件,功能非常強大。前段時間Github上有好心人放出了CobaltStrike3.12的試用版,接著Lz1y很快就放出了破解版,加上熱心老哥提供了的xor64.bin(試用版中沒有這個檔案),一個比較完美的最新可用版本誕生了。下面我們看下最新試用版是如何被完美破解的。
2 - 上手
CobaltStrike(下面簡稱CS)主體程式碼是用Java開發的,逆起來比較友好。用jd-gui反編譯cobaltstrike.jar檔案,可以看到程式碼幾乎沒有做防破解。Java原始碼沒有任何混淆。但是檢視反編譯的原始碼時,很多地方出現了// INTERNAL ERROR //
,這裡我推薦一款Java反編譯工具luyten
,幾乎可以100%反編譯獲得cobaltstrike.jar原始碼。
CS的License處理邏輯在common/License.java
檔案中:
package common; import aggressor.*; import javax.swing.*; import java.awt.*; public class License{ private static long life; private static long today; private static long start; private static long difference; private static long getTimeSinceStart() { final Prefs options = Prefs.getPreferences(); License.today = System.currentTimeMillis(); License.start = options.getLongNumber("cobaltstrike.start.int", 0L); if (License.start == 0L) { options.set("cobaltstrike.start.int", License.today + ""); options.save(); License.start = License.today; } return License.difference = (License.today - License.start) / 86400000L; } public static void checkLicenseGUI(final Authorization auth) { getTimeSinceStart(); if (License.difference > License.life || License.today - License.start < 0L) { JOptionPane.showMessageDialog(null, "Your Cobalt Strike trial is now expired.\nPlease purchase a license and use the\nsoftware update feature to continue.\n\nFor details, visit:\nhttps://www.cobaltstrike.com/", null, 0); System.exit(0); } else { final long left = License.life - License.difference; String form = left + " day"; if (left != 1L) { form += "s"; } CommonUtils.print_warn("This is a trial version of Cobalt Strike. You have " + form + " left of your trial. If you purchased Cobalt Strike. Run the Update program and enter your license."); CommonUtils.print_trial("WARNING! This trial is *built* to get caught by standard defenses. The licensed product does not have these restrictions. See: http://blog.cobaltstrike.com/2015/10/14/the-cobalt-strike-trials-evil-bit/"); JOptionPane.showMessageDialog(null, "This is a trial version of Cobalt Strike.\nYou have " + form + " left of your trial.\n\nIf you purchased Cobalt Strike. Run the\nUpdate program and enter your license.", null, 1); } } public static boolean isTrial() { return true; } public static void checkLicenseConsole(final Authorization auth) { getTimeSinceStart(); if (License.difference > License.life || License.today - License.start < 0L) { CommonUtils.print_error("Your Cobalt Strike trial is now expired. Please purchase a license and use the software update feature to continue. For details, visit: https://www.cobaltstrike.com/"); System.exit(0); } else { final long left = License.life - License.difference; String form = left + " day"; if (left != 1L) { form += "s"; } CommonUtils.print_warn("This is a trial version of Cobalt Strike. You have " + form + " left of your trial. If you purchased Cobalt Strike. Run the Update program and enter your license."); CommonUtils.print_trial("WARNING! This trial is *built* to get caught by standard defenses. The licensed product does not have these restrictions. See: http://blog.cobaltstrike.com/2015/10/14/the-cobalt-strike-trials-evil-bit/"); } } static { License.life = 21L; License.today = 0L; License.start = 0L; License.difference = 0L; }}
程式碼邏輯很清晰,這裡我們有兩個方向進行patch:
-
修改
License.life
無限延長試用 -
修改
isTrial()
返回值,偽造成正式版
因為CS很多地方的試用版和正式版處理邏輯不同,所以修改了isTrial()
返回值之後,我們還需要修改所有呼叫了isTrial()
函式的地方,對程式碼進行調整。另外試用版CS留了一些特徵指紋和限制,我們也需要去除相應的特徵程式碼。
修改重打包
既然知道了破解思路,我們看下如何動手操作去修改原始碼並重編譯。Java程式設計中我們可以使用jar
工具將一系列的.class檔案打包成jar包,供其他java程式使用。我們也可以修改jar包中.class檔案的內容,並重新編譯打包。比如修改demo.jar中的example.class並重新編譯的過程如下:
1. 使用jd-gui、luyten等工具把demo.jar包中的class反編譯成原始碼,從中提取得到example.java
2. 執行 jar xvf demo.jar 解壓demo.jar得到jar包的子檔案(注意會解壓到當前目錄),將example.java檔案放置到與example.class檔案同一目錄
3. 執行 javac -cp a.jar;b.jar;c.jar example.java 重新編譯(或者javac -cp demo.jar example.java ),得到新的example.class檔案。其中a.jar、b.jar、c.jar是依賴包,一般直接依賴一個原始解壓的demo.jar包即可。
4. 確保編譯後的example.class替換了原來的example.class檔案(可以通過jd-gui反編譯檢視)
5. 執行 jar -uvf demo.jar com/some/path/example.class 更新demo.jar包
更新jar包中的class檔案時,新的class檔案目錄路徑需要與原package路徑保持一致。比如修改了aggressor.AggressorClient.java
並重新編譯之後,更新jar包的命令如下:
17:16 KINGX modified_java_files >jar -uvf cobaltstrike-with-xor64.jar aggressor/AggressorClient*.class 正在新增: aggressor/AggressorClient$1.class(輸入 = 650) (輸出 = 403)(壓縮了 38%) 正在新增: aggressor/AggressorClient$2.class(輸入 = 1263) (輸出 = 704)(壓縮了 44%) 正在新增: aggressor/AggressorClient.class(輸入 = 11115) (輸出 = 5196)(壓縮了 53%)
可能遇到的問題
修改後的java檔案在重新編譯為class檔案時,可能會遇到很多奇怪的報錯。有時候是因為反編譯出的原始碼存在錯誤導致的,這個時候我們可以將luyten、jad、jd-gui等反編譯工具結合使用,儘量還原成正確的原始碼,再重新編譯。 比如:AggressorClient.java,jad aggressor/AggressorClient*.class
和luyten
反編譯得到的原始碼是不一樣的。
3 - 試用版補丁詳細分析
Tips: 以下程式碼片段中行首的 - 代表刪除,+ 代表新增
Patch 試用版本
修改common.License,去掉checkLicenseGUI()、checkLicenseConsole()函式體,修改isTrial()返回值為true
修改主程式標題
aggressor.AggressorClient,修改getTitle()函式
解除listener同類數量限制
一個teamserver預設只能監聽一個listener,可以通過修改程式碼去除限制。
aggressor.dialogs.ListenerDialog,去除以下程式碼:
...else if (Listener.isEgressBeacon(payload) && DataUtils.isBeaconDefined(this.datal) && !name.equals(DataUtils.getEgressBeaconListener(this.datal))) { DialogUtils.showError("You may only define one egress Beacon per team server.\nThere are a few things I need to sort before you can\nput multiple Beacon HTTP/DNS listeners on one server.\nSpin up a new team server and add your listener there.");}...
去除EICAR後門指紋特徵
試用版有幾個地方存在EICAR特徵字元:X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
,都需要進行清理:
common.ListenerConfig
修改pad()函式:
-result.append("5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\u0000");+result.append("123\u0000");
resources/template.x64.ps1、resources/template.x86.ps1
-$eicar = 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*'+$eicar = ''
server.ProfileEdits
-c2profile.addCommand(".http-get.server", "!header", "X-Malware: X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");-c2profile.addCommand(".http-post.server", "!header", "X-Malware: X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");-c2profile.addCommand(".http-stager.server", "!header", "X-Malware: X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");-c2profile.addCommand(".stage.transform-x86", "append", "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");-c2profile.addCommand(".stage.transform-x64", "append", "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");
common.ArtifactUtils
因為已經修改了License.isTrial()返回值為false,所以下面這段改不改也沒什麼影響。
if (License.isTrial()) { packer.addString("X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*"); CommonUtils.print_trial("Added EICAR string to " + s);}
新增XOR64.BIN
生成payload時,會呼叫common.ArtifactUtils中的XorEncode()進行編碼:
public static byte[] _XorEncode(final byte[] data, final String arch) { AssertUtils.TestArch(arch); if ("x86".equals(arch)) { final byte[] decoder = XorStub(); final byte[] payload = XorEncoder.encode(data); return CommonUtils.join(decoder, payload); } if ("x64".equals(arch)) { final byte[] decoder = CommonUtils.readResource("resources/xor64.bin"); final byte[] payload = XorEncoder.encode(data); return CommonUtils.join(decoder, payload); } return new byte[0];}public static byte[] XorEncode(final byte[] data, final String arch) { if (License.isTrial()) { CommonUtils.print_trial("Disabled " + arch + " payload stage encoding."); return data; } AssertUtils.Test(data.length > 16384, "XorEncode used on a stager (or some other small thing)"); return _XorEncode(data, arch);}
試用版不會進行payload stage encoding
,所以試用版軟體包中並沒有帶xor.bin/xor64.bin檔案,如果有這兩個檔案的話,可以新增到resources/xor.bin
、resources/xor64.bin
路徑下。Github上有熱心老哥提供了xor64的生成指令碼:https://github.com/verctor/CS_xor64
原始碼逐個修改完,重新編譯更新到cobaltstrike.jar包中,再拷貝替換掉原版的jar包就OK了。