├── LICENSE ├── README.md ├── core ├── api_list_aws.json ├── arguments │ └── arguments.py ├── detection │ ├── googledork.py │ ├── sigma.py │ ├── vtgrep.py │ ├── yara.py │ └── yaral.py └── helper │ ├── search.py │ ├── transform.py │ └── yarastringgen.py ├── images ├── CAPICHE_Banner.png ├── CAPICHE_Example_Usage_Screenshot.png ├── CAPICHE_GoogleDorking.png ├── CAPICHE_NonInteractive_Screenshot.png ├── CAPICHE_Sigma.png ├── CAPICHE_VTGrep.png ├── CAPICHE_YARA-L.png └── CAPICHE_YARA.png ├── main.py └── requirements.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![image](./images/CAPICHE_Banner.png) 2 | 3 | # CAPICHE Detection Framework 4 | 5 | **Permiso:** [https://permiso.io](https://permiso.io/) 6 | 7 | **Read our release blog:** https://permiso.io/blog/introducing-open-source-tool-capiche 8 | 9 | **Release Date: November 2nd, 2024** 10 | 11 | **Event: BSides Chicago** 12 | 13 | ## Introduction 14 | 15 | **CAPICHE Detection Framework (Cloud API Conversion Helper Express)** is an open-source tool designed to simplify each step of the cloud API detection translation pipeline, enabling any defender to instantly create numerous styles of detection rules from groupings of APIs. With this framework defenders can dynamically assemble multiple sets of cloud APIs, translate these API names into their SDK-specific counterparts and then generate any number of final detection rule formats. 16 | 17 | A small example is shown below: 18 | ![image](./images/CAPICHE_Example_Usage_Screenshot.png) 19 | 20 | ## Key Features 21 | - **Cloud Related:** Compatible with AWS cloud provider. 22 | - **Development:** Created in Python programming language and easy to use. 23 | - **Usability:** Easy to use regular expressions for searching APIs based on name or description. 24 | - **Detection:** Different styles of detection rule formats from groupings of APIs. 25 | - **Final Formats:** YARA, YARA-L, Sigma, VirusTotal VTGrep content searching and Google Dorking. 26 | 27 | ## Tool Structure 28 | 29 | - **./core/helper/search.py:** This script provides functionality to search for APIs by name or description within the API data. 30 | - **./core/helper/transform.py:** This script transforms API lists based on the provided SDK type (boto or awscli). It reads the API data from the loaded JSON file and transforms the user's input API names accordingly. 31 | - **./core/helper/yarastringgen.py:** This script generates YARA strings based on the transformed API lists. It includes options to add variable prefix, modifiers, comments and API descriptions. 32 | - **./core/detection/yara.py:** This script generates a complete YARA rule using the provided YARA strings, rule name, author, description and condition. 33 | - **./core/detection/yaral.py:** This script generates YARA-L rules for input list of APIs. 34 | - **./core/detection/sigma.py:** This script generates Sigma rules for input list of APIs. 35 | - **./core/detection/googledork.py:** This script generates Google Dork rules for input list of APIs. 36 | - **./core/detection/vtgrep.py:** This script generates VirusTotal VTGrep rules for input list of APIs. 37 | - **./core/api_list_aws.json:** This is a JSON file containing the full list of AWS API data (API names and descriptions) for searchability functions. 38 | 39 | ## Prerequisites 40 | 41 | >```bash 42 | ># Set up a virtual environment 43 | >python3 -m venv ./venv 44 | >source venv/bin/activate 45 | > 46 | ># Install required dependencies 47 | >python3 -m pip install -r requirements.txt 48 | >``` 49 | 50 | ## Running CAPICHE 51 | CAPICHE is best run as individual modules, so we recommend running `python` or `python3` and entering into an interactive REPL (Read-Eval-Print Loop) session and importing select functions as shown below: 52 | 53 | ### General Imports: 54 | ```python 55 | # Import all functions 56 | from core.helper.transform import load_api_data, transform_api_list 57 | from core.helper.search import search_api_name, search_api_description 58 | from core.helper.yarastringgen import generate_yara_string 59 | from core.detection.yara import generate_yara_rule 60 | from core.detection.sigma import generate_sigma_rule 61 | from core.detection.vtgrep import generate_vtgrep_content 62 | from core.detection.googledork import generate_google_dork_syntax 63 | from core.detection.yaral import generate_yara_l_rule, define_events, define_associations 64 | 65 | # Optionally import some colors to brighten your day when running these demo commands :) 66 | from termcolor import colored 67 | 68 | # Import JSON list of all AWS API names and descriptions 69 | api_data = load_api_data('./core/api_list_aws.json') 70 | ``` 71 | 72 | ### Generate Transformed API Lists 73 | ```python 74 | # First let's dynamically build two lists of APIs based on regex searches against the AWS API names and descriptions. 75 | # Then we will transfrom these lists based on select SDKs to be used as input in remainder of examples. 76 | 77 | # Search for different patterns for API name 78 | api_group = search_api_name(api_data, '^(iam|sso):(Create|Delete|Update)Group$') 79 | 80 | # Search for different patterns for API description 81 | api_policy = search_api_description(api_data, '^(Create|Update|Delete).*Policy', 'iam|sso') 82 | 83 | # Specify the SDK and pass the results from search functions to transform the APIs 84 | api_group_boto = transform_api_list(api_data, "boto", api_group) 85 | api_group_awscli = transform_api_list(api_data, "awscli", api_group) 86 | api_policy_boto = transform_api_list(api_data, "boto", api_policy) 87 | api_policy_awscli = transform_api_list(api_data, "awscli", api_policy) 88 | ``` 89 | 90 | ### Generating YARA Rules: 91 | ```python 92 | # Create strings with transformed APIs and put Variable Prefix, Modifier, Comment, and the Description should be on True to show description for each API 93 | yara_strings_group = generate_yara_string(api_data, api_group_boto + api_group_awscli, "group", "wide ascii", "group", True) 94 | yara_strings_policy = generate_yara_string(api_data, api_policy_boto + api_policy_awscli, "policy", "wide ascii", "policy", False) 95 | 96 | # Generate the final YARA rule with Name, Author, Description, pass the transformed strings, Condition and some additional content for metadata 97 | final_yara_rule = generate_yara_rule("GroupAndPolicyRule", "CAPICHE", "Demo YARA rule", yara_strings_group + yara_strings_policy, "((any of $group*) and (any of $policy*))", [{"MD5": "1234567890"}]) 98 | 99 | print(colored(final_yara_rule,'green')) 100 | ``` 101 | ![image](./images/CAPICHE_YARA.png) 102 | 103 | ### Generating VTGrep Rules: 104 | ```python 105 | # Generate the VTGrep content with some extra Header, Logical Operators and content from transformed strings 106 | vt_syntax = generate_vtgrep_content("import boto3") + " AND (" + generate_vtgrep_content(api_group_boto, "OR") + " AND " + generate_vtgrep_content(api_policy_boto, "OR") + ")" 107 | 108 | print(colored(vt_syntax,'green')) 109 | ``` 110 | ![image](./images/CAPICHE_VTGrep.png) 111 | 112 | ### Generating Google Dorking Rules: 113 | ```python 114 | # Generate the Google Dorking syntax while passing the transformed strings, Logical Operator and Modifier 115 | google_dork_syntax = 'intext="import boto3" AND ' + generate_google_dork_syntax(api_group_boto, "OR", "intext") + " AND " + generate_google_dork_syntax(api_policy_boto, "OR", "intext") 116 | 117 | print(colored(google_dork_syntax,'green')) 118 | ``` 119 | ![image](./images/CAPICHE_GoogleDorking.png) 120 | 121 | ### Generating Sigma Rules: 122 | ```python 123 | # Generate the Sigma rule with a Name, Description, passing the content from JSON file, results from search functions, specify the SDK, specify what userAgent should contain 124 | sigma_rule = generate_sigma_rule("test","testing comment", api_data, api_group + api_policy,"boto", "Boto3") 125 | 126 | print(colored(sigma_rule,'green')) 127 | ``` 128 | ![image](./images/CAPICHE_Sigma.png) 129 | 130 | ### Generating YARA-L Rules: 131 | ```python 132 | # Define additional input parameters 133 | variable_prefix_associations = "testPrefix" 134 | section_associations = "metadata" 135 | type_associations = "eventname" 136 | 137 | # Pass the results from transformed APIs in events 138 | events = define_events(api_group + api_policy, variable_prefix_associations, section_associations) 139 | associations = define_associations(variable_prefix_associations, section_associations, type_associations) 140 | 141 | # Generate the YARA-L rule with Name, Description and input events and associations 142 | yara_l_rule = generate_yara_l_rule('Example', "Description of test rule", events, associations) 143 | 144 | print(colored(yara_l_rule,'green')) 145 | ``` 146 | ![image](./images/CAPICHE_YARA-L.png) 147 | 148 | ### Non-Interactive Mode: 149 | ```python 150 | # Non-interactive one-liner generation of Google Dork rule 151 | python3 ./main.py GOOGLEDORK -s create_access_key delete_access_key update_access_key -lo OR -o intext 152 | 153 | # Non-interactive one-liner generation of Sigma rule 154 | python3 ./main.py SIGMA -r RuleName -Description descriptionGoesHere -api iam:CreateAccessKey iam:DeleteAccessKey iam:UpdateAccessKey -SDK awscli -u boto3 155 | 156 | # Non-interactive one-liner generation of VirusTotal VTGrep rule 157 | python3 ./main.py VTGREP -Strings create_access_key delete_access_key update_access_key -lo OR 158 | 159 | # Non-interactive one-liner generation of YARA rule 160 | python3 ./main.py YARA -r TestYaraRule -a CAPICHE -d "OneLiner YARA Rule Example" -s create_access_key delete_access_key update_access_key -c "any of them" 161 | ``` 162 | ![image](./images/CAPICHE_NonInteractive_Screenshot.png) 163 | 164 | ### Notes 165 | - Search functions support regular expressions against the API description or the combined EventSource:EventName syntax (e.g. `iam:CreateAccessKey`). 166 | - The currently supported SDK specifications are `boto` and `awscli`. 167 | - This release version contains only the AWS APIs. 168 | 169 | ## Contributing 170 | Contributions are welcome! 171 | Feel free to reach out for any questions or feedback. -------------------------------------------------------------------------------- /core/arguments/arguments.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def parseargs(): 4 | parser = argparse.ArgumentParser( 5 | prog='CAPICHE Detection Framework (Cloud API Conversion Helper Express)', 6 | description='A tool designed to simplify each step of the cloud API detection translation pipeline, enabling any defender to instantly create numerous styles of detection rules from groupings of APIs.', 7 | epilog='Thank you for using CAPICHE!') 8 | subparsers = parser.add_subparsers(dest='usage') 9 | 10 | ##GOOGLEDORK 11 | google_dork_parser = subparsers.add_parser('GOOGLEDORK', help='Generate Google Dork rule') 12 | google_dork_parser.add_argument("-s", "-Strings", nargs='+', required=True, help="Strings for Google Dork rule") 13 | google_dork_parser.add_argument("-lo", "-LogicalOperator", required=True, help="Logical operator to apply between each input string for Google Dork rule") 14 | google_dork_parser.add_argument("-o", "-Operator", required=True, help="Google Dork operator to be applied (as prefix) to each input string for Google Dork rule") 15 | 16 | ##SIGMA 17 | sigma_parser = subparsers.add_parser('SIGMA', help='Generate Sigma rule') 18 | sigma_parser.add_argument("-r", "-RuleName", required=True, help="Rule name") 19 | sigma_parser.add_argument("-d", "-Description", required=True, help="Description for the Sigma rule") 20 | sigma_parser.add_argument("-api", "-APIList", nargs='+', required=True, help="List of APIs in the format 'EventSource:EventName'") 21 | sigma_parser.add_argument("-s", "-SDK", required=True, choices=['boto', 'awscli'], help="SDK type (boto or awscli)") 22 | sigma_parser.add_argument("-ua", "-UserAgent", required=True, help="User agent to match the selected SDK") 23 | 24 | ##VTGREP 25 | vtgrep_parser = subparsers.add_parser('VTGREP', help='Generate VTGrep rule') 26 | vtgrep_parser.add_argument("-s", "-Strings", nargs='+', required=True, help="Strings for VTGrep rule") 27 | vtgrep_parser.add_argument("-lo", "-LogicalOperator", required=True, help="Logical operator to apply between each input string for VTGrep rule") 28 | 29 | ##YARA 30 | yara_parser = subparsers.add_parser('YARA', help='Generate YARA rule') 31 | yara_parser.add_argument("-r", "-RuleName", required=True, help="Rule name") 32 | yara_parser.add_argument("-a", "-MetaAuthor", required=True, help="Author for the YARA rule") 33 | yara_parser.add_argument("-d", "-MetaDescription", required=True, help="Description for the YARA rule") 34 | yara_parser.add_argument("-s", "-Strings", nargs='+', required=True, help="List of YARA strings") 35 | yara_parser.add_argument("-c", "-Condition", required=True, help="Condition for the YARA rule") 36 | yara_parser.add_argument("-dd", "-MetaDynamicDictionary", nargs='+', help="Dynamic metadata for the YARA rule") 37 | 38 | ##YARA-L 39 | yara_l_parser = subparsers.add_parser('YARAL', help='Generate YARA-L rule') 40 | yara_l_parser.add_argument("-r", "-RuleName", required=True, help="Rule name") 41 | yara_l_parser.add_argument("-d", "-GeneralDescription", required=True, help="Description of the YARA-L rule") 42 | yara_l_parser.add_argument("-e", "-Events", required=True, help="Events for the YARA-L rule") 43 | yara_l_parser.add_argument("-a", "-Associations", nargs='+', required=True, help="Associations for the YARA-L rule") 44 | yara_l_parser.add_argument("-m", "-Match", required=True, help="Match logic for the YARA-L rule") 45 | yara_l_parser.add_argument("-c", "-Condition", nargs='+', help="Condition logic for the YARA-L rule") 46 | 47 | return parser.parse_args() -------------------------------------------------------------------------------- /core/detection/googledork.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | 4 | def generate_google_dork_syntax(strings, logical_operator="", operator=""): 5 | if logical_operator not in {"OR", "AND", ""}: 6 | raise ValueError("Logical operator must be 'OR', 'AND' or an empty string.") 7 | 8 | if isinstance(strings, str): 9 | return f'{operator}:"{strings}"' 10 | 11 | dork_syntax = "" 12 | 13 | for string in strings: 14 | 15 | match = re.search(r'"([^"]+)"', string) 16 | api_name = match.group(1) if match else string 17 | api_name = api_name.split(":")[-1] 18 | 19 | dork_syntax += f'{operator}:"{api_name}" ' 20 | if logical_operator: 21 | dork_syntax += f"{logical_operator} " 22 | 23 | dork_syntax = dork_syntax[:-len(logical_operator) - 1] 24 | 25 | return f"({dork_syntax.strip()})" 26 | 27 | 28 | if __name__ == "__main__": 29 | parser = argparse.ArgumentParser(description="Generate Google Dork rule") 30 | parser.add_argument("-s", "-Strings", nargs='+', required=True, help="Strings for Google Dork rule") 31 | parser.add_argument("-lo", "-LogicalOperator", required=True, help="Logical operator to apply between each input string for Google Dork rule") 32 | parser.add_argument("-o", "-Operator", required=True, help="Google Dork operator to be applied (as prefix) to each input string for Google Dork rule") 33 | args = parser.parse_args() 34 | 35 | google_dork_rule = generate_google_dork_syntax(args.Strings, args.LogicalOperator, args.Operator) 36 | print(google_dork_rule) -------------------------------------------------------------------------------- /core/detection/sigma.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from datetime import datetime 3 | from core.helper.transform import load_api_data,transform_api_list 4 | 5 | def generate_sigma_rule(rule_name, description, api_data, api_list, sdk, useragent): 6 | transformed_api_list = transform_api_list(api_data, sdk, api_list) 7 | 8 | event_names = [api.split(':')[1] for api in transformed_api_list] 9 | event_sources = set(api.split(':')[0] + ".amazonaws.com" for api in transformed_api_list) 10 | 11 | if not event_names: 12 | raise ValueError("No matching event names found in the provided API data.") 13 | 14 | logsource_product = "aws" 15 | logsource_service = "cloudtrail" 16 | 17 | sigma_rule = { 18 | "title": rule_name, 19 | "id": None, 20 | "status": None, 21 | "description": description, 22 | "references": None, 23 | "author": "CAPICHE", 24 | "date": datetime.now().strftime("%Y/%m/%d"), 25 | "tags": None, 26 | "logsource": { 27 | "product": logsource_product, 28 | "service": logsource_service 29 | }, 30 | "detection": { 31 | "selection": { 32 | "eventSource": list(event_sources), 33 | "eventName": event_names, 34 | "userAgent|contains": useragent 35 | }, 36 | "condition": "selection" 37 | }, 38 | "falsepositives": [ 39 | "Valid usage" 40 | ], 41 | "level": "medium" 42 | } 43 | 44 | newline = "\n" 45 | yaml_output = f""" 46 | title: {sigma_rule['title']} 47 | description: {sigma_rule['description']} 48 | author: {sigma_rule['author']} 49 | date: {sigma_rule['date']} 50 | logsource: 51 | product: {sigma_rule['logsource']['product']} 52 | service: {sigma_rule['logsource']['service']} 53 | detection: 54 | selection: 55 | eventSource: 56 | - {f"{newline} - ".join([f"'{source}'" for source in sigma_rule['detection']['selection']['eventSource']])} 57 | eventName: 58 | - {f"{newline} - ".join([f"'{name}'" for name in sigma_rule['detection']['selection']['eventName']])} 59 | userAgent|contains: '{sigma_rule['detection']['selection']['userAgent|contains']}' 60 | condition: {sigma_rule['detection']['condition']} 61 | falsepositives: 62 | - {sigma_rule['falsepositives'][0]} 63 | level: {sigma_rule['level']} 64 | """ 65 | 66 | if sigma_rule['id']: 67 | yaml_output += f"id: {sigma_rule['id']}\n" 68 | 69 | if sigma_rule['status']: 70 | yaml_output += f"status: {sigma_rule['status']}\n" 71 | 72 | if sigma_rule['references']: 73 | yaml_output += f"references: {sigma_rule['references']}\n" 74 | 75 | if sigma_rule['tags']: 76 | yaml_output += f"tags: {sigma_rule['tags']}\n" 77 | 78 | return yaml_output.strip() 79 | 80 | 81 | if __name__ == "__main__": 82 | parser = argparse.ArgumentParser(description="Generate Sigma rule") 83 | parser.add_argument("-r", "-RuleName", required=True, help="Rule name") 84 | parser.add_argument("-d", "-Description", required=True, help="Description for the Sigma rule") 85 | parser.add_argument("-api", "-APIList", nargs='+', required=True, help="List of APIs in the format 'EventSource:EventName'") 86 | parser.add_argument("-s", "-SDK", required=True, choices=['boto', 'awscli'], help="SDK type (boto or awscli)") 87 | parser.add_argument("-ua", "-UserAgent", required=True, help="User agent to match the selected SDK") 88 | args = parser.parse_args() 89 | 90 | api_data = load_api_data('./core/api_list_aws.json') 91 | 92 | sigma_rule = generate_sigma_rule(args.RuleName, args.Description, api_data, args.APIList, args.SDK, args.UserAgent) 93 | print(sigma_rule) -------------------------------------------------------------------------------- /core/detection/vtgrep.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | 4 | def generate_vtgrep_content(strings, logical_operator=""): 5 | if isinstance(strings, str): 6 | return f'content:"{strings}"' 7 | 8 | if not isinstance(strings, list): 9 | raise ValueError("Input must be a list of strings or a single string.") 10 | 11 | if logical_operator not in {"OR", "AND"}: 12 | raise ValueError("Logical operator must be 'OR' or 'AND'.") 13 | 14 | content_strings = [] 15 | 16 | for string in strings: 17 | 18 | match = re.search(r'"([^"]+)"', string) 19 | api_name = match.group(1) if match else string 20 | api_name = api_name.split(":")[-1] 21 | 22 | content_strings.append(f'content:"{api_name}"') 23 | 24 | vt_syntax = f" {logical_operator} ".join(content_strings) 25 | 26 | if not content_strings: 27 | return "" 28 | 29 | return f"({vt_syntax})" 30 | 31 | 32 | if __name__ == "__main__": 33 | parser = argparse.ArgumentParser(description="Generate VTGrep rule") 34 | parser.add_argument("-s", "-Strings", nargs='+', required=True, help="Strings for VTGrep rule") 35 | parser.add_argument("-lo", "-LogicalOperator", required=True, help="Logical operator to apply between each input string for VTGrep rule") 36 | args = parser.parse_args() 37 | 38 | vtgrep_rule = generate_vtgrep_content(args.Strings, args.LogicalOperator) 39 | print(vtgrep_rule) -------------------------------------------------------------------------------- /core/detection/yara.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def generate_yara_rule(rule_name, meta_author, meta_description, strings, condition, meta_dynamic_dictionary=None): 4 | yara_rule = f'rule {rule_name}\n{{\n' 5 | 6 | yara_rule += ' meta:\n' 7 | yara_rule += f' author = "{meta_author}"\n' 8 | yara_rule += f' description = "{meta_description}"\n' 9 | 10 | if meta_dynamic_dictionary: 11 | for item in meta_dynamic_dictionary: 12 | for key, value in item.items(): 13 | yara_rule += f' {key} = "{value}"\n' 14 | 15 | yara_rule += ' strings:\n' 16 | for yara_string in strings: 17 | yara_rule += f' {yara_string}\n' 18 | 19 | yara_rule += f' condition:\n {condition}\n}}' 20 | 21 | return yara_rule 22 | 23 | 24 | if __name__ == "__main__": 25 | parser = argparse.ArgumentParser(description="Generate YARA rule") 26 | parser.add_argument("-r", "-RuleName", required=True, help="Rule name") 27 | parser.add_argument("-a", "-MetaAuthor", required=True, help="Author for the YARA rule") 28 | parser.add_argument("-d", "-MetaDescription", required=True, help="Description for the YARA rule") 29 | parser.add_argument("-s", "-Strings", nargs='+', required=True, help="List of YARA strings") 30 | parser.add_argument("-c", "-Condition", required=True, help="Condition for the YARA rule") 31 | parser.add_argument("-dd", "-MetaDynamicDictionary", nargs='+', help="Dynamic metadata for the YARA rule") 32 | args = parser.parse_args() 33 | 34 | yara_rule = generate_yara_rule(args.RuleName, args.MetaAuthor, args.MetaDescription, args.Strings, args.Condition, args.MetaDynamicDictionary) 35 | print(yara_rule) -------------------------------------------------------------------------------- /core/detection/yaral.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def generate_yara_l_rule(rule_name, general_description, events, associations, match='all over 5m', condition='all of them'): 4 | yara_l_rule = f'rule {rule_name}\n{{\n' 5 | 6 | yara_l_rule += ' meta:\n' 7 | yara_l_rule += f' description = "{general_description}"\n' 8 | 9 | yara_l_rule += ' events:\n' 10 | for key, value in events.items(): 11 | yara_l_rule += f' {key} = "{value}"\n' 12 | 13 | yara_l_rule += ' associations:\n' 14 | for association in associations: 15 | yara_l_rule += f' {association}\n' 16 | 17 | yara_l_rule += f' match:\n {match}\n' 18 | yara_l_rule += f' condition:\n {condition}\n}}' 19 | 20 | return yara_l_rule 21 | 22 | 23 | def define_events(api_events, variable_prefix_associations, section_associations): 24 | events = {} 25 | for i, api in enumerate(api_events): 26 | event_parts = api.split(":") 27 | event_name_key = f'${variable_prefix_associations}.{section_associations}.eventName_{i + 1}' 28 | event_source_key = f'${variable_prefix_associations}.{section_associations}.eventSource_{i + 1}' 29 | events[event_name_key] = event_parts[1] 30 | events[event_source_key] = event_parts[0] 31 | return events 32 | 33 | 34 | def define_associations(variable_prefix_associations, section_associations, type_associations): 35 | associations = [] 36 | associations.append(f'${variable_prefix_associations}.{section_associations}.{type_associations} = ${type_associations}') 37 | return associations 38 | 39 | 40 | if __name__ == "__main__": 41 | parser = argparse.ArgumentParser(description="Generate YARA-L rule") 42 | parser.add_argument("-r", "-RuleName", required=True, help="Rule name") 43 | parser.add_argument("-d", "-GeneralDescription", required=True, help="Description of the YARA-L rule") 44 | parser.add_argument("-e", "-Events", required=True, help="Events for the YARA-L rule") 45 | parser.add_argument("-a", "-Associations", nargs='+', required=True, help="Associations for the YARA-L rule") 46 | parser.add_argument("-m", "-Match", required=True, help="Match logic for the YARA-L rule") 47 | parser.add_argument("-c", "-Condition", nargs='+', help="Condition logic for the YARA-L rule") 48 | args = parser.parse_args() 49 | 50 | yara_l_rule = generate_yara_l_rule(args.RuleName, args.GeneralDescription, args.Events, args.Associations, args.Match, args.Condition) 51 | print(yara_l_rule) -------------------------------------------------------------------------------- /core/helper/search.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def search_api_name(api_data, pattern): 4 | matched_apis = [] 5 | pattern = re.compile(pattern, re.IGNORECASE) 6 | for api in api_data: 7 | if pattern.search(api['API']): 8 | matched_apis.append(api['API']) 9 | return matched_apis 10 | 11 | 12 | def search_api_description(api_data, pattern, event_sources=None): 13 | matched_apis = [] 14 | pattern = re.compile(pattern, re.IGNORECASE) 15 | 16 | if event_sources is None: 17 | event_sources = [None] 18 | 19 | for api in api_data: 20 | if (pattern.search(api['Description']) 21 | and (event_sources is None or api['EventSource'] in event_sources)): 22 | matched_apis.append(api['EventSource'] + ":" + api['EventName']) 23 | 24 | return matched_apis -------------------------------------------------------------------------------- /core/helper/transform.py: -------------------------------------------------------------------------------- 1 | import json 2 | import argparse 3 | import re 4 | 5 | def load_api_data(file_path): 6 | with open(file_path, 'r') as f: 7 | return json.load(f) 8 | 9 | 10 | def transform_api_list(api_data, sdk: str, matched_apis): 11 | if isinstance(matched_apis, str): 12 | matched_apis = [matched_apis] 13 | 14 | if not isinstance(matched_apis, list): 15 | raise ValueError("Invalid format for matched APIs. Expected string or list.") 16 | 17 | transformed_api_list = [] 18 | 19 | for api_info in api_data: 20 | api_key = api_info['EventSource'] + ':' + api_info['EventName'] 21 | if api_key in matched_apis: 22 | service = api_info['EventSource'] 23 | api_name = api_info['EventName'] 24 | 25 | transformed_api_name = re.sub(r'(?<=[a-z])([A-Z])', r'_\1', api_name).lower().lstrip('_') 26 | 27 | if sdk.lower() == 'boto': 28 | transformed_api = transformed_api_name 29 | elif sdk.lower() == 'awscli': 30 | transformed_api = transformed_api_name.replace('_', '-') 31 | else: 32 | raise ValueError("Invalid SDK type. Supported values are 'boto' or 'awscli'.") 33 | 34 | transformed_api_list.append(f"{service}:{transformed_api}") 35 | 36 | return transformed_api_list 37 | 38 | 39 | if __name__ == "__main__": 40 | parser = argparse.ArgumentParser(description="Transform API list") 41 | parser.add_argument("-SDK", required=True, choices=['boto', 'awscli'], help="SDK type (boto or awscli)") 42 | parser.add_argument("-MatchedAPIs", nargs='+', required=True, help="List of matched APIs in the format 'EventSource:EventName' or directly provided API names") 43 | args = parser.parse_args() 44 | 45 | api_data = load_api_data('./core/api_list_aws.json') 46 | 47 | api_result = transform_api_list(api_data, args.SDK, args.MatchedAPIs) 48 | print(json.dumps(api_result)) 49 | -------------------------------------------------------------------------------- /core/helper/yarastringgen.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | from core.helper.transform import load_api_data,transform_api_list 4 | 5 | def generate_yara_string(api_data, strings, variable_prefix, modifiers, comment, comment_api_description): 6 | yara_strings = [] 7 | 8 | for i, api in enumerate(strings, start=1): 9 | for info in api_data: 10 | original_api_name = info['EventSource'] + ':' + info['EventName'] 11 | transformed_api_name = info['EventSource'] + ':' + re.sub(r'(?<=[a-z])([A-Z])', r'_\1', info['EventName']).lower().lstrip('_') 12 | if transformed_api_name == api or original_api_name == api or transformed_api_name.replace('_', '-') == api: 13 | description = info['Description'] 14 | break 15 | else: 16 | description = "No description available" 17 | 18 | api_name = api.split(":")[-1] 19 | 20 | variable_name = f'{variable_prefix}_{i:02}' 21 | yara_comment = f"{comment} - API Description: {description}" if comment_api_description else comment 22 | yara_string = f"${variable_name} = \"{api_name}\" {modifiers} // {yara_comment}" 23 | yara_strings.append(yara_string) 24 | 25 | return yara_strings 26 | 27 | 28 | if __name__ == "__main__": 29 | parser = argparse.ArgumentParser(description="Generate YARA strings") 30 | parser.add_argument("-SDK", required=True, help="SDK type (boto or awscli)") 31 | parser.add_argument("-APIList", nargs='+', required=True, help="List of APIs") 32 | parser.add_argument("-VariablePrefix", required=True, help="Variable prefix") 33 | parser.add_argument("-Modifiers", required=True, help="YARA string modifiers") 34 | parser.add_argument("-Comment", required=True, help="Comment for the YARA strings") 35 | parser.add_argument("-CommentApiDescription", action="store_true", help="Include API description in comments") 36 | args = parser.parse_args() 37 | 38 | api_data = load_api_data('api_list_aws.json') 39 | 40 | api_result = transform_api_list(args.SDK, args.APIList) 41 | yara_strings = generate_yara_string(api_data, api_result, args.VariablePrefix, args.Modifiers, args.Comment, args.CommentApiDescription) 42 | 43 | for yara_string in yara_strings: 44 | print(yara_string) 45 | -------------------------------------------------------------------------------- /images/CAPICHE_Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_Banner.png -------------------------------------------------------------------------------- /images/CAPICHE_Example_Usage_Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_Example_Usage_Screenshot.png -------------------------------------------------------------------------------- /images/CAPICHE_GoogleDorking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_GoogleDorking.png -------------------------------------------------------------------------------- /images/CAPICHE_NonInteractive_Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_NonInteractive_Screenshot.png -------------------------------------------------------------------------------- /images/CAPICHE_Sigma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_Sigma.png -------------------------------------------------------------------------------- /images/CAPICHE_VTGrep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_VTGrep.png -------------------------------------------------------------------------------- /images/CAPICHE_YARA-L.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_YARA-L.png -------------------------------------------------------------------------------- /images/CAPICHE_YARA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Permiso-io-tools/capiche/1528bf6a20da4d8cb181d49829dc5444dc9fdebe/images/CAPICHE_YARA.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # Import all functions 2 | from core.helper.transform import load_api_data, transform_api_list 3 | from core.helper.search import search_api_name, search_api_description 4 | from core.helper.yarastringgen import generate_yara_string 5 | from core.detection.yara import generate_yara_rule 6 | from core.detection.sigma import generate_sigma_rule 7 | from core.detection.vtgrep import generate_vtgrep_content 8 | from core.detection.googledork import generate_google_dork_syntax 9 | from core.detection.yaral import generate_yara_l_rule, define_events, define_associations 10 | from core.arguments.arguments import parseargs 11 | 12 | # Import JSON list of all AWS API names and descriptions 13 | api_data = load_api_data('./core/api_list_aws.json') 14 | 15 | args = parseargs() 16 | 17 | if __name__ == '__main__': 18 | 19 | if args.usage == "GOOGLEDORK": 20 | google_dork_rule = generate_google_dork_syntax(args.s, args.lo, args.o) 21 | print(google_dork_rule) 22 | 23 | elif args.usage == "SIGMA": 24 | sigma_rule = generate_sigma_rule(args.r, args.d, api_data, args.api, args.s, args.ua) 25 | print(sigma_rule) 26 | 27 | elif args.usage == "VTGREP": 28 | vtgrep_rule = generate_vtgrep_content(args.s, args.lo) 29 | print(vtgrep_rule) 30 | 31 | elif args.usage == "YARA": 32 | yara_rule = generate_yara_rule(args.r, args.a, args.d, args.s, args.c, args.dd) 33 | print(yara_rule) 34 | 35 | elif args.usage == "YARAL": 36 | yara_l_rule = generate_yara_l_rule(args.r, args.d, args.e, args.a, args.m, args.c) 37 | print(yara_l_rule) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests~=2.31.0 2 | urllib3==2.2.1 3 | pip==23.2.1 4 | certifi==2024.2.2 5 | charset-normalizer==3.3.2 6 | idna==3.7 7 | argparse --------------------------------------------------------------------------------