From aaa62c4b3cca515841a6a2e7ed4ec7949adcdb6e Mon Sep 17 00:00:00 2001 From: CATALPA <2209799148@qq.com> Date: Tue, 2 Apr 2024 18:00:19 +0800 Subject: [PATCH] Update v1.2 --- README.md | 18 +- gen_license/gen_license_new.py | 243 ++++++++++++++++++++++ main.py => gen_license/gen_license_old.py | 6 +- gen_license/patcher.py | 100 +++++++++ 4 files changed, 360 insertions(+), 7 deletions(-) create mode 100644 gen_license/gen_license_new.py rename main.py => gen_license/gen_license_old.py (97%) create mode 100644 gen_license/patcher.py diff --git a/README.md b/README.md index 7a1900e..83aebed 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,17 @@ -## Usage +# Fortigate gadgets -python3 main.py +## Generate license -## Related articles +In "gen_license" folder. -https://wzt.ac.cn/2023/03/02/fortios_padding/ +The "gen_license_old.py" script is used for older versions. eg: Fortigate VM64 v7.4.1 + +The "gen_license_new.py" script is used for newer versions. eg: Fortigate VM64 v7.4.3 + +### Usage + +For older versions, executing the command `python3 gen_license_old.py` will generate the `License.lic` file. Just import the file into the system. + +For newer versions, you need to unpack the `init` file first and then execute the `python3 patcher.py init` command to patch the file. After starting the system, run the `python3 gen_license_new.py` command and import the generated `License.lic` file to the system. + +For newer versions, you also need to patch the `flatkc` file to disable rootfs checks. Please see https://wzt.ac.cn/2024/04/02/fortigate_debug_env2/ for more details. diff --git a/gen_license/gen_license_new.py b/gen_license/gen_license_new.py new file mode 100644 index 0000000..2f41078 --- /dev/null +++ b/gen_license/gen_license_new.py @@ -0,0 +1,243 @@ +""" +FortiGate keygen v1.2 (For newer versions [FGTVM64 v7.4.3?]) +Copyright (C) 2024 CataLpa + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import struct +import base64 +import hashlib +from Crypto.Cipher import AES + +lic_key_array = { + "SERIALNO": (0x73, 0x0), + "CERT": (0x73, 0x8), + "KEY": (0X73, 0x10), + "CERT2": (0X73, 0x18), + "KEY2": (0X73, 0x20), + "CREATEDATE": (0x73, 0x28), + "UUID": (0x73, 0x30), + "CONTRACT": (0x73, 0x38), + "USGFACTORY": (0x6e, 0x40), + "LENCFACTORY": (0x6e, 0x44), + "CARRIERFACTORY": (0x6e, 0x48), + "EXPIRY": (0x6e, 0x4c) + } + +class License: + fixed_aes_key = b"\x4C\x7A\xD1\x3C\x95\x3E\xB5\xC1\x06\xDA\xFC\xC3\x90\xAE\x3E\xCB" + fixed_aes_iv = b"\x4C\x7A\xD1\x3C\x95\x3E\xB5\xC1\x06\xDA\xFC\xC3\x90\xAE\x3E\xCB" + fixed_rsa_header = b"\x78\x99\xBF\xA5\xEF\x56\xAA\x98\xC1\x0B\x87\x2E\x30\x8E\x54\xF9\x71\xAD\x13\xEA\xAA\xBC\xE2\x0C\xB3\xAE\x65\xAE\xF9\x0E\x9B\xD1\x88\xC7\xFE\xBC\x86\x65\xFE\xE7\x62\xDE\x43\x0B\x02\x15\x36\xC8\xC5\xCD\x0E\xB9\x01\x97\xCE\x82\x27\x0F\x69\x7F\x6A\x29\xEC\x1C" + + rsa_header_length = len(fixed_rsa_header) # 4 bytes + aes_key = fixed_aes_iv + fixed_aes_key # 32 bytes iv + key + enc_data_length = None + enc_data = None + license_data = None + + license_header = "-----BEGIN FGT VM LICENSE-----\r\n" + license_tail = "-----END FGT VM LICENSE-----\r\n" + + def __init__(self, licensedata): + self.license_data = licensedata + + def encrypt_data(self): + tmp_buf = b"\x00" * 4 + struct.pack("= len(raw_license): + break + lic += raw_license[n:n + 64] + lic += "\r\n" + n += 64 + + with open("./License.lic", "w") as f: + f.write(_lic.license_header + lic + _lic.license_tail) + + print("[+] Saved to ./License.lic") diff --git a/main.py b/gen_license/gen_license_old.py similarity index 97% rename from main.py rename to gen_license/gen_license_old.py index 9ab9540..8bf2ec0 100644 --- a/main.py +++ b/gen_license/gen_license_old.py @@ -1,6 +1,6 @@ """ -FortiGate keygen v1.1 -Copyright (C) 2023 CataLpa +FortiGate keygen v1.1 (For older versions [FGVM64 v7.2.x v7.4.?-?]) +Copyright (C) 2024 CataLpa This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -102,7 +102,7 @@ class LicenseDataBlock: if __name__ == "__main__": license_data_list = [ - LicenseDataBlock("SERIALNO", "FGVMPGLICENSEDTOCATALPA"), + LicenseDataBlock("SERIALNO", "FGVMPG0000000000"), LicenseDataBlock("CREATEDATE", "1696089600"), LicenseDataBlock("USGFACTORY", "0"), LicenseDataBlock("LENCFACTORY", "0"), diff --git a/gen_license/patcher.py b/gen_license/patcher.py new file mode 100644 index 0000000..e9ca759 --- /dev/null +++ b/gen_license/patcher.py @@ -0,0 +1,100 @@ +""" +FortiGate patcher v1.0 (For newer versions [FGTVM64 v7.4.3?]) +Disable integrity checks +Copyright (C) 2024 CataLpa + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import re +import sys + +def gen_regex_from_sig(sig): + regex = "" + _split_sig = sig.split() + i = 0 + while True: + if i >= len(_split_sig): + break + if _split_sig[i] != "??": + regex += "\\x" + regex += _split_sig[i] + i += 1 + elif _split_sig[i] == "??": + _dyn_byte_count = 1 + while True: + i += 1 + if i >= len(_split_sig): + break + if _split_sig[i] == "??": + _dyn_byte_count += 1 + else: + break + regex += ".{%d}" % _dyn_byte_count + return regex + +def find_matches(pattern, data): + matches = [] + for match in re.finditer(pattern, data): + start = match.start() + end = match.end() + match_data = { + 'match': match.group(), + 'start': start, + 'end': end + } + matches.append(match_data) + return matches + +def do_patch(data, pos, codes): + for c in codes: + data[pos] = c + pos += 1 + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("[*] Usage: python3 %s init" % sys.argv[0]) + exit(0) + + init_file = sys.argv[1] + patch_pattern1 = gen_regex_from_sig("55 BE ?? ?? ?? ?? BF ?? ?? ?? ?? 48 89 E5 41 54 48 ?? ?? ?? 64 ?? ?? ?? ?? ?? ?? ?? ?? 48 89 ?? ?? 31 C0 E8 ?? ?? ?? ?? BE ?? ?? ?? ?? BF ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74") + patch_pattern2 = gen_regex_from_sig("44 8B 07 8B 06 44 0F ?? ?? ?? 0F B7 ?? ?? 44 0F ?? ?? ?? 0F B7 ?? ?? 41 39 C0") + patch_pattern3 = gen_regex_from_sig("55 48 89 E5 41 57 49 89 F7 41 56 49 89 FE BF ?? ?? ?? ?? 41 55 41 54 53 48 ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 ?? 49 89 C4 E8 ?? ?? ?? ?? 48 89 C7 E8 ?? ?? ?? ?? 49 89 C5 48 85 C0 74") + code1 = [0xC3] # ret + code2 = [0xB8, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x90, 0x90, 0x90, 0x90] # mov eax, 0; ret + code3 = [0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3] # mov eax, 1; ret + + with open(init_file, "rb") as f: + data = f.read() + + p1 = find_matches(patch_pattern1.encode(), data) + p2 = find_matches(patch_pattern2.encode(), data) + p3 = find_matches(patch_pattern3.encode(), data) + + if (len(p1) != 1) or (len(p2) != 1) or (len(p3) != 1): + print("[-] Error: Too many results returned") + exit(0) + try: + print("[*] Patching") + buffer = list(data) + do_patch(buffer, p1[0].get("start"), code1) + do_patch(buffer, p2[0].get("start"), code2) + do_patch(buffer, p3[0].get("start"), code3) + + with open("./init.patched", "wb") as f: + f.write(bytes(buffer)) + + print("[+] Patched!") + except Exception as e: + print("[-] Error: %s" % e)