๋ฌธ์ : DreamHack โ EXP-NaN (Dreamhack CTF Season 4 Round #5, ๐ฉDiv1 ์ถ์ ์) ๋ถ๋ฅ: Pwnable (Browser / V8 JIT) ๋์ด๋: ๐ Diamond 1 ์ด ๊ธ: ์๋ฆฌ์ฆ 2ํธ โ ์์ค ๋ถ๊ฒ. (1ํธ: ๋ฒ๊ทธยทํธ๋ฆฌ๊ฑฐ์ ํ๋๋์ ๋ฒฝ)
1ํธ์์ Math.exp ํ ์ค ํจ์น๋ก ํ์
ํผ๋์ ํธ๋ฆฌ๊ฑฐํ๊ณ , ์์ฐ ํฐ์ด์
์์ ๊ทธ ์ด๊ธ๋จ์ ๋ฐฐ์ด ์ธ๋ฑ์ค๊น์ง ์ด๋ ค ๋ณด๋ด๋ ๋ฐ ์ฑ๊ณตํ๋ค. ๊ทธ๋ฐ๋ฐ ์ธ๋ฑ์ค๊ฐ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ์๊ฐ, wild OOB๊ฐ ์๋๋ผ int3 ํธ๋ฉ(SIGTRAP) ์ผ๋ก ๋๊ฒผ๋ค.
์ด 2ํธ์ ๊ทธ ํธ๋ฉ์ V8 11.2 ์์ค๋ก ๋ถ๊ฒํ๋ค. ์ 11.2์์ typer๋ฅผ ์์ฌ CheckBounds๋ฅผ ์ง์ฐ๋ ์ ์(abiondoยทdoar-eยทQuick-Maffs)์ด ํต์งธ๋ก ๋ฌด๋ ฅํ๋๋์ง โ ๊ทธ๊ฒ ๋จ์ํ "์ด๋ ต๋ค"๊ฐ ์๋๋ผ ์์ค์ ๋ฐํ ๋ฌด์กฐ๊ฑด ํ๋๋๊ณผ, doar-e์ ์ฐํ๋ฅผ ์ฝ ์ง์ด ๋ง์ ๋ ๋ฒ์งธ ๊ฒ์ฌ ๋๋ฌธ์์ ์ค ๋ฒํธ์งธ๋ก ์ง๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ ์๊ธฐ ํ๋๋์ด ์ผ์ง V8์ ์ค์ ๋ก ๋ซ์ ์ต์ค๋ค์ด ์ ๊ฐ-range typer ๋ฒ๊ทธ๋ฅผ ๋ฒ๋ฆฌ๊ณ ๋ค๋ฅธ ๋ฒ๊ทธ ํด๋์ค๋ก ๊ฐ์ํ๋์ง ๋์กฐํ๋ฉฐ ๋๋ธ๋ค.
์์ค๋ ์ฑ๋ฆฐ์ง
patches/README.md๊ฐ ์ง์ ํ ๋น๋ ๊ธฐ์ค โ V8 commit6538a20a(11.2.214.14). ์ธ์ฉํ ์ค ๋ฒํธ๋ ๊ทธ commit์ ๊ฒ์ด๋ค.
๐ 1ํธ์ ๋๋ฌ์
1ํธ ๊ฒฐ๋ก ์ ํ ์ค๋ก: ๋ฒ๊ทธยทํธ๋ฆฌ๊ฑฐ๋ ์ด์ ์๊ณ , ์ด๊ธ๋จ์ ์ธ๋ฑ์ค๊น์ง ๋๋ฌํ๋๋ฐ, ๊ฑฐ๊ธฐ์ ํธ๋ฉ์ด๋ค.
ํต์ฌ ๊ฐ์ ฏ์ ์ด๊ฑฐ์๋ค โ b = Object.is(Math.exp(x), NaN)์ typer์ false(=0), ๋ฐํ์์ true(=1). ๋น๊ต์ ๋ฐฐ์ด ์ ๊ทผ ์ฌ์ด์ store๋ฅผ ๋ผ์ b๋ฅผ ํด๋ฉ ๋ชป ํ๊ฒ ์ด๋ ค๋๋ฉด, a[b*K]์ ์ธ๋ฑ์ค๊ฐ typer์ 0, ๋ฐํ์์ K๋ก ๊ฐ๋ฆฐ๋ค. ๋ฐฐ์ด ๊ธธ์ด๋ง 8๊ณผ 4๋ก ๋ฐ๊ฟ ๋๋ฆฌ๋ฉด ํ ํ๋ฉด์ ๋ค ๋์จ๋ค.
$ ./out/d8 --allow-natives-syntax trap_poc.js
len 8 : a[0](b=false)=1.1 trigger a[b*7] = 8.1 (๋ฐํ์ ์ธ๋ฑ์ค 7 โ a[7]=8.1)
len 4 : a[0](b=false)=1.1 trigger โ
$ echo $?
133 # 128 + 5 = SIGTRAPlen 8์ ์ธ๋ฑ์ค 7์ด ๊ธธ์ด 8 ์์ด๋ผ a[7]=8.1์ ๋ฌด์ฌํ ๋๋ ค์ค๋ค โ ์ด๊ธ๋จ์ด ๋ฐํ์ ์ธ๋ฑ์ค๊น์ง ์ด์ ๋๋ฌํ๋ค๋ ์ง์ ์ฆ๊ฑฐ(typer๋ 0์ด๋ผ ๋ฏฟ์๋ค). len 4๋ ๋๊ฐ์ ์ธ๋ฑ์ค 7์ด ์ด๋ฒ์ ๋ฒ์ ๋ฐ์ด๊ณ , ๊ทธ ์๊ฐ ์ฆ์ฌํ๋ค. ๋ฐฐ์ด ๊ธธ์ด ํ๋๋ง ๋ฐ๊ฟจ๋๋ฐ ๊ฒฐ๊ณผ๊ฐ ๊ฐ๋ฆฌ๋, ํธ๋ฉ์ ์์ธ์ด ๊ฒฝ๊ณ๊ฒ์ฌ๋ผ๋ ๊ฑด ๋ถ๋ช
ํ๋ค. ๋จ์ ์ง๋ฌธ์ ํ๋ โ ์ wild read๊ฐ ์๋๋ผ ํธ๋ฉ์ธ๊ฐ?
๐ฃ ๊ณ๊ฐ์ง โ ํจ์น๋ ์ด๋ป๊ฒ ์ ํ๋๊ฐ
๋ถ๊ฒ ์ ์ ํ๋ ์ง๊ณ ๊ฐ์. Math.exp ๋ฐํํ์
์ PlainNumber๋ก ๋ฐ๊พผ ํจ์น๋ typer.cc์ JSCallTyper์ ์๋ค โ ์ฆ Math.exp๊ฐ JSCall ๋
ธ๋๋ก ๋จ์ ์์ ๋๋ง ํ๋ค. ๊ทธ๋ฐ๋ฐ ํ๋ฒํ๊ฒ Math.exp(x)๋ฅผ ํธ์ถํ๊ณ hotํด์ง๋ฉด, JSCallReducer::ReduceMathUnary๊ฐ ๊ทธ๊ฑธ NumberExp๋ผ๋ ์ ์์ค ๋
ธ๋๋ก ๋ฎ์ถฐ๋ฒ๋ฆฐ๋ค. NumberExp์ ํ์
์ OperationTyper๊ฐ ๋งค๊ธฐ๋๋ฐ ๊ฑฐ๊ธด ํจ์น๊ฐ ์์ด ์ ์งํ๊ฒ NaN์ ํฌํจํ๋ค โ ๊ทธ๋์ ํจ์น๋ typer๊ฐ ํ์ง๋ฅผ ์๋๋ค. ๋ฒ๊ทธ๊ฐ ์๋๋ฐ๋ ํ๋ฒํ๊ฒ ์ง๋ฉด ์ฌํ์ด ์ ๋๋ ์ด์ ๋ค.
ReduceMathUnary๋ ์ฝ์ฌ์ดํธ speculation์ด ๊บผ์ ธ ์์ผ๋ฉด(kDisallowSpeculation) ๋ฎ์ถ๊ธฐ๋ฅผ ํฌ๊ธฐํ๊ณ Math.exp๋ฅผ JSCall๋ก ๋๋ค. ๊ทธ์ ์์ผ ํจ์น๋ JSCallTyper โ PlainNumber๊ฐ ์ ์ฉ๋ผ ๋ฒ๊ทธ๊ฐ ๊นจ์ด๋๋ค. speculation์ ํ ๋ฒ deopt๊ฐ ๋๋ฉด ๊ทธ ์ฝ์ฌ์ดํธ์์ ๊บผ์ง๋ฏ๋ก โ ๋๋ ์ฒ์๋ถํฐ ์ซ์ ์๋ ์ธ์(๋ฌธ์์ด)๋ก ๋ถ๋ฅด๋ฉด ์ซ์ speculation์ด ์ ๊น๋ฆฌ๋ฏ๋ก โ ๋ ์ค ํ๋๋ก ๋๋ฉด ๋๋ค. 1ํธ ํธ๋ฆฌ๊ฑฐ(f(NaN)=111)๊ฐ ๊ทธ "deopt๋ก speculation off โ JSCall ์ ์ง โ PlainNumber" ๊ฒฝ๋ก๊ณ , ์๋ฒ์ฉ ์์ฐ ํฐ์ด์
๋ฒ์ ์ ๋ฌธ์์ด ์ธ์(Math.exp("z")=NaN)๋ก ๊ฐ์ ํจ๊ณผ๋ฅผ ๋ธ๋ค. ์์ปจ๋ ์ด ๋ฒ๊ทธ๋ ๊ธฐ๋ณธ ๊ฒฝ๋ก์์ ์ ๋ค์ด ์๊ณ , speculation์ ๊บผ์ผ ๋น๋ก์ ๊นจ์ด๋๋ค.
๐ญ CheckBounds๊ฐ ๊ธฐ๊ณ์ด๊ฐ ๋๊ธฐ๊น์ง
arr[i]์ ๊ฒฝ๊ณ๊ฒ์ฌ๋ TurboFan ๊ทธ๋ํ์์ CheckBounds ๋
ธ๋ ํ๋๋ก ์์ํด, ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ ๊ธฐ๊ณ์ด๊ฐ ๋๋ค. ์ฐ๋ฆฌ ๋ถ๊ฒ์ ํ์ํ ์ธ ๋จ๊ณ๋ง ์ถ๋ฆฌ๋ฉด ์ด๋ ๋ค.
SimplifiedLowering : CheckBounds โ CheckedUint32Bounds(flags)
EffectControlLinearization : CheckedUint32Bounds โ Uint32LessThan(idx, len)
โ ๋น๊ต ๊ฒฐ๊ณผ์ ๋ฐ๋ผ LoadElement ๋๋ (deopt | Unreachable)์ฌ๊ธฐ์ ์ด๋ช
์ ๊ฐ๋ฅด๋ ๊ฑด CheckedUint32Bounds์ ๋ถ๋ flags ๋ค. ๊ทธ ํ๋๊ทธ์ kAbortOnOutOfBounds๊ฐ ์๋๋ ์๋๋๊ฐ, ๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ ๋ "์์ ํ๊ฒ deopt"ํ ์ง "๊ทธ ์๋ฆฌ์์ abort(ํธ๋ฉ)"ํ ์ง๋ฅผ ์ ํ๋ค. ๊ทธ๋ฆฌ๊ณ 1ํธ์์ ๋ดค๋ฏ ์ V8์ ์
์งธ ์ ํ์ง โ ๊ฒ์ฌ๋ฅผ ํต์งธ๋ก ์ ๊ฑฐ(= ๊ฒ์ฌ ์๋ wild OOB) โ ๊ฐ ์์๋ค. 11.2๋ ๊ทธ ์
์งธ ์ ํ์ง๋ฅผ ์์ด๋ค.
๐งฑ ๋ฌด์กฐ๊ฑด abort โ simplified-lowering.cc
CheckBounds๋ฅผ ๋ฎ์ถ๋ VisitCheckBounds์ ํต์ฌ์ ์ด ํ ์กฐ๊ฐ์ด๋ค.
// src/compiler/simplified-lowering.cc โ VisitCheckBounds (์์ฝ)
Type const index_type = TypeOf(node->InputAt(0));
Type const length_type = TypeOf(node->InputAt(1));
...
if (lower<T>()) {
if (index_type.IsNone() || length_type.IsNone() ||
(index_type.Min() >= 0.0 &&
index_type.Max() < length_type.Min())) {
// The bounds check is redundant if we already know that
// the index is within the bounds of [0.0, length[.
new_flags |= CheckBoundsFlag::kAbortOnOutOfBounds; // โ ์ ๊ฑฐ๊ฐ ์๋๋ผ abort
}
ChangeOp(node, simplified()->CheckedUint32Bounds(feedback, new_flags));
}์ฝ๋ ๋ฒ: typer๊ฐ index.Max() < length.Min() โ ์ฆ "์ธ๋ฑ์ค๋ ํญ์ ๊ธธ์ด๋ณด๋ค ์๋ค"๋ฅผ ์ฆ๋ช
ํ๋ฉด, ์๋ ์ ์ด CheckBounds๋ฅผ ์ง์ ๋ค(redundant๋๊น). 11.2๋ ์ง์ฐ๋ ๋์ kAbortOnOutOfBounds ํ๋๊ทธ๋ฅผ ๋ถ์ธ๋ค. ์ด๊ฒ ์ ๋ถ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋น๋(commit 6538a20a)์ ์ด ๋์์ ๋๋ ํ๋๊ทธ ์์ฒด๊ฐ ์๋ค โ ์ฝ๋์ ๋ฌด์กฐ๊ฑด ๋ฐํ ์๋ค.
์ฐ๋ฆฌ ๊ฐ์ ฏ์ด ์ ํํ ์ด ํจ์ ์ ๊ฑธ๋ฆฐ๋ค. ์์ฐ ํฐ์ด์
์์ b๋ ํผ๋๋ฐฑ์ ํญ์ false๋ผ typer๊ฐ b*K๋ฅผ Range(0,0)์ผ๋ก ์ขํ๋ค โ index.Max()=0 < length.Min()=4 โ ํ๋๊ทธ ON. typer๋ "์ด ์ธ๋ฑ์ค๋ ์ ๋ 4 ๋ฏธ๋ง"์ด๋ผ ์ฆ๋ช
ํ๊ณ , ๋ฐํ์์ด ๊ทธ ์ฆ๋ช
์ ๊นจ๋ ์๊ฐ์ด ๊ณง abort๋ค.
ํ ๊ฐ์ง ๊ณ๊ฐ์ง โ ์ ๋ถ๊ธฐ๋ length_type.Is(Type::Unsigned31())์ผ ๋๋ง์ด๋ค(์ผ๋ฐ JSArray๋ ๊ธธ์ด๊ฐ 2ยณยน ๋ฏธ๋ง์ด๋ผ ์ฌ๊ธฐ ํด๋น). length๊ฐ ๊ทธ๋ณด๋ค ํฐ ๊ฒฝ์ฐ(์: TypedArray, ๊ธธ์ด ์ต๋ 2โตยณ)๋ else ๊ฐ์ง๋ก ๋น ์ ธ CheckedUint64Bounds๊ฐ ๋๋๋ฐ, ๊ทธ์ชฝ์ abort ํ๋๊ทธ๋ฅผ ์ ๋ถ์ธ๋ค. ์ฆ TypedArray OOB๋ ํธ๋ฉ์ด ์๋๋ผ deopt(์์ ๋ณต๊ท)๋ค. ์๊นํ์ง๋ง ๊ฒฐ๊ตญ wild๋ ์๋๋ค โ deopt๋ ์ธํฐํ๋ฆฌํฐ๋ก ๋น ์ ธ undefined๋ฅผ ์ค ๋ฟ์ด๊ณ , ๊ฒ๋ค๊ฐ TypedArray๋ backing์ด ๋ถ๋ฆฌ๋ผ ์์ด OOB๊ฐ ๋ค๋ฅธ ๊ฐ์ฒด๋ก ๋์ด๊ฐ์ง๋ ์๋๋ค. ์ด๋ ๋ถ๊ธฐ๋ "๊ฒ์ฌ ์๋ ๋ก๋"๋ ์๋ค.
๐ Unreachable์ ์ ์ฒด โ effect-control-linearizer.cc
๊ทธ abort๊ฐ ์ค์ ๋ก ๋ฌด์จ ๊ธฐ๊ณ์ด๊ฐ ๋๋์ง๋ ๋ค์ ๋จ๊ณ์ ์๋ค.
// src/compiler/effect-control-linearizer.cc โ LowerCheckedUint32Bounds
Node* check = __ Uint32LessThan(index, limit);
if (!(params.flags() & CheckBoundsFlag::kAbortOnOutOfBounds)) {
__ DeoptimizeIfNot(DeoptimizeReason::kOutOfBounds, ..., check, frame_state); // ์์ deopt
} else {
auto if_abort = __ MakeDeferredLabel();
auto done = __ MakeLabel();
__ Branch(check, &done, &if_abort);
__ Bind(&if_abort);
__ Unreachable(); // โ abort. ๋ฒ์ ๋ฐ์ด๋ฉด ์ฌ๊ธฐ๋ก.
__ Bind(&done);
}
return index;ํ๋๊ทธ๊ฐ ์์ผ๋ฉด DeoptimizeIfNot โ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ฉด deopt(์ธํฐํ๋ฆฌํฐ๋ก ์์ ๋ณต๊ท, undefined ๋ฐํ). ํ๋๊ทธ๊ฐ ์์ผ๋ฉด Branch โ Unreachable() โ ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ฉด ์ฃฝ๋๋ค. ์ด Unreachable์ด 1ํธ ๋์ค์ด์
๋ธ๋ฆฌ์์ ๋ณธ jnc โ call [r13+โฆ] โ int3๋ค. deopt๊ฐ ์๋๋ผ ํธ๋ฉ์ธ ์ด์ ๊ฐ ์ฌ๊ธฐ ์๋ค. typer๊ฐ "๋ถ๊ฐ๋ฅ"์ด๋ผ๊ณ ์ฝ์ํ ๋ถ๊ธฐ๋ฅผ ์ปดํ์ผ๋ฌ๊ฐ ๋๋ฌ ๋ถ๊ฐ๋ก ๊ตณํ๋ฒ๋ ธ์ผ๋, ๊ทธ ์ฝ์์ด ๊นจ์ง๋ฉด ๊ฐ ๊ณณ์ด int3๋ฟ์ด๋ค.
์ ๋ฆฌํ๋ฉด โ typer๊ฐ ์ธ๋ฑ์ค๋ฅผ in-bounds๋ก ์ฆ๋ช ํ ์๋ก ์ฝ๋๋ ๋ ์ํํด์ง๋ค. ์ฆ๋ช ์ด ๊ณง abort ๊ฐ๋๋ฅผ ๋ถ๋ฅด๊ณ , ์ฐ๋ฆฌ ๋ฒ๊ทธ๋ ๋ฐ๋ก ๊ทธ ์ฆ๋ช ์ ๊ฑฐ์ง์ผ๋ก ๋ง๋ ๋ค.
๐ ์ฝ ์ฌ๋ฆฌ๋ ์ง์ โ ์์ฐ ํฐ์ด์ ์ abiondo์ ์กฐ๊ฑด์ ๋ค ๋ง์กฑํ๋ค
์ฌ๊ธฐ๊ฐ ์ด ๋ฌธ์ ์์ ๊ฐ์ฅ ์ฝ ์ค๋ฅด๋ ๋ถ๋ถ์ด๋ค. ์์ฐ ํฐ์ด์ ์ผ๋ก ์ปดํ์ผํ ๊ฐ์ ฏ์ ๋์ค์ด์ ๋ธํด๋ณด๋ฉด, abiondo๊ฐ OOB๋ฅผ ์ป์ผ๋ ค๊ณ ํ์๋ก ํ๋ ์กฐ๊ฑด์ ์ด ๋น๋๊ฐ ๋ค ๋ง๋ค์ด์ค๋ค. ๊ทธ๋ฐ๋ฐ๋ ํธ๋ฉ์ด๋ค.
$ ./out/d8 --allow-natives-syntax --print-opt-code dis.js
...
0x.. setzl al ; b = Object.is(...) โ ๋ฐํ์์ *๊ณ์ฐ*๋๋ค (์์ ์๋)
0x.. imull rcx, rax, 0x14 ; rcx = b * 20 โ ๊ณฑ์
๋
ธ๋๊ฐ ์ด์์๋ค
0x.. cmpl [rbp-0x28], 0x4 ; idx vs length(4)
0x.. jnc <+0x331> ; ๋ฒ์ ๋ฐ์ด๋ฉด โ abort ์ํ์ค(call โ int3)
0x.. vmovsd xmm1,[r8+rcx*8+0x7] ; a[idx] ๋ก๋๋ชจ์์ฒ๋ผ ๋ณด์ธ๋ค โ b๊ฐ setzl๋ก ๋ฐํ์์ ๊ณ์ฐ๋๊ณ (์์ 0์ผ๋ก ํด๋ฉ๋์ผ๋ฉด ์ด ๋ช
๋ น ์์ฒด๊ฐ ์๋ค), b*20๋ imull๋ก ์ด์์๋ค(๊ณฑ์
๋
ธ๋ ์์กด). ์ฆ ๋ฒฝ 1์ ํต๊ณผํ๋ค: ์ด๊ธ๋จ์ด ์ฃฝ์ง ์๊ณ ์ธ๋ฑ์ค๊น์ง ํ๋ฅธ๋ค. ๋์์ cmpl idx, 0x4์ abort๊ฐ ๋ถ์๋ค๋ ๊ฑด, typer๊ฐ idx๋ฅผ Range(0,0)(= ํญ์ < 4)์ผ๋ก ์ขํ๋ค๋ ๋ป์ด๋ค.
์ด๊ฒ ์ ํํ abiondo๊ฐ ์ํ๋ ๊ทธ๋ฆผ์ด๋ค โ "๋ฐํ์์ ๊ณ์ฐ๋๋๋ฐ typer๋ ์๋ค๊ณ ๋ฏฟ๋ ์ธ๋ฑ์ค". ์ V8์ด์๋ค๋ฉด ๊ทธ "typer๊ฐ ์๋ค๊ณ ๋ฏฟ์"์ด ๊ณง CheckBounds ์ ๊ฑฐ์๊ณ , ๋ฐํ์ b=1 โ idx=20์ด ๊ฒ์ฌ ์์ด ํต๊ณผํด wild OOB๊ฐ ๋์ ๊ฒ์ด๋ค. 11.2๋ ๊ทธ "๋ฏฟ์"์ ๊ฒ์ฌ ์ ๊ฑฐ๊ฐ ์๋๋ผ abort ๊ฐ๋๋ก ๋ฐ๊ฟจ๋ค. ๊ทธ๋์ ๊ฐ์ ๊ทธ๋ฆผ์ด wild OOB ๋์ ์ฆ์ฌ๋ค.
์ฝ ์ค๋ฅด๋ ๊ฑด โ ์ด๊ธ๋จ์ ์ธ๋ฑ์ค๊น์ง ์ด๋ ค๋ณด๋ด๋ ์ด๋ ค์ด ๋ถ๋ถ์ ๋ค ๋๋๋ฐ, ๋ง์ง๋ง ํ ์นธ(๊ฒ์ฌ ์ ๊ฑฐ โ abort)์์ ๋งํ๋ค๋ ์ ์ด๋ค. 1ํธ์์ ๊ฐ์ฒด ํ๋ ์ผํฐ๋ฌ์ด Boolean์ผ๋ก ๋จ์ ๋ชป ์ขํ์ก๋ ๊ฒ๊ณผ ๋ฌ๋ฆฌ, ์์ฐ ํฐ์ด์
์ ํผ๋๋ฐฑ("b๋ ํญ์ false")์ผ๋ก b๋ฅผ Range(0,0)๊น์ง ์ขํ๋ฉด์๋ ๋
ธ๋๋ฅผ ์ด๋ ค๋๋ค. abiondoยทdoar-e ์๋์๋ค๋ฉด ์ฌ๊ธฐ์ ๊ฒ์์ด ๋๋ฌ๋ค. 11.2์์ ์ฌ๊ธฐ๊ฐ ์์์ด๊ณ ๋์์ ๋์ด๋ค.
turbolizer๋ก ์ง์ ํ์ธ
๊ฐ์ ์ด๊ธ๋จ ํจํด์ ๋ด์ ๋ ์งง์ ๋ณํ(dis.js, a[b*20] ๋์ a[b*5])์ --trace-turbo๋ก ๋ฝ์ turbolizer์ ์ฌ๋ ค๋ดค๋ค. effect-linearization ๋จ๊ณ์ sea-of-nodes ๊ทธ๋ํ๋ค.

๊ทธ๋ํ๋ง์ผ๋ก CheckBounds ๋
ธ๋ ํ๋๋ฅผ ๋์ผ๋ก ์ง๊ธฐ ์ด๋ ค์์, ๊ฐ์ ์ปดํ์ผ์ --trace-turbo-reduction์ผ๋ก๋ ๋ฝ์ ๊ฐ ๋ฆฌ๋์๊ฐ CheckBounds๋ฅผ ์ด๋ป๊ฒ ๊ฑด๋๋ฆฌ๋์ง ํ
์คํธ๋ก ๋์กฐํ๋ค.
./out/d8 --allow-natives-syntax --trace-turbo-reduction dis.js 2>&1 | grep -iE 'CheckBounds' | head -6
ํต์ฌ์ #110: CheckBounds[...]๊ฐ ์ฌ๋ฌ ๋ฆฌ๋์(TyperโTypedOptimizationโRedundancyEliminationโLoadElimination)๋ฅผ ๊ฑฐ์น๋ ๋์ ์ง์์ง์ง ์๊ณ ๊ณ์ in-place update๋ง ๋๋ค๋ ์ ์ด๋ค โ ๋
ธ๋ ์์ฒด๊ฐ ํ์ดํ๋ผ์ธ ๋๊น์ง ์ด์๋จ๋๋ค. ์์์ ๋์ค์ด์
๋ธ๋ฆฌ๋ก ๋ณธ "๊ฒ์ฌ ์์ฒด๋ ์ ์ง์์ง๋ค"๋ ์ฃผ์ฅ์ด ์ปดํ์ผ ํ์ดํ๋ผ์ธ ๋ก๊ทธ๋ก๋ ๊ทธ๋๋ก ํ์ธ๋๋ค.
๐ชฆ doar-e ์ฐํ์ ๊ทธ ๋ฌด๋ค โ js-native-context-specialization.cc
์ฌ๊ธฐ๊น์ง๋ "ํ์ค ๊ฒฝ๊ณ๊ฒ์ฌ" ์ด์ผ๊ธฐ๋ค. ๊ทธ๋ฐ๋ฐ doar-e๊ฐ 2019๋
์ ๋ณด์ฌ์ค ์ฐํ๊ฐ ์๋ค โ OOB-load-mode. ๋ฐฐ์ด ์ ๊ทผ์ด ์ธํฐํ๋ฆฌํฐ ๋จ๊ณ์์ ๋ฒ์ ๋ฐ ์ธ๋ฑ์ค๋ฅผ ๋ณธ ์ ์ด ์์ผ๋ฉด, IC๊ฐ ๊ทธ ์ฌ๋กฏ์ "OOB ํ์ฉ ๋ก๋"๋ก ๊ธฐ๋กํ๋ค. ๊ทธ๋ฌ๋ฉด TurboFan์ abortํ๋ CheckBounds ๋์ NumberLessThan(idx, len) + (์คํจ ์ undefined) ํจํด์ผ๋ก ์ปดํ์ผํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด NumberLessThan์ ์์ ๋น๊ต ๋
ธ๋๋ผ, typer๊ฐ idx < len์ ์ฆ๋ช
ํ๋ฉด ConstantFoldingReducer๊ฐ ํต์งธ๋ก ์ง์๋ฒ๋ฆฐ๋ค โ abort ๊ฐ๋ ์์ด. doar-e๋ ๊ฑฐ๊ธฐ์ ๊ฒ์ฌ ์๋ OOB๋ฅผ ์ป์๋ค.
11.2์ element-access ๋น๋ ์์ค๋ฅผ ์ด์ด๋ณด๋ฉด, ๊ทธ NumberLessThan ์์ชฝ์ ๋ ๋ฒ์งธ CheckBounds๊ฐ ๋ฐํ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ฃผ์์ด ์๋๋ฅผ ํ ์จ ํ๋ ์ ์จ๊ธฐ๊ณ ์ ์ด๋จ๋ค.
// src/compiler/js-native-context-specialization.cc โ LOAD_IGNORE_OUT_OF_BOUNDS
Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
{
// Do a real bounds check against {length}. This is in order to
// protect against a potential typer bug leading to the elimination of
// the NumberLessThan above.
index = etrue = graph()->NewNode(
simplified()->CheckBounds(
FeedbackSource(), CheckBoundsFlag::kConvertStringAndMinusZero |
CheckBoundsFlag::kAbortOnOutOfBounds), // โ ๋ abort
index, length, etrue, if_true);
vtrue = etrue = graph()->NewNode(simplified()->LoadElement(element_access), ...);
}NumberLessThan์ typer๋ก ์ง์๋, if_true ๋ถ๊ธฐ ์์์ kAbortOnOutOfBounds๊ฐ ๋ถ์ ๋ ๋ฒ์งธ CheckBounds ๊ฐ ๋ ๋ง๋๋ค. ์ฃผ์์ด ๊ทธ๋๋ก โ "protect against a potential typer bug leading to the elimination of the NumberLessThan above" โ "NumberLessThan์ ์ง์ฐ๋ typer ๋ฒ๊ทธ"๋ฅผ ์ฝ ์ง์ด ๋ฐฉ์ดํ๋ค๊ณ ์ ์ธํ๋ค. doar-e์ ์ฐํ๋ฅผ ์ ์กฐ์คํ ๋ฌด๋ค์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋๊ฐ์ ์ฃผ์+ํจํด์ด jnscs.cc์ ๋ค ๋ฒ(load 3243, store/grow 3662ยท3742ยท3822) ๋ฐํ ์๋ค โ load๋ store๋ grow๋ , OOB ํ์ฉ ๋ชจ๋๋ฅผ ์ฐ๋ ๋ชจ๋ element-access ๊ฒฝ๋ก์ ๊ฐ์ ๋ฐฉ์ด๋ฅผ ๊น์๋ ๊ฒ์ด๋ค.
๊ฒ๋ค๊ฐ ์ฐ๋ฆฌ ๋ฒ๊ทธ๋ ์ด ์ฐํ๋ฅผ ์๋ํ ์๊ฒฉ์กฐ์ฐจ ๋ชป ๊ฐ์ถ๋ค. OOB-load-mode๋ฅผ ์ผ๋ ค๋ฉด ํ๋ จ ์ค ๋ฒ์ ๋ฐ ์ ๊ทผ์ ๋ด์ผ ํ๊ณ (= b=true ํธ์ถ ํ์), ๊ทธ๋ฌ๋ฉด Object.is ํผ๋๋ฐฑ์ด Boolean์ด ๋ผ ์ธ๋ฑ์ค๊ฐ Range(0,K)๋ก ๋์ด์ง๋ค โ NumberLessThan์ด ํด๋ฉ ์ ๋จ. ๋ฐ๋๋ก b=false๋ง ์ฃผ๋ฉด OOB-mode๊ฐ ์ ์ผ์ ธ ์ฒซ ๋ฒ์งธ CheckBounds[abort]. ๋์ด ์ํธ๋ฐฐํ๋ค. doar-e์ lastIndexOf ๋ฒ๊ทธ๋ ๊ฒฐ๊ณผ๋ฅผ ์ ์ ์ผ๋ก ์์ ๋ฒ์๋ก ์คํ์
ํด์ ํผ๋๋ฐฑ๊ณผ ๋ฌด๊ดํ๊ฒ Range(0,0)์ด ๋์์ง๋ง, Math.exp๋ ๊ฒฐ๊ณผ๋ฅผ ๋์ PlainNumber๋ก ์คํ์
ํ ๋ฟ์ด๋ผ ์ธ๋ฑ์ค๋ฅผ ์๊ฒ ๋ง๋๋ ๊ฒ ํผ๋๋ฐฑ ์ถ์ธก์ ์์กดํ๊ณ , ๊ทธ๊ฒ OOB-mode์ ์ถฉ๋ํ๋ค.
![ํด๋ฆญํ์ฌ ํ๋ V8 11.2 ์์ค(jnscs.cc) โ doar-e์ NumberLessThan ํด๋ฉ ์ฐํ๋ฅผ ์ฝ ์ง์ด ๋ง์ ๋ ๋ฒ์งธ CheckBounds[kAbortOnOutOfBounds]. loadยทstoreยทgrow ๊ฒฝ๋ก๋ง๋ค ๊ฐ์ ์ฃผ์+ํจํด์ด ๋ฐํ ์๋ค](/_next/image?url=%2Fimages%2Fblog%2Fdreamhack-exp-nan-writeup-2%2F01_doare_patch.png&w=3840&q=75)
๐ช ๋ชจ๋ ์ถ๊ตฌ๊ฐ ๋งํ ์ด์
"์ธ๋ฑ์ค๊ฐ ์ ๋๋ฉด ๋ค๋ฅธ sink๋?" ์ถ์ด ๊ฐ๋ฅํ ์ถ๊ตฌ๋ฅผ ํ๋์ฉ ๋ค ๋๋๋ ค๋ดค๋ค. ๊ฐ์ ์ด๊ธ๋จ(typer๋ ์๋ค ๋ฏฟ๊ณ ๋ฐํ์์ ํผ/NaN)์ ์ด๋์ ๊ฝ์๋ ๋งํ๋ค. ๊ฐ ์ถ๊ตฌ๊ฐ ์ด๋ ์ฝ๋์ ๋งํ๋์ง๊น์ง ์ ๋๋ค.
| ์ถ๊ตฌ | ๊ฒฐ๊ณผ | ๋ง์ ๋ฉ์ปค๋์ฆ (์์ค) |
|---|---|---|
a[b*K] ์ธ๋ฑ์ค ๋ก๋/์คํ ์ด | SIGTRAP | kAbortOnOutOfBounds (simplified-lowering.cc) |
OOB-load-mode + b*K | undefined / ํธ๋ฉ | ์์ชฝ ๋ ๋ฒ์งธ CheckBounds[abort] (jnscs.cc) |
Array(b*K) ๊ธธ์ด ๊ตฌ์ฑ | ์ค์ 4096์นธ ํ ๋น | b*K๊ฐ ์์ ๋
ธ๋ ์๋ โ ๋ฐํ์๊ฐ์ผ๋ก ์ ์ ์์ฑ |
arr.length = b*K | backing ์ค์ grow(FixedDoubleArray[4096]) | ๋ฐํ์๊ฐ์ผ๋ก grow, ๊ธธ์ด/์ฉ๋ ๋ถ์ผ์น ์์ |
store-and-grow a[b*K]=v | SIGTRAP | MaybeGrowFastElements ํ๋๋ |
t*0 โ cvttsd2si(NaN)=INT_MIN | undefined | CheckedUint64Bounds (์์โ๊ฑธ๋ฌ์ง) |
Math.max(0,t)/Math.min(t,5) | divergence ์ง์ง์ง๋ง ๋ฐํ์โtyper๋ฒ์ | OOB/length๋ก ๋ชป ํค์ |
๋ฃจํ induction var i+=Math.exp | i = NaN | NaN sticky โ ํฐ ์ ํ ์ธ๋ฑ์ค๊ฐ ์ ๋์ด |
๋ง์ง๋ง ์ค์ด ํฅ๋ฏธ๋กญ๋ค. Project Zero์ "Chrome Infinity Bug"๋ induction ๋ณ์๊ฐ -Inf + Inf๋ก ํฐ ์ ํ๊ฐ์ด ๋๋ฉฐ typer ๋ฒ์๋ฅผ ๋ฒ์ด๋ OOB๊ฐ ๋๋ค. ๊ฐ์ ํธ๋ฆญ์ Math.exp(NaN)์ผ๋ก ์๋ํ๋ฉด โ NaN์ ์ฐ์ ์์ NaN์ผ๋ก ์ ํ๋๋ โ induction ๋ณ์๊ฐ ๊ทธ๋ฅ NaN์ด ๋๋ค.
$ ./out/d8 --allow-natives-syntax induction.js
for(i<1;i+=exp) ret i => NaN # i += NaN โ NaN, ๋ฃจํ ์ฆ์ ํ์ถ
while(i<exp)i++ ret i => 0 # NaN > 0 false โ ๋ฃจํ 0ํ
arr[ind] OOB? => undefined # NaN ์ธ๋ฑ์ค โ ์ด๋ฆ ์กฐํ
Array(ind*4096) => RangeError # Array(NaN) ์์ธ์ด๊ฒ ์ด ๋ฒ๊ทธ์ ๋ณธ์ง์ ํ๊ณ๋ค โ NaN์ ๋์ ํ๋ค. ์ฐ์ ์ ๋ฃ์ผ๋ฉด NaN์ผ๋ก ์ ํ๋๊ณ (โ ToInt32=0 / Array=์์ธ), ๋น๊ต์ ๋ฃ์ผ๋ฉด ๋ถ๋ฆฌ์ธ์ด ๋๋๋ฐ ๊ทธ๊ฑด ํด๋ฉ๋๊ฑฐ๋(์์) ์ธ๋ฑ์ค๋ก ์ฐ๋ฉด ํธ๋ฉ์ด๋ค. ํฐ ์ ํ ์ ์ ์ธ๋ฑ์ค๋ก ์๋ผ๋ ๊ธธ์ด ์๋ค.

โถ๐ฌ ์ถ๊ตฌ๋ฅผ ํ๋์ฉ ๋๋๋ฆฐ ๊ธฐ๋ก (์ค์ธก ๋ก๊ทธ)
ํ์ ๊ฐ ์ค์ ์ถ์ธก์ด ์๋๋ผ ๋ก์ปฌ d8 ์ธก์ ์ด๋ค. ๋งํ ๋ฐฉ์์ด ์ถ๊ตฌ๋ง๋ค ๋ฏธ๋ฌํ๊ฒ ๋ฌ๋ผ์, ๊ทธ ์ฐจ์ด๊ฐ ๊ฒฐ๊ตญ "๊ฐ-range๋ ์ด๋๋ก๋ ์ ์๋๋ค"๋ฅผ ๊ฐ๋ฆฌ์ผฐ๋ค.
Array(b*K) โ ๋ถ์ผ์น ์์. typer๊ฐ b*K๋ฅผ Range(0,0)์ผ๋ก ๋ฏฟ์ผ๋ size-0 ํ ๋น + ๋ฐํ์ length๋ก ๋ถ์ผ์น(Bug-1051017์)๋ฅผ ๋
ธ๋ ธ๋ค. ๊ทธ๋ฐ๋ฐ b*K๋ ์์ ๋
ธ๋๊ฐ ์๋๋ผ ๊ณ์ฐ ๋
ธ๋๋ผ, JSCreateArray๊ฐ ์ธ๋ผ์ธ ๊ณ ์ ํ ๋น์ ์ ํ๊ณ ๋ฐํ์๊ฐ์ผ๋ก ์์ฑ์๋ฅผ ํธ์ถํ๋ค.
$ ./out/d8 --allow-natives-syntax len_live.js
Array(b*4096).length [TF] => len=4096
Array(b*4096) a[500] oob? [TF] => len=4096 a[500]=undef # ์ง์ง 4096์นธ, ๋ถ์ผ์น Xarr.length = b*K โ backing์ด ์ค์ ๋ก ํฐ๋ค. typer๊ฐ length-set์ "0์ผ๋ก ์ค์(shrink)"์ผ๋ก ๋ฏฟ์ด grow๋ฅผ ์๋ตํ๊ธธ(โ length๋ง ๊ฑฐ๋, capacity ์์) ๊ธฐ๋ํ๋ค. %DebugPrint๋ก ๋ณด๋ฉด backing์ด ๊ทธ๋ฅ 4096์นธ์ผ๋ก ์๋๋ค.
$ ./out/d8 --allow-natives-syntax shrink_probe.js
a.length = 4096
DebugPrint: [JSArray]
- elements: <FixedDoubleArray[4096]> [HOLEY_DOUBLE_ELEMENTS] # capacity ๋ 4096 โ ๋ถ์ผ์น Xstore-and-grow a[b*K]=v โ ํธ๋ฉ. MaybeGrowFastElements ๊ฒฝ๋ก๋ ํ๋๋๋ผ ์์ด, ๋น ๋ฐฐ์ด์์ grow ํ๋ จ ํ ํธ๋ฆฌ๊ฑฐํ๋ฉด SIGTRAP(exit 133).
t*0 โ cvttsd2si(NaN) = INT_MIN. floatโint ๋ณํ์์ NaN ๋ณด์ ์๋ vcvttsd2si๊ฐ emit๋ผ INT_MIN(๊ฑฐ๋ ์์)์ด ๋์ค์ง๋ง, CheckedUint64Bounds๊ฐ unsigned๋ก ๊ฑธ๋ฌ undefined.
Math.max/min โ ์ด๊ธ๋จ์ ์ง์ง์ธ๋ฐ ๋ฌด์ฉ. typer๊ฐ no-NaN์ ๋ฏฟ์ด maxsd/minsd์ NaN ๋ณด์ ์ ์๋ตํ๋ฏ๋ก, Math.max(0,t)๊ฐ ์คํ(NaN)๊ณผ ๋ฌ๋ฆฌ 0์ ๋๋ ค์ค๋ค(์ง์ง miscompile). ํ์ง๋ง ๋ฐํ์ ๊ฐ์ด ํญ์ typer ๋ฒ์ ์์ด๋ผ(0 โ [0,โ), 5 โ (-โ,5]) OOB๋ก๋ length๋ก๋ ๋ชป ํค์ด๋ค.
$ ./out/d8 --allow-natives-syntax probe_minmax.js
Math.max(0,t) OPT=0 SPEC=NaN DIVERGE # maxsd(0,NaN)=0, ๊ทธ๋ฌ๋ 0โ[0,โ)
Math.min(t,5) OPT=5 SPEC=NaN DIVERGE # minsd(NaN,5)=5, ๊ทธ๋ฌ๋ 5โ(-โ,5]์ฌ๋ ์ถ๊ตฌ์ ๊ณตํต ๊ฒฐ๋ก โ ์ด๊ธ๋จ์ด ๊ฐ์ผ๋ก ์ฐ์ด๋ฉด ํด๋ฉ๋๊ฑฐ๋(์์) ๋ฐํ์์ด typer ๋ฒ์ ์์ด๊ณ , ์ธ๋ฑ์ค๋ก ์ฐ์ด๋ฉด abort๋ค. ๊ทธ ์ฌ์ด๊ฐ ์๋ค.
๐งฉ ๊ทผ๋ณธ ์์ธ โ ๋ ๋ฐฉํฅ์ ๋ฒฝ
์ ์ด๋ ๊ฒ ํ ๊ณณ๋ ์ ๋ซ๋ฆฌ๋์ง, ๋ ๋ฒฝ์ด ์ง๊ตํ๋ค.
๋ฒฝ 1 โ fold โ live. ์ด๊ธ๋จ์ด ์ต์ค์ ์ธ๋ชจ ์์ผ๋ ค๋ฉด โ ์ปดํ์ผ ํ์์ ์์๋ก ํด๋ฉ๋๋ฉด ์ ๋๊ณ (ํด๋ฉ๋๋ฉด typer ๊ฐ = ๋ฐํ์ ๊ฐ, self-consistent), โก ๊ทธ๋ฌ๋ฉด์ typer๊ฐ ๊ทธ ํ์
์ ์ข๊ฒ ๋ฏฟ์ด์ค์ผ ํ๋ค(๊ฒ์ฌ ์ ๊ฑฐ์ ๊ทผ๊ฑฐ). ๋ฆฌํฐ๋ด NaN ์ผํฐ๋ฌ์ โ ์ ์ด๊ฒจ ์ฆ์ ํด๋ฉ๋๊ณ , ๊ฐ์ฒด ํ๋ ์ผํฐ๋ฌ์ โก๋ฅผ ๋ชป ์ป์ด SameValue๊ฐ Boolean์ผ๋ก ๋จ๋๋ค. abiondo๊ฐ ๊ธฐ๋ "ํ๋์ ์จ๊ธฐ๋ฉด ๋ฆ๊ฒ false๋ก ์ฌํ์
๋๋ค"๋ ๋์์ด 11.2์์ ์ผ์ด๋์ง ์๋๋ค(์ phase ์ถ์ ์ผ๋ก ํ์ธ โ SameValue๋ SimplifiedLowering๊น์ง Boolean).
๋ฒฝ 2 โ ์ขํ ์๊ฐ abort. ์์ฐ ํฐ์ด์
์ ๋๋๊ฒ๋ ๋ฒฝ 1์ ํต๊ณผํ๋ค โ b๊ฐ ํด๋ฉ ์ ๋๊ณ (store๋ก live) typer๊ฐ Range(0,0)์ผ๋ก ์ขํ๋ค. abiondo๊ฐ ํ์๋ก ํ ์กฐ๊ฑด์ ๋ค ๋ง์กฑํ๋ค. ๊ทธ๋ฐ๋ฐ ์ ํํ ๊ทธ "typer๊ฐ ์ขํ๋ค"๊ฐ kAbortOnOutOfBounds๋ฅผ ๋ถ๋ฅธ๋ค(๋ฒฝ 2). ์ V8์์ ์ขํ = ๊ฒ์ฌ ์ ๊ฑฐ = wild OOB์๋๋ฐ, 11.2์์ ์ขํ = abort ๊ฐ๋๋ค.
๊ทธ๋์ ๋ ๋ฒฝ ์ฌ์ด์ ํต๊ณผ ์ง์ ์ด ์๋ค. ์ ์ขํ๋ฉด ํด๋ฉ ์ ๋ผ๋ ๊ฒ์ฌ๊ฐ ์ด์ deopt(undefined), ์ขํ๋ฉด ๊ฒ์ฌ๊ฐ abort. ๊ทธ ์ฌ์ด "๊ฒ์ฌ๊ฐ ํต์งธ๋ก ์ฌ๋ผ์ง๋" ๊ตฌ๊ฐ์ 11.2๊ฐ ๋ฉ์๋ฒ๋ ธ๋ค.

๐ ๏ธ ๊ทธ๋ผ ์์ฆ์ ์ด๋ป๊ฒ ๋ซ๋ โ ๋ฒ๊ทธ ํด๋์ค๊ฐ ๊ฐ๋ ธ๋ค
์ฌ๊ธฐ์ ์์ฐ์ค๋ฌ์ด ์๋ฌธ โ ๊ทธ๋ผ ํ๋๋ ์ผ์ง ์์ฆ V8์ ์๋ฌด๋ ๋ชป ๋ซ๋? ์๋๋ค. ๋ค๋ง ๋ฒ๊ทธ ํด๋์ค๊ฐ ๊ฐ๋ ธ๋ค. ๊ฐ์ ์๊ธฐ(ํ๋๋+์๋๋ฐ์ค ON) V8์ ์ค์ ๋ก ๋ซ์ ์ต์ค ๋์ ๋ณด๋ฉด ๋ต์ด ๋ณด์ธ๋ค.
- Google v8CTF (Issue 1472121) โ IC clone ๋ฒ๊ทธ(๊ฐ์ฒด ๋ ์ด์์ ๋ถ์ผ์น). ๊ธ์ด ๋ช
์ํ๋ค: "representation checks๋ก ์ฐํ โ typer range bug๊ฐ ์๋๋ค. ์์์ด nominal bounds๋ฅผ ์ด๊ธฐ๋ ๊ฒ ์๋๋ผ, ๊ทธ bounds๊ฐ ํด์๋๋ ๋ฐฉ์์ ์์ด์ abort-on-bounds๋ฅผ ์ฐํํ๋ค." ์ฆ ๋ฐฐ์ด์ length/capacity ํ๋๋ฅผ ์ง์ ์์์ํจ ๋ค, ๊ทธ (์์๋) ๊ธธ์ด ์์ ์ธ๋ฑ์ค๋ก ์ ๊ทผํ๋ค. ์ธ๋ฑ์ค๊ฐ "in-bounds"๋ผ
kAbortOnOutOfBounds๊ฐ ๋ฐ๋ํ ์ผ์ด ์๋ค. - openECSC (Array.xor) โ
valueOf()๋ถ์ํจ๊ณผ๋ก ์ธ์ ๊ฐ์ ๋ณํ๊ณผ ํ์ ๊ฒ์ฌ ์ฌ์ด์ ๋ฐฐ์ด์ element kind๋ฅผ ๋ฐ๊พธ๋ type confusion. ์ญ์ length๋ฅผ ์ง์ ์์์์ผ OOB ๋ฐฐ์ด์ ๋ง๋ ๋ค.
์กฐ๊ธ ๋ ๊ตฌ์ฒด์ ์ผ๋ก, v8CTF Issue 1472121์ ์ฒด์ธ์ด ์ ํ์ ์ด๋ค. clone IC ๋ฒ๊ทธ๋ก source ๊ฐ์ฒด์ in-object ํ๋กํผํฐ๋ค์ด target ๊ฐ์ฒด์ ๋ ์์ ์ฌ๋กฏ์ ๊ทธ๋๋ก ๋ณต์ฌ๋ผ, 5~9๋ฒ ํ๋กํผํฐ๊ฐ ํ๋กํผํฐ ์คํ ์ด ํฌ์ธํฐยท๋ฉํ๋ฐ์ดํฐ์ ๊ฒน์น๋ค. ์ด ๊ฒน์นจ์ผ๋ก ์ธ์ ๋ฐฐ์ด์ length ํ๋๋ฅผ ํฐ ๊ฐ์ผ๋ก ๋ฎ์ผ๋ฉด ๊ทธ๊ฒ ๊ณง OOB ๋ฐฐ์ด์ด๋ค. ๊ฑฐ๊ธฐ์๋ถํฐ๋ ์ ์ โ OOB ๋ฐฐ์ด๋ก ์ ๊ฐ์ฒด ์ฃผ์๋ฅผ ์ฝ์ด addrof, ArrayBuffer์ (40๋นํธ ์๋๋ฐ์ค) backing ํฌ์ธํฐยทํฌ๊ธฐ๋ฅผ ๋ฎ์ด ์ผ์ด์ง ๋ด๋ถ ์์ R/W, ๋ง์ง๋ง์ผ๋ก WASM์ RWX ์์ญ์ ROP/์
ธ์ฝ๋๋ฅผ ์น์ด ์๋๋ฐ์ค ํ์ถ. ์์์ ๋ง ๋ค๋ฅผ ๋ฟ ๋ท๋ถ๋ถ์ ์ฐ๋ฆฌ๊ฐ 1ํธ์์ ๋งคํํ ๊ฒ๊ณผ ๊ฐ์ ๊ธธ์ด๋ค.
openECSC์ Array.xor๋ ๋ฒ๊ทธ ๋ชจ์์ด ๋ ๋ค๋ฅด๋ค โ TOCTOU(๊ฒ์ฌ-์ฌ์ฉ ์์ ์ฐจ). ๋นํธ์ธ์ด ์ธ์๋ฅผ ์ซ์๋ก ๊ฐ์ ๋ณํํ๊ธฐ ์ ์ ๋ฐฐ์ด์ element kind๊ฐ PACKED_DOUBLE์ธ์ง ๊ฒ์ฌํ๋๋ฐ, ๊ทธ ์ธ์๊ฐ ์
์์ valueOf๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ผ ๊ฐ์ ๋ณํ ๋์ค arr[0] = {}๋ก ๋ฐฐ์ด์ PACKED_ELEMENTS(๊ฐ์ฒด ํฌ์ธํฐ ์ ์ฅ)๋ก ์ ์ด์์ผ ๋ฒ๋ฆฐ๋ค. ๊ฒ์ฌ๋ ํต๊ณผํ๊ณ , ์ ์ XOR ์ฐ์ฐ์ ๊ฐ์ฒด ํฌ์ธํฐ๋ฅผ raw 64๋นํธ๋ก ์ฃผ๋ฌด๋ฅธ๋ค โ ํฌ์ธํฐ ์์ โ ๊ฐ์ง ๋ฐฐ์ด โ OOB. ์ฌ๊ธฐ์๋ typer๋ฅผ ์์ด๋ ๊ฒ ์๋๋ค. ๊ฒ์ฌ์ ์ค์ ๋์ ์ฌ์ด์ ์์ ์ฐจ๋ฅผ ๋น์ง์ด ๊ฐ์ฒด์ ์ข
๋ฅ๋ฅผ ๋ฐ๊พผ๋ค. ๊ฐ-range ๋ฒ๊ทธ๊ฐ "typer์ ๋ฏฟ์"์ ๊ณต๊ฒฉํ๋ค๋ฉด, ์ด ๋์ "๊ฐ์ฒด์ ํํ/์ข
๋ฅ"๋ฅผ ์ง์ ์ด๊ทธ๋ฌ๋จ๋ฆฐ๋ค โ kAbortOnOutOfBounds๊ฐ ์๋ ์ ์๋ ์ง์ ์ด๋ค.
ํต์ฌ์ ์ด๊ฑฐ๋ค โ ์ด ์ต์ค๋ค์ bounds ๊ฒ์ฌ๋ฅผ ์ฐํํ์ง ์๋๋ค. ๊ฒ์ฌ๊ฐ ๋ณด๋ length ์์ฒด๋ฅผ ๊ฑฐ์ง์ผ๋ก ๋ง๋ ๋ค. typer๋ฅผ ์์ฌ "์์ ์ธ๋ฑ์ค"๋ฅผ ๋ง๋๋ ๊ฒ ์๋๋ผ, ๋ฒ๊ทธ๋ก length ํ๋๋ฅผ ์ง์ ํค์ ์ธ๋ฑ์ค๊ฐ ์ง์ง๋ก in-bounds๊ฐ ๋๊ฒ ํ๋ค. kAbortOnOutOfBounds๋ "์ธ๋ฑ์ค โฅ length"์ผ ๋๋ง ํธ๋ฉ์ด๋, length๋ฅผ ํค์๋๋ฉด ๋ฐ๋ํ์ง ์๋๋ค. ์ฆ 11.2์ ํ๋๋์ "typer ์ฆ๋ช
์ผ๋ก ๊ฒ์ฌ๋ฅผ ์์ ๋" ํํ๋ง ์ ์กฐ์คํ์ง, "๊ฒ์ฌ๊ฐ ์ฐธ์กฐํ๋ length๋ฅผ ์์์ํค๋" ํํ๋ ์๋์ง ์๋๋ค โ ์๋ ์๊ฐ ์๋ค(๊ทธ๊ฑด ์ ์ ์ฝ๋์ ์ ์ ๋์์ด๋๊น).
๊ทธ๋ฐ๋ฐ length/element-kind๋ฅผ ์ง์ ์์์ํค๋ ค๋ฉด OOB ์ฐ๊ธฐ๋ element-kind ํผ๋ ๊ฐ์ ์ด๊ธฐ ํ๋ฆฌ๋ฏธํฐ๋ธ๊ฐ ํ์ํ๋ค. Math.exp ๋ฒ๊ทธ๊ฐ ์ฃผ๋ ๊ฑด ๊ฐ-range ์ด๊ธ๋จ(typer๋ ์๋ค ๋ฏฟ๊ณ ๋ฐํ์ ํผ)๋ฟ โ ์ด๊ฑด ์ ํํ kAbortOnOutOfBounds๊ฐ ์ ์กฐ์คํด ๋ง์ ํด๋์ค๊ณ , length๋ฅผ ์ง์ ๊ฑด๋๋ฆด ์์ก์ด๊ฐ ์๋ค. ๊ทธ๋์ ์ด ๋ฒ๊ทธ๋ ํ๋๋ ์ผ์ง 11.2์์ ์ด ํ์ค ๊ฒฝ๋ก๋ก๋ ์ ์๋๋ค.
| ๋ฒ๊ทธ ํด๋์ค | ์ | ํ๋๋ 11.2์์ |
|---|---|---|
| ๊ฐ-range (typer๊ฐ ๊ฐ์ ๋ฒ์๋ฅผ ์ค์ธ) | Math.exp(์ด ๋ฌธ์ ), Math.expm1(-0), lastIndexOf | kAbortOnOutOfBounds์ ์ฃฝ์ โ ์ขํ ์ธ๋ฑ์ค = abort |
| layout / element-kind / IC (๊ฐ์ฒด ํํยท์ข ๋ฅ๋ฅผ ์ค์ธ) | v8CTF clone, openECSC Array.xor | ์ด์์์ โ length/capacity๋ฅผ ์ง์ ์์ โ in-bounds ์ ๊ทผ |

๐ฐ๏ธ ๋ฒ์ ์ด ์ ๋ถ๋ค
๊ฐ์ ๋ฒ๊ทธยท๊ฐ์ ๊ฐ์ ฏ์ด๋ผ๋ ์ด๋ V8์ด๋๊ฐ ์ด๋ช
์ ๊ฐ๋ฅธ๋ค. kAbortOnOutOfBounds ์์ฒด๋ 11.2 ์์ค์ ๋ฌด์กฐ๊ฑด ๋ฐํ ์์ง๋ง, ์ดํ V8์ ์ด๊ฑธ turbo_typer_hardening์ด๋ผ๋ READONLY ํ๋๊ทธ๋ก ํ ๊ธ ๊ฐ๋ฅํ๊ฒ ๋ฐ๊ฟจ๋ค. HTB GCSB 2026์ ํ TurboFan typer ๋ฌธ์ ๋ โ ๊ฐ์ ๊ฐ-range ์ด๊ธ๋จ์ ์ฐ๋๋ฐ โ ์ถ์ ์๊ฐ ๊ทธ ํ๋๊ทธ๋ฅผ ๊บผ๋ฒ๋ฆฌ๊ณ (turbo_typer_hardening=false) ์๋๋ฐ์ค๊น์ง ๋(v8_enable_sandbox=false) ๋น๋๋ก ๋๋ค. ๊ทธ๋ฌ๋ ๊ฑฐ๊ธฐ์ ์ ์ ์์ด ๊ทธ๋๋ก ํตํ๋ค.
์ฐ๋ฆฌ EXP-NaN์ 11.2๋ผ ๊ทธ ํ๋๊ทธ ์์ฒด๊ฐ ์์ง ์๊ณ (์ฝ๋์ ๋ฌด์กฐ๊ฑด), v8_enable_sandbox=true๊น์ง ์ผ์ง ๋น๋๋ค. ๊ฐ์ ๋ถ๋ฅ์ ๋ฒ๊ทธ๊ฐ, ํ์ชฝ ๋ํ์์ "๊ต์ก์ฉ ์๋ฐ์
"์ด๊ณ ๋ค๋ฅธ ์ชฝ(์ด ๋ฌธ์ )์์ "๋ค์ด์1"์ธ ์ด์ ๊ฐ ์ ํํ ์ด ๋น๋ ์ค์ ์ฐจ์ด๋ค.
๐ค ์ ํํ NaN์ธ๊ฐ โ ๊ฐ์ ํด๋์ค, ๋ ๋์ ํ ๊ฐ
์ด ๋ฒ๊ทธ๋ abiondo๊ฐ 2019๋
์ ํฐ๋จ๋ฆฐ Math.expm1(-0) ๋ฒ๊ทธ์ ์ ํํ ๊ฐ์ ํด๋์ค๋ค โ "typer๊ฐ ์ด๋ค ๋นํธ์ธ์ ์ถ๋ ฅ์์ ํน์๊ฐ(๊ทธ์ชฝ์ -0, ์ด์ชฝ์ NaN)์ ๋น ๋จ๋ ค, Object.is(๋นํธ์ธ, ํน์๊ฐ)์ด typer์ falseยท๋ฐํ์์ true์ธ ๋ถ๋ฆฌ์ธ์ด ๋๋ค." ๊ฐ์ ฏ์ ๊ณจ๊ฒฉ์ด ๋๊ฐ๋ค.

๊ทธ๋ฐ๋ฐ NaN์ -0๋ณด๋ค ํ ๊ฒน ๋ ๊ฐ๊ฐํ๋ค. -0์ ์ฐ์ ์์ ์ฌ์ค์ 0์ฒ๋ผ ํ๋ฅธ๋ค(-0 + x = x, ToInt32(-0)=0) โ ์ฆ "๊ฑฐ์ 0์ธ ์ ํ๊ฐ"์ด๋ผ, ๋ถ๋ฆฌ์ธ์ผ๋ก ์ ๊ฐ๋ ๊ฐ ์์ฒด๋ฅผ ์ด๋ป๊ฒ๋ ํ๋ ค๋ณผ ์ฌ์ง๊ฐ (์ ๋น๋์์ ) ์์๋ค. ๋ฐ๋ฉด NaN์ ๋ชจ๋ ์ฐ์ ์์ NaN์ผ๋ก ์ ํ๋๋ค. NaN + x = NaN, NaN * 0 = NaN, Math.max(NaN, k)๋ (๋ณด์ ์์ผ๋ฉด) NaN, ๋ฃจํ์์ i += NaN๋ NaN. ๊ทธ๋ฆฌ๊ณ ๊ทธ NaN์ ์ ์๋ก ๋จ๊ตฌ๋ฉด ToInt32(NaN)=0(์์์ง) ์๋๋ฉด cvttsd2si(NaN)=INT_MIN(์์โ๊ฑธ๋ฌ์ง), ๋น๊ต์ ๋ฃ์ผ๋ฉด ํญ์ false(ํด๋ฉ). ํฐ ์ ํ ์ ์๋ก ์๋ผ๋ ๊ธธ์ด ์ฐ์ ์ด๋์๋ ์๋ค.
๊ทธ๋์ NaN ๋ฒ๊ทธ๋ ์ฌ์ค์ ๋ถ๋ฆฌ์ธ ๊ฐ์ ฏ ํ๋์ ์ด๋ช
์ ๊ฑด๋ค. min/maxยทinductionยทlength-construction ๊ฐ์ "๊ฐ์ผ๋ก ํ๋ฆฌ๋" ์ฐํ๊ฐ 1ํธยท2ํธ ๋ด๋ด ์ ๋ฉธํ ๊ฒ ์ฐ์ฐ์ด ์๋๋ค โ NaN์ ๋์ ํจ์ด ๊ทธ ๊ธธ๋ค์ ์์ฒ๋ด์ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ ์ผํ ๋ถ๋ฆฌ์ธ ๊ฐ์ ฏ์ด 11.2์ kAbortOnOutOfBounds์ ์ ์กฐ์ค๋ผ ๋งํ๋ค. ์ถ์ ์๊ฐ -0์ด ์๋๋ผ NaN์, ๊ทธ๊ฒ๋ ํ๋๋ ์ผ์ง 11.2์ ์ฌ๋ฆฐ ์ ํ์ โ ๊ณ ์ ๊ฐ์ ฏ์ด ์ ์ ํตํ๋์ง ์์ค๊น์ง ๋ด๋ ค๊ฐ ๋ถ๋ชํ๋ณด๋ผ๋, ๊ฝค ์๋์ ์ธ ๋์ด๋ ์ค๊ณ๋ก ์ฝํ๋ค.
๐ช ๊ทธ ๋ค๋ ๋ค ์ค๋น๋ผ ์๋ค โ ์ฒซ OOB๋ง ์ด๋ฆฌ๋ฉด
์ด ๋ถ๊ฒ์ ๊ฒฐ๋ก ์ "์ฒซ OOB๊ฐ ์ ์ด๋ฆฐ๋ค"์ง๋ง, ๋ง์ฝ ์ด๋ฆฐ๋ค๋ฉด ๊ทธ ๋ค๋ 11.2 ๊ธฐ์ค์ผ๋ก ์ด๋ฏธ ์ ์์ด ์กํ ์๋ค(1ํธ์์ ๋งคํํ ํ๋ฐ๋ถ). ๋ฌด๊ฒ์ค์ฌ์ด ์ด๋ ์ ๋ ค ์๋์ง ๋ณด์ด๊ฒ ๊ธฐ๋กํด๋๋ค.
- ์์ OOB ๋ฐฐ์ด. ์ฒซ OOB๋ก ์ธ์ ๋ฐฐ์ด์ length/capacity๋ฅผ ๊ฑฐ๋๊ฐ์ผ๋ก ๋ฎ์ด, ๊ฒ์ฌ์ ์ ๊ฑธ๋ฆฌ๋ "๊ธธ์ด ๋ถํ๋ฆฐ" float ๋ฐฐ์ด์ ๋ง๋ ๋ค. (์์ ๋ณธ ํ์ญ ์ต์ค๋ค์ด ์ฒ์๋ถํฐ ํ๋ ๊ฒ ๋ฐ๋ก ์ด length ์์์ด๋ค โ ์ฐ๋ฆฌ๋ง ๊ทธ ์ ๊ตฌ๊ฐ ๋งํ๋ค.)
- addrof / fakeobj. float ๋ฐฐ์ด(PACKED_DOUBLE)๊ณผ object ๋ฐฐ์ด(PACKED_ELEMENTS)์ ์ธ์ ์์ผ, object๋ฅผ ๋ฃ๊ณ float๋ก ์ฝ์ผ๋ฉด ์์ถ ํฌ์ธํฐ(์ฃผ์)๊ฐ ๋ถ๋์์์ ๋นํธ๋ก ๋์จ๋ค(addrof). ๋ฐ๋๋ก ๊ฐ์ง ์ฃผ์๋ฅผ ์จ๋ฃ๊ณ object๋ก ์ฝ์ผ๋ฉด ์์ ์ฃผ์๋ฅผ ๊ฐ์ฒด๋ก ๋๊ฐ(fakeobj).
- ์ผ์ด์ง ๋ด๋ถ ์์ R/W.
ArrayBuffer์backing_store(JSArrayBuffer+0x14)๋ฅผ ์์์์ผ ๊ทธ ์DataView/TypedArray๋ก 4GB ์ผ์ด์ง ๋ด๋ถ๋ฅผ ์์๋ก ์ฝ๊ณ ์ด๋ค. (11.2๋backing_store๊ฐ caged sandboxed pointer๋ผ ์ด๊ฒ๋ง์ผ๋ก ์ผ์ด์ง๋ฅผ ๋ชป ๋ฒ์ด๋๋ค.) - WASM ์๋๋ฐ์ค ํ์ถ. ์ผ์ด์ง ๋ฐ์ผ๋ก ๋๊ฐ๋ ์ ์ผํ ์์ก์ด๊ฐ
WasmInstanceObject.jump_table_start(instance+~0x60) โ ์ผ์ด์ง ๋ฐ RWX WASM ์ฝ๋ ํ์ด์ง๋ฅผ ๊ฐ๋ฆฌํค๋ raw 64๋นํธ ํฌ์ธํฐ๋ค(11.2์ ์์ง raw; WASM trusted-space ๋ถ๋ฆฌ๋ 2024/~v8 12.5). ์ผ์ด์ง ๋ด๋ถ R/W๋ ์ผ์ด์ง ๋ฐ RWX์ ์ง์ ๋ชป ๋ฟ์ผ๋,jump_table_start๋ฅผ export ํจ์ ๋ณธ๋ฌธ์mov [rax],rdx(48 89 10) Liftoff ๊ฐ์ ฏ ์ฃผ์๋ก ๋ฎ๊ณ , ์๊ทธ๋์ฒ ๋ฏธ๊ฒ์ฆ export ํธ์ถ๋ก ์ธ์๋ฅผ rax/rdx์ ์ค์ด ์ผ์ด์ง ๋ฐ์ 8๋ฐ์ดํธ์ฉ ์ ธ์ฝ๋๋ฅผ ํ๋ ค๋ฃ์ ๋ค,jump_table_start๋ฅผ ๋ณต๊ตฌํ๊ณ export(main)๋ฅผ ํธ์ถํด ์คํํ๋ค. (์ฒด์ธ: JSFunction โ SFI(+0x0C) โ WasmExportedFunctionData(+0x08) โ instance(+0x10).) - ์
ธ์ฝ๋ โ flag. ํ์ค x64
execve("./flag_reader-<md5>")(์๋๊ฒฝ๋ก, ์๋ฒ cwd=/home/pwn). d8์read/load/os๋ฑ ์ ์ถ๋ ฅ์ด ๋ค ์ ๊ฑฐ๋์ผ๋, ์ด setuid ๋ฐ์ด๋๋ฆฌ๋ฅผ ์คํํ๋ ๊ฒ๋ง์ด flag๋ก ๊ฐ๋ ๊ธธ์ด๋ค.
์ 4๋ฒ(WASM ํ์ถ)์ด ๋ฐ๋ก ํ์ํ์ง๋ V8 ์๋๋ฐ์ค ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด ํ๋์ ๋ค์ด์จ๋ค. ์๋๋ฐ์ค๋ V8 ํ์ ํ๋์ ํฐ ์์ญ(์ผ์ด์ง)์ ๊ฐ๋๊ณ , ๊ทธ ์์ ๊ฐ์ฒด๋ ์๋ก๋ฅผ ์คํ์ ์ผ๋ก๋ง ๊ฐ๋ฆฌํค๊ฒ ํ๋ค. ์ผ์ด์ง ๋ฐ ๊ฐ์ฒด(์: ArrayBuffer์ ์ค์ backing)๋ ์ง์ ํฌ์ธํฐ๊ฐ ์๋๋ผ ํฌ์ธํฐ ํ ์ด๋ธ์ ์ธ๋ฑ์ค๋ก๋ง ๋ฟ๋๋ค. ๊ทธ๋์ ์ผ์ด์ง ์์์ ์๋ฌด๋ฆฌ ์์ R/W๊ฐ ์์ด๋, ๊ทธ๊ฒ๋ง์ผ๋ก ์ผ์ด์ง ๋ฐ ์ง์ง ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฑด๋๋ฆด ์ ์๋ค โ ๋ณ๋์ ํ์ถ๊ตฌ๊ฐ ํ์ํ ์ด์ ๋ค.

11.2์์ ์ด ์ผ์ด์ง๋ฅผ ๋ฒ์ด๋๋ ์ ์ผํ ํ์ด jump_table_start์๋ค โ ์ผ์ด์ง ๋ฐ RWX ํ์ด์ง๋ฅผ ๊ฐ๋ฆฌํค๋, ์์ง ๋ณดํธ๋ฐ์ง ์๋ raw ํฌ์ธํฐ. (2024๋
WASM trusted-space ๋ถ๋ฆฌ๋ก ๊ทธ ํ๋ง์ ๋ซํ๋ค.)
์ ๋ง 1๋ฒ(์ฒซ OOB)๋ง ์ด๋ฆฌ๋ฉด 2~5๋ฒ์ ๋งํ๋ ๋ฐ ์์ด ํ๋ฅธ๋ค. ๊ทธ๋์ ์ด ๋ฌธ์ ์ ๋ฌด๊ฒ์ค์ฌ์ ์ ์ ์ผ๋ก "์ฒซ OOB๋ฅผ ์ด๋ป๊ฒ ์ฌ๋๋" ์ ์ ๋ ค ์๊ณ , ๊ทธ๊ฒ ์ด ๋ถ๊ฒ์ ์ฃผ์ ์๋ค โ ๊ทธ๋ฆฌ๊ณ 11.2์ ๋ต์ "ํ์ค ๊ฐ-range ๊ฒฝ๋ก๋ก๋ ์ ์ฐ๋ค"์๋ค.
๐ ๊ฒฐ๋ก โ ํ ์๋์ ์ข ๋ฃ
์ด ๋ถ๊ฒ์ ๊ฒฐ๋ก ์ ๋ช
๋ฃํ๋ค. Math.exp์ NaN ์คํ์
๊ฐ์ ๊ฐ-range typer ๋ฒ๊ทธ๋, ํ๋๋์ด ์ผ์ง V8 11.2์์ ํ์ค OOB ๊ฒฝ๋ก๋ก๋ ์ฃฝ์ ํด๋์ค๋ค.
- ์ V8: typer๊ฐ ์ธ๋ฑ์ค๋ฅผ in-bounds๋ก ์ฆ๋ช
ํ๋ฉด
CheckBounds๋ฅผ ์ ๊ฑฐ โ ๊ฒ์ฌ ์๋ wild OOB. abiondoยทdoar-eยทQuick-Maffs๊ฐ ์ฐ ์๋. - V8 11.2: ์ฆ๋ช ํ๋ฉด ์ ๊ฑฐ๊ฐ ์๋๋ผ abort. doar-e์ OOB-load-mode ์ฐํ๊น์ง ์์ค ์ฃผ์์ผ๋ก ์ฝ ์ง์ด ๋ง์. ๊ทธ๋์ ์ขํ ์ธ๋ฑ์ค๋ ํธ๋ฉ, ์ ์ขํ ์ธ๋ฑ์ค๋ deopt โ wild OOB ๊ตฌ๊ฐ์ด ์ฌ๋ผ์ก๋ค.
- ๊ทธ๋์ ํ์ญ ์ต์ค๋ ๊ฐ-range๋ฅผ ๋ฒ๋ฆฌ๊ณ length/element-kind/IC๋ฅผ ์ง์ ์์์ํค๋ ๋ฒ๊ทธ๋ก ๊ฐ์ํ๋ค.
JIT ์ปดํ์ผ๋ฌ์์ ํ์
์ "ํํธ"๊ฐ ์๋๋ผ ์์ ๊ฒ์ฌ๋ฅผ ์ ๊ฑฐํ๋ ๊ทผ๊ฑฐ๋ค. ๊ทธ ์ ๊ฑฐ๋ฅผ "์ ๊ฑฐ ๋์ abort"๋ก ๋ฐ๊พผ ํ ์ค(new_flags |= kAbortOnOutOfBounds)์ด, typer-ํผ๋ ์ต์ค์ ํ ์ธ๋๋ฅผ ํต์งธ๋ก ๋ซ์๋ค. ์ ํํ ๊ทธ ํ ์ค์ ์ค ๋ฒํธ๋ก ์ง์ด๋ด๋ ๊ฒ โ ๊ทธ๊ฒ ์ด ๋ค์ด์1 ๋ฌธ์ ์ ์ง์ง ํ์ต ํฌ์ธํธ๋ผ๊ณ ๋ณธ๋ค. 100% ๋ง๋ ๊ฒ ์๋๋ผ(layout ํด๋์ค๋ ์ด์์๋ค), "typer ์ฆ๋ช
๊ธฐ๋ฐ ๊ฒ์ฌ ์ ๊ฑฐ"๋ผ๋ ํน์ ์ต์ค ํํ๋ฅผ ๋น์ธ๊ฒ ๋ง๋๋ best-effort ํ๋๋์ด, ํ ๋ฒ๊ทธ๋ฅ์ ๊ณต๊ฒฉ ๋์ด๋๋ฅผ ์๋ฐ์
์์ ๋ค์ด์1๋ก ๋์ด์ฌ๋ฆฐ๋ค.
๋ฒ๊ทธ๋ฅผ ๋ชป ๋ซ์๋ค๊ณ ๋น์์ ์๋๋ค. "์ ์ ๋ซ๋ฆฌ๋"๋ฅผ ์ค ๋ฒํธ๋ก ๋ตํ ์ ์๋ค๋ ๊ฒ ์์ฒด๊ฐ, ์ด ๋น๋์ ๋ฐฉ์ด๊ฐ ์ด๋์ ์ด๋ป๊ฒ ์ณ์ ธ ์๋์ง์ ์ง๋๋ค. ๊ทธ ์ง๋๋ฅผ ๋ค์ ์ ์ ์ ๋ฆฌํ๋ค.
๐ก๏ธ ๋ฐฉ์ด ๊ด์ โ ์ด ๋ถ๊ฒ์ด ๊ทธ๋ฆฐ ์ง๋
- typer๋ ์ฐจ๋ถ ํผ์ง๊ณผ ํ์
๋จ์ธ์ผ๋ก ์งํจ๋ค. ์ด๋ค ๋นํธ์ธ์ ์ถ๋ ฅ ํ์
์ ์ขํ ๋ NaNยท-0ยทยฑInfinity ๊ฒฝ๊ณ๋ฅผ ๋ฐ๋์ ํฌํจํ๋์ง ๊ฒ์ฆํ๋ค(V8์
--assert-types๋ก ๋ฐํ์ ํ์ ๋จ์ธ์ ์ผ๋ ์ต์ ๋ ์๋ค). ์ด ๋ฒ๊ทธ๋ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ค์๊ฐ ์๋๋ผ ์ปดํ์ผ๋ฌ๊ฐ ํ ๊ฐ์ ๋ํด ๊ฐ์ง ๋ฏฟ์์ ์ค๋ฅ์๋ค โMath.exp์ ๋ฐํ์์NaNํ ์นธ์ ๋บ ๊ฒ๋ฟ. JIT์์ ํ์ ์ "ํํธ"๊ฐ ์๋๋ผ ์์ ๊ฒ์ฌ๋ฅผ ์ ๊ฑฐํ๋ ๊ทผ๊ฑฐ๋ผ, ์ค์ ๋ณด๋ค ์ข์ ํ์ ์ ๊ทธ๋๋ก ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ ๊ตฌ๋ฉ์ด ๋๋ค. - ์ฌ์ธต ๋ฐฉ์ด๊ฐ ์ค์ ๋ก ์ผํ๋ค.
kAbortOnOutOfBounds๋ typer ๋ฒ๊ทธ๋ฅผ 100% ๋ง์ง ๋ชปํ๋ค(layout/element-kind ํด๋์ค๋ ์ด์์๋ค). ํ์ง๋ง "typer ์ฆ๋ช ๊ธฐ๋ฐ ๊ฒ์ฌ ์ ๊ฑฐ"๋ผ๋ ํน์ ์ต์ค ํํ๋ฅผ ๋น์ธ๊ฒ ๋ง๋๋ ๊ฒ๋ง์ผ๋ก, ๊ฐ์ ๋ถ๋ฅ ๋ฒ๊ทธ์ ๋์ด๋๋ฅผ ์๋ฐ์ (ํ๋๋ off ๋น๋)์์ ๋ค์ด์1(์ด ๋ฌธ์ )๋ก ๋์ด์ฌ๋ฆฐ๋ค. best-effort ํ๋๋์ ๊ฐ์น๊ฐ ์ ํํ ์ด ๊ฒฉ์ฐจ๋ค. - ๋ฐฉ์ด๋ ์๊ฐ์ด ์ง๋๋ฉฐ ๋จ๋จํด์ง๋ค. 11.2(2023)๋
backing_store๊ฐ ์ผ์ด์ง์ ๋ฌถ์ฌ ArrayBuffer๋ก๋ ๋ชป ๋๊ฐ์ง๋งjump_table_start๊ฐ ์์ง raw RWX๋ผ WASM์ผ๋ก ํ์ถ์ด ๊ฐ๋ฅํ๋ค. 2024๋ WASM trusted-space ๋ถ๋ฆฌ(~v8 12.5) ์ดํ์ ๊ทธ ๋ง์ง๋ง ์์ก์ด๋ ๋ซํ๋ค. ์ต์ค๋ฅผ ์งค ๋ ๊ทธ ๋ฒ์ ์ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์์ ์ ํํ ํ์ธํด์ผ ํ๊ณ , ๋ฐฉ์ด์ ์ ์ฅ์์ ํ ๊ฒน(typer)์ด ๋ซ๋ ค๋ ๋ค์ ๊ฒน(bounds ํ๋๋ โ ์๋๋ฐ์ค โ trusted-space)์ด ๋น์ฉ์ ๊ณ์ ์ฌ๋ฆฌ๋๋ก ์๋ ๊ฒ ํต์ฌ์ด๋ค. - ๋ฒ๊ทธ ํด๋์ค๋ฅผ ๊ตฌ๋ถํด ์ฐ์ ์์๋ฅผ ๋๋ค. ๊ฐ-range typer ๋ฒ๊ทธ๋
kAbortOnOutOfBounds๋ก ์ฌ์ค์ ์ฌ์ฅ๋์ง๋ง, layout/element-kind/IC/TOCTOU ๋ฒ๊ทธ๋ length๋ฅผ ์ง์ ์์์์ผ ์ด์์๋ค. ํผ์งยท๋ฆฌ๋ทฐ ์์์ ํ์(๊ฐ์ฒด ํํยท์ ์ดยทcloneยทvalueOf์ฌ์ง์ )์ ๋ ์๋ ๊ฒ ํฉ๋ฆฌ์ ์ด๋ค.
๐ ์ฐธ๊ณ
- V8 11.2.214.14 ์์ค (commit 6538a20a) โ
src/compiler/{simplified-lowering,effect-control-linearizer,js-native-context-specialization}.cc. ์ธ์ฉํkAbortOnOutOfBoundsยทdoar-e ํจ์น ์ฃผ์์ 1์ฐจ ์ถ์ฒ. - abiondo, Exploiting the Math.expm1 typing bug in V8 ยท mem2019, Problems about Expm1 โ ๊ฐ-range ์ ์(๊ตฌ๋ฒ์ )๊ณผ
SameValue์ฌํ์ ํ์ด๋ฐ. - doar-e, Circumventing Chrome's hardening of typer bugs โ 11.2๊ฐ ์ฝ ์ง์ด ๋ง์ ๊ทธ ์ฐํ.
- gts3, Exploiting TurboFan Through Bounds Check Elimination โ CheckBounds ๋ฎ์ถค ํ๋ฆ.
- Project Zero, In-the-Wild: Chrome Infinity Bug โ induction-variable NaN ํด๋์ค.
- madStacks, Bug 1051017 ยท v8CTF ์ฒซ ๊น๋ฐ ยท Part 6 (WASM ํ์ถ) โ length-construction, ํ๋๋ ์ผ์ง ์ค์ Chrome ์ต์ค, ์๋๋ฐ์ค ํ์ถ.
- lyra, Exploiting V8 at openECSC โ ํ๋๋ ์ผ์ง V8์ ๋ค๋ฅธ ๋ฒ๊ทธ ํด๋์ค๋ก.
- sh3n, Objection! โ HTB GCSB 2026 โ
turbo_typer_hardeningยท์๋๋ฐ์ค๋ฅผ ๋๊ณ ์ถ์ ํ ๋์กฐ๊ตฐ.
์ด๋ฏธ์ง ์ถ์ฒ โ V8 ์๋๋ฐ์ค: V8 ๊ณต์ ๋ธ๋ก๊ทธ(v8.dev/blog/sandbox). ๋ถ๋์์์ ๊ตฌ์ฑ: Wikimedia Commons (CC BY-SA 3.0). ํ๋ฆ๋ยทํฐ๋ฏธ๋ ์บก์ณ๋ ์ง์ ์ ์/์ธก์ .
๋ค์ โ 3ํธ(์์ ). ์ด ๋ถ๊ฒ์ "ํ์ค ๊ฐ-range ๊ฒฝ๋ก๋ 11.2์์ ๋ซํ๋ค"๊น์ง๋ค. ์ฒซ OOB๋ฅผ ์ฌ๋ ๊ทธ ๋นํ์ค ํ ์๋ฅผ ์ฐพ์ผ๋ฉด, ์ ํ๋ฐ๋ถ ์ฒด์ธ์ ๊ทธ๋๋ก ํ์ ๋ผ์ด๋ธ flag๊น์ง ๊ฐ๋ 3ํธ์ผ๋ก ์๋๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