setuidのプログラムからシェルを起動する
結論
#include <stdlib.h> #include <unistd.h> int main() { execl("/bin/sh", "/bin/sh", "-p", NULL); }
もしくは
#include <stdlib.h> #include <unistd.h> int main() { setreuid(geteuid(), -1); system("/bin/sh"); }
参考
詳細
Linuxでは、
[kusano@hoge suid]$ sudo chown kusano2: a.out [kusano@hoge suid]$ sudo chmod u+s a.out [kusano@hoge suid]$ ll total 32 -rwsrwxr-x 1 kusano2 kusano2 7073 Aug 3 15:22 a.out
とsetuidビットを立てると、ユーザーkusanoがa.outを実行した場合に、a.outはkusano2の権限で動く。ちなみにスクリプトには効かない。
では、a.outの中で/bin/shを呼び出すとkusano2の権限になるかというとならない。
// test1.cpp #include <stdlib.h> #include <unistd.h> int main() { system("/bin/sh"); }
// test2.cpp #include <stdlib.h> #include <unistd.h> int main() { execl("/bin/sh", "/bin/sh", NULL); }
[kusano@hoge suid]$ g++ test1.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out sh-4.1$ id uid=500(kusano) gid=500(kusano) groups=500(kusano),10(wheel) sh-4.1$ exit exit [kusano@hoge suid]$ g++ test2.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out sh-4.1$ id uid=500(kusano) gid=500(kusano) groups=500(kusano),10(wheel) sh-4.1$ exit exit
ユーザーIDには実ユーザーIDと実効ユーザーIDがあり、setuidビットを立てたプログラムは、実効ユーザーIDはファイルの所有者になるが、実ユーザーIDはプログラムを実行したユーザーとなる。/bin/shは起動時に実効ユーザーIDと実ユーザーIDが一致しているか調べ、一致していなかったら実効ユーザーIDを実ユーザーIDに変えてしまう。-pオプションを付けることでこの動作を抑制できる。このようにして起動したシェルは実ユーザーIDはkusanoだが、実効ユーザーがkusano2なのでkuasno2の権限でファイル操作などができる。ただし、実ユーザーIDはkusanoなので、さらにシェルを起動すると実効ユーザーIDもkusanoになる。
// test3.cpp #include <stdlib.h> #include <unistd.h> int main() { execl("/bin/sh", "/bin/sh", "-p", NULL); }
[kusano@hoge suid]$ g++ test3.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out sh-4.1$ id uid=500(kusano) gid=500(kusano) euid=506(kusano2) groups=507(kusano2),10(wheel),500(kusano) sh-4.1$ touch hoge sh-4.1$ ls -l hoge -rw-rw-r-- 1 kusano2 kusano 0 Aug 3 15:54 hoge sh-4.1$ sh sh-4.1$ id uid=500(kusano) gid=500(kusano) groups=500(kusano),10(wheel)
system関数ではうまくいかない。
// test4.cpp #include <stdlib.h> #include <unistd.h> int main() { system("/bin/sh -p"); }
[kusano@hoge suid]$ g++ test4.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out sh-4.1$ id uid=500(kusano) gid=500(kusano) groups=500(kusano),10(wheel) sh-4.1$ exit exit
system関数では/bin/sh -cに引数の文字列を渡してコマンドを実行しているから。
// test5.cpp #include <stdlib.h> #include <unistd.h> int main() { system("/usr/bin/id"); }
// test6.cpp #include <stdlib.h> #include <unistd.h> int main() { execl("/usr/bin/id", "/usr/bin/id", NULL); }
[kusano@hoge suid]$ g++ test5.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out uid=500(kusano) gid=500(kusano) groups=500(kusano),10(wheel) [kusano@hoge suid]$ g++ test6.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out uid=500(kusano) gid=500(kusano) euid=506(kusano2) groups=507(kusano2),10(wheel),500(kusano)
systemでもkusano2で動かすためには、実ユーザーIDを実効ユーザーIDにすれば良い。ただし、下記のようにsetuid(geteuid())ではダメ。
// test7.cpp #include <stdlib.h> #include <unistd.h> int main() { setuid(geteuid()); system("/bin/sh"); }
[kusano@hoge suid]$ g++ test7.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out sh-4.1$ id uid=500(kusano) gid=500(kusano) groups=500(kusano),10(wheel)
setuidのマニュアルに書いてあるように、setuidは0(root)が指定された場合には実ユーザーIDも変更するが、それ以外のユーザーでは実効ユーザーしか変更しないため。setreuidならば実ユーザーIDを変更できる。この場合は、起動したシェルの中でさらシェルを立ち上げてもユーザーIDは変化しない。
// test8.cpp #include <stdlib.h> #include <unistd.h> int main() { setreuid(geteuid(), -1); system("/bin/sh"); }
[kusano@hoge suid]$ g++ test8.cpp; sudo chown kusano2: a.out; sudo chmod u+s a.out; ./a.out sh-4.1$ id uid=506(kusano2) gid=500(kusano) groups=507(kusano2),10(wheel),500(kusano) sh-4.1$ sh sh-4.1$ id uid=506(kusano2) gid=500(kusano) groups=507(kusano2),10(wheel),500(kusano)