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)