├── README.md ├── upload_yara_rule.py ├── yaraify_check_taskid.py ├── yaraify_list_tasks.py ├── yaraify_lookup_hash.py ├── yaraify_lookup_yara-rule.py ├── yaraify_rescan.py └── yaraify_submit.py /README.md: -------------------------------------------------------------------------------- 1 | # YARAify 2 | YARAify is an open YARA scan- and search engine. This repository provides some sample python3 scripts on how to interact with the YARAify API. 3 | 4 | ## Obtain an Auth-Key 5 | In order to query the YARA API, you need to obtain an ```Auth-Key```. If you don't have an Auth-Key yet, you can get one at https://auth.abuse.ch/ for free. 6 | 7 | ## Submit a file for YARA scanning 8 | This script calls the [scan endpoint](https://yaraify.abuse.ch/api/#file-scan) to scan a file using the YARAify service. 9 | 10 | ``` 11 | python3 yaraify_submit.py 12 | ``` 13 | 14 | Pro tip: Before you start using our YARA scan engine, we recommend you to have a look at the various paramenters you can use to customize your submission: 15 | 16 | https://yaraify.abuse.ch/api/#file-scan 17 | 18 | ## Lookup file hash (MD5, sha256, SHA1 or SHA3-384 hash) 19 | This scripts calls the [hash lookup endpoint](https://yaraify.abuse.ch/api/#query-filehash) to lookup up a hash (MD5, SHA256, SHA1 or SHA3-384 hash) in the YARAify database: 20 | 21 | ``` 22 | python3 yaraify_lookup_hash.py 97e8b8205a9be4ddbed90d7c354a58aab170c15e458564baee9f50e17ca79649 23 | ``` 24 | 25 | ## Lookup YARA rule matches 26 | This script calls the [YARA lookup endpoint](https://yaraify.abuse.ch/api/#yara) to get recent files matching a specific YARA rule. 27 | 28 | ``` 29 | python3 yaraify_lookup_yara-rule.py CobaltStrikeBeacon 50 30 | ``` 31 | 32 | ## Lookup a task ID 33 | This script calls the [task ID endpoint](https://yaraify.abuse.ch/api/#query-taskid), looking up a particular task ID in the YARAify databasel: 34 | 35 | ``` 36 | python3 yaraify_check_taskid.py cfa49f83-6d98-11ed-a71a-42010aa4000b 37 | ``` 38 | 39 | ## List tasks for an identifier 40 | YARAify provides you an easy way to track your YARA scans without the need of e.g. signing up for an account: identifiers! Before you start using identifiers to keep track of your submissions, we recommend you to have a look at the corresponding documentation: 41 | 42 | https://yaraify.abuse.ch/api/#identifiers 43 | 44 | Once you created your own (private) identifier and submited files to the YARAify YARA scan engine, you can use this script to get a list of recent tasks. If you e.g. want to get a list of queued tasks associated with your identifier, you can use the script as follow: 45 | 46 | ``` 47 | python3 yaraify_list_tasks.py queued 48 | ``` 49 | 50 | 51 | ## Trigger a rescan of a file 52 | This script calls the endpoint for [file rescan](https://yaraify.abuse.ch/api/#rescan) to trigger an immediate rescan of a file. 53 | 54 | ``` 55 | python3 yaraify_rescan.py 56 | ``` 57 | 58 | ## Deploy a YARA rule for live hunting 59 | This script calls the endpoint for [deploying a YARA rule](https://yaraify.abuse.ch/api/#deploy-yara) to conduct live hunting on YARAify with your own rule. 60 | 61 | ``` 62 | python3 upload_yara_rule.py 63 | ``` 64 | 65 | ## API documentation 66 | 67 | The documentation for the YARAify API is available here: 68 | 69 | https://yaraify.abuse.ch/api/ 70 | -------------------------------------------------------------------------------- /upload_yara_rule.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | import sys 4 | import json 5 | 6 | if len(sys.argv) < 3: 7 | print("Upload a YARA rule to YARAify for live hunting") 8 | print("Usage: python3 upload_yara_rule.py ") 9 | print("Note: If you don't have an Auth-Key yet, you can obtain one at https://auth.abuse.ch/") 10 | quit() 11 | 12 | headers = { 13 | "Auth-Key" : sys.argv[1] 14 | } 15 | 16 | files = { 17 | 'yara_file': (open(sys.argv[2],'rb')) 18 | } 19 | 20 | response = requests.post('https://yaraify-api.abuse.ch/api/v1/', files=files, verify=True, headers=headers) 21 | print(response.content.decode("utf-8", "ignore")) 22 | -------------------------------------------------------------------------------- /yaraify_check_taskid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import requests 3 | import urllib3 4 | import json 5 | import sys 6 | 7 | if len(sys.argv) < 3: 8 | print("Query YARAify for a task ID") 9 | print("Usage: python3 yaraifier_check_taskid.py ") 10 | print("Note: If you don't have an Auth-Key yet, you can obtain one at https://auth.abuse.ch/") 11 | quit() 12 | 13 | headers = { 14 | "Auth-Key" : sys.argv[1] 15 | } 16 | 17 | pool = urllib3.HTTPSConnectionPool('yaraify-api.abuse.ch', port=443, maxsize=50, headers=headers) 18 | 19 | data = { 20 | 'query': 'get_results', 21 | 'malpedia-token': 'xxx', # Your malpedia token. If you don't have any, simply obmit this field or keep it empty 22 | 'task_id': sys.argv[2] 23 | } 24 | json_data = json.dumps(data) 25 | response = pool.request("POST", "/api/v1/", body=json_data) 26 | response = response.data.decode("utf-8", "ignore") 27 | print(response) 28 | -------------------------------------------------------------------------------- /yaraify_list_tasks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import requests 3 | import urllib3 4 | import json 5 | import sys 6 | 7 | if len(sys.argv) < 4: 8 | print("Lists YARAify tasks associated with a specific identifier") 9 | print("Usage: python3 yaraify_list_tasks.py ") 10 | print("Note: If you don't have an Auth-Key yet, you can obtain one at https://auth.abuse.ch/") 11 | quit() 12 | 13 | headers = { 14 | "Auth-Key" : sys.argv[1] 15 | } 16 | 17 | pool = urllib3.HTTPSConnectionPool('yaraify-api.abuse.ch', port=443, maxsize=50, headers=headers) 18 | 19 | data = { 20 | 'query': 'list_tasks', 21 | 'identifier': sys.argv[2], 22 | 'task_status': sys.argv[3] 23 | } 24 | json_data = json.dumps(data) 25 | response = pool.request("POST", "/api/v1/", body=json_data) 26 | response = response.data.decode("utf-8", "ignore") 27 | print(response) 28 | -------------------------------------------------------------------------------- /yaraify_lookup_hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import requests 3 | import urllib3 4 | import json 5 | import sys 6 | 7 | if len(sys.argv) < 3: 8 | print("Looks up a file hash on YARAify") 9 | print("Usage: python3 yaraify_lookup_hash.py ") 10 | print("Note: If you don't have an Auth-Key yet, you can obtain one at https://auth.abuse.ch/") 11 | quit() 12 | 13 | headers = { 14 | "Auth-Key" : sys.argv[1] 15 | } 16 | 17 | pool = urllib3.HTTPSConnectionPool('yaraify-api.abuse.ch', port=443, maxsize=50, headers=headers) 18 | 19 | data = { 20 | 'query': 'lookup_hash', 21 | 'malpedia-token': 'xxx', # Your malpedia token. If you don't have any, simply obmit this field or keep it empty 22 | 'search_term': sys.argv[2] 23 | } 24 | json_data = json.dumps(data) 25 | response = pool.request("POST", "/api/v1/", body=json_data) 26 | response = response.data.decode("utf-8", "ignore") 27 | print(response) 28 | -------------------------------------------------------------------------------- /yaraify_lookup_yara-rule.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import requests 3 | import urllib3 4 | import json 5 | import sys 6 | 7 | if len(sys.argv) < 4: 8 | print("Script to query YARAify for files matching a particular YARA rule") 9 | print("Usage: python3 yaraify_lookup_yara-rule.py ") 10 | print("Note: If you don't have an Auth-Key yet, you can obtain one at https://auth.abuse.ch/") 11 | quit() 12 | 13 | headers = { 14 | "Auth-Key" : sys.argv[1] 15 | } 16 | 17 | pool = urllib3.HTTPSConnectionPool('yaraify-api.abuse.ch', port=443, maxsize=50, headers=headers) 18 | 19 | data = { 20 | 'query': 'get_yara', 21 | 'search_term': sys.argv[2], 22 | 'result_max': sys.argv[3] 23 | } 24 | json_data = json.dumps(data) 25 | response = pool.request("POST", "/api/v1/", body=json_data) 26 | response = response.data.decode("utf-8", "ignore") 27 | print(response) 28 | -------------------------------------------------------------------------------- /yaraify_rescan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import logging 3 | import requests 4 | import urllib3 5 | import json 6 | import time 7 | import sys 8 | 9 | 10 | if len(sys.argv) < 2: 11 | print("Trigger a re-scan of a file") 12 | print("Usage: python3 yaraify_rescan.py ") 13 | print("Note: hash can be a MD5, SHA1, SHA256 or SHA3-384 hash") 14 | quit() 15 | 16 | logging.basicConfig(level=logging.INFO,format='%(asctime)s %(levelname)-8s %(message)s') 17 | headers = { 18 | "Auth-Key" : "YOUR-AUTH-KEY-HERE" # You can get one here: https://auth.abuse.ch/user/me 19 | } 20 | pool = urllib3.HTTPSConnectionPool('yaraify-api.abuse.ch', port=443, maxsize=50, headers=headers) 21 | 22 | 23 | def rescan_yaraify(hash): 24 | data = { 25 | 'query': 'rescan_file', 26 | 'hash': hash 27 | } 28 | json_data = json.dumps(data) 29 | response = pool.request("POST", "/api/v1/", body=json_data) 30 | response = response.data.decode("utf-8", "ignore") 31 | data = json.loads(response) 32 | logging.info(f"{hash} queued for re-scan with task ID {data['data']['task_id']}") 33 | return data['data']['task_id'] 34 | 35 | def check_taskid(task_id): 36 | data = { 37 | 'query': 'get_results', 38 | 'malpedia-token': 'xxx', # Your malpedia token. If you don't have any, simply obmit this field or keep it empty 39 | 'task_id': task_id 40 | } 41 | json_data = json.dumps(data) 42 | response = pool.request("POST", "/api/v1/", body=json_data) 43 | response = response.data.decode("utf-8", "ignore") 44 | return json.loads(response) 45 | 46 | 47 | 48 | task_id = rescan_yaraify(sys.argv[1]) # hash 49 | while True: 50 | data = check_taskid(task_id) 51 | if data['data'] == 'queued': 52 | logging.info(f"{task_id} is queued") 53 | time.sleep(1) 54 | continue 55 | logging.info(f"{task_id} is done") 56 | print(json.dumps(data['data'], indent=2)) 57 | break 58 | -------------------------------------------------------------------------------- /yaraify_submit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | import sys 4 | import json 5 | 6 | if len(sys.argv) > 1: 7 | file = sys.argv[1] 8 | else: 9 | print("Usage: python3 yaraifier_submit.py ") 10 | quit() 11 | 12 | data = { 13 | 'clamav_scan': 1, 14 | 'unpack': 0, 15 | 'skip_known': 0, 16 | } 17 | files = { 18 | 'json_data': (None, json.dumps(data), 'application/json'), 19 | 'file': (open(file,'rb')) 20 | } 21 | response = requests.post('https://yaraify-api.abuse.ch/api/v1/', files=files, verify=True) 22 | print(response.content.decode("utf-8", "ignore")) 23 | --------------------------------------------------------------------------------