Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependency_installer.py #270

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7b5f3bf
Use unquote to convert spaces. Fixes #151.
ccsplit May 8, 2017
7cc5116
Merge branch 'develop' of github:mwrlabs/needle into develop
ccsplit May 10, 2017
68e05c4
Merge branch 'develop' of github:mwrlabs/needle into develop
ccsplit May 10, 2017
fef010d
Merge branch 'develop' of github:mwrlabs/needle into develop
ccsplit May 12, 2017
f928ef3
Add the start of a way to handle special characters.
ccsplit May 15, 2017
212165f
Merge pull request #162 from ccsplit/develop
marco-lancini May 22, 2017
a7b7911
Remove print statement
May 22, 2017
937bb3d
Merge conflict
Jul 12, 2017
f4d9143
[FIX] Reintroduce comment
Jul 12, 2017
9512c6d
Merge pull request #185 from mwrlabs/pr162
marco-lancini Jul 12, 2017
6de54ae
Merge branch 'develop' of github.com:mwrlabs/needle into develop
Jul 12, 2017
c2a89ca
Improved output
Jul 12, 2017
a3c23c1
[FIX] Search PID for System Apps
Jul 12, 2017
1610218
Automatically spawn Agent when sending a command
Jul 12, 2017
6e79b23
Frida: Switching to default OPEN
Jul 12, 2017
97824f4
Minor on metadata parsing
Jul 12, 2017
760b299
[FIX] Minor fixes
Jul 13, 2017
b5f7f68
[MODULE] Improved certificate pinning bypass
Jul 13, 2017
a0622f2
Merge pull request #186 from mwrlabs/develop
marco-lancini Jul 13, 2017
3e71e42
[V1.3.2] Update version and changelog
Jul 13, 2017
104a7f1
Minor - revert
marco-lancini Jul 13, 2017
fca6775
Update pinning_bypass_frida.py
marco-lancini Jul 13, 2017
e39499e
Update README.md
marco-lancini Aug 7, 2017
d26cf51
External sshpass command now supports complex passwords
invalid-email-address Nov 4, 2017
a937d4e
Update README.md
marco-lancini Dec 5, 2017
e48f1fc
Update README.md
marco-lancini Dec 26, 2017
f627185
Merge pull request #203 from notoriousturtle/master
marco-lancini Jul 26, 2018
77210e9
Revert "External sshpass command now supports complex passwords"
marco-lancini Jul 26, 2018
6a73b82
Merge pull request #259 from mwrlabs/revert-203-master
marco-lancini Jul 26, 2018
52e14f6
Update dependency_installer.py
whoot Jan 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).



## Unreleased
#### Added

## [1.3.2] - 2017-07-13
#### Fixed
- **[CORE]** Search PID for System Apps
- **[MODULE]** Keychain extraction of data not encodable in UTF8 _[from @federicodotta]_
- **[MODULE]** Improved jailbreak detection bypass (`dynamic/detection/script_jailbreak-detection-bypass.py`)

#### Removed
- **[MODULE]** Improved certificate pinning bypass (`comms/proxy/pinning_bypass_frida`)



