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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2022-2590-analysis — userfaultfdとFOLL_FORCE/COWバイパスを介して読み取り専用ページへの任意書き込みを可能にする、共有メモリ(shmem)におけるLinuxカーネルの競合状態CVE-2022-2590の詳細な分析。 | Kitploit
ツール/GitHubGitHub/hyeonjun17/cve-2022-2590-analysis
脆弱性分析エクスプロイト論文と研究学習と教育バイナリエクスプロイト
GitHubhyeonjun17/cve-2022-2590-analysis

CVE-2022-2590-analysis

userfaultfdとFOLL_FORCE/COWバイパスを介して読み取り専用ページへの任意書き込みを可能にする、共有メモリ(shmem)におけるLinuxカーネルの競合状態CVE-2022-2590の詳細な分析。

リポジトリを見る

人気

すべて見る →

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

すべてのツールを探索

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

すべてのツールを見る →
622年前未レビュー
共有

CVE-2022-2590、shmem に制限された Dirty COW

Linux カーネルバージョン: Linux/x86 6.0.0-rc1 (コミット 37887783b3fef877bf34b8992c9199864da4afcb)

はじめに:

この脆弱性により、攻撃者は can_follow_write_pte 関数を満たすことで、読み取り専用の共有メモリページに任意の内容を書き込むことができます。この関数は FOLL_FORCE、FOLL_COW、pte_dirty をチェックし、UFFDIO_CONTINUE を使用します。

分析:

root@kitploit:~
/*
 * FOLL_FORCE can write to even unwritable pte's, but only
 * after we've gone through a COW cycle and they are dirty.
 */
static inline bool can_follow_write_pte(pte_t pte, unsigned int flags)
{
	return pte_write(pte) ||
		((flags & FOLL_FORCE) && (flags & FOLL_COW) && pte_dirty(pte));
}

上記の関数は pte が書き込み可能かどうかを判定します。

pte は、以下の条件のいずれかを満たす場合に書き込み可能と見なされます。

  1. pte のフラグに書き込み可能フラグが含まれている。
  2. flags 変数に FOLL_FORCE と FOLL_COW のフラグが含まれており、かつ pte のフラグに dirty フラグが含まれている。

条件 2 を満たすには、以下の 3 つのフラグを設定する必要があります。

  1. FOLL_FORCE

    root@kitploit:~
    static ssize_t mem_rw(struct file *file, char __user *buf,
    			size_t count, loff_t *ppos, int write)
    {
    	...
    
    	flags = FOLL_FORCE | (write ? FOLL_WRITE : 0);
    
    	while (count > 0) {
    		size_t this_len = min_t(size_t, count, PAGE_SIZE);
    
    		if (write && copy_from_user(page, buf, this_len)) {
    			copied = -EFAULT;
    			break;
    		}
    
    		this_len = access_remote_vm(mm, addr, page, this_len, flags); // __get_user_pages with FOLL_FORCE on
    		if (!this_len) {
    			if (!copied)
    				copied = -EIO;
    			break;
    		}
    
    		...
    }
    

    上記の関数は /proc/<pid>/mem に対する読み取り/書き込み操作を実行します。

    この関数を使用すると、FOLL_FORCE フラグを有効にして __get_user_pages 関数に到達できます。

    FOLL_FORCE は ptrace で使用されることを意図していますが、以下のコミットでその必要性が証明されています: https://github.com/torvalds/linux/commit/f511c0b17b081562dca8ac5061dfa86db4c66cc2

  2. FOLL_COW

    root@kitploit:~
    /*
    * mmap_lock must be held on entry.  If @locked != NULL and *@flags
    * does not include FOLL_NOWAIT, the mmap_lock may be released.  If it
    * is, *@locked will be set to 0 and -EBUSY returned.
    */
    static int faultin_page(struct vm_area_struct *vma,
    	unsigned long address, unsigned int *flags, bool unshare,
    	int *locked)
    {
      ...
    
      /*
      * The VM_FAULT_WRITE bit tells us that do_wp_page has broken COW when
      * necessary, even if maybe_mkwrite decided not to set pte_write. We
      * can thus safely do subsequent page lookups as if they were reads.
      * But only do so when looping for pte_write is futile: in some cases
      * userspace may also be wanting to write to the gotten user page,
      * which a read fault here might prevent (a readonly page might get
      * reCOWed by userspace write).
      */
      if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
        *flags |= FOLL_COW;
      return 0;
    }
    

    faultin_page 関数内での FOLL_COW との OR 演算が利用可能です。これは Dirty COW のパッチの一部です。

    root@kitploit:~
    static long __get_user_pages(struct mm_struct *mm,
     		unsigned long start, unsigned long nr_pages,
     		unsigned int gup_flags, struct page **pages,
     		struct vm_area_struct **vmas, int *locked)
     {
     	...
     retry:
     		/*
     		 * If we have a pending SIGKILL, don't keep faulting pages and
     		 * potentially allocating memory.
     		 */
     		if (fatal_signal_pending(current)) {
     			ret = -EINTR;
     			goto out;
     		}
     		cond_resched();
    
     		page = follow_page_mask(vma, start, foll_flags, &ctx); // can_follow_write_pte
     		if (!page || PTR_ERR(page) == -EMLINK) {
     			ret = faultin_page(vma, start, &foll_flags,
     					   PTR_ERR(page) == -EMLINK, locked); // flags |= FOLL_COW
     			switch (ret) {
     			case 0:
     				goto retry; // try follow page again
     			case -EBUSY:
     			case -EAGAIN:
     				ret = 0;
     				fallthrough;
     			case -EFAULT:
     			case -ENOMEM:
     			case -EHWPOISON:
     				goto out;
     			}
     			BUG();
     		} else if (PTR_ERR(page) == -EEXIST) {
        ...
     }
    

    FOLL_COW フラグを有効にして can_follow_write_pte 関数に到達するには、__get_user_pages 関数内で faultin_page 関数を呼び出し、retry ラベルにリダイレクトして、再度 faultin_page 関数を呼び出す必要があります。

  3. pte_dirty

    この条件は、以下のコミットにより満たすことができます: https://github.com/torvalds/linux/commit/9ae0f87d009ca6c4aab2882641ddfc319727e3db

    root@kitploit:~
    /*
    * Install PTEs, to map dst_addr (within dst_vma) to page.
    *
    * This function handles both MCOPY_ATOMIC_NORMAL and _CONTINUE for both shmem
    * and anon, and for both shared and private VMAs.
    */
    int mfill_atomic_install_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,
    		     struct vm_area_struct *dst_vma,
    		     unsigned long dst_addr, struct page *page,
    		     bool newly_allocated, bool wp_copy)
    {
     	...
    
     	_dst_pte = mk_pte(page, dst_vma->vm_page_prot);
     	_dst_pte = pte_mkdirty(_dst_pte); // set pte dirty unconditionally
     	if (page_in_cache && !vm_shared)
     		writable = false;
    
     	...
    }
    

    上記のパッチにより、読み取り専用の共有メモリページに対して、pte を無条件でダーティ状態でインストールすることが可能になります。

 

これら 3 つのフラグが満たされると、can_follow_write_pte 関数は true を返します。

root@kitploit:~
static struct page *follow_page_pte(struct vm_area_struct *vma,
		unsigned long address, pmd_t *pmd, unsigned int flags,
		struct dev_pagemap **pgmap)
{
	...
    
	// true && !true == false
	if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
		pte_unmap_unlock(ptep, ptl);
		return NULL;
	}

	page = vm_normal_page(vma, address, pte); // get read-only shared memory page

	...

out:
	pte_unmap_unlock(ptep, ptl);
	return page;
no_page:
	pte_unmap_unlock(ptep, ptl);
	if (!pte_none(pte))
		return NULL;
	return no_page_table(vma, flags);
}

