Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
schrodingers-toctou — 安全なCコードをTOCTOU脆弱性に変える、コンパイラが生成したメモリロードを検出します。自動化されたソースコード監査、Unicornベースのバイナリ解析、100以上のプロジェクトにわたるコンパイラ/アーキテクチャ/フラグのスイープを含みます。 | Kitploit
ツール/GitHubGitHub/xoreaxeaxeax/schrodingers-toctou
動的分析 (サンドボックス)静的コード分析 (SAST)脆弱性分析エクスプロイトバイナリ解析学習と教育
GitHubxoreaxeaxeax/schrodingers-toctou

schrodingers-toctou

安全なCコードをTOCTOU脆弱性に変える、コンパイラが生成したメモリロードを検出します。自動化されたソースコード監査、Unicornベースのバイナリ解析、100以上のプロジェクトにわたるコンパイラ/アーキテクチャ/フラグのスイープを含みます。

リポジトリを見る
944412ヶ月前Kitploit レビュー済み

人気

すべて見る →

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

すべてのツールを探索

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

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

シュレーディンガーのTOCTOU

「...『正気のコンパイラ』の定義はますます緩くなっていく」

実行するバイナリは、あなたが書いたプログラムではない。コンパイラの最適化は、あなたのソースを 決して目にすることのない形で書き換え — そして、そうした変更の一部は 黙って、しかも合法的に、一見安全に見えるコードを 脆弱なバイナリへと変えてしまうことがある。同じコード行が あるコンパイラでは安全で、別のコンパイラでは悪用可能 になり得る。ソースコードにはどちらになるかを示す手掛かりは何もない。脆弱性は 重ね合わせの状態にあり、ビルドしたときにのみ収束する。Schrödinger's TOCTOU は コンパイラが作り出すロード と、それらが time-of-check to time-of-use(TOCTOU)脆弱性に与える広範な影響を探求する — 見つかるのは オープンソースの カーネル、 ハイパーバイザ、 エンクレーブ、 ファームウェア、 そしてライブラリ。 どこを見ても、一見安全に見えるコードはコンパイラの気まぐれに さらされたままになっている。しかし、これらは標本であって境界ではない。 同じバグはあなたのコードにも存在する可能性が非常に高い。

チャレンジ

「簡単なものから始めよう。」

この関数は *p を何回ロードするだろうか?```c unsigned int g(unsigned short *p) { short t = p; / copy *p into a local for safekeeping */ return (unsigned short)t - t; }

ヒント: 答えは 1 です — ソースは `*p` を `t` に一度だけロードします。

それを [Compiler Explorer](https://godbolt.org/z/c5K9P4dPd) (`arm gcc 14.2.0`,
`-O2`) に貼り付けて、`p` を保持する `r0` からのロードを数えてください:```asm
g:
        ldrh    r2, [r0]     # load *p, once
        ldrsh   r0, [r0]     # load *p, twice
        subs    r0, r2, r0
        bx      lr

ソースには1つのロード、バイナリには2つのロードがある。2つ目は作り出されたロード — コンパイラが生成した、あなたが決して書いていない読み出しだ。これはCの 抽象機械の下では合法であり、2つの読み出しの間にメモリが変化しないと仮定する。しかし、 そのメモリが攻撃者によって書き換え可能な場合、その仮定は悪用へと変わる。つまり、 作り出されたロードはセキュリティチェックの後に落ちる可能性があり、静かに プログラマーが閉じたと思っていたtime-of-check to time-of-use (TOCTOU) ウィンドウを 再び開く。検証した値と使用する値は、もはや 同じであることは保証されない — たとえ、それを再読み出しするコードを書いていなくても。

どこからともなく現れるバッファオーバーフロー

このチャレンジは、作り出されたロードが存在することを証明している。それがどのようにメモリ破壊に つながるのかを見てみよう。

