I’m investigating the possible ways of obtaining superuser privileges in a Java Android application and/or its own JNI. The well-known answer seems to be that it’s only possible to run a “su” subshell and command line commands from there, which is neither neat nor very practical. I am willing to accept this resolution but still I’d like to hear an opinion on this “what if” scenario.
Reading through Android sources near java.com.android.server.am.ActivityManagerService, java.android.os.Process and the dalvik_system_Zygote.cpp file, it seems to me that during application launch, the application record is examined for the UID and (a list of) GID(s) and all these values are passed to the Zygote through a socket. Z subsequently picks the data up and passes it, without further checks, to setuid() posterior to a fork() call. Therefore, it seems to me that if the Activity Manager pathway was altered, a simple passing of --setuid=0 and perhaps --setgid=0 to the Zygote socket should result in running my Activity with the root UID.
It all seems almost too simple, I suspect that something would go wrong along the way. Unfortunately, there’s too much code and new stuff for an inexperienced programmer like me to actually go and try. Has anyone gone this way, or is there any obvious reason why this would NOT work?
I think I just found the answer to my own question. Credits go to @Chris Stratton who pointed me at using the emulator and also pointed out how ridiculous a situation this would be.
The key was in one place where I did not look, between sending commands through the Zygote socket and the Zygote binary itself. The point where the check takes place is
com.android.internal.os.ZygoteConnection, methodapplyUidSecurityPolicy. If the caller process belongs to root, the UID of the spawn may be indeed requested to be zero (or anything else, for that matter). A regular user may use the socket as well but asking for a new UID or GID results in aZygoteSecurityException.