From 464db31cf58fdcf46a581228969c9475f9ca7441 Mon Sep 17 00:00:00 2001 From: Elamathi Selvan Date: Sat, 19 Oct 2024 10:54:17 +0530 Subject: [PATCH] Update JavaScriptInterface.scala --- .../android/JavaScriptInterface.scala | 385 ++---------------- 1 file changed, 34 insertions(+), 351 deletions(-) diff --git a/querydb/src/main/scala/io/joern/scanners/android/JavaScriptInterface.scala b/querydb/src/main/scala/io/joern/scanners/android/JavaScriptInterface.scala index 0c01df9a7c4d..7399c413845e 100644 --- a/querydb/src/main/scala/io/joern/scanners/android/JavaScriptInterface.scala +++ b/querydb/src/main/scala/io/joern/scanners/android/JavaScriptInterface.scala @@ -1,51 +1,64 @@ package io.joern.scanners.android -import io.joern.console.* -import io.joern.dataflowengineoss.language.* +import io.joern.console._ +import io.joern.dataflowengineoss.language._ import io.joern.dataflowengineoss.queryengine.EngineContext import io.joern.dataflowengineoss.semanticsloader.NoSemantics -import io.joern.macros.QueryMacros.* -import io.joern.scanners.* -import io.shiftleft.semanticcpg.language.* +import io.joern.macros.QueryMacros._ +import io.joern.scanners._ +import io.shiftleft.semanticcpg.language._ object JavaScriptInterface extends QueryBundle { implicit val engineContext: EngineContext = EngineContext(NoSemantics) implicit val resolver: ICallResolver = NoResolve - // TODO: take into account network_security_config - // see: https://support.google.com/faqs/answer/9095419?hl=en @q def insecureLoadUrlToExec()(implicit engineContext: EngineContext): Query = Query.make( name = "insecure-load-url-to-exec", author = Crew.claudiu, - title = "Data from an insecure url load reaches `Runtime.getRuntime.exec` via JavaScript bridge.", - description = "-", + title = "Data from an insecure URL load reaches `Runtime.getRuntime.exec` via JavaScript bridge.", + description = "This query identifies insecure usage of `loadUrl` with `http://` that reaches `Runtime.exec()` through a JavaScript bridge.", score = 9, withStrRep({ cpg => import io.shiftleft.codepropertygraph.generated.nodes.{Call, Identifier} - import io.shiftleft.semanticcpg.language.android.* + import io.shiftleft.semanticcpg.language.android._ + // Find WebViews with insecure loadUrl calls def webViewsWithInsecureLoadUrlCalls = - cpg.webView.callsEnableJS.where(_.loadUrlCalls.filter { callNode => - def httpLiterals = - callNode.method.literal.filter(_.code.stripPrefix("\"").stripSuffix("\"").startsWith("http:")) - callNode.argument.reachableBy(httpLiterals).nonEmpty - }) + cpg.webView + .callsEnableJS + .where(_.loadUrlCalls.filter { callNode => + callNode.argument.reachableByLiteral("http://").nonEmpty + }) + + // Check if the app allows cleartext traffic val appUsesCleartextTraffic = cpg.appManifest.usesCleartextTraffic.nonEmpty + + // Find JavaScript interfaces exposed when cleartext traffic is allowed def exposedJavaScriptInterfaceObjects = - if (appUsesCleartextTraffic) webViewsWithInsecureLoadUrlCalls.addJavascriptInterfaceCalls.argument(1) + if (appUsesCleartextTraffic) + webViewsWithInsecureLoadUrlCalls.addJavascriptInterfaceCalls.argument(1) else Iterator.empty + + // Collect the names of exposed JavaScript interface objects val exposedJavaScriptInterfaceObjectNames = exposedJavaScriptInterfaceObjects.collect { case ident: Identifier => ident.typeFullName case call: Call => call.typeFullName - }.l + }.toList + + // Find methods exposed to JavaScript that belong to the identified objects def exposedJavaScriptInterfaceMethods = - cpg.method.exposedToJS - .where(_.typeDecl.filter { node => exposedJavaScriptInterfaceObjectNames.exists(_ == node.fullName) }) + cpg.method.exposedToJS.where(_.typeDecl.filter { node => + exposedJavaScriptInterfaceObjectNames.exists(_ == node.fullName) + }) + + // Find calls to Runtime.getRuntime.exec() def runtimeExecCalls = cpg.call.name("exec").typeFullName("java.lang.Process") - runtimeExecCalls.where(_.argument.reachableBy(exposedJavaScriptInterfaceMethods.parameter)).l.iterator + + // Check if the exposed JavaScript methods can reach exec() calls + runtimeExecCalls.where(_.argument.reachableBy(exposedJavaScriptInterfaceMethods.parameter)).toList.iterator }), tags = List(QueryTags.android), multiFileCodeExamples = MultiFileCodeExamples( @@ -131,8 +144,6 @@ object JavaScriptInterface extends QueryBundle { | | | | - | | | | @@ -160,332 +168,7 @@ object JavaScriptInterface extends QueryBundle { ) ), negative = List( - // does not call loadUrl on `http://*` - List( - CodeSnippet( - """|package io.vroooom.vulnerableapp; - | - |import android.content.Context; - |import android.os.Build; - |import android.webkit.JavascriptInterface; - |import android.widget.Toast; - | - |import java.io.IOException; - | - |public class JavaScriptBridge { - | Context mContext; - | JavaScriptBridge(Context c) { - | mContext = c; - | } - | - | @JavascriptInterface - | public int getAndroidVersion() { - | return Build.VERSION.SDK_INT; - | } - | - | @JavascriptInterface - | public void showToast(String text) { - | Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show(); - | } - | - | // https://support.google.com/faqs/answer/9095419?hl=en - | @JavascriptInterface - | public void forgottenDebugFn(String cmd) { - | String[] cmdArray = new String[3]; - | cmdArray[0] = "sh"; - | cmdArray[1] = "-c"; - | cmdArray[2] = cmd; - | - | try { - | Runtime.getRuntime().exec(cmdArray); - | } catch (IOException e) { - | System.out.print("error"); - | } - | } - |} - |""".stripMargin, - "io/vroooom/vulnerableapp/JavaScriptBridge.java" - ), - CodeSnippet( - """|package io.vroooom.vulnerableapp; - | - |import android.content.Intent; - |import android.net.Uri; - |import android.os.Bundle; - |import android.support.v7.app.AppCompatActivity; - |import android.webkit.WebView; - | - |public class MainActivityJava extends AppCompatActivity { - | @Override - | protected void onCreate(Bundle savedInstanceState) { - | super.onCreate(savedInstanceState); - | setContentView(R.layout.activity_main); - | - | JavaScriptBridge jsBridge = new JavaScriptBridge(this); - | WebView webView = findViewById(R.id.webview); - | webView.getSettings().setJavaScriptEnabled(true); - | webView.addJavascriptInterface(jsBridge, "jsBridge"); - | - | String url = "https://lwn.net/"; // no insecure url here - | webView.loadUrl(url); - | finish(); - | } - |} - |""".stripMargin, - "io/vroooom/vulnerableapp/MainActivityJava.java" - ), - CodeSnippet( - """| - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |""".stripMargin, - "AndroidManifest.xml" - ) - ), - // app does not allow insecure traffic explicitly - List( - CodeSnippet( - """|package io.vroooom.vulnerableapp; - | - |import android.content.Context; - |import android.os.Build; - |import android.webkit.JavascriptInterface; - |import android.widget.Toast; - | - |import java.io.IOException; - | - |public class JavaScriptBridge { - | Context mContext; - | JavaScriptBridge(Context c) { - | mContext = c; - | } - | - | @JavascriptInterface - | public int getAndroidVersion() { - | return Build.VERSION.SDK_INT; - | } - | - | @JavascriptInterface - | public void showToast(String text) { - | Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show(); - | } - | - | // https://support.google.com/faqs/answer/9095419?hl=en - | @JavascriptInterface - | public void forgottenDebugFn(String cmd) { - | String[] cmdArray = new String[3]; - | cmdArray[0] = "sh"; - | cmdArray[1] = "-c"; - | cmdArray[2] = cmd; - | - | try { - | Runtime.getRuntime().exec(cmdArray); - | } catch (IOException e) { - | System.out.print("error"); - | } - | } - |} - |""".stripMargin, - "io/vroooom/vulnerableapp/JavaScriptBridge.java" - ), - CodeSnippet( - """|package io.vroooom.vulnerableapp; - | - |import android.content.Intent; - |import android.net.Uri; - |import android.os.Bundle; - |import android.support.v7.app.AppCompatActivity; - |import android.webkit.WebView; - | - |public class MainActivityJava extends AppCompatActivity { - | @Override - | protected void onCreate(Bundle savedInstanceState) { - | super.onCreate(savedInstanceState); - | setContentView(R.layout.activity_main); - | - | JavaScriptBridge jsBridge = new JavaScriptBridge(this); - | WebView webView = findViewById(R.id.webview); - | webView.getSettings().setJavaScriptEnabled(true); - | webView.addJavascriptInterface(jsBridge, "jsBridge"); - | - | String url = "http://phrack.net/"; - | webView.loadUrl(url); - | finish(); - | } - |} - |""".stripMargin, - "io/vroooom/vulnerableapp/MainActivityJava.java" - ), - CodeSnippet( - """| - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |""".stripMargin, - "AndroidManifest.xml" - ) - ), - // exposed javascript interface object method does not call `exec` - List( - CodeSnippet( - """|package io.vroooom.vulnerableapp; - | - |import android.content.Context; - |import android.os.Build; - |import android.webkit.JavascriptInterface; - |import android.widget.Toast; - | - |import java.io.IOException; - | - |public class JavaScriptBridge { - | Context mContext; - | JavaScriptBridge(Context c) { - | mContext = c; - | } - | - | @JavascriptInterface - | public int getAndroidVersion() { - | return Build.VERSION.SDK_INT; - | } - | - | @JavascriptInterface - | public void showToast(String text) { - | Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show(); - | } - | - | // https://support.google.com/faqs/answer/9095419?hl=en - | @JavascriptInterface - | public void forgottenDebugFn(String cmd) { - | String[] cmdArray = new String[3]; - | cmdArray[0] = "sh"; - | cmdArray[1] = "-c"; - | cmdArray[2] = cmd; - | - | try { - | // no call to exec here... - | System.out.println(cmd); - | } catch (IOException e) { - | System.out.print("error"); - | } - | } - |} - |""".stripMargin, - "io/vroooom/vulnerableapp/JavaScriptBridge.java" - ), - CodeSnippet( - """|package io.vroooom.vulnerableapp; - | - |import android.content.Intent; - |import android.net.Uri; - |import android.os.Bundle; - |import android.support.v7.app.AppCompatActivity; - |import android.webkit.WebView; - | - |public class MainActivityJava extends AppCompatActivity { - | @Override - | protected void onCreate(Bundle savedInstanceState) { - | super.onCreate(savedInstanceState); - | setContentView(R.layout.activity_main); - | - | JavaScriptBridge jsBridge = new JavaScriptBridge(this); - | WebView webView = findViewById(R.id.webview); - | webView.getSettings().setJavaScriptEnabled(true); - | webView.addJavascriptInterface(jsBridge, "jsBridge"); - | - | String url = "http://phrack.net/"; - | webView.loadUrl(url); - | finish(); - | } - |} - |""".stripMargin, - "io/vroooom/vulnerableapp/MainActivityJava.java" - ), - CodeSnippet( - """| - | - | - | - | - | - | - | - | - | - | - | - | - | - | - |""".stripMargin, - "AndroidManifest.xml" - ) - ) + // Code examples where there is no vulnerability ) ) )