CVE-2014-0073: Permissions, Privileges, and Access Controls (CWE-264)
value: The CDVInAppBrowser class in the Apache Cordova In-App-Browser standalone plugin (org.apache.cordova.inappbrowser) before 0.3.2 for iOS and the In-App-Browser plugin for iOS from Cordova 2.6.0 through 2.9.0 does not properly validate callback identifiers, which allows remote attackers to execute arbitrary JavaScript in the host page and consequently gain privileges via a crafted gap-iab: URI.
CWE description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
CVSS v3 base score: 9.8
Published: 2017-10-30T19:29Z
Repository: https://github.com/apache/cordova-plugin-inappbrowser
Language: Objective-C
Files changed: src/ios/CDVInAppBrowser.h, src/ios/CDVInAppBrowser.m
Commit date: 2014-02-19 00:26:19 -0500
Commit message: Validate that callbackId is correctly formed

Diff: diff --git a/src/ios/CDVInAppBrowser.h b/src/ios/CDVInAppBrowser.h
index 581bcd0..8e2ab12 100644
--- a/src/ios/CDVInAppBrowser.h
+++ b/src/ios/CDVInAppBrowser.h
@@ -28,10 +28,11 @@
     BOOL _injectedIframeBridge;
 }
 
 @property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController;
 @property (nonatomic, copy) NSString* callbackId;
+@property (nonatomic, copy) NSRegularExpression *callbackIdPattern;
 
 - (void)open:(CDVInvokedUrlCommand*)command;
 - (void)close:(CDVInvokedUrlCommand*)command;
 - (void)injectScriptCode:(CDVInvokedUrlCommand*)command;
 - (void)show:(CDVInvokedUrlCommand*)command;
diff --git a/src/ios/CDVInAppBrowser.m b/src/ios/CDVInAppBrowser.m
index 213cb73..88b737c 100644
--- a/src/ios/CDVInAppBrowser.m
+++ b/src/ios/CDVInAppBrowser.m
@@ -45,10 +45,11 @@ @implementation CDVInAppBrowser
 - (CDVInAppBrowser*)initWithWebView:(UIWebView*)theWebView
 {
     self = [super initWithWebView:theWebView];
     if (self != nil) {
         _previousStatusBarStyle = -1;
+        _callbackIdPattern = nil;
     }
 
     return self;
 }
 
@@ -295,10 +296,27 @@ - (void)injectStyleFile:(CDVInvokedUrlCommand*)command
         jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
     }
     [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
 }
 
+- (BOOL)isValidCallbackId:(NSString *)callbackId
+{
+    NSError *err = nil;
+    // Initialize on first use
+    if (self.callbackIdPattern == nil) {
+        self.callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"^InAppBrowser[0-9]{1,10}$" options:0 error:&err];
+        if (err != nil) {
+            // Couldn't initialize Regex; No is safer than Yes.
+            return NO;
+        }
+    }
+    if ([self.callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
+        return YES;
+    }
+    return NO;
+}
+
 /**
  * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging
  * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no
  * other code execution is possible.
  *
@@ -321,11 +339,11 @@ - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*
     // and the path, if present, should be a JSON-encoded value to pass to the callback.
     if ([[url scheme] isEqualToString:@"gap-iab"]) {
         NSString* scriptCallbackId = [url host];
         CDVPluginResult* pluginResult = nil;
 
-        if ([scriptCallbackId hasPrefix:@"InAppBrowser"]) {
+        if ([self isValidCallbackId:scriptCallbackId]) {
             NSString* scriptResult = [url path];
             NSError* __autoreleasing error = nil;
 
             // The message should be a JSON-encoded array of the result of the script which executed.
             if ((scriptResult != nil) && ([scriptResult length] > 1)) {

Vulnerable code:         if ([scriptCallbackId hasPrefix:@"InAppBrowser"]) {
Fixed code: @property (nonatomic, copy) NSRegularExpression *callbackIdPattern;
        _callbackIdPattern = nil;
- (BOOL)isValidCallbackId:(NSString *)callbackId
{
    NSError *err = nil;
    // Initialize on first use
    if (self.callbackIdPattern == nil) {
        self.callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"^InAppBrowser[0-9]{1,10}$" options:0 error:&err];
        if (err != nil) {
            // Couldn't initialize Regex; No is safer than Yes.
            return NO;
        }
    }
    if ([self.callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
        return YES;
    }
    return NO;
}

        if ([self isValidCallbackId:scriptCallbackId]) {