<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    DBeaver Ultimate edition 算法分析/本地服務器搭建

    VSole2022-07-29 16:52:08

    DBeaver Ultimate edition 算法分析

    官方下載地址: https://dbeaver.com/download/ultimate/

    當前分析版本號: dbeaver-ue-22.1.0

    前記

    前幾天出了 該軟件的 爆破分析; 機票 :

    DBeaver Ultimate edition JAVA程序逆向分析

    這次來嘗試下不一樣的...;

    環境

    JAVA/javassist/Python

    流程:

    1.解析license算法, 生成注冊器, 生成 publicKey/privateKey/license/subscription

    2.替換程序自帶的 publicKey

    3.配置 host 映射, 搭建本地驗證服務器

    4.利用第一步生成的 license 注冊

    WIN10/Mac m1 測試通過

    分析

    license

    首先通過在程序界面 import 一個license ,查看關鍵字, 定位到目標 jar包 (也可以查看程序 log 日志, 位置: /Users/artemis/Library/DBeaverData/workspace6/.metadata)

    可以看到, 程序先將我們輸入到 icense去除空格以及刪除‘-’,‘#’這些特殊標記符號之后 base64 解密成字節數組

    這里格式化字符串寫的挺好,

    就是你的license 可以有換行, 可以有 ----PUBLIC-KEY--- 這些文件頭, 也可以有 # this is comment 這些注釋, 格式化之后會只留下關鍵的 license

    程序操作publicKey/license/subscription 都有這個邏輯;  當然; 這個并不重要..

    然后在licenseManager.importLicense 將 字節數組 解析成 注冊憑證,

    核心的算法都在 importLicense 里面,我們繼續看

    可以看到, 獲取了一個publicKey 用來解密, 既然公鑰解密, 那么加密就是私鑰了 (這里有用,我們后面會生成密鑰,用來注冊license)

    剛開始以為 publicKey 是走接口獲取到的, 畢竟里面有個 URL 什么的, 后來發現其實是 jar包里的資源文件;

    我們模糊搜索下‘-public.key’, 定位到了這個 jar包: com.dbeaver.app.ultimate_22.1.0.202206121904.jar (這里有用, 后面我們會替換該 publicKey )

    推算出 productId = ‘dbeaver-ue’ , 這個應該就是跟著發版來走的; 而且是固定值

    眾所周知,publicKey 與 privateKey 是 成雙成對的, 而且 我們拿不到私鑰, 后面只能想辦法用我們自己生成的 公鑰與私鑰了

    接著往下看, 拿到publicKey 后,開始解密, ras 解密, 沒什么好說的, 關鍵是解密后的 license 格式:

    即使解密成功, 但是 license 格式不對, 也是不行的 ;

     復制代碼 隱藏代碼
    STANDARD((byte)0, 218, "Initial basic license format"),
    EXTENDED((byte)1, 238, "Extended format with owner email and corporate license info");if (buffer.capacity() != this.licenseFormat.getEncryptedLength()) {
        throw new LMException("Bad " + this.licenseFormat + " license length (" + buffer.capacity() + ")");
    

    可以看到 license 第一位字節為 憑證格式,是個枚舉類型, 不同的格式 license 長度是不同的,

    后面我們選 STANDARD 這種license的話 , 第一位就是0,并且 license總字節長度必須是 218

    后 16為 是 license_id字符串, 在往后是 licenseType,licenseStartTime,licenseEndTime..... 這里不多解析了, 生成的時候按這個規則來就行

    有一點注意到事, 必須滿足位數要求, 比如 license_id 是16位, 你可以只寫10位, 剩下的6為必須用0 或者空填充

    可以看到, license 的讀取是用的 JAVA 的Buffer 來操作的; 我們生成的話 也可以用 Buffer 來操作;

    好; 接下來開始手寫 license 的生成,具體代碼如下;

     復制代碼 隱藏代碼
    // 填充字符串for (int i = 0; i < 64; i++) {
        FILL += " ";
    }// 生成公鑰/私鑰KeyPair keyPair = LMEncryption.generateKeyPair(2048);
    publicKey = keyPair.getPublic();
    privateKey = keyPair.getPrivate();
    String pubKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
    String priKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());// license 原始ByteBuffer buffer = ByteBuffer.allocate(218);
    buffer.put((byte) 0);// licenseIdfillString("this is lice_id", 16, buffer);// licenseType ULTIMATE('U', "Ultimate license"),buffer.put((byte) 85); // 'U' = 85// licenseIssueTimebuffer.putLong(SIMPLE_DATE_FORMAT.parse("2020-01-01").getTime());// licenseStartTimebuffer.putLong(SIMPLE_DATE_FORMAT.parse("2020-01-01").getTime());// licenseEndTimebuffer.putLong(SIMPLE_DATE_FORMAT.parse("2099-01-01").getTime());// flagsbuffer.putLong(250L);// proeudcIdfillString("dbeaver-ue", 16, buffer);// productVersionfillString("prod_version", 8, buffer);// ownerIdfillString("this is owner_id", 16, buffer);// ownerCompanyfillString("this is ownerCompany", 64, buffer);// ownerNamefillString("this is ownerName", 64, buffer);// license 私鑰加密encodeToString = Base64.getEncoder().encodeToString(LMEncryption.encrypt(buffer.array(), privateKey));// 將 publicKey/privateKey/license 保存到文件System.out.println("--- PUBLIC KEY ---");
    System.out.println(org.jkiss.utils.Base64.splitLines(org.jkiss.utils.Base64.encode(publicKey.getEncoded()), 76));try (FileWriter fileWriter = new FileWriter(outPath + "dbeaver-ue-public.key")) {
        fileWriter.write(org.jkiss.utils.Base64.splitLines(org.jkiss.utils.Base64.encode(publicKey.getEncoded()), 76));
    }try (FileWriter fileWriter = new FileWriter(outPath + "dbeaver-ue-private.key")) {
        fileWriter.write(org.jkiss.utils.Base64.splitLines(org.jkiss.utils.Base64.encode(privateKey.getEncoded()), 76));
    }try (FileWriter fileWriter = new FileWriter(outPath + "license.key")) {
        fileWriter.write(org.jkiss.utils.Base64.splitLines(org.jkiss.utils.Base64.encode(LMEncryption.encrypt(buffer.array(), privateKey)), 76));
    }// 替換原程序中的公鑰JarFileReplaceUtil.replaceFile(srcPath + "/com.dbeaver.app.ultimate_22.1.0.202206121904.jar", outPath + "dbeaver-ue-public.key", "keys/dbeaver-ue-public.key");
    

    生成之后,可以看到多了這三個文件

    然后復制生成的 license.key 注冊一下試試;

    哎,居然報錯了; 跟進去再看看,

    發現個壞消息 , 原來除了用 publicKey 解密, 還有一層校驗,

    程序會會帶著 本地解密后的 license信息 請求 服務器,來獲取 subscription 訂閱信息;

    服務器的 數據 我們是沒法子整的 , 但是我們也可以 本地搭建一個服務器; 讓程序來請求我們自己的本地服務器來驗證;

    subscription

    仔細瞅瞅,看到 接口響應成功的 response 也是有些規則的,示例如下:

     復制代碼 隱藏代碼
    response = "VALID:SUCCESS" + "\r\n" + "-- SUBSCRIPTION" + "\r\n" + subscription
    

    VALID 與 SUCCESS 為狀態, 并且必須用: 切割, 程序例有教研 ,':' 的ascii 為 58;

    如果響應文本沒有包含':'的話, 會直接報錯;

     復制代碼 隱藏代碼
    int divPos = licenseStatusString.indexOf(58);if (divPos == -1) {
        var19 = new LicenseCheckResult(GeneralUtils.makeErrorStatus("Bad check status: " + licenseStatusString), LMLicenseStatus.INVALID);
        return var19;
    }
    

    除了狀態, resposne 后面的文本才是 subscription 主體, 其中包含了服務器返回的訂閱信息;

    如果這里為空 也是會注冊失敗的;

    不過好消息是, subscription 的算法 與 license 如出一轍;只不過 subscription 的 格式有些不一樣;

     復制代碼 隱藏代碼
    try {
        this.format = LMSubscriptionFormat.valueOf(buffer.get());
    } catch (Exception var5) {
        log.warning("Unsupported subscription format: " + buffer.get(0));
        this.format = LMSubscriptionFormat.STANDARD;
    }if (buffer.capacity() != this.format.getEncryptedLength()) {
        throw new LMException("Bad " + this.format + " subscription length (" + buffer.capacity() + ")");
    } else {
        this.licenseId = LMUtils.getStringFromBuffer(buffer, 16);
        this.period = LMSubscriptionPeriod.getById((char)buffer.get());
        this.periodDays = buffer.getInt();
        this.lastRenewDate = LMUtils.getDateFromBuffer(buffer);
        this.expirationDate = LMUtils.getDateFromBuffer(buffer);
        this.activationDate = LMUtils.getDateFromBuffer(buffer);
        this.deactivationDate = LMUtils.getDateFromBuffer(buffer);
        this.totalRenewCount = buffer.getInt();
        this.active = buffer.get() != 0;
    }
     復制代碼 隱藏代碼
    public enum LMSubscriptionFormat implements LMSerializeFormat {
        STANDARD((byte)0, 59, "Initial subscription format");
    }
    

    可以看到, subscription 第一位 LMSubscriptionFormat 固定為 0,

    并且 subscription 解密后的字節長度 也固定為 59,

    后面的也看不懂是啥, 只看到有個過期時間 expirationDate, 別的瞎填寫吧;

    看懂規則后, 我們按前面生成 license 的地方 順便把 subscription 也生成一份出來, 代碼如下:

     復制代碼 隱藏代碼
    // STANDARD((byte)0, 59, "Initial subscription format");ByteBuffer subscriptionBuffer = ByteBuffer.allocate(59);
    subscriptionBuffer.put((byte) 0);
    fillString("this is lice_id", 16, subscriptionBuffer);
    subscriptionBuffer.put((byte) 89);
    subscriptionBuffer.putInt(1);
    subscriptionBuffer.putLong(SIMPLE_DATE_FORMAT.parse("2099-01-01").getTime());
    subscriptionBuffer.putLong(SIMPLE_DATE_FORMAT.parse("2099-01-01").getTime());
    subscriptionBuffer.putLong(SIMPLE_DATE_FORMAT.parse("2099-01-01").getTime());
    subscriptionBuffer.putLong(SIMPLE_DATE_FORMAT.parse("2099-01-01").getTime());
    subscriptionBuffer.putInt(1);
    subscriptionBuffer.put((byte) 1);
    subscriptionToString = Base64.getEncoder().encodeToString(LMEncryption.encrypt(subscriptionBuffer.array(), privateKey));try (FileWriter fileWriter = new FileWriter(outPath + "subscription.key")) {
        fileWriter.write(subscriptionToString);
    }
    System.out.println("subscriptionToString : \r\n" + subscriptionToString);
    

    現在文件都有了, 還差一部, 就是讓搭建一個本地服務器用來驗證 subscription ;

    這里我就用 python 搭了; (簡單方便快捷)

    然后還需要配置 host 將 ‘dbeaver.com’ 轉發到 127.0.0.1 (這個地址是 開charles 之后 將 DBeaver 配置代{過}{濾}理后抓到的)

    這里踩了一些坑, 因為 原域名是開啟了 SSL 的, 所以我們也必須開始; 但是 java虛擬機又不信任這個SSL證書, 折騰了好久才折騰通過;

    具體代碼如下: (實際上就提供一個接口, 將我們之前生成的 subscription 返回, 可以順便把 請求信息記錄下)

    搭建本地服務器

     復制代碼 隱藏代碼
    @app.route('/<re(".*"):path>', methods=['GET', 'POST', 'PUT', "DELETE", "PATCH"])def welcome(path):    f = open('/Users/artemis/Downloads/keys/subscription.key', 'r')
        sub_str = "".join(f.readlines())
        return "VALID:SUCCESS" + "\r\n" + "-- SUBSCRIPTION" + "\r\n" + sub_strif __name__ == "__main__":
        '''
        # 配置文件mySsl.conf:
        # 生成私鑰:
        openssl genrsa -out server.key 4096
        # 生成證書請求文件:
        openssl req -new -sha256 -out server.csr -key server.key -config mySsl.conf 
        # 檢查證書申請文件內容:
        openssl req -text -noout -in server.csr
        # 利用證書請求文件生成證書,執行如下命令:
        openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt -extensions req_ext -extfile mySsl.conf
        '''    '''
        # 配置 JDK 信任證書
        keystore=/Users/artemis/Library/Java/JavaVirtualMachines/azul-11.0.13/Contents/Home/lib/security/cacerts
        keytool -delete -alias alias_dev  -keystore $keystore -storepass changeit
        keytool -import -file /Users/artemis/Documents/work_space/python/python_demo/tools/ssl/server.crt -alias alias_dev -storepass changeit -keystore $keystore
        keytool -v -list -keystore $keystore -storepass changeit
        '''    current_directory = os.path.dirname(os.path.abspath(__file__))
        app.run(ssl_context=(
            f'{current_directory}/tools/ssl/server.crt',
            f'{current_directory}/tools/ssl/server.key'    ), host='0.0.0.0', port=443)
    

    配置好 host 文件, 然后我們再次復制 license 注冊;

    可以看到 python web 服務, 收到請求了,請求信息如下:

     復制代碼 隱藏代碼
    * Running on https://127.0.0.1:443
     * Running on https://172.18.20.57:443 (Press CTRL+C to quit)
    {
        "method": "GET",
        "root": "https://dbeaver.com/",
        "full_path": "/lmp/checkLicense?crc=1ae7edc9687e503c9fc6d079a91bca5f&product=dbeaver-ue&version=22.1&license=this%20is%20lice_id",
        "headers": [
            "X-Referrer: D1XXSUQM97WAZN",
            "User-Agent: DBeaver Ultimate 22.1.0 [Mac OS X aarch64]",
            "Host: dbeaver.com",
            "Connection: Keep-Alive",
            "Accept-Encoding: gzip",
            "",
            ""    ],
        "args": {
            "crc": "1ae7edc9687e503c9fc6d079a91bca5f",
            "product": "dbeaver-ue",
            "version": "22.1",
            "license": "this is lice_id"    },
        "form_data": {},
        "raw_data": "",
        "cookies": {}
    }127.0.0.1 - - [28/Jul/2022 17:11:48] "GET /lmp/checkLicense?crc=1ae7edc9687e503c9fc6d079a91bca5f&product=dbeaver-ue&version=22.1&license=this%20is%20lice_id HTTP/1.1" 200 -
    

    然后在看看我們的程序;

    終于注冊成功 !!

    后記

    總體研究下來, 這個過程比之前的爆破要麻煩也復雜的多, 但是刺激啊....

    僅供研究學習使用,請勿用于非法用途

    注:若轉載請注明來源(本貼地址)與作者信息。

    base64dbeaver
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    官方下載地址: https://dbeaver.com/download/ultimate/
    適用場景:使用burp作為代理,經過burp的數據包會自動進行base64編碼。
    多次測試后發現確實都相等,確定就是標準的Base64編碼。發現與之前的111極為相似。
    菜刀、冰蝎、蟻劍、哥斯拉的流量特征
    [VNCTF2022]gocalc0復現
    2022-05-07 16:02:38
    看雪論壇作者ID:H3h3QAQ
    Satacom下載程序,也稱為LegionLoader,是2019年出現的一個著名的惡意軟件家族。該惡意軟件利用查詢DNS服務器的技術獲取base64編碼的URL,以便接收當前由Satacom傳播的另一惡意軟件家族的下一階段。
    在滲透測試過程中,我們經常會遇到一些特殊的編碼,如地址欄的URL編碼,如抓包過程中遇到的Unicode編碼、base64編碼等。這款工具界面美觀,使用穩定,相當好用。之前使用的小葵轉碼工具,一旦輸錯就會崩潰,相當淡疼。下面看下它的樣子:可以看到它支持多種編碼轉換,前幾個選項應用場景比較少,從base64往下的選項就比較常用了。
    Decodify是一款功能強大的字符串安全處理工具,在該工具的幫助下,廣大研究人員能夠輕松地以遞歸的方式檢測和解碼編碼字符串。假設現在有一個字符串“s0md3v”,然后使用Base64對其編碼,結果如下:czBtZDN2. 解碼凱撒密碼我們可以使用--rot選項來提供偏移量,或者使用--rot all命令來告訴Decodify使用1-26偏移量來解碼目標字符串。
    前言前幾天對自己學校進行的一次滲透測試,由于深信服過于變態,而且攔截會直接封ip,整個過程有點曲折期間進行了后綴名繞過,jspx命名空間繞過、獲取網站根目錄、base64五層編碼寫入shell等操作0x01 獲取網站接口主界面:上傳點:由于該應用是內嵌企業微信的套皮Html,所以我們首先用Burp Suite抓包獲取接口和cookie任意文件上傳:文件名強制命名為code+學號,后綴為最后一次點號出現之后的字母0x02 后綴名繞過代碼不限制后綴名,但是waf限制呀!
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类