follow_page_pte 関数は、読み取り専用の共有メモリページを返します。

競合シナリオ:

以下は、後述の PoC によって実証された競合シナリオです。

madvise と readUFFDIO_CONTINUE ioctlpwrite
madvise // ページを zap
shmem_fault // read fault
handle_userfault
userfaultfd_continue
mcontinue_atomic_pte
ret = shmem_getpage(inode, pgoff, &page, SGP_NOALLOC); // ページを取得
mfill_atomic_install_pte
_dst_pte = pte_mkdirty(_dst_pte); // pte をダーティにする
set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); // pte をインストールmem_rw
access_remote_vm // FOLL_FORCE 付き
__get_user_pages
can_follow_write_pte // FOLL_COW なし、0 を返す
faultin_page
flags |= FOLL_COW
retry:
follow_page_pte
madvise // ページを zap
shmem_fault // read fault
handle_userfault
userfaultfd_continue
mcontinue_atomic_pte
ret = shmem_getpage(inode, pgoff, &page, SGP_NOALLOC); // ページを取得
mfill_atomic_install_pte
_dst_pte = pte_mkdirty(_dst_pte); // pte をダーティにする
set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); // pte をインストール
can_follow_write_pte // 1 を返す
copy_to_user(buf, page, this_len) // 読み取り専用ページに内容を書き込む

madvise と read のルーチンは 2 回実行されます。最初の実行は __get_user_pages の retry を誘発し、2 回目の実行は follow_page_pte が mfill_atomic_install_pte によってダーティにされた pte が指すページを返すように導きます。

PoC とパッチ:

https://www.openwall.com/lists/oss-security/2022/08/15/1

上記のリンクで David Hildenbrand の再現コードを確認できます。

ただし、その再現コードは正確な競合シナリオを認識するのが難しいため、私はより線形的な実行を優先するように再現コードを変更し、それに応じて Linux カーネルソースコードも変更しました。

.config:

root@kitploit:~
...
CONFIG_USERFAULTFD=y
CONFIG_HAVE_ARCH_USERFAULTFD_WP=y
CONFIG_HAVE_ARCH_USERFAULTFD_MINOR=y
...

PoC: (poc.c)

パッチ: (poc_deayzl.patch)

テストには、以下の事前手順を実行する必要があります。(david の再現コードの手順ではファイルパスは /tmp/foo とありますが、現在のバージョンでは tmpfs は共有メモリではないため、userfaultfd の登録に失敗します)

root@kitploit:~
sudo -s
echo "asdf" > /dev/shm/foo
chmod 0404 /dev/shm/foo
exit

PoC を実行すると、以下の画面が表示されます。

PoC-result

参考文献:

コミット: https://github.com/torvalds/linux/commit/5535be3099717646781ce1540cf725965d680e7b

lore.kernel パッチ v1: https://lore.kernel.org/linux-mm/[email protected]/#r

lore.kernel パッチ v2: https://lore.kernel.org/all/[email protected]/T/#u

openwall: https://www.openwall.com/lists/oss-security/2022/08/08/1

openwall2: https://lists.openwall.net/linux-kernel/2022/08/08/418

ツールをダウンロード