All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.scanners.android.JavaScriptInterface.scala Maven / Gradle / Ivy

package io.joern.scanners.android

import io.joern.console.*
import io.joern.dataflowengineoss.language.*
import io.joern.dataflowengineoss.queryengine.EngineContext
import io.joern.dataflowengineoss.semanticsloader.Semantics
import io.joern.macros.QueryMacros.*
import io.joern.scanners.*
import io.shiftleft.semanticcpg.language.*

object JavaScriptInterface extends QueryBundle {
  implicit val engineContext: EngineContext = EngineContext(Semantics.empty)
  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 = "-",
      score = 9,
      withStrRep({ cpg =>
        import io.shiftleft.codepropertygraph.generated.nodes.{Call, Identifier}
        import io.shiftleft.semanticcpg.language.android.*

        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
          })
        val appUsesCleartextTraffic = cpg.appManifest.usesCleartextTraffic.nonEmpty
        def exposedJavaScriptInterfaceObjects =
          if (appUsesCleartextTraffic) webViewsWithInsecureLoadUrlCalls.addJavascriptInterfaceCalls.argument(1)
          else Iterator.empty
        val exposedJavaScriptInterfaceObjectNames = exposedJavaScriptInterfaceObjects.collect {
          case ident: Identifier => ident.typeFullName
          case call: Call        => call.typeFullName
        }.l
        def exposedJavaScriptInterfaceMethods =
          cpg.method.exposedToJS
            .where(_.typeDecl.filter { node => exposedJavaScriptInterfaceObjectNames.exists(_ == node.fullName) })
        def runtimeExecCalls =
          cpg.call.name("exec").typeFullName("java.lang.Process")
        runtimeExecCalls.where(_.argument.reachableBy(exposedJavaScriptInterfaceMethods.parameter)).l.iterator
      }),
      tags = List(QueryTags.android),
      multiFileCodeExamples = MultiFileCodeExamples(
        positive = List(
          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.org";
                |        webView.loadUrl(url);
                |        finish();
                |    }
                |}
                |""".stripMargin,
              "io/vroooom/vulnerableapp/MainActivityJava.java"
            ),
            CodeSnippet(
              """|
                |
                |    
                |
                |    
                |
                |        
                |            
                |                
                |                
                |            
                |            
                |        
                |    
                |
                |""".stripMargin,
              "AndroidManifest.xml"
            )
          )
        ),
        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"
            )
          )
        )
      )
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy