Skip to content

Commit

Permalink
Report QA, base64 decode
Browse files Browse the repository at this point in the history
  • Loading branch information
ajinabraham committed Nov 25, 2023
1 parent f245882 commit 196b418
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 254 deletions.
36 changes: 18 additions & 18 deletions mobsf/DynamicAnalyzer/tools/frida_scripts_ios/dump/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCrypt"),
}

if(ptr(args[3]) != 0 ) {
cccrypt['key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4])));
cccrypt['Key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4])));
} else {
cccrypt['key'] = 0;
cccrypt['Key'] = 0;
}

if(ptr(args[5]) != 0 ) {
cccrypt['iv'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16));
cccrypt['IV'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16));
} else {
cccrypt['iv'] = 0;
cccrypt['IV'] = 0;
}

this.dataInLength = parseInt(args[7]);

if(ptr(args[6]) != 0 ) {

cccrypt['dataIn'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[6]),this.dataInLength))
cccrypt['dataInput'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[6]),this.dataInLength))

} else {
cccrypt['dataIn'] = null;
cccrypt['dataInput'] = null;
}

this.dataOut = args[8];
Expand All @@ -47,10 +47,10 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCrypt"),
onLeave: function(retval) {
var cccrypt_re = {};
if(ptr(this.dataOut) != 0 ) {
cccrypt_re['dataOut'] = base64ArrayBuffer(Memory.readByteArray(this.dataOut,parseInt(ptr(Memory.readU32(ptr(this.dataOutLength),4)))));
cccrypt_re['dataOutput'] = base64ArrayBuffer(Memory.readByteArray(this.dataOut,parseInt(ptr(Memory.readU32(ptr(this.dataOutLength),4)))));

} else {
cccrypt_re['dataOut'] = null;
cccrypt_re['dataOutput'] = null;
}
send(JSON.stringify({'[MBSFDUMP] crypto': cccrypt_re}));

Expand All @@ -69,16 +69,16 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorCreate"
}

if(ptr(args[3]) != 0 ) {
cccryptorcreate['key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4])));
cccryptorcreate['Key'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[3]),parseInt(args[4])));

} else {
cccryptorcreate['key'] = 0;
cccryptorcreate['Key'] = 0;
}

if(ptr(args[5]) != 0 ) {
cccryptorcreate['iv'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16));
cccryptorcreate['IV'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[5]),16));
} else {
cccryptorcreate['iv'] = 0;
cccryptorcreate['IV'] = 0;
}
send(JSON.stringify({'[MBSFDUMP] crypto': cccryptorcreate}));

Expand All @@ -94,10 +94,10 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorUpdate"
onEnter: function(args) {
var cryptorupdate = {}
if(ptr(args[1]) != 0) {
cryptorupdate['dataIn'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[1]),parseInt(args[2])));
cryptorupdate['dataInput'] = base64ArrayBuffer(Memory.readByteArray(ptr(args[1]),parseInt(args[2])));

} else {
cryptorupdate['dataIn'] = null;
cryptorupdate['dataInput'] = null;
}

