Preface
When digging into mobile terminals, we usually pay attention to cross-terminal issues, because we can directly access the native code from the webview container. From the client's perspective, we can directly go deep into the client from the front end, so there is more research on the cross-terminal aspect. significance.
This article introduces in detail the basic knowledge of cross-end communication from Android front-end to client and related vulnerability mining ideas.
basic knowledge
When we want to understand cross-end (here refers to front-end and client cross-end), we usually need to master some basic knowledge, as follows:
1. Cross-end
Cross-end development of mobile front-end and client usually refers to using a technology or framework to simultaneously apply it to the front-end and client development of mobile applications. This cross-end technology typically uses Web technologies (such as HTML, CSS, and JavaScript) to develop applications and then integrates them into native applications through WebView or similar technologies.
In this cross-end development, front-end developers can use familiar Web technologies to develop applications without the need to learn and use native development languages and tools. At the same time, client developers can use native development languages and tools to integrate front-end developed applications.
2. Common mobile front-end and client cross-end technologies
1. React Native: React Native can use Web technology to develop applications and interact with native applications through JavaScript Bridge technology to achieve cross-end development.
2. Flutter: Flutter can use Web technology to develop applications and interact with native applications through Flutter Engine technology to achieve cross-end development.
3. WebView: WebView is a native control that can embed Web applications in native applications. Front-end developers can use Web technologies to develop applications and then integrate them into native applications through WebView technology.
4. Hybrid: Hybrid is a cross-end development model that combines native and Web technologies. Web technologies can be used to develop applications, and native technologies can be used to implement underlying functions.
3.1 React Native - ReactContextBaseJavaModule
From an attacker's perspective, I'm more concerned about how the front-end calls the client.
In React Native, Native Modules are used to encapsulate native functions into functions or methods that JavaScript can call.
In the native code, create a Native Module through which the client code can be called. For example, on the Android platform, you can create a Java class, implement ReactContextBaseJavaModule
the interface, and define a function in the class that can be called by JavaScript. The sample code is as follows
public class MyNativeModule extends ReactContextBaseJavaModule {
private Context mContext;
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
mContext = reactContext;
}
@Override
public String getName() {
return "MyNativeModule";
}
@ReactMethod
public void showSessionID() {
Toast.makeText(mContext, getCookie(), Toast.LENGTH_SHORT).show();
}
}
3.2 React Native - ReactApplication
Inherit ReactApplication and rewrite the getPackages function to register the module we created in 3.1.
import com.example.MyNativeModule;
...
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.asList(
new MainReactPackage(),
new MyNativeModule()
);
}
};
}
3.3 React Native - Front-end call
The front end can directly trigger the nativemodule's showSessionID method through events.
import React from 'react';
import { TouchableOpacity, Text, NativeModules } from 'react-native';
const MyButton = () => {
const onPress = () => {
NativeModules.MyNativeModule.showSessionID();
};
return (
<TouchableOpacity onPress={onPress}>
<Text>Click me</Text>
</TouchableOpacity>
);
};
export default MyButton;
So if there is remote dynamic loading of some front-end code, or storage classes or other security issues can control the front-end code, then we can further call the registered client code, such as the above-mentioned sessionid.
4.1 webview - @JavascriptInterface
Create a new javascript interface
class MyNativeInterface {
@JavascriptInterface
public void showCookie() {
Toast.makeText(mContext, getcookie(), Toast.LENGTH_SHORT).show();
}
}
4.2 webview - addJavascriptInterface
Register the interface into the webview container
import android.webkit.WebView;
import android.webkit.WebViewClient;
...
public class MainActivity extends AppCompatActivity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = findViewById(R.id.webView);
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new MyNativeInterface(), "MyNativeInterface");
}
}
4.3 webview - front-end call
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebView Example</title>
</head>
<body>
<button onclick="window.MyNativeInterface.showcookie()">Click me</button>
</body>
</html>
Webview attacks are relatively smooth, because as long as you open a webview container, you can have the opportunity to attack the registered cross-end interface. Scan, Deeplink, IM, search, etc.
5. Hybrid
Hybrid applications are more complex than ordinary WebView applications and need to implement some native functions in the client code, such as calling system APIs, accessing local databases, etc., a development model that combines Web technology and native technology for development Cross-platform applications. But the implementation principle is webview. So just refer to 4.1~4.3.
6. Flutter
The implementation principle is similar to React Native. Inherit MethodCallHandler to define the channel and write the cross-end method -> setMethodCallHandler registers the channel to Flutter -> the front end calls the cross-end method.
public class MyChannel implements MethodCallHandler {
private static final String CHANNEL_NAME = "com.example.my_channel";
private Context mContext;
private MyChannel(Context context) {
mContext = context;
}
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
channel.setMethodCallHandler(new MyChannel(registrar.context()));
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("showToast")) {
String message = call.argument("message");
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
result.success(true);
} else {
result.notImplemented();
}
}
}
--------
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL_NAME = "com.example.my_channel";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL_NAME)
.setMethodCallHandler(new MyChannel(this));
}
}
-----------
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
static const platform = const MethodChannel('com.example.my_channel');
void _showToast() async {
try {
await platform.invokeMethod('showToast', {'message': 'Hello, world!'});
} on PlatformException catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: RaisedButton(
child: Text('Click me'),
onPressed: _showToast,
),
),
),
);
}
}
Practical exercises
We reverse the APK, search whether the webview container has injected cross-end methods, and search globally for @JavascriptInterface
Then the found cross-end method analyzes the structure of the calling parameters and filters out all sensitive methods, then constructs the front-end calling point and triggers the client code.
For example poc
<script>window.xxxx.yyyy(aaa,bbb);
Afterword
Different application vendors may choose different solutions for cross-end applications, or develop some cross-end frameworks themselves. At this time, you may need to reverse-engineer the code logic before proceeding. In addition, cross-end calls are often not smooth sailing. Developers will add various restrictions, such as permission restrictions, access domain name restrictions, etc. This also requires further bypassing ideas, and this requires slowly diverging ideas and learning.