- 更新apktool为最新版本

- 优化部分环节流程
- 修复excle文件导出时超时行数限制
- 修复脚本执行时卡顿的问题
- 修复Mac下Playload文件权限不足的问题
v1.0.09 V1.0.9_Releases
kelvinBen 2 years ago
parent 51ca5a0c5b
commit 827ef29e65
  1. 40
      app.py
  2. 142
      config.py
  3. 5
      libs/core/__init__.py
  4. 35
      libs/core/download.py
  5. 62
      libs/core/net.py
  6. 92
      libs/core/parses.py
  7. 103
      libs/task/android_task.py
  8. 94
      libs/task/base_task.py
  9. 24
      libs/task/download_task.py
  10. 108
      libs/task/ios_task.py
  11. 86
      libs/task/net_task.py
  12. 23
      libs/task/web_task.py
  13. 2
      requirements.txt
  14. BIN
      tools/apktool.jar
  15. 7
      update.md

@ -8,21 +8,24 @@ import click
from libs.core import Bootstrapper
from libs.task.base_task import BaseTask
@click.group(help="Python script for automatically retrieving key information in app.")
def cli():
pass
# 创建Android任务
@cli.command(help="Get the key information of Android system.")
@click.option("-i", "--inputs", required=True, type=str, help="Please enter the APK file or DEX file to be scanned or the corresponding APK download address.")
@click.option("-r", "--rules", required=False, type=str, default="", help="Please enter a rule for temporary scanning of file contents.")
@click.option("-s", "--sniffer", is_flag=True, default=False, help="Enable the network sniffer function. It is on by default.")
@click.option("-n", '--no-resource', is_flag=True, default=False,help="Ignore all resource files, including network sniffing. It is not enabled by default.")
@click.option("-a", '--all',is_flag=True, default=False,help="Output the string content that conforms to the scan rules.It is on by default.")
@click.option("-t", '--threads',required=False, type=int,default=10,help="Set the number of concurrency. The larger the concurrency, the faster the speed. The default value is 10.")
@click.option("-o", '--output',required=False, type=str,default=None,help="Specify the result set output directory.")
@click.option("-p", '--package',required=False,type=str,default="",help="Specifies the package name information that needs to be scanned.")
def android(inputs: str, rules: str, sniffer: bool, no_resource:bool, all:bool, threads:int, output, package:str) -> None:
@click.option("-n", '--no-resource', is_flag=True, default=False, help="Ignore all resource files, including network sniffing. It is not enabled by default.")
@click.option("-a", '--all', is_flag=True, default=False, help="Output the string content that conforms to the scan rules.It is on by default.")
@click.option("-t", '--threads', required=False, type=int, default=10, help="Set the number of concurrency. The larger the concurrency, the faster the speed. The default value is 10.")
@click.option("-o", '--output', required=False, type=str, default=None, help="Specify the result set output directory.")
@click.option("-p", '--package', required=False, type=str, default="", help="Specifies the package name information that needs to be scanned.")
def android(inputs: str, rules: str, sniffer: bool, no_resource: bool, all: bool, threads: int, output, package: str) -> None:
try:
bootstrapper = Bootstrapper(__file__, output, all, no_resource)
bootstrapper.init()
@ -31,15 +34,16 @@ def android(inputs: str, rules: str, sniffer: bool, no_resource:bool, all:bool,
except Exception as e:
raise e
@cli.command(help="Get the key information of iOS system.")
@click.option("-i", "--inputs", required=True, type=str, help="Please enter IPA file or ELF file to scan or corresponding IPA download address. App store is not supported at present.")
@click.option("-r", "--rules", required=False, type=str, default="", help="Please enter a rule for temporary scanning of file contents.")
@click.option("-s", "--sniffer", is_flag=True, default=False, help="Enable the network sniffer function. It is on by default.")
@click.option("-n", '--no-resource', is_flag=True, default=False,help="Ignore all resource files, including network sniffing. It is not enabled by default.")
@click.option("-a", '--all',is_flag=True, default=False,help="Output the string content that conforms to the scan rules.It is on by default.")
@click.option("-t", '--threads',required=False, type=int,default=10,help="Set the number of concurrency. The larger the concurrency, the faster the speed. The default value is 10.")
@click.option("-o", '--output',required=False, type=str,default=None,help="Specify the result set output directory.")
def ios(inputs: str, rules: str, sniffer: bool, no_resource:bool, all:bool, threads:int, output:str) -> None:
@click.option("-n", '--no-resource', is_flag=True, default=False, help="Ignore all resource files, including network sniffing. It is not enabled by default.")
@click.option("-a", '--all', is_flag=True, default=False, help="Output the string content that conforms to the scan rules.It is on by default.")
@click.option("-t", '--threads', required=False, type=int, default=10, help="Set the number of concurrency. The larger the concurrency, the faster the speed. The default value is 10.")
@click.option("-o", '--output', required=False, type=str, default=None, help="Specify the result set output directory.")
def ios(inputs: str, rules: str, sniffer: bool, no_resource: bool, all: bool, threads: int, output: str) -> None:
try:
bootstrapper = Bootstrapper(__file__, output, all, no_resource)
bootstrapper.init()
@ -48,15 +52,16 @@ def ios(inputs: str, rules: str, sniffer: bool, no_resource:bool, all:bool, thre
except Exception as e:
raise e
@cli.command(help="Get the key information of Web system.")
@click.option("-i", "--inputs", required=True, type=str, help="Please enter the site directory or site file to scan or the corresponding site download address.")
@click.option("-r", "--rules", required=False, type=str, default="", help="Please enter a rule for temporary scanning of file contents.")
@click.option("-s", "--sniffer", is_flag=True, default=False, help="Enable the network sniffer function. It is on by default.")
@click.option("-n", '--no-resource', is_flag=True, default=False,help="Ignore all resource files, including network sniffing. It is not enabled by default.")
@click.option("-a", '--all',is_flag=True, default=False,help="Output the string content that conforms to the scan rules.It is on by default.")
@click.option("-t", '--threads',required=False, type=int,default=10,help="Set the number of concurrency. The larger the concurrency, the faster the speed. The default value is 10.")
@click.option("-o", '--output',required=False, type=str,default=None,help="Specify the result set output directory.")
def web(inputs: str, rules: str, sniffer: bool, no_resource:bool, all:bool, threads:int, output:str) -> None:
@click.option("-n", '--no-resource', is_flag=True, default=False, help="Ignore all resource files, including network sniffing. It is not enabled by default.")
@click.option("-a", '--all', is_flag=True, default=False, help="Output the string content that conforms to the scan rules.It is on by default.")
@click.option("-t", '--threads', required=False, type=int, default=10, help="Set the number of concurrency. The larger the concurrency, the faster the speed. The default value is 10.")
@click.option("-o", '--output', required=False, type=str, default=None, help="Specify the result set output directory.")
def web(inputs: str, rules: str, sniffer: bool, no_resource: bool, all: bool, threads: int, output: str) -> None:
try:
bootstrapper = Bootstrapper(__file__, output, all, no_resource)
bootstrapper.init()
@ -65,9 +70,10 @@ def web(inputs: str, rules: str, sniffer: bool, no_resource:bool, all:bool, thre
except Exception as e:
raise e
def main():
cli()
if __name__ == "__main__":
main()

@ -8,7 +8,7 @@
# com.alibaba.fastjson -> fastjson
# com.google.gson -> gson
# com.fasterxml.jackson -> jackson
# net.sf.json ->
# net.sf.json ->
# javax.xml.parsers.DocumentBuilder -> dom方式
# javax.xml.parsers.SAXParser -> sax方式
# org.jdom.input.SAXBuilder -> jdom
@ -28,7 +28,7 @@ filter_components = [
# 1. https://以及http://开头的
# 2. IPv4的ip地址
# 3. URI地址,URI不能很好的拼接所以此处忽略
filter_strs =[
filter_strs = [
r'https://.*|http://.*',
# r'.*://([[0-9]{1,3}\.]{3}[0-9]{1,3}).*',
r'.*://([\d{1,3}\.]{3}\d{1,3}).*',
@ -50,79 +50,80 @@ filter_no = [
r'.*w3school.com.cn',
r'.*apple.com',
r'.*.amap.com',
r'.*slf4j.org',
]
# AK集合
filter_ak_map = {
"Aliyun_OSS": [
r'.*accessKeyId.*".*"',
r'.*accessKeySecret.*".*"',
r'.*secret.*".*"'
r'.*accessKeyId.*".*?"',
r'.*accessKeySecret.*".*?"',
r'.*secret.*".*?"'
],
#"Amazon_AWS_Access_Key_ID": r"([^A-Z0-9]|^)(AKIA|A3T|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{12,}",
#"Amazon_AWS_S3_Bucket": [
# r"//s3-[a-z0-9-]+\\.amazonaws\\.com/[a-z0-9._-]+",
# r"//s3\\.amazonaws\\.com/[a-z0-9._-]+",
# r"[a-z0-9.-]+\\.s3-[a-z0-9-]\\.amazonaws\\.com",
# r"[a-z0-9.-]+\\.s3-website[.-](eu|ap|us|ca|sa|cn)",
# r"[a-z0-9.-]+\\.s3\\.amazonaws\\.com",
# r"amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
#],
#"Artifactory_API_Token": r"(?:\\s|=|:|\"|^)AKC[a-zA-Z0-9]{10,}",
#"Artifactory_Password": r"(?:\\s|=|:|\"|^)AP[\\dABCDEF][a-zA-Z0-9]{8,}",
# "Authorization_Basic": r"basic\\s[a-zA-Z0-9_\\-:\\.=]+",
# "Authorization_Bearer": r"bearer\\s[a-zA-Z0-9_\\-:\\.=]+",
#"AWS_API_Key": r"AKIA[0-9A-Z]{16}",
#"Basic_Auth_Credentials": r"(?<=:\/\/)[a-zA-Z0-9]+:[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z]+",
#"Cloudinary_Basic_Auth": r"cloudinary:\/\/[0-9]{15}:[0-9A-Za-z]+@[a-z]+",
#"DEFCON_CTF_Flag": r"O{3}\\{.*\\}",
#"Discord_BOT_Token": r"((?:N|M|O)[a-zA-Z0-9]{23}\\.[a-zA-Z0-9-_]{6}\\.[a-zA-Z0-9-_]{27})$",
#"Facebook_Access_Token": r"EAACEdEose0cBA[0-9A-Za-z]+",
#"Facebook_ClientID": r"[f|F][a|A][c|C][e|E][b|B][o|O][o|O][k|K](.{0,20})?['\"][0-9]{13,17}",
#"Facebook_OAuth": r"[f|F][a|A][c|C][e|E][b|B][o|O][o|O][k|K].*['|\"][0-9a-f]{32}['|\"]",
#"Facebook_Secret_Key": r"([f|F][a|A][c|C][e|E][b|B][o|O][o|O][k|K]|[f|F][b|B])(.{0,20})?['\"][0-9a-f]{32}",
#"Firebase": r"[a-z0-9.-]+\\.firebaseio\\.com",
#"Generic_API_Key": r"[a|A][p|P][i|I][_]?[k|K][e|E][y|Y].*['|\"][0-9a-zA-Z]{32,45}['|\"]",
#"Generic_Secret": r"[s|S][e|E][c|C][r|R][e|E][t|T].*['|\"][0-9a-zA-Z]{32,45}['|\"]",
#"GitHub": r"[g|G][i|I][t|T][h|H][u|U][b|B].*['|\"][0-9a-zA-Z]{35,40}['|\"]",
#"GitHub_Access_Token": r"([a-zA-Z0-9_-]*:[a-zA-Z0-9_-]+@github.com*)$",
#"Google_API_Key": r"AIza[0-9A-Za-z\\-_]{35}",
#"Google_Cloud_Platform_OAuth": r"[0-9]+-[0-9A-Za-z_]{32}\\.apps\\.googleusercontent\\.com",
#"Google_Cloud_Platform_Service_Account": r"\"type\": \"service_account\"",
#"Google_OAuth_Access_Token": r"ya29\\.[0-9A-Za-z\\-_]+",
#"HackerOne_CTF_Flag": r"[h|H]1(?:[c|C][t|T][f|F])?\\{.*\\}",
#"HackTheBox_CTF_Flag": r"[h|H](?:[a|A][c|C][k|K][t|T][h|H][e|E][b|B][o|O][x|X]|[t|T][b|B])\\{.*\\}$",
#"Heroku_API_Key": r"[h|H][e|E][r|R][o|O][k|K][u|U].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}",
# "IP_Address": r"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])",
#"JSON_Web_Token": r"(?i)^((?=.*[a-z])(?=.*[0-9])(?:[a-z0-9_=]+\\.){2}(?:[a-z0-9_\\-\\+\/=]*))$",
# "LinkFinder": r"(?:\"|')(((?:[a-zA-Z]{1,10}:\/\/|\/\/)[^\"'\/]{1,}\\.[a-zA-Z]{2,}[^\"']{0,})|((?:\/|\\.\\.\/|\\.\/)[^\"'><,;| *()(%%$^\/\\\\\\[\\]][^\"'><,;|()]{1,})|([a-zA-Z0-9_\\-\/]{1,}\/[a-zA-Z0-9_\\-\/]{1,}\\.(?:[a-zA-Z]{1,4}|action)(?:[\\?|#][^\"|']{0,}|))|([a-zA-Z0-9_\\-\/]{1,}\/[a-zA-Z0-9_\\-\/]{3,}(?:[\\?|#][^\"|']{0,}|))|([a-zA-Z0-9_\\-]{1,}\\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:[\\?|#][^\"|']{0,}|)))(?:\"|')",
#"Mac_Address": r"(([0-9A-Fa-f]{2}[:]){5}[0-9A-Fa-f]{2}|([0-9A-Fa-f]{2}[-]){5}[0-9A-Fa-f]{2}|([0-9A-Fa-f]{4}[\\.]){2}[0-9A-Fa-f]{4})$",
#"MailChimp_API_Key": r"[0-9a-f]{32}-us[0-9]{1,2}",
#"Mailgun_API_Key": r"key-[0-9a-zA-Z]{32}",
#"Mailto": r"(?<=mailto:)[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9.-]+",
#"Password_in_URL": r"[a-zA-Z]{3,10}://[^/\\s:@]{3,20}:[^/\\s:@]{3,20}@.{1,100}[\"'\\s]",
#"PayPal_Braintree_Access_Token": r"access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}",
#"PGP_private_key_block": r"-----BEGIN PGP PRIVATE KEY BLOCK-----",
#"Picatic_API_Key": r"sk_live_[0-9a-z]{32}",
#"RSA_Private_Key": r"-----BEGIN RSA PRIVATE KEY-----",
#"Slack_Token": r"(xox[p|b|o|a]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32})",
#"Slack_Webhook": r"https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}",
#"Square_Access_Token": r"sq0atp-[0-9A-Za-z\\-_]{22}",
#"Square_OAuth_Secret": r"sq0csp-[0-9A-Za-z\\-_]{43}",
#"SSH_DSA_Private_Key": r"-----BEGIN DSA PRIVATE KEY-----",
#"SSH_EC_Private_Key": r"-----BEGIN EC PRIVATE KEY-----",
#"Stripe_API_Key": r"sk_live_[0-9a-zA-Z]{24}",
#"Stripe_Restricted_API_Key": r"rk_live_[0-9a-zA-Z]{24}",
#"TryHackMe_CTF_Flag": r"[t|T](?:[r|R][y|Y][h|H][a|A][c|C][k|K][m|M][e|E]|[h|H][m|M])\\{.*\\}$",
#"Twilio_API_Key": r"SK[0-9a-fA-F]{32}",
#"Twitter_Access_Token": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R].*[1-9][0-9]+-[0-9a-zA-Z]{40}",
#"Twitter_ClientID": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R](.{0,20})?['\"][0-9a-z]{18,25}",
#"Twitter_OAuth": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R].*['|\"][0-9a-zA-Z]{35,44}['|\"]",
#"Twitter_Secret_Key": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R](.{0,20})?['\"][0-9a-z]{35,44}"
# "Amazon_AWS_Access_Key_ID": r"([^A-Z0-9]|^)(AKIA|A3T|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{12,}",
# "Amazon_AWS_S3_Bucket": [
# r"//s3-[a-z0-9-]+\\.amazonaws\\.com/[a-z0-9._-]+",
# r"//s3\\.amazonaws\\.com/[a-z0-9._-]+",
# r"[a-z0-9.-]+\\.s3-[a-z0-9-]\\.amazonaws\\.com",
# r"[a-z0-9.-]+\\.s3-website[.-](eu|ap|us|ca|sa|cn)",
# r"[a-z0-9.-]+\\.s3\\.amazonaws\\.com",
# r"amzn\\.mws\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
# ],
# "Artifactory_API_Token": r"(?:\\s|=|:|\"|^)AKC[a-zA-Z0-9]{10,}",
# "Artifactory_Password": r"(?:\\s|=|:|\"|^)AP[\\dABCDEF][a-zA-Z0-9]{8,}",
# "Authorization_Basic": r"basic\\s[a-zA-Z0-9_\\-:\\.=]+",
# "Authorization_Bearer": r"bearer\\s[a-zA-Z0-9_\\-:\\.=]+",
# "AWS_API_Key": r"AKIA[0-9A-Z]{16}",
# "Basic_Auth_Credentials": r"(?<=:\/\/)[a-zA-Z0-9]+:[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z]+",
# "Cloudinary_Basic_Auth": r"cloudinary:\/\/[0-9]{15}:[0-9A-Za-z]+@[a-z]+",
# "DEFCON_CTF_Flag": r"O{3}\\{.*\\}",
# "Discord_BOT_Token": r"((?:N|M|O)[a-zA-Z0-9]{23}\\.[a-zA-Z0-9-_]{6}\\.[a-zA-Z0-9-_]{27})$",
# "Facebook_Access_Token": r"EAACEdEose0cBA[0-9A-Za-z]+",
# "Facebook_ClientID": r"[f|F][a|A][c|C][e|E][b|B][o|O][o|O][k|K](.{0,20})?['\"][0-9]{13,17}",
# "Facebook_OAuth": r"[f|F][a|A][c|C][e|E][b|B][o|O][o|O][k|K].*['|\"][0-9a-f]{32}['|\"]",
# "Facebook_Secret_Key": r"([f|F][a|A][c|C][e|E][b|B][o|O][o|O][k|K]|[f|F][b|B])(.{0,20})?['\"][0-9a-f]{32}",
# "Firebase": r"[a-z0-9.-]+\\.firebaseio\\.com",
# "Generic_API_Key": r"[a|A][p|P][i|I][_]?[k|K][e|E][y|Y].*['|\"][0-9a-zA-Z]{32,45}['|\"]",
# "Generic_Secret": r"[s|S][e|E][c|C][r|R][e|E][t|T].*['|\"][0-9a-zA-Z]{32,45}['|\"]",
# "GitHub": r"[g|G][i|I][t|T][h|H][u|U][b|B].*['|\"][0-9a-zA-Z]{35,40}['|\"]",
# "GitHub_Access_Token": r"([a-zA-Z0-9_-]*:[a-zA-Z0-9_-]+@github.com*)$",
# "Google_API_Key": r"AIza[0-9A-Za-z\\-_]{35}",
# "Google_Cloud_Platform_OAuth": r"[0-9]+-[0-9A-Za-z_]{32}\\.apps\\.googleusercontent\\.com",
# "Google_Cloud_Platform_Service_Account": r"\"type\": \"service_account\"",
# "Google_OAuth_Access_Token": r"ya29\\.[0-9A-Za-z\\-_]+",
# "HackerOne_CTF_Flag": r"[h|H]1(?:[c|C][t|T][f|F])?\\{.*\\}",
# "HackTheBox_CTF_Flag": r"[h|H](?:[a|A][c|C][k|K][t|T][h|H][e|E][b|B][o|O][x|X]|[t|T][b|B])\\{.*\\}$",
# "Heroku_API_Key": r"[h|H][e|E][r|R][o|O][k|K][u|U].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}",
# "IP_Address": r"(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])",
# "JSON_Web_Token": r"(?i)^((?=.*[a-z])(?=.*[0-9])(?:[a-z0-9_=]+\\.){2}(?:[a-z0-9_\\-\\+\/=]*))$",
# "LinkFinder": r"(?:\"|')(((?:[a-zA-Z]{1,10}:\/\/|\/\/)[^\"'\/]{1,}\\.[a-zA-Z]{2,}[^\"']{0,})|((?:\/|\\.\\.\/|\\.\/)[^\"'><,;| *()(%%$^\/\\\\\\[\\]][^\"'><,;|()]{1,})|([a-zA-Z0-9_\\-\/]{1,}\/[a-zA-Z0-9_\\-\/]{1,}\\.(?:[a-zA-Z]{1,4}|action)(?:[\\?|#][^\"|']{0,}|))|([a-zA-Z0-9_\\-\/]{1,}\/[a-zA-Z0-9_\\-\/]{3,}(?:[\\?|#][^\"|']{0,}|))|([a-zA-Z0-9_\\-]{1,}\\.(?:php|asp|aspx|jsp|json|action|html|js|txt|xml)(?:[\\?|#][^\"|']{0,}|)))(?:\"|')",
# "Mac_Address": r"(([0-9A-Fa-f]{2}[:]){5}[0-9A-Fa-f]{2}|([0-9A-Fa-f]{2}[-]){5}[0-9A-Fa-f]{2}|([0-9A-Fa-f]{4}[\\.]){2}[0-9A-Fa-f]{4})$",
# "MailChimp_API_Key": r"[0-9a-f]{32}-us[0-9]{1,2}",
# "Mailgun_API_Key": r"key-[0-9a-zA-Z]{32}",
# "Mailto": r"(?<=mailto:)[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9.-]+",
# "Password_in_URL": r"[a-zA-Z]{3,10}://[^/\\s:@]{3,20}:[^/\\s:@]{3,20}@.{1,100}[\"'\\s]",
# "PayPal_Braintree_Access_Token": r"access_token\\$production\\$[0-9a-z]{16}\\$[0-9a-f]{32}",
# "PGP_private_key_block": r"-----BEGIN PGP PRIVATE KEY BLOCK-----",
# "Picatic_API_Key": r"sk_live_[0-9a-z]{32}",
# "RSA_Private_Key": r"-----BEGIN RSA PRIVATE KEY-----",
# "Slack_Token": r"(xox[p|b|o|a]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32})",
# "Slack_Webhook": r"https://hooks.slack.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}",
# "Square_Access_Token": r"sq0atp-[0-9A-Za-z\\-_]{22}",
# "Square_OAuth_Secret": r"sq0csp-[0-9A-Za-z\\-_]{43}",
# "SSH_DSA_Private_Key": r"-----BEGIN DSA PRIVATE KEY-----",
# "SSH_EC_Private_Key": r"-----BEGIN EC PRIVATE KEY-----",
# "Stripe_API_Key": r"sk_live_[0-9a-zA-Z]{24}",
# "Stripe_Restricted_API_Key": r"rk_live_[0-9a-zA-Z]{24}",
# "TryHackMe_CTF_Flag": r"[t|T](?:[r|R][y|Y][h|H][a|A][c|C][k|K][m|M][e|E]|[h|H][m|M])\\{.*\\}$",
# "Twilio_API_Key": r"SK[0-9a-fA-F]{32}",
# "Twitter_Access_Token": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R].*[1-9][0-9]+-[0-9a-zA-Z]{40}",
# "Twitter_ClientID": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R](.{0,20})?['\"][0-9a-z]{18,25}",
# "Twitter_OAuth": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R].*['|\"][0-9a-zA-Z]{35,44}['|\"]",
# "Twitter_Secret_Key": r"[t|T][w|W][i|I][t|T][t|T][e|E][r|R](.{0,20})?['\"][0-9a-z]{35,44}"
}
# 此处配置壳信息
shell_list =[
shell_list = [
'com.stub.StubApp',
's.h.e.l.l.S',
'com.Kiwisec.KiwiSecApplication',
@ -150,7 +151,7 @@ apk_permissions = [
]
# 此处配置需要扫描的web文件后缀
web_file_suffix =[
web_file_suffix = [
"html",
"js",
"xml",
@ -163,7 +164,7 @@ web_file_suffix =[
]
# 配置需要忽略网络嗅探的文件后缀名,此处根据具体需求进行配置,默认为不过滤
sniffer_filter=[
sniffer_filter = [
"jpg",
"png",
"jpeg",
@ -172,8 +173,8 @@ sniffer_filter=[
# 配置自动下载Apk文件或者缓存HTML的请求头信息
headers = {
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0",
"Connection":"close"
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0",
"Connection": "close"
}
# 配置自动下载Apk文件或者缓存HTML的请求体信息
@ -183,4 +184,3 @@ data = {
# 配置自动下载Apk文件或者缓存HTML的请求方法信息,目前仅支持GET和POST
method = "GET"

@ -26,7 +26,7 @@ output_path = ""
download_flag = False
# excel 起始行号
excel_row = 0
excel_row = 1
class Bootstrapper(object):
@ -78,7 +78,7 @@ class Bootstrapper(object):
apktool_path = os.path.join(tools_dir, "apktool.jar")
download_path = os.path.join(out_dir,"download")
txt_result_path = os.path.join(out_dir,"result_"+str(create_time)+".txt")
xls_result_path = os.path.join(out_dir,"result_"+str(create_time)+".xls")
xls_result_path = os.path.join(out_dir,"result_"+str(create_time)+".xlsx")
app_history_path = os.path.join(history_path,"app_history.txt")
domain_history_path = os.path.join(history_path,"domain_history.txt")
@ -100,7 +100,6 @@ class Bootstrapper(object):
print("[*] Create directory {}".format(output_path))
if not os.path.exists(download_path):
# shutil.rmtree(download_path)
os.makedirs(download_path)
print("[*] Create directory {}".format(download_path))

@ -2,22 +2,20 @@
# -*- coding: utf-8 -*-
# Author: kelvinBen
# Github: https://github.com/kelvinBen/AppInfoScanner
import re
import os
import sys
import time
import config
import requests
import threading
import libs.core as cores
from requests.packages import urllib3
from requests.adapters import HTTPAdapter
from requests.adapters import HTTPAdapter
class DownloadThreads(threading.Thread):
def __init__(self,input_path,file_name,cache_path,types):
threading.Thread.__init__(self)
self.url = input_path
class DownloadThreads(threading.Thread):
def __init__(self, input_path, file_name, cache_path, types):
threading.Thread.__init__(self)
self.url = input_path
self.types = types
self.cache_path = cache_path
self.file_name = file_name
@ -27,23 +25,24 @@ class DownloadThreads(threading.Thread):
session = requests.Session()
session.mount('http://', HTTPAdapter(max_retries=3))
session.mount('https://', HTTPAdapter(max_retries=3))
session.keep_alive =False
session.keep_alive = False
session.adapters.DEFAULT_RETRIES = 5
urllib3.disable_warnings()
if config.method.upper() == "POST":
resp = session.post(url=self.url,params=config.data ,headers=config.headers,timeout=30)
resp = session.post(
url=self.url, params=config.data, headers=config.headers, timeout=30)
else:
resp = session.get(url=self.url,data=config.data ,headers=config.headers,timeout=30)
resp = session.get(url=self.url, data=config.data,
headers=config.headers, timeout=30)
if resp.status_code == requests.codes.ok:
if self.types == "Android" or self.types == "iOS":
count = 0
progress_tmp = 0
time1 = time.time()
length = float(resp.headers['content-length'])
with open(self.cache_path, "wb") as f:
for chunk in resp.iter_content(chunk_size = 512):
for chunk in resp.iter_content(chunk_size=512):
if chunk:
f.write(chunk)
count += len(chunk)
@ -51,19 +50,19 @@ class DownloadThreads(threading.Thread):
if progress != progress_tmp:
progress_tmp = progress
print("\r", end="")
print("[*] Download progress: {}%: ".format(progress), "" * (progress // 2), end="")
print(
"[*] Download progress: {}%: ".format(progress), "" * (progress // 2), end="")
sys.stdout.flush()
f.close()
else:
html = resp.text
with open(self.cache_path,"w",encoding='utf-8',errors='ignore') as f:
with open(self.cache_path, "w", encoding='utf-8', errors='ignore') as f:
f.write(html)
f.close()
cores.download_flag = True
except Exception as e:
raise Exception(e)
return
def run(self):
threadLock = threading.Lock()
self.__requset__()
self.__requset__()

@ -9,21 +9,21 @@ import requests
import libs.core as cores
class NetThreads(threading.Thread):
def __init__(self,threadID,name,domain_queue,worksheet):
threading.Thread.__init__(self)
def __init__(self, threadID, name, domain_queue, worksheet):
threading.Thread.__init__(self)
self.name = name
self.threadID = threadID
self.lock = threading.Lock()
self.domain_queue = domain_queue
self.lock = threading.Lock()
self.domain_queue = domain_queue
self.worksheet = worksheet
def __get_Http_info__(self,threadLock):
def __get_Http_info__(self, threadLock):
while True:
if self.domain_queue.empty():
break
domains = self.domain_queue.get(timeout=5)
domain = domains["domain"]
domain = domains["domain"]
url_ip = domains["url_ip"]
time.sleep(2)
result = self.__get_request_result__(url_ip)
@ -31,23 +31,33 @@ class NetThreads(threading.Thread):
if result != "error":
if self.lock.acquire(True):
cores.excel_row = cores.excel_row + 1
self.worksheet.write(cores.excel_row, 0, label = cores.excel_row)
self.worksheet.write(cores.excel_row, 1, label = url_ip)
self.worksheet.write(cores.excel_row, 2, label = domain)
self.worksheet.cell(row=cores.excel_row,
column=1, value=cores.excel_row-1)
self.worksheet.cell(row=cores.excel_row,
column=2, value=url_ip)
self.worksheet.cell(row=cores.excel_row,
column=3, value=domain)
if result != "timeout":
self.worksheet.write(cores.excel_row, 3, label = result["status"])
self.worksheet.write(cores.excel_row, 4, label = result["des_ip"])
self.worksheet.write(cores.excel_row, 5, label = result["server"])
self.worksheet.write(cores.excel_row, 6, label = result["title"])
self.worksheet.write(cores.excel_row, 7, label = result["cdn"])
# self.worksheet.write(cores.excel_row, 8, label = "")
self.worksheet.cell(
row=cores.excel_row, column=4, value=result["status"])
self.worksheet.cell(
row=cores.excel_row, column=5, value=result["des_ip"])
self.worksheet.cell(
row=cores.excel_row, column=6, value=result["server"])
self.worksheet.cell(
row=cores.excel_row, column=7, value=result["title"])
self.worksheet.cell(
row=cores.excel_row, column=8, value=result["cdn"])
self.lock.release()
def __get_request_result__(self,url):
result={"status":"","server":"","cookie":"","cdn":"","des_ip":"","sou_ip":"","title":""}
def __get_request_result__(self, url):
result = {"status": "", "server": "", "cookie": "",
"cdn": "", "des_ip": "", "sou_ip": "", "title": ""}
cdn = ""
try:
with requests.get(url, timeout=5,stream=True) as rsp:
with requests.get(url, timeout=5, stream=True) as rsp:
status_code = rsp.status_code
result["status"] = status_code
headers = rsp.headers
@ -59,21 +69,21 @@ class NetThreads(threading.Thread):
cdn = cdn + headers['X-Via']
if "Via" in headers:
cdn = cdn + headers['Via']
result["cdn"] = cdn
result["cdn"] = cdn
sock = rsp.raw._connection.sock
if sock:
des_ip = sock.getpeername()[0]
sou_ip = sock.getsockname()[0]
if des_ip:
result["des_ip"] = des_ip
result["des_ip"] = des_ip
if sou_ip:
result["sou_ip"] = sou_ip
result["sou_ip"] = sou_ip
sock.close()
html = rsp.text
title = re.findall('<title>(.+)</title>',html)
title = re.findall('<title>(.+)</title>', html)
if title:
result["title"] = title[0]
result["title"] = title[0]
rsp.close()
return result
except requests.exceptions.InvalidURL as e:

@ -11,76 +11,81 @@ import libs.core as cores
class ParsesThreads(threading.Thread):
def __init__(self,threadID,name,file_queue,result_dict,types):
threading.Thread.__init__(self)
def __init__(self, threadID, name, file_queue, result_dict, types):
threading.Thread.__init__(self)
self.file_queue = file_queue
self.name = name
self.threadID = threadID
self.result_list = []
self.result_dict=result_dict
self.result_dict = result_dict
self.types = types
def __regular_parse__(self):
while True:
if self.file_queue.empty():
break
file_path = self.file_queue.get(timeout = 5)
file_path = self.file_queue.get(timeout=5)
scan_str = ("[+] Scan file : %s" % file_path)
if self.types == "iOS":
self.__get_string_by_iOS__(file_path)
else:
self.__get_string_by_file__(file_path)
result_set = set(self.result_list)
if len(result_set) != 0:
self.result_dict[file_path] = result_set
def __get_string_by_iOS__(self,file_path):
def __get_string_by_iOS__(self, file_path):
output_path = cores.output_path
strings_path = cores.strings_path
temp = os.path.join(output_path,"temp.txt")
cmd_str = ('"%s" "%s" > "%s"') % (str(strings_path),str(file_path),str(temp))
temp = os.path.join(output_path, "temp.txt")
cmd_str = ('"%s" "%s" > "%s"') % (
str(strings_path), str(file_path), str(temp))
if os.system(cmd_str) == 0:
with open(temp,"r",encoding='utf-8',errors='ignore') as f:
with open(temp, "r", encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
for line in lines:
self.__parse_string__(line)
def __get_string_by_file__(self,file_path):
with open(file_path,"r",encoding="utf8",errors='ignore') as f :
file_content = f.read()
def __get_string_by_file__(self, file_path):
with open(file_path, "r", encoding="utf8", errors='ignore') as f:
file_content = f.read()
# 获取到所有的字符串
pattern = re.compile(r'\"(.*?)\"')
pattern = re.compile(r'\"(.*?)\"')
results = pattern.findall(file_content)
# 搜素AK和SK信息,由于iOS的逻辑处理效率过慢暂时忽略对iOS的AK检测
if not (".js" == file_path[-3:] and self.types == "iOS"):
for key,values in config.filter_ak_map.items():
if isinstance(values,list):
for value in values:
self.__ak_and_sk__(key, value, file_content)
else:
self.__ak_and_sk__(key, values, file_content)
# 未包含相关字段不进行ak或者sk信息采集
if "access" in file_content or "secret" in file_content:
for key, values in config.filter_ak_map.items():
if isinstance(values, list):
for value in values:
self.__ak_and_sk__(key, value, file_content)
else:
self.__ak_and_sk__(key, values, file_content)
# 遍历所有的字符串
for result in set(results):
self.__parse_string__(result)
def __ak_and_sk__(self, name, ak_rule,content):
for result in set(results):
if ("http://" == result) or ("https://" == result) or result.startswith("https://.") or result.startswith("http://.") :
continue
self.__parse_string__(result)
def __ak_and_sk__(self, name, ak_rule, content):
akAndSkList = re.compile(ak_rule).findall(content)
for akAndSk in akAndSkList:
ak = ("[%s]-->:%s") % (name,akAndSk.strip())
ak = ("[%s]-->:%s") % (name, akAndSk.strip())
self.result_list.append(ak)
print(("[+] [%s] AK or SK in %s:") % (name, akAndSk.strip()))
def __parse_string__(self,result):
def __parse_string__(self, result):
# 通过正则筛选需要过滤的字符串
for filter_str in config.filter_strs:
filter_str_pat = re.compile(filter_str)
filter_str_pat = re.compile(filter_str)
filter_resl = filter_str_pat.findall(result)
# 过滤掉未搜索到的内容
if len(filter_resl)!=0:
if len(filter_resl) != 0:
# 提取第一个结果
resl_str = filter_resl[0]
# 过滤
@ -89,29 +94,32 @@ class ParsesThreads(threading.Thread):
self.threadLock.acquire()
if cores.all_flag:
print(("[+] The string searched for matching rule is: %s") % (resl_str))
print(
("[+] The string searched for matching rule is: %s") % (resl_str))
self.result_list.append(resl_str)
self.threadLock.release()
continue
def __filter__(self,resl_str):
return_flag = 1
resl_str = resl_str.replace("\r","").replace("\n","").replace(" ","")
def __filter__(self, resl_str):
return_flag = 1
resl_str = resl_str.replace("\r", "").replace(
"\n", "").replace(" ", "")
if len(resl_str) == 0:
return 0
for filte in set(config.filter_no):
resl_str = resl_str.replace(filte,"")
resl_str = resl_str.replace(filte, "")
if len(resl_str) == 0:
return_flag = 0
return_flag = 0
continue
if re.match(filte,resl_str):
return_flag = 0
if re.match(filte, resl_str):
return_flag = 0
continue
return return_flag
return return_flag
def run(self):
self.threadLock = threading.Lock()
self.__regular_parse__()
self.__regular_parse__()

@ -12,141 +12,142 @@ import libs.core as cores
class AndroidTask(object):
def __init__(self,path,package):
def __init__(self, path, package):
self.path = path
self.package = package
self.file_queue = Queue()
self.shell_flag=False
self.packagename=""
self.comp_list=[]
self.file_identifier=[]
self.shell_flag = False
self.packagename = ""
self.comp_list = []
self.file_identifier = []
self.permissions = []
def start(self):
# 检查java环境是否存在
if os.system("java -version") !=0 :
if os.system("java -version") != 0:
raise Exception("Please install the Java environment!")
input_file_path = self.path
if os.path.isdir(input_file_path):
self.__decode_dir__(input_file_path)
else:
if self.__decode_file__(input_file_path) == "error":
raise Exception("Retrieval of this file type is not supported. Select APK file or DEX file.")
return {"comp_list":self.comp_list,"shell_flag":self.shell_flag,"file_queue":self.file_queue,"packagename":self.packagename,"file_identifier":self.file_identifier,"permissions":self.permissions}
if self.__decode_file__(input_file_path) == "error":
raise Exception(
"Retrieval of this file type is not supported. Select APK file or DEX file.")
def __decode_file__(self,file_path):
return {"comp_list": self.comp_list, "shell_flag": self.shell_flag, "file_queue": self.file_queue, "packagename": self.packagename, "file_identifier": self.file_identifier, "permissions": self.permissions}
def __decode_file__(self, file_path):
apktool_path = str(cores.apktool_path)
backsmali_path = str(cores.backsmali_path)
base_out_path = str(cores.output_path)
filename = os.path.basename(file_path)
filename = os.path.basename(file_path)
suffix_name = filename.split(".")[-1]
if suffix_name == "apk":
if suffix_name == "apk" or suffix_name == "hpk":
name = filename.split(".")[0]
output_path = os.path.join(base_out_path,name)
self.__decode_apk__(file_path,apktool_path,output_path)
output_path = os.path.join(base_out_path, name)
self.__decode_apk__(file_path, apktool_path, output_path)
elif suffix_name == "dex":
f = open(file_path,'rb')
f = open(file_path, 'rb')
md5_obj = hashlib.md5()
while True:
r = f.read(1024)
r = f.read(1024)
if not r:
break
md5_obj.update(r)
dex_md5 = md5_obj.hexdigest().lower()
self.file_identifier.append(dex_md5)
output_path = os.path.join(base_out_path,dex_md5)
output_path = os.path.join(base_out_path, dex_md5)
if not os.path.exists(output_path):
os.makedirs(output_path)
self.__decode_dex__(file_path,backsmali_path,output_path)
self.__decode_dex__(file_path, backsmali_path, output_path)
else:
return "error"
def __decode_dir__(self,root_dir):
def __decode_dir__(self, root_dir):
dir_or_files = os.listdir(root_dir)
for dir_or_file in dir_or_files:
dir_or_file_path = os.path.join(root_dir,dir_or_file)
dir_or_file_path = os.path.join(root_dir, dir_or_file)
if os.path.isdir(dir_or_file_path):
self.__decode_dir__(dir_or_file_path)
else:
else:
if self.__decode_file__(dir_or_file_path) == "error":
continue
# 分解apk
def __decode_apk__(self,file_path,apktool_path,output_path):
cmd_str = ('java -jar "%s" d -f "%s" -o "%s" --only-main-classe') % (str(apktool_path),str(file_path),str(output_path))
def __decode_apk__(self, file_path, apktool_path, output_path):
cmd_str = ('java -jar "%s" d -f "%s" -o "%s" --only-main-classe') % (
str(apktool_path), str(file_path), str(output_path))
if os.system(cmd_str) == 0:
self.__shell_test__(output_path)
self.__scanner_file_by_apktool__(output_path)
else:
print("[-] Decompilation failed, please submit error information at https://github.com/kelvinBen/AppInfoScanner/issues")
raise Exception(file_path + ", Decompilation failed.")
# 分解dex
def __decode_dex__(self,file_path,backsmali_path,output_path):
cmd_str = ('java -jar "%s" d "%s"') % (str(backsmali_path),str(file_path))
def __decode_dex__(self, file_path, backsmali_path, output_path):
cmd_str = ('java -jar "%s" d "%s"') % (str(backsmali_path),
str(file_path))
if os.system(cmd_str) == 0:
self.__get_scanner_file__(output_path)
else:
print("[-] Decompilation failed, please submit error information at https://github.com/kelvinBen/AppInfoScanner/issues")
raise Exception(file_path + ", Decompilation failed.")
# 初始化检测文件信息
def __scanner_file_by_apktool__(self,output_path):
def __scanner_file_by_apktool__(self, output_path):
file_names = os.listdir(output_path)
for file_name in file_names:
file_path = os.path.join(output_path,file_name)
file_path = os.path.join(output_path, file_name)
if not os.path.isdir(file_path):
continue
if "smali" in file_name or "assets" in file_name:
scanner_file_suffixs = ["smali","js","xml"]
scanner_file_suffixs = ["smali", "js", "xml"]
if cores.resource_flag:
scanner_file_suffixs =["smali"]
self.__get_scanner_file__(file_path,scanner_file_suffixs)
scanner_file_suffixs = ["smali"]
self.__get_scanner_file__(file_path, scanner_file_suffixs)
def __get_scanner_file__(self,scanner_dir,scanner_file_suffixs=["smali"]):
def __get_scanner_file__(self, scanner_dir, scanner_file_suffixs=["smali"]):
dir_or_files = os.listdir(scanner_dir)
for dir_or_file in dir_or_files:
dir_file_path = os.path.join(scanner_dir,dir_or_file)
dir_file_path = os.path.join(scanner_dir, dir_or_file)
if os.path.isdir(dir_file_path):
self.__get_scanner_file__(dir_file_path,scanner_file_suffixs)
self.__get_scanner_file__(dir_file_path, scanner_file_suffixs)
else:
if ("." not in dir_or_file) or (len(dir_or_file.split(".")) < 1) or (dir_or_file.split(".")[-1] not in scanner_file_suffixs):
continue
self.file_queue.put(dir_file_path)
for component in config.filter_components:
comp = component.replace(".","/")
comp = component.replace(".", "/")
if(comp in dir_file_path):
if(component not in self.comp_list):
self.comp_list.append(component)
def __shell_test__(self,output):
am_path = os.path.join(output,"AndroidManifest.xml")
with open(am_path,"r",encoding='utf-8',errors='ignore') as f:
def __shell_test__(self, output):
am_path = os.path.join(output, "AndroidManifest.xml")
with open(am_path, "r", encoding='utf-8', errors='ignore') as f:
am_str = f.read()
am_package= re.compile(r'<manifest.*package=\"(.*?)\".*')
am_package = re.compile(r'<manifest.*package=\"(.*?)\".*')
apackage = am_package.findall(am_str)
if len(apackage) >=1:
if len(apackage) >= 1:
self.packagename = apackage[0]
self.file_identifier.append(apackage[0])
am_name = re.compile(r'<application.*android:name=\"(.*?)\".*>')
am_name = re.compile(r'<application.*android:name=\"(.*?)\".*>')
aname = am_name.findall(am_str)
if aname and len(aname)>=1:
if aname and len(aname) >= 1:
if aname[0] in config.shell_list:
self.shell_flag = True
am_permission = re.compile(r'<uses-permission android:name="(.*)"/>')
ampermissions = am_permission.findall(am_str)
for ampermission in ampermissions:
if ampermission in config.apk_permissions:
self.permissions.append(ampermission)
self.permissions.append(ampermission)

@ -3,9 +3,7 @@
# Author: kelvinBen
# Github: https://github.com/kelvinBen/AppInfoScanner
import os
import re
import config
import threading
from queue import Queue
import libs.core as cores
from libs.task.ios_task import iOSTask
@ -15,12 +13,14 @@ from libs.core.parses import ParsesThreads
from libs.task.android_task import AndroidTask
from libs.task.download_task import DownloadTask
class BaseTask(object):
thread_list =[]
thread_list = []
result_dict = {}
app_history_list=[]
domain_history_list=[]
app_history_list = []
domain_history_list = []
# 统一初始化入口
def __init__(self, types="Android", inputs="", rules="", sniffer=True, threads=10, package=""):
self.types = types
self.path = inputs
@ -30,17 +30,18 @@ class BaseTask(object):
self.threads = threads
self.package = package
self.file_queue = Queue()
# 统一调度平台
def start(self):
print("[*] AI is analyzing filtering rules......")
# 获取历史记录
self.__history_handle__()
print("[*] The filtering rules obtained by AI are as follows: %s" % (set(config.filter_no)) )
print("[*] The filtering rules obtained by AI are as follows: %s" %
(set(config.filter_no)))
# 任务控制中心
task_info = self.__tast_control__()
@ -53,37 +54,39 @@ class BaseTask(object):
packagename = task_info["packagename"]
file_identifier = task_info["file_identifier"]
permissions = task_info["permissions"]
if shell_flag:
print('[-] \033[3;31m Error: This application has shell, the retrieval results may not be accurate, Please remove the shell and try again!')
return
# 线程控制中心
print("[*] ========= Searching for strings that match the rules ===============")
print(
"[*] ========= Searching for strings that match the rules ===============")
self.__threads_control__(file_queue)
# 等待线程结束
for thread in self.thread_list:
thread.join()
# 结果输出中心
self.__print_control__(packagename,comp_list,file_identifier,permissions)
# 结果输出中心
self.__print_control__(packagename, comp_list,
file_identifier, permissions)
def __tast_control__(self):
task_info = {}
# 自动根据文件后缀名称进行修正
cache_info = DownloadTask().start(self.path,self.types)
cache_info = DownloadTask().start(self.path, self.types)
cacar_path = cache_info["path"]
types = cache_info["type"]
if (not os.path.exists(cacar_path) and cores.download_flag):
print("[-] File download failed! Please download the file manually and try again.")
print(
"[-] File download failed! Please download the file manually and try again.")
return task_info
# 调用Android 相关处理逻辑
if types == "Android":
task_info = AndroidTask(cacar_path,self.package).start()
task_info = AndroidTask(cacar_path, self.package).start()
# 调用iOS 相关处理逻辑
elif types == "iOS":
task_info = iOSTask(cacar_path).start()
@ -92,23 +95,26 @@ class BaseTask(object):
task_info = WebTask(cacar_path).start()
return task_info
def __threads_control__(self,file_queue):
for threadID in range(1,self.threads):
def __threads_control__(self, file_queue):
for threadID in range(1, self.threads):
name = "Thread - " + str(int(threadID))
thread = ParsesThreads(threadID,name,file_queue,self.result_dict,self.types)
thread = ParsesThreads(
threadID, name, file_queue, self.result_dict, self.types)
thread.start()
self.thread_list.append(thread)
def __print_control__(self,packagename,comp_list,file_identifier,permissions):
def __print_control__(self, packagename, comp_list, file_identifier, permissions):
txt_result_path = cores.txt_result_path
xls_result_path = cores.xls_result_path
all_flag = cores.all_flag
if self.sniffer:
print("[*] ========= Sniffing the URL address of the search ===============")
NetTask(self.result_dict,self.app_history_list,self.domain_history_list,file_identifier,self.threads).start()
if packagename:
print(
"[*] ========= Sniffing the URL address of the search ===============")
NetTask(self.result_dict, self.app_history_list,
self.domain_history_list, file_identifier, self.threads).start()
if packagename:
print("[*] ========= The package name of this APP is: ===============")
print(packagename)
@ -116,16 +122,17 @@ class BaseTask(object):
print("[*] ========= Component information is as follows: ===============")
for json in comp_list:
print(json)
if len(permissions) != 0:
print("[*] ========= Sensitive permission information is as follows: ===============")
print(
"[*] ========= Sensitive permission information is as follows: ===============")
for permission in permissions:
print(permission)
if all_flag:
value_list = []
with open(txt_result_path,"a+",encoding='utf-8',errors='ignore') as f:
for key,value in self.result_dict.items():
with open(txt_result_path, "a+", encoding='utf-8', errors='ignore') as f:
for key, value in self.result_dict.items():
f.write(key+"\r")
for result in value:
if result in value_list:
@ -133,36 +140,37 @@ class BaseTask(object):
value_list.append(result)
f.write("\t"+result+"\r")
f.close()
print("[*] For more information about the search, see TXT file result: %s" %(txt_result_path))
print("[*] For more information about the search, see TXT file result: %s" %
(txt_result_path))
if self.sniffer:
print("[*] For more information about the search, see XLS file result: %s" %(xls_result_path))
print("[*] For more information about the search, see XLSX file result: %s" %
(xls_result_path))
def __history_handle__(self):
domain_history_path = cores.domain_history_path
domain_history_path = cores.domain_history_path
app_history_path = cores.app_history_path
if os.path.exists(domain_history_path):
domain_counts = {}
app_size = 0
with open(app_history_path,"r",encoding='utf-8',errors='ignore') as f:
app_size = 0
with open(app_history_path, "r", encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
app_size = len(lines)
for line in lines:
self.app_history_list.append(line.replace("\r","").replace("\n",""))
for line in lines:
self.app_history_list.append(
line.replace("\r", "").replace("\n", ""))
f.close()
with open(domain_history_path,"r",encoding='utf-8',errors='ignore') as f:
with open(domain_history_path, "r", encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
cout = 3
if (app_size>3) and (app_size%3==0):
if (app_size > 3) and (app_size % 3 == 0):
cout = cout + 1
for line in lines:
domain = line.replace("\r","").replace("\n","")
domain = line.replace("\r", "").replace("\n", "")
self.domain_history_list.append(domain)
domain_count = lines.count(line)
if domain_count >= cout:
config.filter_no.append(".*" + domain)
f.close()

@ -11,20 +11,21 @@ from queue import Queue
import libs.core as cores
from libs.core.download import DownloadThreads
class DownloadTask(object):
def start(self,path,types):
def start(self, path, types):
create_time = time.strftime("%Y%m%d%H%M%S", time.localtime())
if path.endswith("apk"):
types = "Android"
file_name = create_time+ ".apk"
file_name = create_time + ".apk"
elif path.endswith("ipa"):
types = "iOS"
file_name = create_time + ".ipa"
else:
if types == "Android":
types = "Android"
file_name = create_time+ ".apk"
file_name = create_time + ".apk"
elif types == "iOS":
types = "iOS"
file_name = create_time + ".ipa"
@ -32,15 +33,16 @@ class DownloadTask(object):
types = "WEB"
file_name = create_time + ".html"
if not(path.startswith("http://") or path.startswith("https://")):
if not os.path.isdir(path): # 不是目录
return {"path":path,"type":types}
else: # 目录处理
return {"path":path,"type":types}
if not os.path.isdir(path): # 不是目录
return {"path": path, "type": types}
else: # 目录处理
return {"path": path, "type": types}
else:
print("[*] Detected that the task is not local, preparing to download file......")
cache_path = os.path.join(cores.download_path, file_name)
thread = DownloadThreads(path,file_name,cache_path,types)
print(
"[*] Detected that the task is not local, preparing to download file......")
cache_path = os.path.join(cores.download_path, file_name)
thread = DownloadThreads(path, file_name, cache_path, types)
thread.start()
thread.join()
print()
return {"path":cache_path,"type":types}
return {"path": cache_path, "type": types}

@ -11,125 +11,133 @@ import platform
import libs.core as cores
from queue import Queue
class iOSTask(object):
elf_file_name = ""
def __init__(self,path):
def __init__(self, path):
self.path = path
self.file_queue = Queue()
self.shell_flag = False
self.file_identifier= []
self.file_identifier = []
self.permissions = []
def start(self):
file_path = self.path
if file_path.split(".")[-1] == 'ipa':
self.__decode_ipa__(cores.output_path)
self.__scanner_file_by_ipa__(cores.output_path)
elif self.__get_file_header__(file_path):
elif self.__get_file_header__(file_path):
self.file_queue.put(file_path)
else:
raise Exception("Retrieval of this file type is not supported. Select IPA file or Mach-o file.")
return {"shell_flag":self.shell_flag,"file_queue":self.file_queue,"comp_list":[],"packagename":None,"file_identifier":self.file_identifier,"permissions":self.permissions}
raise Exception(
"Retrieval of this file type is not supported. Select IPA file or Mach-o file.")
return {"shell_flag": self.shell_flag, "file_queue": self.file_queue, "comp_list": [], "packagename": None, "file_identifier": self.file_identifier, "permissions": self.permissions}
def __get_file_header__(self,file_path):
def __get_file_header__(self, file_path):
hex_hand = 0x0
macho_name = os.path.split(file_path)[-1]
macho_name = os.path.split(file_path)[-1]
self.file_identifier.append(macho_name)
with open(file_path,"rb") as macho_file:
macho_file.seek(hex_hand,0)
with open(file_path, "rb") as macho_file:
macho_file.seek(hex_hand, 0)
magic = binascii.hexlify(macho_file.read(4)).decode().upper()
macho_magics = ["CFFAEDFE","CEFAEDFE","BEBAFECA","CAFEBABE"]
macho_magics = ["CFFAEDFE", "CEFAEDFE", "BEBAFECA", "CAFEBABE"]
if magic in macho_magics:
self.__shell_test__(macho_file,hex_hand)
self.__shell_test__(macho_file, hex_hand)
macho_file.close()
return True
macho_file.close()
return False
def __shell_test__(self,macho_file,hex_hand):
def __shell_test__(self, macho_file, hex_hand):
while True:
magic = binascii.hexlify(macho_file.read(4)).decode().upper()
if magic == "2C000000":
macho_file.seek(hex_hand,0)
encryption_info_command = binascii.hexlify(macho_file.read(24)).decode()
cryptid = encryption_info_command[-8:len(encryption_info_command)]
macho_file.seek(hex_hand, 0)
encryption_info_command = binascii.hexlify(
macho_file.read(24)).decode()
cryptid = encryption_info_command[-8:len(
encryption_info_command)]
if cryptid == "01000000":
self.shell_flag = True
break
hex_hand = hex_hand + 4
hex_hand = hex_hand + 4
def __scanner_file_by_ipa__(self,output):
scanner_file_suffix = ["plist","js","xml","html"]
scanner_dir = os.path.join(output,"Payload")
self.__get_scanner_file__(scanner_dir,scanner_file_suffix)
def __scanner_file_by_ipa__(self, output):
scanner_file_suffix = ["plist", "js", "xml", "html"]
scanner_dir = os.path.join(output, "Payload")
self.__get_scanner_file__(scanner_dir, scanner_file_suffix)
def __get_scanner_file__(self,scanner_dir,file_suffix):
def __get_scanner_file__(self, scanner_dir, file_suffix):
dir_or_files = os.listdir(scanner_dir)
for dir_file in dir_or_files:
dir_file_path = os.path.join(scanner_dir,dir_file)
dir_file_path = os.path.join(scanner_dir, dir_file)
if os.path.isdir(dir_file_path):
if dir_file.endswith(".app"):
self.elf_file_name = dir_file.replace(".app","")
self.__get_scanner_file__(dir_file_path,file_suffix)
self.elf_file_name = dir_file.replace(".app", "")
self.__get_scanner_file__(dir_file_path, file_suffix)
else:
if self.elf_file_name == dir_file:
self.__get_file_header__(dir_file_path)
self.file_queue.put(dir_file_path)
continue
if cores.resource_flag:
dir_file_suffix = dir_file.split(".")
if cores.resource_flag:
dir_file_suffix = dir_file.split(".")
if len(dir_file_suffix) > 1:
if dir_file_suffix[-1] in file_suffix:
self.__get_file_header__(dir_file_path)
self.file_queue.put(dir_file_path)
def __decode_ipa__(self,output_path):
with zipfile.ZipFile(self.path,"r") as zip_files:
def __decode_ipa__(self, output_path):
with zipfile.ZipFile(self.path, "r") as zip_files:
zip_file_names = zip_files.namelist()
zip_files.extract(zip_file_names[0],output_path)
zip_files.extract(zip_file_names[0], output_path)
try:
new_zip_file = zip_file_names[0].encode('cp437').decode('utf-8')
new_zip_file = zip_file_names[0].encode(
'cp437').decode('utf-8')
except UnicodeEncodeError:
new_zip_file = zip_file_names[0].encode('utf-8').decode('utf-8')
old_zip_dir = self.__get_parse_dir__(output_path,zip_file_names[0])
new_zip_dir = self.__get_parse_dir__(output_path,new_zip_file)
os.rename(old_zip_dir,new_zip_dir)
new_zip_file = zip_file_names[0].encode(
'utf-8').decode('utf-8')
old_zip_dir = self.__get_parse_dir__(
output_path, zip_file_names[0])
new_zip_dir = self.__get_parse_dir__(output_path, new_zip_file)
os.rename(old_zip_dir, new_zip_dir)
for zip_file in zip_file_names:
old_ext_path = zip_files.extract(zip_file,output_path)
old_ext_path = zip_files.extract(zip_file, output_path)
if not "Payload" in old_ext_path:
continue
start = str(old_ext_path).index("Payload")
dir_path = old_ext_path[start:len(old_ext_path)]
old_ext_path = os.path.join(output_path,dir_path)
old_ext_path = os.path.join(output_path, dir_path)
try:
new_zip_file = zip_file.encode('cp437').decode('utf-8')
except UnicodeEncodeError:
new_zip_file = zip_file.encode('utf-8').decode('utf-8')
new_ext_path = os.path.join(output_path,new_zip_file)
new_ext_path = os.path.join(output_path, new_zip_file)
if platform.system() == "Windows":
new_ext_path = new_ext_path.replace("/","\\")
new_ext_path = new_ext_path.replace("/", "\\")
if not os.path.exists(new_ext_path):
dir_path = os.path.dirname(new_ext_path)
if not os.path.exists(dir_path):
os.makedirs(dir_path)
shutil.move(old_ext_path, new_ext_path)
if os.path.exists(old_ext_path):
if os.path.exists(old_ext_path) and (".app" in old_ext_path):
try:
# mac发生权限问题的时候做处理
os.remove(old_ext_path)
except Exception:
shutil.rmtree(old_ext_path)
def __get_parse_dir__(self,output_path,file_path):
def __get_parse_dir__(self, output_path, file_path):
start = file_path.index("Payload/")
end = file_path.index(".app")
root_dir = file_path[start:end]
if platform.system() == "Windows":
root_dir = root_dir.replace("/","\\")
old_root_dir = os.path.join(output_path,root_dir+".app")
return old_root_dir
root_dir = root_dir.replace("/", "\\")
old_root_dir = os.path.join(output_path, root_dir+".app")
return old_root_dir

@ -3,108 +3,108 @@
# Author: kelvinBen
# Github: https://github.com/kelvinBen/AppInfoScanner
import re
import xlwt
import socket
import openpyxl
import config
from queue import Queue
import libs.core as cores
from libs.core.net import NetThreads
import requests
class NetTask(object):
value_list = []
domain_list=[]
def __init__(self,result_dict,app_history_list,domain_history_list,file_identifier,threads):
domain_list = []
def __init__(self, result_dict, app_history_list, domain_history_list, file_identifier, threads):
self.result_dict = result_dict
self.app_history_list = app_history_list
self.file_identifier = file_identifier
self.domain_queue = Queue()
self.threads = int(threads)
self.threads = int(threads)
self.thread_list = []
self.domain_history_list = domain_history_list
def start(self):
xls_result_path = cores.xls_result_path
workbook = xlwt.Workbook(encoding = 'utf-8')
workbook = openpyxl.Workbook()
worksheet = self.__creating_excel_header__(workbook)
self.__write_result_to_txt__()
self.__start_threads__(worksheet)
for thread in self.thread_list:
thread.join()
workbook.save(xls_result_path)
def __creating_excel_header__(self,workbook):
worksheet = workbook.add_sheet("Result",cell_overwrite_ok=True)
worksheet.write(0,0, label = "Number")
worksheet.write(0,1, label = "IP/URL")
worksheet.write(0,2, label = "Domain")
worksheet.write(0,3, label = "Status")
worksheet.write(0,4, label = "IP")
worksheet.write(0,5, label = "Server")
worksheet.write(0,6, label = "Title")
worksheet.write(0,7, label = "CDN")
# worksheet.write(0,8, label = "Finger")
return worksheet
def __creating_excel_header__(self, workbook):
worksheet = workbook.create_sheet("Result", 0)
worksheet.cell(row=1, column=1, value="Number")
worksheet.cell(row=1, column=2, value="IP/URL")
worksheet.cell(row=1, column=3, value="Domain")
worksheet.cell(row=1, column=4, value="Status")
worksheet.cell(row=1, column=5, value="IP")
worksheet.cell(row=1, column=6, value="Server")
worksheet.cell(row=1, column=7, value="Title")
worksheet.cell(row=1, column=8, value="CDN")
worksheet.cell(row=1, column=9, value="Finger")
return worksheet
def __write_result_to_txt__(self):
txt_result_path = cores.txt_result_path
append_file_flag = True
for key,value in self.result_dict.items():
for key, value in self.result_dict.items():
for result in value:
if result in self.value_list:
continue
self.value_list.append(result)
if (("http://" in result) or ("https://" in result)) and ("." in result):
domain = result.replace("https://","").replace("http://","")
if "{" in result or "}" in result or "[" in result or "]" in result or "\\" in result or "!" in result or "," in result:
continue
domain = result.replace(
"https://", "").replace("http://", "")
if "/" in domain:
domain = domain[:domain.index("/")]
if "|" in result:
result = result[:result.index("|")]
# 目前流通的域名中加上协议头最短长度为11位
if len(result) <= 10:
continue
url_suffix = result[result.rindex(".")+1:].lower()
if not(cores.resource_flag and url_suffix in config.sniffer_filter):
self.domain_queue.put({"domain":domain,"url_ip":result})
self.domain_queue.put(
{"domain": domain, "url_ip": result})
for identifier in self.file_identifier:
if identifier in self.app_history_list:
if not(domain in self.domain_history_list):
if not(domain in self.domain_history_list):
self.domain_list.append(domain)
self.__write_content_in_file__(cores.domain_history_path,domain)
self.__write_content_in_file__(
cores.domain_history_path, domain)
continue
if not(domain in self.domain_list):
self.domain_list.append(domain)
self.__write_content_in_file__(cores.domain_history_path,domain)
self.__write_content_in_file__(
cores.domain_history_path, domain)
if append_file_flag:
self.__write_content_in_file__(cores.app_history_path,identifier)
self.__write_content_in_file__(
cores.app_history_path, identifier)
append_file_flag = False
def __start_threads__(self,worksheet):
for threadID in range(0,self.threads) :
def __start_threads__(self, worksheet):
for threadID in range(0, self.threads):
name = "Thread - " + str(threadID)
thread = NetThreads(threadID,name,self.domain_queue,worksheet)
thread = NetThreads(threadID, name, self.domain_queue, worksheet)
thread.start()
self.thread_list.append(thread)
def __write_content_in_file__(self,file_path,content):
with open(file_path,"a+",encoding='utf-8',errors='ignore') as f:
def __write_content_in_file__(self, file_path, content):
with open(file_path, "a+", encoding='utf-8', errors='ignore') as f:
f.write(content+"\r")
f.close()

@ -7,8 +7,9 @@ import config
import hashlib
from queue import Queue
class WebTask(object):
thread_list =[]
thread_list = []
value_list = []
result_dict = {}
@ -19,29 +20,29 @@ class WebTask(object):
self.permissions = []
def start(self):
if len(config.web_file_suffix) <=0:
scanner_file_suffix = ["html","js","html","xml"]
if len(config.web_file_suffix) <= 0:
scanner_file_suffix = ["html", "js", "html", "xml"]
scanner_file_suffix = config.web_file_suffix
if os.path.isdir(self.path):
self.__get_scanner_file__(self.path,scanner_file_suffix)
self.__get_scanner_file__(self.path, scanner_file_suffix)
else:
if not (self.path.split(".")[-1] in scanner_file_suffix):
err_info = ("Retrieval of this file type is not supported. Select a file or directory with a suffix of %s" % ",".join(scanner_file_suffix))
raise Exception(err_info)
self.file_queue.put(self.path)
return {"comp_list":[],"shell_flag":False,"file_queue":self.file_queue,"packagename":None,"file_identifier":self.file_identifier,"permissions":self.permissions}
return {"comp_list": [], "shell_flag": False, "file_queue": self.file_queue, "packagename": None, "file_identifier": self.file_identifier, "permissions": self.permissions}
def __get_scanner_file__(self,scanner_dir,file_suffix):
def __get_scanner_file__(self, scanner_dir, file_suffix):
dir_or_files = os.listdir(scanner_dir)
for dir_file in dir_or_files:
dir_file_path = os.path.join(scanner_dir,dir_file)
dir_file_path = os.path.join(scanner_dir, dir_file)
if os.path.isdir(dir_file_path):
self.__get_scanner_file__(dir_file_path,file_suffix)
self.__get_scanner_file__(dir_file_path, file_suffix)
else:
if len(dir_file.split("."))>1:
if len(dir_file.split(".")) > 1:
if dir_file.split(".")[-1] in file_suffix:
with open(dir_file_path,'rb') as f:
dex_md5 = str(hashlib.md5().update(f.read()).hexdigest()).upper()
self.file_identifier.append(dex_md5)
self.file_queue.put(dir_file_path)
self.file_queue.put(dir_file_path)

@ -1,3 +1,3 @@
requests
click
xlwt
openpyxl

Binary file not shown.

@ -1,3 +1,10 @@
### V1.0.9
- 更新apktool为最新版本
- 优化部分环节流程
- 修复excle文件导出时超时行数限制
- 修复脚本执行时卡顿的问题
- 修复Mac下Playload文件权限不足的问题
### V1.0.8
- 添加AK和SK的检测
- 添加检测规则提交入口

Loading…
Cancel
Save