//this.len = args[4];
Expand All @@ -110,9 +110,9 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorUpdate"
onLeave: function(retval) {
var cryptorupdate_re = {}
if(ptr(this.out) != 0) {
cryptorupdate_re['dataOut'] = base64ArrayBuffer(Memory.readByteArray(this.out,parseInt(ptr(Memory.readU32(ptr(this.len),4)))))
cryptorupdate_re['dataOutput'] = base64ArrayBuffer(Memory.readByteArray(this.out,parseInt(ptr(Memory.readU32(ptr(this.len),4)))))
} else {
cryptorupdate_re['dataOut'] = null;
cryptorupdate_re['dataOutput'] = null;
}
send(JSON.stringify({'[MBSFDUMP] crypto': cryptorupdate_re}));
}
Expand All @@ -129,9 +129,9 @@ Interceptor.attach(Module.findExportByName("libSystem.B.dylib","CCCryptorFinal")
onLeave: function(retval) {
var cccryptorfinal_re = {}
if(ptr(this.out2) != 0) {
cccryptorfinal_re['dataOut'] = base64ArrayBuffer(Memory.readByteArray(this.out2,parseInt(ptr(Memory.readU32(ptr(this.len2),4)))))
cccryptorfinal_re['dataOutput'] = base64ArrayBuffer(Memory.readByteArray(this.out2,parseInt(ptr(Memory.readU32(ptr(this.len2),4)))))
} else {
cccryptorfinal_re['dataOut'] = null;
cccryptorfinal_re['dataOutput'] = null;
}
send(JSON.stringify({'[MBSFDUMP] crypto': cccryptorfinal_re}));
}
Expand Down
6 changes: 3 additions & 3 deletions mobsf/DynamicAnalyzer/views/android/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ def safe_extract(tar, path='.',
tar.extractall(path, members, numeric_owner=numeric_owner)

safe_extract(tar, untar_dir, members=safe_paths(tar))
except FileExistsError:
pass
except (FileExistsError, tarfile.ReadError):
logger.warning('Failed to extract tar file')
except Exception:
logger.exception('Tar extraction failed')
return True
Expand Down Expand Up @@ -247,7 +247,7 @@ def get_app_files(apk_dir, package):
all_files['plist'].append(
{'type': 'plist', 'file': fileparam})
else:
with open(file_path, # lgtm [py/path-injection]
with open(file_path,

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
This path depends on a
user-provided value
.
'r',
encoding='ISO-8859-1') as flip:
file_cnt_sig = flip.read(6)
Expand Down
2 changes: 1 addition & 1 deletion mobsf/DynamicAnalyzer/views/android/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def view_file(request, api=False):
mode='r',
encoding='ISO-8859-1') as flip:
dat = flip.read()
if fil.endswith('.plist') and 'bplist00' in dat:
if fil.endswith('.plist') and dat.startswith('bplist0'):
dat = writePlistToString(dat).decode('utf-8', 'ignore')
if fil.endswith(('.xml', '.plist')) and typ in ['xml', 'plist']:
rtyp = 'xml'
Expand Down
21 changes: 11 additions & 10 deletions mobsf/DynamicAnalyzer/views/ios/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def get_logs_data(app_dir):
def run_analysis(app_dir, checksum):
"""Run Dynamic File Analysis."""
analysis_result = {}
logger.info('iOS Dynamic File Analysis')
logger.info('Dynamic File Analysis')
domains = {}
# Collect Log data
data = get_logs_data(app_dir)
Expand All @@ -57,21 +57,22 @@ def run_analysis(app_dir, checksum):
# Domain Extraction and Malware Check
logger.info('Performing Malware Check on extracted Domains')
domains = MalwareDomainCheck().scan(urls)

# Email Etraction Regex
emails = []
emails = set()
for email in EMAIL_REGEX.findall(data.lower()):
if (email not in emails) and (not email.startswith('//')):
emails.append(email)
pfiles = get_app_files(
app_dir,
f'{checksum}-app-container')
if email.startswith('//'):
continue
if email.endswith('.png'):
continue
emails.add(email)
# App data files analysis
pfiles = get_app_files(app_dir, f'{checksum}-app-container')
analysis_result['sqlite'] = pfiles['sqlite']
analysis_result['plist'] = pfiles['plist']
analysis_result['others'] = pfiles['others']
analysis_result['urls'] = urls
analysis_result['domains'] = domains
analysis_result['emails'] = emails
analysis_result['emails'] = list(emails)
return analysis_result


Expand All @@ -95,7 +96,7 @@ def ios_api_analysis(app_dir):
dump_file = app_dir / 'mobsf_dump_file.txt'
if not dump_file.exists():

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
return dump
logger.info('Frida Analyzing Dump data')
logger.info('Analyzing Frida Data Dump')
data = dump_file.read_text(

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
encoding='utf-8',
errors='ignore').splitlines()
Expand Down
4 changes: 2 additions & 2 deletions mobsf/DynamicAnalyzer/views/ios/corellium_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def get_supported_models(request, api=False):
if not apikey:
data = {
'status': 'failed',
'message': 'Missing corellium API key'}
'message': 'Missing Corellium API key'}
return send_response(data, api)
cm = CorelliumModelsAPI(apikey)
r = cm.get_models()
Expand All @@ -280,7 +280,7 @@ def get_supported_os(request, api=False):
model = request.POST['model']
apikey = getattr(settings, 'CORELLIUM_API_KEY', '')
if not apikey:
data['message'] = 'Missing corellium API key'
data['message'] = 'Missing Corellium API key'
return send_response(data, api)
cm = CorelliumModelsAPI(apikey)
r = cm.get_supported_os(model)
Expand Down
4 changes: 4 additions & 0 deletions mobsf/DynamicAnalyzer/views/ios/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
common_check,
)
from mobsf.MobSF.utils import (
base64_decode,
is_md5,
key,
pretty_json,
print_n_send_error_response,
replace,
)
Expand All @@ -27,6 +29,8 @@
logger = logging.getLogger(__name__)
register.filter('key', key)
register.filter('replace', replace)
register.filter('pretty_json', pretty_json)
register.filter('base64_decode', base64_decode)