TOCTOU脆弱性では、プログラムは値が安全であることをチェックしてから、その値を 使用する。 しかし、攻撃者がその2つの読み出しの間のわずかな時間に値を変更できるなら、 悪用の余地が存在する。 – 無害な値は チェックを通過する一方、危険な値が実際に使用されることになる:```c if (shared->len <= 20) // CHECK reads shared->len // ** attacker modifies shared->len ** memcpy(out, shared->data, shared->len); // USE reads it again: buffer overflow

教科書的な修正方法は、**まずスナップショットを取る**ことです。攻撃者が改ざんし得るデータを、攻撃者の到達できないローカルにコピーし、そのローカル以外は一切信用しません。`len` がローカルに入れば、それは固定されます——共有メモリと競合する攻撃者はもはやそれに触れることはできません——したがって、チェックとコピーが同じ値を参照することが保証されます。これが、以下の `receive` のコードが TOCTOU を修正する仕組みです。メッセージのスナップショットを取り、そのスナップショットを検証し、検証済みのコピーを `slot` に公開して、コンシューマーが転送できるようにします:```c
#include <string.h>

struct message {
    int  len;          /* payload length */
    char data[20];     /* payload        */
};

struct message slot;   /* the most recently validated message */
char out[20];          /* fixed 20-byte destination           */

void receive(struct message *shared) {
    struct message local = *shared;    /* 1. snapshot untrusted input   */
    if (local.len <= 20)               /* 2. validate the snapshot      */
        slot = local;                  /* 3. publish the validated copy */
}

void forward(void) {                   /* the time of use, later        */
    memcpy(out, slot.data, slot.len);  /* slot.len was checked <= 20 ... right? */
}

ソースコードによれば、これは正しい。len は正確に一度だけ読み取られる — スナップショットへ — したがって、<= 20 チェックを通過する値は、slot に公開された値である。 TOCTOU のウィンドウは閉じられ、コードは安全である。

しかし実際はそうではない。x86-64 gcc -O2 の下では、receive は それを 元の共有メモリから2回読み取る: 1回は チェックをゲートするスカラーとして、そして再び slot に公開される バルク コピー の一部として:```nasm receive: cmp DWORD PTR [rdi], 20 ; READ #1: the CHECK reads shared->len directly movdqu xmm0, XMMWORD PTR [rdi] ; READ #2: the bulk copy re-reads it (len is byte 0) mov rax, QWORD PTR [rdi+16] ; (the bulk copy's tail: struct bytes 16-23) jg .L1 ; len > 20? skip the publish mov QWORD PTR slot[rip+16], rax ; (publish that tail) movaps XMMWORD PTR slot[rip], xmm0 ; and publish the TOCTOU-vulnerable snapshot .L1: ret forward: movsx rdx, DWORD PTR slot[rip] ; copy size = slot.len, the unchecked READ #2 value mov esi, OFFSET FLAT:slot+4 ; src = slot.data mov edi, OFFSET FLAT:out ; dst = out[20] jmp memcpy ; copies slot.len bytes into out[20]

The check runs on READ #1; the value that lands in `slot.len` is READ #2. An
attacker who flips `len` between them passes a safe value to the `<= 20` check
while an oversized one is published into `slot` — and `forward` then copies that
many bytes into `out[20]`, the exact overflow the snapshot was meant to prevent,
reintroduced by the optimizer.

This is turned into a complete proof-of-concept in
[`poc/example.c`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/main/poc/example.c), where the code uses the canonical
TOCTOU-hardened approach: an untrusted `message` struct gets snapshotted into
`local` so that it cannot be modified, the snapshot's `local.len` is validated
against the buffer capacity, and only the validated copy is published into
`slot`; a consumer later copies `slot.len` payload bytes into a fixed buffer.
Simultaneously, an attacker races `shared->len`. An unexpected invented load
from the compiler re-reads `shared->len` for the bulk publish, so `slot.len`
carries the attacker's oversized value even though the check passed —
reintroducing the TOCTOU the programmer was trying to defend against, and
creating a seemingly impossible buffer overflow — from thin air.

## Cause

> *By the time C reaches machine code, it's been reshaped by frontend lowering,
> IR optimizations, register allocation, and backend codegen — a deep, multi-stage
> pipeline making decisions you can't see. There is no one stage to blame. The
> invented load is an emergent property of the whole pipeline, not a bug in any
> part of it.*

At this point: compilers *can* emit invented loads, and the very idiom meant to
prevent the bug — snapshot, validate, use — is what reintroduces it. The next
step (to know whether we are actually vulnerable) is to characterize *when* it
happens. Turns out that's hard.

In [`cat-states/`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/main/cat-states), we search for the proofs-of-concept that show
it is real — and that it is everywhere:
ツールをダウンロード