Expand Down Expand Up @@ -57,6 +55,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- **[CORE]** Remove infinite loop from `Retry` decorator, which attempts to restore a connection with the device if it fails
- **[CORE]** Metadata parsing for app extensions
- **[CORE]** Re-added support on iOS for: `storage/data/keychain_dump`, `binary/reversing/strings`, `binary/reversing/class_dump`
- **[CORE]** Use unquote to convert spaces. Fixes Issue #15 _[from @ccsplit]_



Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
![Needle](https://labs.mwrinfosecurity.com/assets/needle-logo-blue.jpg)

[![Black Hat Arsenal](https://www.toolswatch.org/badges/arsenal/2016.svg)](https://www.blackhat.com/us-16/arsenal.html#needle)
[![Black Hat Arsenal](https://rawgit.com/toolswatch/badges/master/arsenal/2017.svg)](https://www.blackhat.com/us-17/arsenal.html#needle)

_Needle_ is an open source, modular framework to streamline the process of conducting security assessments of iOS apps.

Expand All @@ -9,7 +10,11 @@ _Needle_ is an open source, modular framework to streamline the process of condu

Assessing the security of an iOS application typically requires a plethora of tools, each developed for a specific need and all with different modes of operation and syntax. The Android ecosystem has tools like "[drozer](https://mwr.to/drozer)" that have solved this problem and aim to be a ‘one stop shop’ for the majority of use cases, however iOS does not have an equivalent.

Needle is an open source modular framework which aims to streamline the entire process of conducting security assessments of iOS applications, and acts as a central point from which to do so. Given its modular approach, Needle is easily extensible and new modules can be added in the form of python scripts. Needle is intended to be useful not only for security professionals, but also for developers looking to secure their code. A few examples of testing areas covered by Needle include: data storage, inter-process communication, network communications, static code analysis, hooking and binary protections.​ The only requirement in order to run Needle effectively is a jailbroken device.
[Needle](https://github.com/mwrlabs/needle) is the MWR's iOS Security Testing Framework, released at Black Hat USA in August 2016. It is an open source modular framework which aims to streamline the entire process of conducting security assessments of iOS applications, and acts as a central point from which to do so. Needle is intended to be useful not only for security professionals, but also for developers looking to secure their code. A few examples of testing areas covered by Needle include: data storage, inter-process communication, network communications, static code analysis, hooking and binary protections. The only requirement in order to run Needle effectively is a jailbroken device.

The release of version 1.0.0 provided a major overhaul of its core and the introduction of a new native agent, written entirely in Objective-C. The new [NeedleAgent](https://github.com/mwrlabs/needle-agent) is an open source iOS app complementary to Needle, that allows to programmatically perform tasks natively on the device, eliminating the need for third party tools. 

Needle has been presented at and used by workshops in various international conferences like Black Hat USA/EU, OWASP AppSec and DEEPSEC. It was also included by ToolsWatch in the shortlist for the [Top Security Tools of 2016](http://www.toolswatch.org/2017/02/2016-top-security-tools-as-voted-by-toolswatch-org-readers/), and it is featured in the [OWASP Mobile Testing Guide](https://github.com/OWASP/owasp-mstg).

Needle is open source software, maintained by [MWR InfoSecurity](https://www.mwrinfosecurity.com/).

Expand Down
21 changes: 14 additions & 7 deletions needle/core/device/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def _retrieve_metadata(self):
metadata_agent = self.__parse_from_agent()

# Content of the app's local Info.plist
plist_info_path = Utils.escape_path('%s/Info.plist' % metadata_agent['binary_directory'])
plist_info_path = Utils.escape_path('%s/Info.plist' % metadata_agent['binary_directory'], escape_accent=True)
plist_info = self._device.remote_op.parse_plist(plist_info_path)
metadata_info = self.__parse_plist_info(plist_info)

Expand Down Expand Up @@ -54,9 +54,9 @@ def __parse_from_agent(self):
name = self.__extract_field(agent_info, 'DisplayName').encode('ascii','replace')
bundle_type = self.__extract_field(agent_info, 'BundleType')
bundle_id = self.__extract_field(agent_info, 'BundleIdentifier')
data_directory = self.__extract_field(agent_info, 'DataContainer', path=True)
bundle_directory = self.__extract_field(agent_info, 'BundleContainer', path=True)
binary_directory = self.__extract_field(agent_info, 'BundleURL', path=True)
data_directory = self.__extract_field(agent_info, 'DataContainer', path=True, urldecode=True)
bundle_directory = self.__extract_field(agent_info, 'BundleContainer', path=True, urldecode=True)
binary_directory = self.__extract_field(agent_info, 'BundleURL', path=True, urldecode=True)
app_version = self.__extract_field(agent_info, 'BundleVersion')
sdk_version = self.__extract_field(agent_info, 'SDKVersion')
entitlements = self.__extract_field(agent_info, 'Entitlements')
Expand Down Expand Up @@ -120,14 +120,21 @@ def __detect_architectures(self, binary):
res = msg.rsplit(': ')[-1].split(' ')
return res

def __extract_field(self, data, field, path=False):
def __extract_field(self, data, field, path=False, urldecode=False):
"""Extract the specified entry from the plist file. Returns empty string if not present."""
try:
temp = data[field]
if urldecode:
try:
from urllib.parse import unquote
except ImportError:
# Python 2.x
from urllib import unquote
temp = unquote(temp)
if path:
prefix = 'file://'
if temp.startswith(prefix):
temp = temp[len(prefix):]
temp = Utils.escape_path(temp[len(prefix):])
return temp
except:
return ""
Expand Down Expand Up @@ -179,7 +186,7 @@ def search_pid(self, binary_name):
cmd = "ps ax | grep -i '{binary_name}'".format(binary_name=binary_name)
out = self._device.remote_op.command_blocking(cmd)
try:
process_list = filter(lambda x: '/var/mobile' in x, out)
process_list = filter(lambda x: 'grep' not in x, out)
if not process_list:
process_list = filter(lambda x: '/var/containers' in x, out)
process = process_list[0].strip()
Expand Down
2 changes: 1 addition & 1 deletion needle/core/framework/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ class FridaScript(FridaModule):
def __init__(self, params):
FridaModule.__init__(self, params)
# Add option for launch mode
opt = ('spawn', True, True, 'If set to True, Frida will be used to spawn the app. '
opt = ('spawn', False, True, 'If set to True, Frida will be used to spawn the app. '
'If set to False, the app will be launched and Frida will be attached to the running instance')
self.register_option(*opt)
opt = ('resume', True, True, 'If set to True, Frida will resume the application process after spawning it (recommended)')
Expand Down
5 changes: 4 additions & 1 deletion needle/core/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Constants(object):
AUTHOR = 'MWR InfoSecurity (@MWRLabs) - Marco Lancini (@LanciniMarco)'
EMAIL = '[email protected]'
WEBSITE = 'mwr.to/needle'
VERSION = '1.3.1'
VERSION = '1.3.2'
VERSION_CHECK = 'https://raw.githubusercontent.com/mwrlabs/needle/master/needle/core/utils/constants.py'

# Name variables
Expand Down Expand Up @@ -50,6 +50,7 @@ class Constants(object):
# AGENT CONSTANTS
AGENT_TAG = "[AGENT]"
AGENT_WELCOME = "Welcome to Needle Agent"
AGENT_BUNDLE_ID = "mwr.needle.agent"
AGENT_VERSION_MARK = "VERSION: "
AGENT_OUTPUT_END = " :OUTPUT_END:"
AGENT_TIMEOUT_READ = 5
Expand All @@ -62,6 +63,8 @@ class Constants(object):
'10': [
'binary/installation/install',
'binary/installation/pull_ipa',
'binary/reversing/class_dump',
'binary/reversing/strings'
]
}

Expand Down
6 changes: 5 additions & 1 deletion needle/core/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ class Utils(object):
# PATH UTILS
# ==================================================================================================================
@staticmethod
def escape_path(path):
def escape_path(path, escape_accent=False):
"""Escape the given path."""
import pipes
path = path.strip() # strip
path = path.strip(''''"''') # strip occasional single/double quotes from both sides
if escape_accent:
# Find the accents/backquotes that do not have a backslash
# in front of them and escape them.
path = re.sub('(?<!\\\\)`', '\`', path)
return pipes.quote(path)

@staticmethod
Expand Down
1 change: 1 addition & 0 deletions needle/modules/comms/proxy/pinning_bypass_frida.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class Module(FridaScript):
return SSLSetSessionOption(context, option, value);
}, 'int', ['pointer', 'int', 'bool']));


//
// OLD WAY
//
Expand Down
2 changes: 1 addition & 1 deletion needle/modules/device/dependency_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __apt_add_repo(self, repo):

def __apt_install(self, package):
"""Install the given package using apt-get."""
cmd = '{apt} install -y --force-yes {package}'.format(apt=Constants.DEVICE_TOOLS['APT-GET'], package=package)
cmd = '{apt} install -y --allow-downgrades --allow-remove-essential --allow-change-held-packages {package}'.format(apt=Constants.DEVICE_TOOLS['APT-GET'], package=package)
self.device.remote_op.command_blocking(cmd, internal=True)

def __install_package(self, toolname, tool):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,6 @@ class Module(FridaScript):
"CYObjectiveC",
"frida_agent_main"];

Interceptor.attach(ObjC.classes.NSFileManager["- fileExistsAtPath:"].implementation, {
onEnter: function (args) {
this.path_to_hide = false;
this.path = ObjC.Object(args[2]).toString();
if (paths.indexOf(this.path) >= 0) {
this.path_to_hide = true;
send("Hooking fileExistsAtPath to return false");
}
},
onLeave: function (retval) {
if (this.path_to_hide) {
retval.replace(0x0);
}
}
});

var resolver = new ApiResolver('objc');
resolver.enumerateMatches('*[* is*ailbroken]', {
onMatch: function (match) {
Expand Down
12 changes: 7 additions & 5 deletions needle/modules/storage/data/keychain_dump_frida.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class Module(FridaScript):
for (var i = 0; i < result.count(); i++){
var entry = result.objectAtIndex_(i);
send(JSON.stringify({
Data: bytesToHex(Memory.readByteArray(entry.objectForKey_("v_Data").bytes(),entry.objectForKey_("v_Data").length())) +
Data: bytesToHex(Memory.readByteArray(entry.objectForKey_("v_Data").bytes(),entry.objectForKey_("v_Data").length())) +
( ObjC.classes.NSString.stringWithUTF8String_(entry.objectForKey_("v_Data").bytes()) ? " (UTF8 String: '" + ObjC.classes.NSString.stringWithUTF8String_(entry.objectForKey_("v_Data").bytes()).valueOf() + "')": "" ),
EntitlementGroup: entry.objectForKey_("agrp").valueOf(),
Protection: constants[entry.objectForKey_("pdmn")].valueOf(),
Expand Down Expand Up @@ -167,7 +167,9 @@ def module_run(self):
self.printer.warning(e)

def module_post(self):
self.printer.info("Keychain Items:")
self.print_cmd_output()
self.add_issue('Keychain items detected ({})'.format(len(self.results)), None, 'INVESTIGATE', self.options['output'])

if self.results:
self.printer.info("Keychain Items:")
self.print_cmd_output()
self.add_issue('Keychain items detected ({})'.format(len(self.results)), None, 'INVESTIGATE', self.options['output'])
else:
self.printer.warning("No items found.")