Hacking 3ware’s management utility for setuid programs

Warning

If you do any of these hacks, be sure that you do NOT install the tw_cli program itself setuid root; use sudo, or another wrapper that filters user access to running tw_cli as root. If you do not take appropriate precautions, any user will be able to run tw_cli, bugs and all, and have all the powers of root while doing so!

Problem and solutions

3Ware’s management utility for their RAID cards under Linux is called tw_cli. I have found that it may be desirable to script certain activities in the tw_cli. One such instance required writing a setuid wrapper program so that a non-root user could invoke tw_cli as root (a sudo setup would be similar). But the tw_cli program unfortunately does a getuid() check against root (the precise system call according to strace(1) is getuid32()). Since in a setuid environment the effective user ID is root but the real user ID is non-root, this check fails and tw_cli refuses to run. Aside from getting 3ware to change this call to geteuid(), the user would be out of luck.

Actually, we are not totally out of luck. tw_cli is stripped, which makes binary analysis difficult, but it is statically linked. This aids analysis because all of the code is included in the binary. On IA-32, Linux system calls are invoked by moving the system call number into eax and executing int $80. The actual system call is performed by a macro in the C library which does exactly this; when statically linked, this code will reside in the binary image.

What I did was to search for a word move placing getuid32()’s system call number into the eax register immediately followed by an int $80. getuid32()’s system call number can be found by checking the Linux kernel source code; all the system call numbers are defined as __NR_syscall. __NR_getuid32 is 199, which is $C7. The op code for a 32-bit move to eax is $B8. So, since IA-32 is little endian, this instruction is B8 C7 00 00 00. The INT instruction has an opcode of $CD and an 8-bit argument ($80 in this case). So the hex string to search for is B8 C7 00 00 00 CD 80. Well wouldn’t you know, there it is. And only one instance! It must be our culprit.

Now, what to change it to? We want to change this call to geteuid32(). Luckily, getuid32() and geteuid32() have the same arguments (none at all) and the same return type, so this hack is trivial. __NR_geteuid32 is 201 ($C9), so just change the move to B8 C9 00 00 00 and save the file. Now your tw_cli works as a setuid program.

A better way to do this might be to skip this call altogether. tw_cli operates on the 3ware device node, which has its own UNIX permissions, so… the tw_cli program does not really even need this check. Since the return value of the system call (the UID number) is placed in eax, we could make this hack just pass every time by changing the move to B8 00 00 00 00, and changing the CD 80 to 90 90 (nop nop). Then the program’s behavior will be controlled by device and file permissions as expected, instead of being controlled by a crude root check.

One Response to “Hacking 3ware’s management utility for setuid programs”

  1. […] is a followup to this blog post, where the author corrects tw_cli to read the effective uid of a process instead of the real […]

Leave a Reply