Given the plugins that are available in the Nativescript community, your Nativescript app may or may not be sufficient to pass security penetration testing.
Below are two plugins to list a few.
https://www.npmjs.com/package/#nstudio/root-detection
https://www.npmjs.com/package/nativescript-jailbreak-detector
In some scenarios, you could achieve better results by manually writing your own checks against a jailbreak and dynamic instrumentation (e.g. Frida), since there are so many tools to bypass jailbreak detection (e.g. HideJB) nowadays.
What are some ways we can detect jailbreak and protect against dynamic instrumentation on iOS Nativescript?
Detection can be carried out on multi-levels:
Check if URLs are openable via illegal URL schemes
Check if files are openable on illegal directories
Check if illegal files exist (incl. Cydia, Sileo, HideJB, etc.)
Check if files are writable on restricted directories
Code
public amIJailbroken(): boolean {
let urlSchemes: Array<string> = ['undecimus://', 'cydia://', 'sileo://', 'zbra://', 'filza://', 'activator://'];
// List of suspicious files associated with jailbreak
let paths: Array<string> = [
'/.bootstrapped_electra',
'/.cydia_no_stash',
'/.installed_unc0ver',
'/Applications/blackra1n.app',
'/Applications/Cydia.app',
'/Applications/FakeCarrier.app',
'/Applications/HideJB.app',
'/Applications/Icy.app',
'/Applications/IntelliScreen.app',
'/Applications/MxTube.app',
'/Applications/RockApp.app',
'/Applications/SBSettings.app',
'/Applications/SBSetttings.app',
'/Applications/Sileo.app',
'/Applications/Snoop-itConfig.app',
'/Applications/WinterBoard.app',
'/bin.sh',
'/bin/bash',
'/bin/sh',
'/etc/apt',
'/etc/apt/sources.list.d/electra.list',
'/etc/apt/sources.list.d/sileo.sources',
'/etc/apt/undecimus/undecimus.list',
'/etc/ssh/sshd_config',
'/jb/amfid_payload.dylib',
'/jb/jailbreakd.plist',
'/jb/libjailbreak.dylib',
'/jb/lzma',
'/jb/offsets.plist',
'/Library/dpkg/info/re.frida.server.list',
'/Library/LaunchDaemons/re.frida.server.plist',
'/Library/MobileSubstrate/CydiaSubstrate.dylib',
'/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist',
'/Library/MobileSubstrate/DynamicLibraries/Veency.plist',
'/Library/MobileSubstrate/HideJB.dylib',
'/Library/MobileSubstrate/MobileSubstrate.dylib',
'/Library/PreferenceBundles/ABypassPrefs.bundle',
'/Library/PreferenceBundles/FlyJBPrefs.bundle',
'/Library/PreferenceBundles/HideJBPrefs.bundle',
'/Library/PreferenceBundles/LibertyPref.bundle',
'/Library/PreferenceBundles/ShadowPreferences.bundle',
'/private/etc/apt',
'/private/etc/dpkg/origins/debian',
'/private/etc/ssh/sshd_config',
'/private/var/cache/apt/',
'/private/var/lib/apt',
'/private/var/lib/apt/',
'/private/var/lib/cydia',
'/private/var/log/syslog',
'/private/var/mobile/Library/SBSettings/Themes',
'/private/var/mobileLibrary/SBSettingsThemes/',
'/private/var/stash',
'/private/var/tmp/cydia.log',
'/private/var/Users/',
'/System/Library/LaunchDaemons/com.ikey.bbot.plist',
'/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist',
'/usr/bin/cycript',
'/usr/bin/ssh',
'/usr/bin/sshd',
'/usr/lib/libcycript.dylib',
'/usr/lib/libhooker.dylib',
'/usr/lib/libjailbreak.dylib',
'/usr/lib/libsubstitute.dylib',
'/usr/lib/substrate',
'/usr/lib/TweakInject',
'/usr/libexec/cydia/',
'/usr/libexec/cydia/firmware.sh',
'/usr/libexec/sftp-server',
'/usr/libexec/ssh-keysign',
'/usr/local/bin/cycript',
'/usr/sbin/frida-server',
'/usr/sbin/sshd',
'/usr/share/jailbreak/injectme.plist',
'/var/binpack',
'/var/cache/apt',
'/var/checkra1n.dmg',
'/var/lib/apt',
'/var/lib/cydia',
'/var/lib/dpkg/info/mobilesubstrate.md5sums',
'/var/log/apt',
'/var/log/syslog',
'/var/tmp/cydia.log',
];
// Check if target is not an iOS simulator
if (!isIOS || !this.isTarget()) return false;
else {
// Check URL schemes
for (const url of urlSchemes) {
if (this.canOpenIllegalURL(url)) return true;
}
// Check files and directories associated with jailbreaks
for (const path of paths) {
if (this.canOpenIllegalFile(path)) return true;
}
// Check file permissions outside device sandbox, if writtable = jailbroken
if (this.canWriteToRestrictedDirectories()) return true;
return false;
}
}
/*
********** Helper Methods **********
*/
/* Check if environment is being run as a RELEASE build */
private isTarget() {
return process.env.RELEASE_ENV;
}
/* Check if we can open illegal URL schemes */
private canOpenIllegalURL(url): boolean {
return UIApplication.sharedApplication.canOpenURL(NSURL.URLWithString(url + 'package/com.example.app'));
}
/* Check if file is openable */
private canOpenIllegalFile(path): boolean {
const file = fopen(path, 'r');
if (!file) {
fclose(file);
return this.fileExists(path) || this.directoryExists(path);
}
fclose(file);
return true;
}
/* Check if file exists at path */
private fileExists(path): boolean {
return NSFileManager.defaultManager.fileExistsAtPath(path);
}
/* Check if directory exists at path */
private directoryExists(path): boolean {
return NSFileManager.defaultManager.fileExistsAtPathIsDirectory(path, new interop.Reference());
}
/* Check if file is writtable to illegal directory */
private canWriteToRestrictedDirectories(): boolean {
let error;
try {
const stringToBeWritten = NSString.stringWithString('I am evil.');
stringToBeWritten.writeToFileAtomicallyEncodingError('/private/jailbreak.txt', true, NSUTF8StringEncoding);
stringToBeWritten.writeToFileAtomicallyEncodingError('/root/jailbreak.txt', true, NSUTF8StringEncoding);
NSFileManager.defaultManager.removeItemAtPathError('/private/jailbreak.txt');
NSFileManager.defaultManager.removeItemAtPathError('/root/jailbreak.txt');
} catch (e) {
error = e;
}
return !error ? true : false;
}
References
The research comes from a consolidation of the following ideas:
https://stackoverflow.com/a/26712383/2192332
https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06j-testing-resiliency-against-reverse-engineering
https://github.com/securing/IOSSecuritySuite/blob/master/IOSSecuritySuite/JailbreakChecker.swift
https://github.com/avltree9798/isJailbroken/blob/master/isJailbroken/JB.m
Improvements
Please feel free to suggest!
E.g. Checking for illegal dynamic libraries in memory using _dyld_get_image_name
I am creating a pst from message files which are located in another machine on a same network. But when I loaded the pst, messages are not rendered. I have added a screenshot. And code is below:
Issue do not occur when message files are imported from my local machine.
private static void GeneratePST(string [] messageFiles, string outputPstPath)
{
RDOSession pstSession = null;
RDOPstStore store = null;
RDOFolder folder = null;
RDOMail rdo_Mail = null;
RDOItems items = null;
try
{
pstSession = new RDOSession();
store = pstSession.LogonPstStore(outputPstPath, 1, Path.GetFileNameWithoutExtension(outputPstPath));
folder = store.IPMRootFolder;
folder = folder.Folders.Add("Loose Messages");
foreach (string messages in messageFiles)
{
items = folder.Items;
rdo_Mail = items.Add("IPM.NOTE");
rdo_Mail.Import(messages, rdoSaveAsType.olMSG);
rdo_Mail.Save();
}
}
catch (Exception ex)
{
//log exception
}
finally
{
Marshal.ReleaseComObject(rdo_Mail);
Marshal.ReleaseComObject(folder);
Marshal.ReleaseComObject(store);
Marshal.ReleaseComObject(items);
pstSession.Logoff();
Marshal.ReleaseComObject(pstSession);
GC.Collect();
}
}
I have also impersonated the network machine before importing message file. But still the issue persist.
The problem only exists for files in another machine. Messages are rendered for msg file located in my machine. Also, I noticed issue is only with message files. Eml file are rendered. So, it might not be the issue of impersonation.
Any help please.
Microsoft does not support accessing PST files on network drives. They must be on a local machine.
Also, there is no reason to continuously retrieve the RDOItems object - you never release on the old value, so those objects stay alive until your app exits. Ditto for the rdo_Mail object:
folder = folder.Folders.Add("Loose Messages");
items = folder.Items;
foreach (string messages in messageFiles)
{
if (rdo_Mail != null) Marshal.ReleaseComObject(rdo_Mail);
rdo_Mail = items.Add("IPM.NOTE");
rdo_Mail.Import(messages, rdoSaveAsType.olMSG);
rdo_Mail.Save();
}
I have been trying to address an error generated in SharePoint 2010 that occurs when I update a list item that has a Microsoft Office document attached. If I make changes to the attached document (by clicking its link in the list item) and then attempt to save the list item I get the error message below.
save_conflict_error
I am trying to capture and deal with this error using the ItemUpdating event receiver
The receiver never catches the save conflict exception in the try catch.
I have tried everything that has been suggested in about 4 pages of google searches and I have run out of things to try. This is a last desperate attempt to find a solution (if there IS one).
Below is my code for the ItemUpdating event receiver.
public override void ItemUpdating(SPItemEventProperties properties)
{
try
{
base.ItemUpdating(properties);
using (SPSite site = properties.OpenSite())
{
using (SPWeb web = site.OpenWeb())
{
//determine list
if (properties.List.Title.ToLower() == "mytestlist")
{
web.AllowUnsafeUpdates = true;
this.EventFiringEnabled = false;
properties.List.Update();
} //endif
} //end using
} //end using
}
catch (Exception ex) {
{
//abort the update
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.ErrorMessage = ex.Message;
properties.Cancel = true;
} //end try
}
} //end function
Here is my Elements.xml file also.
elements_xml
Thank you in advance.
:)
I want to store position coords (latitude, longitude) in a table in my MySQL DB querying a url in a way similar to this one: http://locationstore.com/postlocation.php?latitude=var1&longitude=var2 every ten seconds. PHP script works like a charm. Getting the coords in the device ain't no problem either. But making the request to the server is being a hard one. My code goes like this:
public class LocationHTTPSender extends Thread {
for (;;) {
try {
//fetch latest coordinates
coords = this.coords();
//reset url
this.url="http://locationstore.com/postlocation.php";
// create uri
uri = URI.create(this.url);
FireAndForgetDestination ffd = null;
ffd = (FireAndForgetDestination) DestinationFactory.getSenderDestination
("MyContext", uri);
if(ffd == null)
{
ffd = DestinationFactory.createFireAndForgetDestination
(new Context("MyContext"), uri);
}
ByteMessage myMsg = ffd.createByteMessage();
myMsg.setStringPayload("doesnt matter");
((HttpMessage) myMsg).setMethod(HttpMessage.POST);
((HttpMessage) myMsg).setQueryParam("latitude", coords[0]);
((HttpMessage) myMsg).setQueryParam("longitude", coords[1]);
((HttpMessage) myMsg).setQueryParam("user", "1");
int i = ffd.sendNoResponse(myMsg);
ffd.destroy();
System.out.println("Lets sleep for a while..");
Thread.sleep(10000);
System.out.println("woke up");
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("Exception message: " + e.toString());
e.printStackTrace();
}
}
I haven't run this code to test it, but I would be suspicious of this call:
ffd.destroy();
According to the API docs:
Closes the destination. This method cancels all outstanding messages,
discards all responses to those messages (if any), suspends delivery
of all incoming messages, and blocks any future receipt of messages
for this Destination. This method also destroys any persistable
outbound and inbound queues. If Destination uses the Push API, this
method will unregister associated push subscriptions. This method
should be called only during the removal of an application.
So, if you're seeing the first request succeed (at least sometimes), and subsequent requests fail, I would try removing that call to destroy().
See the BlackBerry docs example for this here
Ok so I finally got it running cheerfully. The problem was with the transport selection; even though this example delivered WAP2 (among others) as an available transport in my device, running the network diagnostics tool showed only BIS as available. It also gave me the connection parameters that I needed to append at the end of the URL (;deviceside=false;ConnectionUID=GPMDSEU01;ConnectionType=mds-public). The code ended up like this:
for (;;) {
try {
coords.refreshCoordinates();
this.defaultUrl();
this.setUrl(stringFuncs.replaceAll(this.getUrl(), "%latitude%", coords.getLatitude() + ""));
this.setUrl(stringFuncs.replaceAll(this.getUrl(), "%longitude%", coords.getLongitude() + ""));
cd = cf.getConnection(this.getUrl());
if (cd != null) {
try {
HttpConnection hc = (HttpConnection)cd.getConnection();
final int i = hc.getResponseCode();
hc.close();
} catch (Exception e) {
}
}
//dormir
Thread.sleep(15000);
} catch (Exception e) {
} finally {
//cerrar conexiones
//poner objetos a null
}
Thanks for your help #Nate, it's been very much appreciated.
I pretty much copied this code right out of the MDN File I/O page.. except I added an if statement to check if the file exists already and if it does, read it instead.
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
var file = Components.classes["#mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("Desk", Components.interfaces.nsIFile);
file.append("test.txt");
if (!file.exists()) {
this.user_id = Math.floor(Math.random()*10001) +'-'+ Math.floor(Math.random()*10001) +'-'+ Math.floor(Math.random()*10001) +'-'+ Math.floor(Math.random()*10001);
var ostream = FileUtils.openSafeFileOutputStream(file)
var converter = Components.classes["#mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var istream = converter.convertToInputStream(this.user_id);
// The last argument (the callback) is optional.
NetUtil.asyncCopy(istream, ostream, function(status) {
if (!Components.isSuccessCode(status)) {
alert('Error '+ status);
return;
}
alert('File created');
});
} else
{
NetUtil.asyncFetch(file, function(inputStream, status) {
if (!Components.isSuccessCode(status)) {
alert('error '+ status);
return;
}
// The file data is contained within inputStream.
// You can read it into a string with
this.user_id = NetUtil.readInputStreamToString(inputStream, inputStream.available());
});
alert('File exists already, do not create');
}
alert(this.user_id);
It creates the file just fine, I can open it and read it. If the file already exists however, it does not populate this.user_id.. just equals null. So my issue is specifically with reading the file.
File reading in your code works asynchronously - meaning that your code completes (including the alert() call which will show that this.user_id is null), then at some point the callback from NetUtil.asyncFetch() gets called with the data. Until that happens this.user_id won't be set of course. If you move alert(this.user_id) into the callback function it should show the correct value.
Note that it is highly recommended to keep file I/O operations asynchronous because they might take significant time depending on the current state of the file system. But you have to structure your code in such a way that it doesn't assume that file operations happen immediately.