def ios_view_report(request, checksum, api=False):
Expand Down
2 changes: 1 addition & 1 deletion mobsf/DynamicAnalyzer/views/ios/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def common_check(instance_id):
if not getattr(settings, 'CORELLIUM_API_KEY', ''):
return {
'status': 'failed',
'message': 'Missing corellium API key'}
'message': 'Missing Corellium API key'}
elif not is_instance_id(instance_id):
return {
'status': 'failed',
Expand Down
30 changes: 29 additions & 1 deletion mobsf/MobSF/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Common Utils."""
import ast
import base64
import hashlib
import io
import json
import logging
import ntpath
import os
Expand Down Expand Up @@ -31,6 +33,7 @@

logger = logging.getLogger(__name__)
ADB_PATH = None
BASE64_REGEX = re.compile(r'^[-A-Za-z0-9+/]*={0,3}$')


class Color(object):
Expand Down Expand Up @@ -243,7 +246,7 @@ def python_dict(value):


def is_base64(b_str):
return re.match('^[A-Za-z0-9+/]+[=]{0,2}$', b_str)
return BASE64_REGEX.match(b_str)


def is_internet_available():
Expand Down Expand Up @@ -715,6 +718,31 @@ def replace(value, arg):
return value.replace(what, to)


def pretty_json(value):
"""Pretty print JSON."""
try:
return json.dumps(json.loads(value), indent=4)
except Exception:
return value


def base64_decode(value):
"""Try Base64 decode."""
commonb64s = ('eyJ0')
decoded = None
try:
if is_base64(value) or value.startswith(commonb64s):
decoded = base64.b64decode(
value).decode('ISO-8859-1')
if set(decoded).difference(string.printable):
decoded = None
except Exception:
pass
if decoded:
return f'{value}\nBase64 Decoded: {decoded}'
return value


def android_component(data):
"""Return Android component from data."""
cmp = ''
Expand Down
8 changes: 4 additions & 4 deletions mobsf/templates/dynamic_analysis/ios/dynamic_analyzer.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ <h4> Dump </h4>
<input name="dump_hooks" type="checkbox" value="nslog" checked>
NSLog
</label>
<label>
<input name="dump_hooks" type="checkbox" value="text-inputs" checked>
Text Inputs
</label>
<label>
<input name="dump_hooks" type="checkbox" value="nsurlcredentialstorage" checked>
URL Credential Storage
Expand All @@ -232,10 +236,6 @@ <h4> Dump </h4>
<input name="dump_hooks" type="checkbox" value="pasteboard" checked>
Pasteboard
</label>
<label>
<input name="dump_hooks" type="checkbox" value="text-inputs" checked>
Text Inputs
</label>

</div>
<hr/>
Expand Down
Loading

0 comments on commit 196b418

Please sign in to comment.