2026-06-07ยท1๋ถ ์ฝ๊ธฐยท
2ํธ์ "ํ์ค ๊ฐ-range ๊ฒฝ๋ก(CheckBounds ์ ๊ฑฐ)๋ 11.2์์ ๋ซํ๋ค"๊น์ง์๋ค. 3ํธ์ ๊ทธ ๋ค โ CheckBounds๋ฅผ ๊ฑฐ์น์ง ์๋ *๋นํ์ค ์ถ๊ตฌ*๋ฅผ ํ๋ํ๋ ์ ๋ถ ๋๋๋ฆฐ ๊ธฐ๋ก์ด๋ค. fill/copyWithin, foldable ๋นํธ์ธ, TypedArrayยทDataView, ์์์ข ๋ฅ ์ ์ด, ํํํ ๋ณํ๊น์ง ํ๋ณด sink๋ฅผ ์ ์๋ก d8์ ๋๋ ค๋ดค๊ณ ์ ๋ถ ์ ํ/ํธ๋ฉ/in-range๋ก ์ฃฝ์๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ฅ ๋ป๋ฐ์ ๋ฐ๊ฒฌ โ ์ด ๋ฒ๊ทธ๋ฅ์ *์๋๋* ํด๋ฒ์ธ new Array(len) length-construction๋ง์ ์ด ๋น๋์์ ๋งํ ์๋ค. ํจ์น๊ฐ typer.cc๋ง ๊ฑด๋๋ฆฌ๊ณ js-create-lowering์ ๊ธธ์ด ๋ณดํธ๋ ์ ํ์ด์๋ค. ์ด๊ธ๋จ์ ์ด์์๋๋ฐ(์์ฐ ํฐ์ด์ ์์ INT_MIN์ผ๋ก ๋ฐ์ฐ) ๋ฐ์์ค sink๊ฐ ์๋ค. ๋์ผ๋ก ๋จ์ ํ ์ โ ๋ฐฐํฌ ๋ฐ์ด๋๋ฆฌ ์ง์ ๋ถ๊ฒ.
์ด ๊ธ์ด ๋์์ด ๋๋์?
๋ฌธ์ : DreamHack โ EXP-NaN (Dreamhack CTF Season 4 Round #5, ๐ฉDiv1 ์ถ์ ์) ๋ถ๋ฅ: Pwnable (Browser / V8 JIT) ๋์ด๋: ๐ Diamond 1 ์ด ๊ธ: ์๋ฆฌ์ฆ 3ํธ โ ๋จ์ ์ถ๊ตฌ ์ ์ ์ ๊ฒ. (1ํธ: ๋ฒ๊ทธยทํธ๋ฆฌ๊ฑฐ ยท 2ํธ: ์์ค ๋ถ๊ฒ)
2ํธ์ ํ ๊ฐ์ง๋ฅผ ์์ค๋ก ํ์ ํ๊ณ ๋๋ฌ๋ค โ ํ์
ํผ๋์ผ๋ก CheckBounds๋ฅผ ์ง์ OOB๋ฅผ ๋ด๋ ํ์ค ๊ฒฝ๋ก(abiondoยทdoar-eยทQuick-Maffs)๋ 11.2์์ ํต์งธ๋ก ๋ซํ๋ค. CheckBounds๋ ์ด ๋น๋์์ ์ ๋ ์ฌ๋ผ์ง์ง ์๊ณ , ์ธ๋ฑ์ค๊ฐ in-bounds๋ก ์ฆ๋ช
๋๋ฉด ๊ฒ์ฌ๊ฐ ์ ๊ฑฐ๊ฐ ์๋๋ผ kAbortOnOutOfBounds ํธ๋ฉ์ผ๋ก ๋ฐ๋๋ค. ๊ทธ๋์ "์ฒซ OOB๋ฅผ ์ฌ๋ ๋นํ์ค ํ ์"๊ฐ ์์ด์ผ ํ๋ค๊ณ ์ ๊ณ ๋ง์ณค๋ค.
์ด 3ํธ์ ๊ทธ ๋นํ์ค ํ ์๋ฅผ ์ฐพ์ผ๋ฌ, CheckBounds๋ฅผ ๊ฑฐ์น์ง ์๋ ์ถ๊ตฌ๋ฅผ ๊ฐ๋ฅํ ํ ์ ๋ถ ๋๋๋ฆฐ ๊ธฐ๋ก์ด๋ค. ๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด โ ์ฒซ OOB๋ ๋ชป ์ด์๋ค. ํ์ง๋ง ๊ทธ ๊ณผ์ ์์ ์ด ๋ฌธ์ ์ ๊ตฌ์กฐ๊ฐ ํ ๊ฒน ๋ ๋๋ฌ๋ฌ๋ค: ์ด ๋ฒ๊ทธ๋ฅ์ ์๋๋ ํด๋ฒ์ผ๋ก ๋ณด์ด๋ ๊ธธ๋ง์ , ํจ์น์ ์ฌ์ ๊ฑฐ๋ฆฌ ๋ฐ์ด๋ผ ๋งํ ์๋ค.
์์ค๋ ์ฑ๋ฆฐ์ง
patches/README.md๊ฐ ์ง์ ํ ๋น๋ โ V8 commit6538a20a(11.2.214.14). ์ค ๋ฒํธ๋ ๊ทธ commit ๊ธฐ์ค.
1ยท2ํธ์ ์ ๋ดค์ด๋ ๋ฐ๋ผ์ฌ ์ ์๊ฒ ํ ์ค ์์ฝ: Math.exp์ ๋ฐํ ํ์
์์ NaN์ ๋นผ๋ ํจ์น ํ ์ค ๋๋ฌธ์, TurboFan์ Math.exp(x)๊ฐ ์ ๋ NaN์ด ์๋๋ผ๊ณ ๋ฏฟ๋๋ค. ํ์ง๋ง Math.exp(NaN)=NaN์ด๋ค. ์ด ์ด๊ธ๋จ์ ์ง์ง๋ก ์ด์ ์๋ค. ๋ฌธ์ ๋ ๊ทธ๊ฑธ ๋ฉ๋ชจ๋ฆฌ ์นจ๋ฒ์ผ๋ก ๋ฐ๊พธ๋ sink๊ฐ 11.2์์ ๋ค ๋งํ ์๋ค๋ ๊ฒ.
CheckBounds ์ ๊ฑฐ๊ฐ ์ฃฝ์ ๊ฒฝ๋ก, doar-e ์ฐํ๋ฅผ ์ ์กฐ์คํด ๋ง์ ๋ ๋ฒ์งธ ๊ฒ์ฌ.CheckBounds๋ฅผ ์ ๊ฑฐ์น๋ ์ถ๊ตฌ๋ฅผ ์ ์๋ก ๋๋๋ฆผ โ ๊ทธ๋๋ ์ ์ด๋ฆผ โ ์๋๋ sink๋ ๋งํ ์๋ค๋ ๋ฐ๊ฒฌ โ ๋ค์ ํ ์.3ํธ์ ์ถ๊ตฌ ์ฌ๋ฅ์ด๋ผ ์ฉ์ด๊ฐ ๋ช ๊ฐ ๋ ํ์ํ๋ค.
Math.exp๋ฅผ "NaN ์๋"์ผ๋ก ์๋ชป ์ขํ๋ ๊ฒ.CheckBounds / kAbortOnOutOfBounds: ๋ฐฐ์ด ๊ฒฝ๊ณ๊ฒ์ฌ ๋
ธ๋. 11.2์์ ์ธ๋ฑ์ค๊ฐ in-bounds๋ก ์ฆ๋ช
๋๋ฉด ์ด ๊ฒ์ฌ๋ฅผ ์ ๊ฑฐํ๋ ๊ฒ ์๋๋ผ **ํธ๋ฉ(abort)**์ผ๋ก ๋ฐ๊พผ๋ค(2ํธ). ๊ทธ๋์ typer๋ฅผ ์์ฌ OOB๋ฅผ ๋ด๋ ค ํ๋ฉด silent OOB๊ฐ ์๋๋ผ SIGTRAP์ด ๋๋ค.cvttsd2si: x86์ double โ int32 ๋ณํ(truncation) ๋ช
๋ น. ์
๋ ฅ์ด NaN/๋ฒ์์ด๊ณผ๋ฉด 0x80000000(INT_MIN)์ ๋ฑ๋๋ค. typer๊ฐ "NaN ์๋"์ ๋ฏฟ์ผ๋ฉด ๋ณด์ ์์ด ์ด ๋ช
๋ น๋ง ๊น๋ฆฌ๊ณ , ๋ฐํ์ NaN์ด ๊ทธ๋๋ก INT_MIN์ผ๋ก ์๋ค. ๋ฐ์ฐ์ ๋ฌผ๋ฆฌ์ ๊ทผ์.a[i]), ๊ธธ์ด(new Array(n)), ๋นํธ์ธ ๋ด๋ถ ์คํ์
๋ฑ. 3ํธ์ ์ฃผ์ = "์ด๋ค sink๊ฐ ์ด๋ ค ์๋".new Array(v)์ ๋ฃ์ด typer๋ "์์ ๋ฐฐ์ด"๋ก ๋ฏฟ๊ณ ๋ฐฑํน์ ์๊ฒ ์ก๋๋ฐ ๋ฐํ์ ๊ธธ์ด๋ ๊ฑฐ๋ํด, length > capacity ๋ถ์ผ์น๊ฐ ๋๋ฉด ๊ทธ ๋ฐฐ์ด๋ก ๋ฐฑํน ๋ฐ์ ์ฝ๊ณ ์ด๋ค.| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | EXP-NaN (Dreamhack CTF Season 4 Round #5, ๐ฉDiv1) |
| ๋ถ๋ฅ / ๋์ด๋ | Pwnable โ Browser / V8 JIT ยท ๐ Diamond 1 |
| ๋์ | ํจ์น๋ d8 (V8 11.2.214.14, v8_enable_sandbox=true) |
| ํจ์น | typer.cc ํ ์ค โ Math.exp ๋ฐํ ํ์
์์ NaN ์ ๊ฑฐ |
| ์คํ ๋ชจ๋ธ | ์๋ฒ runner.py๊ฐ ./out/d8 <payload> (natives ์์ด, ์์ฐ ํฐ์ด์
) |
| flag | read/load/os ์ ๊ฑฐ โ execve("./flag_reader-โฆ") ๋ก๋ง (RCE ํ์) |
V8์ JS๋ฅผ Ignition ๋ฐ์ดํธ์ฝ๋๋ก ๋๋ฆฌ๋ค, ํซํด์ง๋ฉด TurboFan์ผ๋ก ์ต์ ํํ๋ค. ๋ฒ๊ทธ๋ ์ต์ค๋ ์ ๋ถ ๊ทธ ์ต์ ํ ๋จ๊ณ ์์์ ์ผ์ด๋๋ค.

์ถ์ฒ: v8.dev (Google V8 ๊ณต์ ๋ธ๋ก๊ทธ)
patches/v8.patch๋ ๋ ๊ฐ์ง๋ฅผ ํ๋ค. ์ฒซ์งธ, ๋ฒ๊ทธ ๋ณธ์ฒด โ typer.cc์ JSCallTyper์์ Math.exp๋ฅผ Math.abs ๋ฌด๋ฆฌ์์ ๋ผ์ด๋ด NaN์ ๋บ ํ์
์ ์ค๋ค.
// Unary math functions.
case Builtin::kMathAbs:
- case Builtin::kMathExp:
return Type::Union(Type::PlainNumber(), Type::NaN(), t->zone());
+ case Builtin::kMathExp:
+ return Type::PlainNumber();Math.abs๋ ์ฌ์ ํ Union(PlainNumber, NaN) โ NaN์ ํฌํจํ๋ค. Math.exp๋ง PlainNumber()๋ก, NaN์ด ๋น ์ก๋ค. ๋ฑ ์ด ํ ์ค์ด typer์๊ฒ "Math.exp๋ ์ ๋ NaN์ ์ ๋๋ ค์ค๋ค"๋ ๊ฑฐ์ง๋ง์ ์ฌ๋๋ค.
๋์งธ, d8 ๋ฝ๋ค์ด โ d8.cc/d8-posix.cc์์ ํ์ผยทOS ์ ๊ทผ์ ์ ๋ถ ์ฃผ์ ์ฒ๋ฆฌํ๋ค.
- global_template->Set(isolate, "read", ReadFile);
- global_template->Set(isolate, "load", ExecuteFile);
- global_template->Set(isolate, "Realm", CreateRealmTemplate);
+ // global_template->Set(isolate, "read", ReadFile); // ๋งํ
+ // global_template->Set(isolate, "load", ExecuteFile);// ๋งํ
+ // global_template->Set(isolate, "Realm", ...); // ๋งํ
// os.system, d8.file.read/execute, readbuffer/readline/writeFile โฆ ์ ๋ถ ์ฃผ์์ด๊ฒ ๋ฌธ์ ์ ์ฑ๊ฒฉ์ ์ ํ๋ค. readยทloadยทos.systemยทRealmยทd8.file์ด ๋ค ์ฌ๋ผ์ก์ผ๋, flag๋ฅผ ํ์ผ๋ก ์ฝ๊ฑฐ๋ ๋ช
๋ น์ผ๋ก ๋นผ๋ผ ๊ธธ์ด ์๋ค. ๋จ์ ๊ฑด ๋ฉ๋ชจ๋ฆฌ ์์์ผ๋ก ์ฝ๋ ์คํ์ ์ก์ execve("./flag_reader-โฆ")๋ฅผ ์ง์ ๋ถ๋ฅด๋ ๊ฒ๋ฟ โ ์ฆ ์ด ์ฑ๋ฆฐ์ง๋ ์์ ๋ฉ๋ชจ๋ฆฌ ์์ RCE๋ง ์๋ํ๋ค. read ํ๋ฆฌ๋ฏธํฐ๋ธ ํ๋๋ก๋ ์ ๋ ์ ๋๋๋ ์ด์ ๋ค.
2ํธ์ ๊ฒฐ๋ก ์ด ๊ธธ์ ์ขํ์ค๋ค. ๋ชจ๋ ์ธ๋ฑ์ค/๊ธธ์ด ๊ฒ์ฌ๊ฐ CheckBounds๋ก ์๋ ดํ๊ณ , ๊ทธ๊ฒ deopt ์๋๋ฉด abort๋ผ๋ฉด โ ๋ต์ ๋ฐ๋์ CheckBounds๋ฅผ ๊ฑฐ์น์ง ์๋ ๋ฉ๋ชจ๋ฆฌ sink๋ค. ๊ทธ๋์ ํ๋ณด๋ฅผ ์ด๋ ๊ฒ ์ก์๋ค.
"no-NaN(PlainNumber) ๋ฏฟ์์ด ๊นจ๋จ๋ฆด ์ ์๊ณ , ๊ทธ ๊ฒฐ๊ณผ๊ฐ
CheckBounds๊ฐ ์๋ ๋ค๋ฅธ ๊ณณ(ํ ๋น ํฌ๊ธฐ, ๋นํธ์ธ ๋ด๋ถ ์ธ๋ฑ์ค, ํํํ ์ ํ, byteOffset)์ผ๋ก ํ๋ฌ ๋ฉ๋ชจ๋ฆฌ์ ๋ฟ๋ ์ฐ์ฐ์ ๋ฌด์์ธ๊ฐ?"
์ด ๊ธฐ์ค์ผ๋ก ํ๋ณด sink๋ฅผ ์นดํ
๊ณ ๋ฆฌ๋ณ๋ก ์ ๋ถ ๋์ดํ๊ณ , ์ถ์ธก์ผ๋ก ๋๋ด์ง ์๊ณ ๊ฐ๊ฐ์ ํจ์น๋ d8์ ์ง์ ๋๋ ค๋ดค๋ค. ํ์ ๊ธฐ์ค์ ๋จ์ํ๋ค โ exit code 133(SIGTRAP)=ํธ๋ฉ, undefined/๋ฒ์ ์ ๊ฐ=์ ํ, ์ธ์ ๋ฉ๋ชจ๋ฆฌ์ map/ํฌ์ธํฐ ๋นํธ๊ฐ ์์ด ๋์ค๋ฉด=์ง์ง OOB.
์ถ๊ตฌ๋ฅผ ๋๋๋ฆฌ๊ธฐ ์ ์, ๋ฐ์์ค "๋ฐ์ฐํ ๊ฐ"์ด ์ค์ ๋ก ์กด์ฌํ๋์ง๋ถํฐ ๋ชป ๋ฐ์๋ค. cvttsd2si(๋ณด์ ์๋ floatโint ๋ณํ)๋ NaN์ ๋ง๋๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ์ ToInt32(NaN)=0์ด ์๋๋ผ 0x80000000(INT_MIN) ์ ๋๋ ค์ค๋ค(1ํธ). ์ด๊ฑธ Quick-Maffs์ ์ฐ์ ์ฒด์ธ์ ํ์ฐ๋ฉด, typer๋ ๊ฒฐ๊ณผ๋ฅผ ์์ ์์๋ก "์ฆ๋ช
"ํ๋๋ฐ ๋ฐํ์์ INT_MIN์ผ๋ก ๊ฐ๋ผ์ง๋ค.
function foo(x) {
let v = Math.exp(x); // ํจ์น typer: PlainNumber (NaN ์๋) โ JSCall ์ ์ง(speculation off)
v = Math.sign(v); // typer Range(-1,1) ; ๋ฐํ์ sign(NaN)=NaN
v = Math.abs(v); // typer Range(0,1)
v += 2;
v >>= 1; // typer Range(1,1)
v += 10; // typer Range(11,11) โ "๊ฒฐ๊ณผ๋ ๋ฌด์กฐ๊ฑด 11"
return v;
}>>= ๊ฐ์ ๋นํธ์ฐ์ฐ์ด ๋ค์ด๊ฐ typer๋ ๊ฒฐ๊ณผ๋ฅผ Range(11,11)(์์ 11)๋ก ๊ตณ๊ฒ ๋ฏฟ๋๋ค. ๊ทธ๋ฐ๋ฐ ๋ฐํ์ Math.exp(NaN)=NaN์ด ๋ณด์ ์์ด INT_MIN์ผ๋ก ๋จ์ด์ง๊ณ , ๊ทธ๊ฒ ์ฒด์ธ์ ํ๊ณ ๋ด๋ ค๊ฐ ์ ํ ๋ค๋ฅธ ๊ฐ์ด ๋๋ค. ์ต์ ํ๋ ๋ฒ์ (OPT)๊ณผ ์ ๋ ์ต์ ํ ์ ํ ์ฐธ์กฐ(REF)๋ฅผ ๋๋ํ ์ฐ์ด๋ณด๋ฉด:
../extracted/deploy/out/d8 --allow-natives-syntax qm_chain2.js
REF๋ ์ ์งํ๊ฒ 10(ToInt32(NaN)=0 โ ์ฐ์ ), OPT๋ -1073741813. ์ปดํ์ผ๋ฌ๊ฐ "11"์ด๋ผ ์ฆ๋ช
ํ ๋ณ์๊ฐ ๋ฐํ์์ ์์ ๊ฑฐ๋๊ฐ์ด๋ค. soundness๊ฐ ๊นจ์ง ๊ฒ ๋์ ๋ณด์ธ๋ค.
cvttsd2si์ NaN์ด ์์ ๊ฑฐ๋๊ฐ์ ๊ทผ์์ ๋ถ๋์์์ ๋ณํ ๋ช ๋ น ํ๋๋ค. JS์ ์ซ์๋ IEEE-754 ๋ฐฐ์ ๋ฐ๋ 64๋นํธ๋ค.

์ถ์ฒ: Wikimedia Commons, CC BY-SA 4.0
double์ 32๋นํธ ์ ์๋ก ์๋ฅผ ๋ TurboFan์ cvttsd2si(convert-with-truncation)๋ฅผ ์ด๋ค. ์ด ๋ช
๋ น์ ์
๋ ฅ์ด ์ ์ ๋ฒ์๋ฅผ ๋ฒ์ด๋๊ฑฐ๋ NaN์ด๋ฉด "integer indefinite" ๊ฐ 0x80000000(INT_MIN)์ ๋๋ ค์ค๋ค. ํ์์ ๋ฌดํดํ๋ค โ JS ToInt32๋ NaN์ 0์ผ๋ก ๋ฐ๊ฟ์ผ ํ๋ฏ๋ก ์ปดํ์ผ๋ฌ๊ฐ ์๋ค์ "NaN์ด๋ฉด 0" ๋ณด์ ์ ๊น๋ค. ๊ทธ๋ฐ๋ฐ typer๊ฐ "์ด ๊ฐ์ ์ ๋ NaN ์๋"์ ์ฆ๋ช
ํด๋ฒ๋ฆฌ๋ฉด ๊ทธ ๋ณด์ ์ด ์ฃฝ์ ์ฝ๋๋ก ์ ๊ฑฐ๋๊ณ , ๋ณด์ ์๋ cvttsd2si ํ ๋ฐฉ๋ง ๋จ๋๋ค. ๋ฐํ์์ ์ง์ง NaN์ด ๋ค์ด์ค๋ฉด INT_MIN์ด ๊ทธ๋๋ก ์ฐ์ ์ฒด์ธ์ ํ๊ณ ํ๋ฌ -1073741813 ๊ฐ์ ๊ฐ์ด ๋๋ค. Math.exp์ ์คํ์
์ด "NaN ๋ณด์ ์ ๊ฑฐ"๋ผ๋ ๊ธฐ๊ณ์ด ๊ฒฐ์ ์ผ๋ก ๋ฒ์ญ๋๋ ์๊ฐ์ด๋ค. ์ด๊ฒ 3ํธ ๋ด๋ด "๋ฐ์ฐ๊ฐ"์ด๋ผ ๋ถ๋ฅด๋ ์ฌ๋ฃ์ ์ ์ฒด๋ค.
์ฌ๊ธฐ์ ํ ๊ฐ์ง๊ฐ ์ค์ํ๋ค โ ์ด ๋ฐ์ฐ์ --allow-natives-syntax๋ก ๊ฐ์ ์ต์ ํํ์ ๋๋ง ๋๋ ๊ฒ ์๋๋ผ, natives ์์ด ์์ฐ hot-loop ํฐ์ด์
์ผ๋ก๋ ๋๊ฐ์ด ๋๋ค. ์๋ฒ runner.py๋ natives ์์ด ./out/d8 <payload>๋ฅผ ๋๋ฆฌ๋, ์ด๊ฑด ์๊ฒฉ ์คํ ๋ชจ๋ธ์์๋ ์ด์ ์๋ ์ด๊ธ๋จ์ด๋ค. ์ฌ๋ฃ๋ ์ง์ง๋ค. ๋ฌธ์ ๋ ์ด ์์ ๊ฑฐ๋๊ฐ์ ๋ฐ์ OOB๋ก ๋ฐ๊ฟ์ค ๊ณณ์ด ์๋ค๋ ๊ฒ.
ํจ์น๋ JSCallTyper(์ผ๋ฐ ํธ์ถ์ ํ์
์ถ๋ก )๋ฅผ ๊ฑด๋๋ฆฐ๋ค. ๊ทธ๋ฐ๋ฐ TurboFan์ Math.exp(x) ๊ฐ์ ์ ์๋ ค์ง ์ํ ํจ์๋ฅผ ๋ณด๋ฉด, ๋ณดํต ๊ทธ๊ฑธ ์ผ๋ฐ JSCall๋ก ๋์ง ์๊ณ NumberExp ๊ฐ์ ์ ์ฉ ๋
ธ๋๋ก ๋ฎ์ถฐ๋ฒ๋ฆฐ๋ค(speculative inlining). ๊ทธ ์ ์ฉ ๋
ธ๋๋ NaN์ ์ ์งํ๊ฒ ๋ค๋ฃจ๋ ์๊ธฐ๋ง์ ํ์
๊ท์น์ ์จ์, ํจ์น๋ JSCallTyper๋ฅผ ์ ํ๋ค โ ์ฆ ๋ฒ๊ทธ๊ฐ ์ ๋๋ค.
๊ทธ๋์ ํธ๋ฆฌ๊ฑฐ์ ํต์ฌ์ ๊ทธ speculative inlining์ ๋ง์ Math.exp๋ฅผ generic JSCall๋ก ์ ์ง์ํค๋ ๊ฒ์ด๋ค. ํธ์ถ ์ง์ ์ ๋คํ(polymorphic)์ผ๋ก ๋ง๋ค๋ฉด ๋๋ค โ ํซ๋ฃจํ์์ ์ซ์ ์ธ์์ ๋ฌธ์์ด ์ธ์๋ฅผ ์์ด ๋ถ๋ฅด๋ฉด(g(1.1)์ g("x") ๊ต๋), V8์ด "์ด ์ธ์ ํ์
์ ๋ชป ๋ฏฟ๊ฒ ๋ค"๋ฉฐ ์ ์ฉ ๋
ธ๋๋ก ๋ชป ๋ด๋ฆฌ๊ณ ์ผ๋ฐ JSCall๋ก ๋จ๊ธด๋ค. ๊ทธ์ ์์ผ ํจ์น๋ JSCallTyper๊ฐ ์ ์ฉ๋ผ Math.exp์ ๋ฐํ์ด "NaN ์๋"์ผ๋ก ๊ตณ๊ณ , ์ง์ง Math.exp(NaN)=NaN๊ณผ ์ด๊ธ๋๋ค. 1ํธ์ f(NaN)=111๋, ์ ๋ฐ์ฐ๋ ์ ๋ถ ์ด ๋คํ ํธ๋ฆญ์ผ๋ก speculation์ ๋ ์ํ์์ ๋์จ๋ค.
๋ฐ์ฐํ INT_MIN(๋๋ no-NaN ๋ฏฟ์ ์์ฒด)์ ํ๋ ค๋ณด๋ผ ์ ์๋ CheckBounds ๋ฐ๊นฅ sink๋ฅผ, ์นดํ
๊ณ ๋ฆฌ๋ณ๋ก ์ ๋ถ d8์ ๋๋ ค๋ดค๋ค.
| ํ๋ณด ์ถ๊ตฌ | ๋ ธ๋ฆฐ ์ | ์ค์ธก ๊ฒฐ๊ณผ |
|---|---|---|
Array.prototype.fill / copyWithin ์ startยทend | ๊ฒฝ๊ณ์ฒ๋ฆฌ๊ฐ CheckBounds๊ฐ ์๋๋ผ ToInteger+ํด๋จํ+foldable ๋น๊ต | start/end๊ฐ ToInteger๋ก ์ ๊ทํ โ ์์๋ len+arg ํด๋จํ. OOB ์ ๋จ |
indexOf/includes/lastIndexOf/at/slice | ๋ด๋ถ ์ธ๋ฑ์ค ๊ฒ์ฌ๊ฐ ํด๋ฉ ๊ฐ๋ฅํ NumberLessThan | jnscs.cc์ ๋ ๋ฒ์งธ abort CheckBounds ๊ฐ ์ ์กฐ์คํด ๋ง์(2ํธ) |
Math ์ฐ์ฐ sweep (floor/round////) |
์ ๋ถ ์์ฑ์ด๋ค. ๋ ๊ฐ๋๋ ์ค์ธก์ ๋ฐ๋ก ๋ณด์ผ ๊ฐ์น๊ฐ ์๋ค.
CheckBounds๊ฐ ์์ด์ ๋ ์์ ํ๋ค๊ฐ์ฅ ๊ธฐ๋๋ฅผ ๊ฑด ๊ฑด fill/copyWithin์ด์๋ค. ์ด๋ค์ ์ธ๋ฑ์ค๋ฅผ CheckBounds๋ก ๋ง์ง ์์ผ๋, typer๋ฅผ ์์ด๋ฉด ๊ฒ์ฌ๊ฐ ํต์งธ๋ก ์์ ์ค ์์๋ค. ๊ทธ๋์ ๋ฐ์ฐ๊ฐ(typer=11, ๋ฐํ์ ๊ฑฐ๋์์)์ start/end๋ก ์ง์ ๋ฃ์ด๋ดค๋ค.
../extracted/deploy/out/d8 p3_fill_clamp.js
์ ๋ถ ํด๋จํ๋ค. a.fill(7.7, v)๋ v๊ฐ ๊ฑฐ๋์์๋ผ max(len+v, 0)=0์ผ๋ก ์ ํ ๋ฐฐ์ด ์ ์ฒด๋ฅผ ์ฑ์ฐ๊ณ (in-range), fill(7.7, v, v+1)ยทcopyWithin์ ๋น ๋ฒ์๋ก ์ ํ ์๋ฌด ์ผ๋ ์ ํ๋ค. ๋ฐฐ์ด ๊ธธ์ด๋ 4 ๊ทธ๋๋ก, ํฌ๋์ ์์. ์ฆ ์ด ๋นํธ์ธ๋ค์ CheckBounds๋ฅผ ์ ์ฐ๋ ๋์ ์ธ์๋ฅผ ToInteger๋ก ์ ๊ทํํ๊ณ start < 0 ? max(len+start,0) : min(start,len)์ผ๋ก ํด๋จํํ๋ค โ CheckBounds๋ณด๋ค ๋ ๋ถ๋๋ฝ์ง๋ง, OOB๋ก๋ ๋ ๋จ๋จํ ๋ง๋๋ค. --trace-turbo-reduction์ผ๋ก ๋ด๋ ์ธ๋ฑ์ค ๊ฒฝ๋ก์ NumberMin/NumberMax ํด๋จํ ๋
ธ๋๊ฐ ์ด์ ์๋ค.
๋ค๋ฅธ ๊ฐ๋๋ Math ์ฐ์ฐ์ด๋ค. no-NaN์ ๋ฏฟ์ผ๋ฉด min/max/sign์ด NaN ๋ณด์ (SilenceNaN)์ ์๋ตํ๋ค โ ๋ฐ์ฐ์ด ์ค์ฌํ๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ ๋ฐ์ฐ์ด ์ธ๋ชจ๊ฐ ์๋ค.
../extracted/deploy/out/d8 p3_math_sweep.js
Math.max(0, exp(NaN))์ 0, Math.min(exp(NaN), 5)๋ 5. maxsd/minsd๋ ํ ํผ์ฐ์ฐ์๊ฐ NaN์ด๋ฉด ๋ค๋ฅธ ํผ์ฐ์ฐ์๋ฅผ ๊ณ ๋ฅด๋ฏ๋ก(IEEE ๊ท์น), ๊ฒฐ๊ณผ๊ฐ ํญ์ ํด๋จํ ์์ ์ชฝ์ผ๋ก ๋จ์ด์ง๋ค. ์ฆ ๋ฐํ์ ๊ฐ์ด typer๊ฐ ์ฆ๋ช
ํ ๋ฒ์ ์์ ๊ฐํ๋ค โ [0..5]๋ฅผ ๋ชป ๋ฒ์ด๋๋ค. OOB ์ธ๋ฑ์ค๋, ๊ฑฐ๋ ๊ธธ์ด๋ ๋ชป ๋ง๋ ๋ค. ์์ชฝ์ ๋ค clampํ๋ฉด NaN์ด ์๋ชจ๋ผ ๋ฐ์ฐ์ด ์ฌ๋ผ์ง๊ณ , ํ์ชฝ๋ง ํ๋ฉด typer ๋ฒ์๊ฐ ๋ฌดํ์ด๋ผ in-bounds๋ค. ์ด๋ ์ชฝ์ด๋ sink๋ก ๋ชป ์ด๋ค.
TypedArray/DataView๋ ๋ค๋ฅธ ๊ฒฐ์ sink๋ค. ์ฌ๊ธฐ์ ๊ธธ์ด ๊ฒ์ฌ ์ฐํ ๋ง๊ณ ๋ detach UAF(๋ฒํผ๋ฅผ ๋ผ์ด๋ธ ๋ค ์ ํฌ์ธํฐ๋ก ์ ๊ทผ)๋ผ๋ ๊ณ ์ ๊ฐ์ด ์๋ค. ๋ ๋ค ๋ดค๋ค. ta.set(src, offset)ยทta.subarray(a, b)์ ์คํ์
์ ์ ๋ถ ๊ธธ์ด๋ก ๊ฒ์ฌยทํด๋จํ๋๊ณ , ๋ฐ์ฐ๊ฐ์ ๋ฃ์ด๋ OOB๊ฐ ์๋๋ผ RangeError๊ฑฐ๋ ๋น ๋ทฐ๋ค. detach ์ชฝ์ โ ArrayBuffer๋ฅผ ๋ผ๋ฉด ๋ฐฑํน ๋ฐ์ดํฐ ํฌ์ธํฐ๊ฐ ๋ฌดํจ๊ฐ ๋๋๋ฐ, TurboFan์ด detach ๊ฐ๋ฅ์ฑ์ ์๋ฉด ์ ๊ทผ ์ง์ ์ detach ๊ฐ๋๋ฅผ ๊น๋ค(jnscs.cc์ typed-array ๋น๋๊ฐ buffer๋ฅผ ๋ค์ ๋ก๋ํด ๊ฒ์ฌ). no-NaN ๋ฏฟ์์ผ๋ก๋ ๊ทธ ๊ฐ๋๋ฅผ ๋ชป ์ง์ด๋ค. typer ๋ฒ๊ทธ๊ฐ ๊ฑด๋๋ฆฌ๋ ๊ฑด ๊ฐ์ ๋ฒ์์ง ๋ฒํผ์ ์์ฌ๊ฐ ์๋๋ผ์, detach ๊ฒฝ๋ก์ ์์ด ์ ๋ฟ๋๋ค.
CheckBounds๋ง์ง๋ง ํ๋ณด๋ ์์์ข
๋ฅ ์ ์ด(element-kind transition)๋ค. PACKED_SMI โ PACKED_DOUBLE โ PACKED_ELEMENTS๋ก ๋ฐฐ์ด์ ํํ์ด ๋ฐ๋ ๋, ๋๋ MaybeGrowFastElements๋ก ๋ฐฑํน์ ํค์ธ ๋, ์ข
๋ฅ ๊ฐ๋๋ grow ๊ฒ์ฌ๋ฅผ no-NaN์ผ๋ก ๊นจ๋ฉด doubleโtagged ํผ๋์ด๋ grow ์๋ต์ด ๋ ๊น ๊ธฐ๋ํ๋ค. ์ ๋๋ค โ grow ๊ฒฝ๋ก๋ ๊ฒฐ๊ตญ ์ธ๋ฑ์ค๋ฅผ CheckBounds๋ก ๊ฑฐ์น๊ณ (๊ทธ๊ฒ abort ํ๋๋ ๋์), ์ข
๋ฅ ์ ์ด๋ ๊ฐ์ ์ข
๋ฅ(Smi๋ double์ด๋)๋ฅผ ๋ณด์ง NaN ์ฌ๋ถ๋ฅผ ์ ๋ณธ๋ค. Math.exp(NaN)์ด ์ฃผ๋ canonical NaN์ ์ฌ์ ํ double์ด๋ผ ์ข
๋ฅ ๊ฐ๋๋ฅผ ๋ชป ํ๋ ๋ค.
๋๋จธ์ง ํ๋ณด๊น์ง ์ ๋ถ ๊ฐ์ ์ด๋ช ์ด๋ค. ํฅ๋ฏธ๋ก์ ๋ ์ฝ์ง ๋์ ํ ๊ธ์ ๋จ๊ธด๋ค.
fill(value, start, end)ยทcopyWithin(target, start, end)๋ CheckBounds๋ฅผ ์ ์ฐ๋ ๊ธฐ๋๋ฅผ ๊ฑธ์๋ค. ํ์ง๋ง ์ด ๋นํธ์ธ๋ค์ ์ธ์๋ฅผ ToInteger๋ก ์ ๊ทํํ ๋ค start < 0 ? max(len+start, 0) : min(start, len)์ผ๋ก ํด๋จํํ๋ค. INT_MIN์ ๋ฃ์ด๋ len + INT_MIN์ด 0์ผ๋ก ํด๋จํ๋ผ OOB๊ฐ ์ ๋๋ค. --trace-turbo-reduction์ผ๋ก ๋ด๋ ์ธ๋ฑ์ค ๊ฒฝ๋ก์ NumberMin/NumberMax ํด๋จํ ๋
ธ๋๊ฐ ๊ทธ๋๋ก ์ด์ ์๋ค. TypedArray set/subarray์ ์คํ์
๋ ๋์ผํ๊ฒ ์ ๊ทํยท๊ฒ์ฌ๋๋ค.
doar-e์ ์ฐํ์ ํต์ฌ์ "์ด๋ค ๋นํธ์ธ์ ๊ฒฝ๊ณ๊ฒ์ฌ๋ฅผ ํด๋ฉ ๊ฐ๋ฅํ NumberLessThan์ผ๋ก ๋ด๊ณ , ๊ทธ๊ฑธ typer๋ก ์ ์ผ๋ฉด ์ง์ง ๊ฒ์ฌ๊ฐ ์ฌ๋ผ์ง๋ค"๋ ๊ฒ. ๊ทธ๋์ indexOf/includes/at์ ์ธ๋ผ์ธ์ํจ ๋ค ๊ทธ๋ํ๋ฅผ ๋ด๋๋ฐ โ js-native-context-specialization.cc์ element-access ๋น๋๊ฐ ๊ทธ ์๋ฆฌ์ kAbortOnOutOfBounds๊ฐ ๋ฐํ ๋ ๋ฒ์งธ CheckBounds ๋ฅผ ๋ช
์์ ์ผ๋ก ๋ผ์ ๋ฃ๋๋ค(์์ค ์ฃผ์์ "potential typer bug" ์ด์ด). 2ํธ์์ ์ค ๋ฒํธ์งธ ๋ถ๊ฒํ ๊ทธ ๋ฌด๋ค์ด๋ค. ํด๋ฉ์ผ๋ก ์ฒซ ๊ฒ์ฌ๋ฅผ ์ง์๋ ๋ ๋ฒ์งธ๊ฐ ํธ๋ฉ์ผ๋ก ๋๋๋ค.
์ฌ๊ธฐ์๋ถํฐ๊ฐ 3ํธ์ ์ง์ง ์ํ์ด๋ค. Math.exp/expm1 ๊ฐ์ typer ๋ฒ๊ทธ์ ๊ต๊ณผ์์ ์๋ ํด๋ฒ์ ์ธ๋ฑ์ค๊ฐ ์๋๋ผ ๊ธธ์ด๋ฅผ ๋ง๋๋ ๊ฒ์ด๋ค โ Quick-Maffs(linz04)ยทBug-1051017์ด ์ด length-construction. ๋ฐ์ฐํ ๊ฐ์ new Array(v)์ ๋ฃ์ด, typer๋ "์ฉ๋ 11์ง๋ฆฌ ์์ ๋ฐฐ์ด"์ด๋ผ ๋ฏฟ๊ณ ๋ฐฑํน ์คํ ์ด๋ฅผ ์๊ฒ ์ก๋๋ฐ ๋ฐํ์ v๋ ๊ฑฐ๋ํด์ length > capacity ๋ถ์ผ์น๊ฐ ๋๋ฉด, ๊ทธ ๋ฐฐ์ด๋ก ๋ฐฑํน ์คํ ์ด ๋ฐ์ ์ฝ๊ณ ์ธ ์ ์๋ค. ์ ๋ฐ์ฐ ์ฒด์ธ์ด ์ ํํ ๊ทธ ์ฌ๋ฃ(typer 11 / ๋ฐํ์ INT_MIN)๋ฅผ ๋ง๋ ๋ค.
์ธ๋ฑ์ค ๊ณต๊ฒฉ(a[v])์ ํ ๋ฒ ๋ฐฐ์ด ๋ฐ์ ๊ฑด๋๋ฆฌ๊ณ ๋์ด๋ฉฐ, ๊ทธ๋ง์ CheckBounds๊ฐ ๋ง๋๋ค. ๋ฐ๋ฉด length-construction์ ์ง์์ ์ธ OOB ๋ฐฐ์ด ๊ทธ ์์ฒด๋ฅผ ๋ง๋ ๋ค. JSArray๋ ํค๋์ length ํ๋๋ฅผ, ๋ณ๋ FixedDoubleArray(๋ฐฑํน ์คํ ์ด)์ ์ค์ ์์๋ฅผ ๋๋ค. ์ ์์ด๋ผ๋ฉด length โค capacity๋ค. ๊ทธ๋ฐ๋ฐ typer๊ฐ ๊ธธ์ด๋ฅผ "11"๋ก ๋ฏฟ์ด capacity 11์ง๋ฆฌ ๋ฐฑํน์ ์ก์๋๋ฐ ๋ฐํ์ ๊ธธ์ด๊ฐ ๊ฑฐ๋๊ฐ์ด ๋๋ฉด, arr[12], arr[100], arr[1000]์ด ์ ๋ถ *"length ์"*์ผ๋ก ํต๊ณผํด ๋ฐฑํน ๋ฐ ์ธ์ ํ์ ์ฝ๊ณ ์ด๋ค โ ํ ๋ฒ์ด ์๋๋ผ ์์ ํ์์ ์๋ R/W๋ค. ๊ฒ๋ค๊ฐ CheckBounds๋ ์ธ๋ฑ์ค๋ฅผ length ์ ๋น๊ตํ๋๋ฐ ๊ทธ length๊ฐ ์ด๋ฏธ ๊ฑฐ๋ํ๋, ๊ฒ์ฌ ์์ฒด๊ฐ ๋ฌด์๋ฏธํด์ง๋ค(์ธ๋ฑ์ค๊ฐ ์ง์ง๋ก length๋ณด๋ค ์์ผ๋๊น). ๊ทธ๋์ ์ด๊ฒ typer ๊ฐ-๋ฐ์ฐ ๋ฒ๊ทธ์ ์ ์ ์น๊ธ ๊ฒฝ๋ก๋ค โ ๋ฐ์ฐ์ ํ ๋ฒ๋ง ๊ธธ์ด์ ๊ฝ์ผ๋ฉด, ๊ทธ ๋ค๋ก๋ ํ๋ฒํ ๋ฐฐ์ด ์ ๊ทผ์ด ๊ณง OOB๊ฐ ๋๋ค.
๊ทธ๋์ ๊ทธ๋๋ก ํ์๋ดค๋ค โ new Array(v) ํ ๋ค arr[0]=1.1, ๊ทธ๋ฆฌ๊ณ ๊ธธ์ด๋ฅผ ํ์ธ.
../extracted/deploy/out/d8 --allow-natives-syntax qm_newarray.js 2>&1 | head -9![ํด๋ฆญํ์ฌ ํ๋ ์๋๋ sink โ new Array(v): length=11, FixedDoubleArray[11]. ๋ณดํธ๊ฐ ๊ทธ๋๋ก ์ด์ ์๋ค](/images/blog/dreamhack-exp-nan-writeup-3/02_newarray_protected.png)
arr.length = 11. arr[100]ยทarr[1000] ๋ ๋ค undefined. %DebugPrint๋ก ๋ด๋ elements: FixedDoubleArray[11], length: 11 โ length์ capacity๊ฐ 11๋ก ์ผ์นํ๋ค. ๋ถ์ผ์น๊ฐ ์๋ค. ์๋๋ sink๊ฐ ๋งํ ์๋ค.
ํจ์น๋ฅผ ๋ค์ ๋ณด๋ฉด ๋ต์ด ๋์จ๋ค. patches/v8.patch๊ฐ ๊ฑด๋๋ฆฐ ๊ฑด typer.cc(Math.exp โ PlainNumber)์ d8 ๋ฝ๋ค์ด๋ฟ์ด๋ค. length-construction์ด OOB๊ฐ ๋๋ ค๋ฉด JSCreateArray๋ฅผ ๋ฎ์ถ๋ js-create-lowering.cc๊ฐ length๋ฅผ Constant(capacity)๋ก ๊ณ ์ ํ๋ ๋ณดํธ๋ฅผ ์ ๊ฑฐํด์ผ ํ๋๋ฐ, ์ด ํจ์น๋ ๊ฑฐ๊ธฐ๋ฅผ ์ ๊ฑด๋๋ ธ๋ค. ๊ทธ๋์ ReduceJSCreateArray์ ์ธ ๋ถ๊ธฐ๊ฐ ์ ๋ถ ์์ ํ๊ฒ ๋จ์, ๋ฐํ์ ๊ธธ์ด๊ฐ ๊ฑฐ๋ํด๋ capacity์ ๋ฌถ์ฌ ๋ถ์ผ์น๊ฐ ์ ์๊ธด๋ค.
์ ๋ฆฌํ๋ฉด ์ด ๋ฌธ์ ์ ์ง์ง ๊ตฌ์กฐ๋ ์ด๊ฑฐ๋ค โ
typer ๋ฒ๊ทธ๋ ์ด์ ์๋๋ฐ, ๊ทธ ๋ฒ๊ทธ๋ฅผ ๋ฐ์์ค ์๋๋ sink์ ๋ณดํธ๋ ํจ์น๊ฐ ์ ํ์๋ค.
๋ฒ๊ทธ(typer.cc)์ sink ๋ณดํธ(js-create-lowering.cc)๊ฐ ์๋ก ๋ค๋ฅธ ํ์ผ์ ์๊ณ , ํจ์น๋ ์์ชฝ๋ง ์ด์๋ค. 1ยท2ํธ์์ ๋ณธ CheckBounds ํ๋๋์ ๋ชจ๋ ์ธ๋ฑ์ค ๊ฒฝ๋ก๋ฅผ ๋ง๊ณ , length-construction ๋ณดํธ๋ ๊ทธ ๋์ ๊ฒฝ๋ก๋ฅผ ๋ง๋๋ค. ๊ณต๊ฐ ๊ธฐ๋ฒ์ด ๋
ธ๋ฆฌ๋ ๋ ์ถ๊ตฌ๊ฐ ์์ชฝ ๋ค ๋ซํ ์๋ ์
์ด๋ค.
์ฌ๊ธฐ์ ์์ฌ์ด ํ๋ ์๊ธด๋ค. ์ ํ์ ์ ์ ๋ถ ์์ค commit 6538a20a ๋ฅผ ์ฝ๊ณ ํ ๊ฒ์ด๊ณ , length ๋ณดํธ๊ฐ "์์ค์ ์๋ค"๋ ์ฌ์ค์ด๋ค. ๊ทธ๋ฐ๋ฐ ์ค์ ์ฑ์ ์๋ฒ๊ฐ ๋๋ฆฌ๋ ๊ฑด ์์ค๊ฐ ์๋๋ผ ๋น๋๋ d8 ๋ฐ์ด๋๋ฆฌ๋ค. ๋น๋ ํ๋๊ทธยท์ธ๋ผ์ด๋ยท์ต์ ํ ์ฐจ์ด๋ก ์์ค์๋ ์๋ ๋ณดํธ๊ฐ ๋ฐ์ด๋๋ฆฌ์ ๋ค๋ฅด๊ฒ ๋ค์ด๊ฐ์ ๊ฐ๋ฅ์ฑ โ ์ฆ ํจ์นโ๋ฐ์ด๋๋ฆฌ ๋ถ์ผ์น ๋ ๋ฐฐ์ ํ์ง ๋ชปํ๋ค.
๊ทธ๋์ ๋จ์ 1์์๋ ์ถ์ธก์ด ์๋๋ผ ํ์ธ์ด๋ค โ
out/d8์ ReduceJSCreateArray / JSCreateArray ๋ฎ์ถค ์ฝ๋๋ฅผ ์ง์ ๋์ค์ด์
๋ธํด, length = Constant(capacity) ๋ณดํธ๊ฐ ๋ฐ์ด๋๋ฆฌ์ ์ค์ฌํ๋์ง ๋ณธ๋ค. ๊ตฌ์ฒด์ ์ผ๋ก๋ new Array(v)๋ฅผ ๋๋ ํซํจ์๋ฅผ ๋ง๋ค์ด --print-opt-code(๋๋ --trace-turbo๋ก ๊ทธ๋ํ๋ฅผ ๋ objdump)๋ก ๊ธธ์ด ๊ณ์ฐ ๋ถ๋ถ์ ์ถ์ ํ๋ค. ์ฐพ์ ๊ฒ์ โ ํ ๋น ์ง์ ์ length๋ฅผ capacity ์์๋ก ๋ฎ์ด์ฐ๋ ๋ช
๋ น(mov [rax+0xC], <const> ๊ฐ์ JSArray length ์คํ ์ด)์ด ์๋์ง, ์๋๋ฉด ๋ฐํ์ v๊ฐ ๊ทธ๋๋ก length ํ๋๋ก ๋ค์ด๊ฐ๋์ง๋ค. ํ์๋ฉด ๋ถ์ผ์น๊ฐ ์ด์ ์๋ค๋ ๋ป.CheckBounds sink (์ด ๋น๋ ํน์ ์ ์ธ๋ผ์ธ ๋นํธ์ธ ๋ด๋ถ ๊ธธ์ด ์ฐ์ฐ ๋ฑ). ๋ค๋ง ์ ์ ์ ๊ฒ ๊ฒฐ๊ณผ ์ด๊ฑด ๋ฎ๊ฒ ๋ณธ๋ค(์ฒด๊ฐ 15% ๋ฏธ๋ง).4ํธ ่จป: ๊ฒฐ๊ตญ ๊ธธ์ 3๋ฒ์ด์๋ค โ ๋ฐ์ด๋๋ฆฌ ๋ถ๊ฒ์ด ์๋๋ผ, ์์ค์์ ๋ ๋ณธ reducer(
Array.prototype.at())๊ฐ ๋น-CheckBoundssink์๋ค. 3ํธ์ด "๊ณต๊ฐ๋์ง ์์ sink"๋ผ ์ ์ ๊ทธ 15%๊ฐ ์ค์ ๋ก ์ด๋ ค ์์๋ ์ . ๊ทธ๋์ ๋ค์ ํธ์ ์ถ๋ฐ์ ์ ๋ฐ์ด๋๋ฆฌ๊ฐ ์๋๋ผjs-call-reducer.cc์ฌ๋ ์ด ๋๋ค.
์ด๊ฑด ๋ค์ ํธ(๋๋ ์ง์ ๋ ํ๋ณผ) ์ถ๋ฐ์ ์ด๋ค. ์ด๊ธ๋จ์ ์ด๋ฏธ ์์ ์์ผ๋, ์ฒซ OOB ํ ์นธ๋ง ์ด๋ฆฌ๋ฉด ๋๋ค.
2ํธ ๋์์ ๋งคํํด๋ ํ๋ฐ๋ถ ์ฒด์ธ์ sink๊ฐ ๋ฌด์์ด๋ ๋์ผํ๋ค. ์ฒซ OOB read/write๊ฐ ์๊ธฐ๋ ์๊ฐ:
ArrayBuffer์ backing_store(@JSArrayBuffer+0x14)๋ฅผ ๋ฎ์ด โ ์ผ์ด์ง ๋ด๋ถ ์์ R/WWasmInstanceObject.jump_table_start(@instance+~0x60, ์ผ์ด์ง ๋ฐ raw RWX)๋ฅผ ์ฐพ์ ์
ธ์ฝ๋ ๊ธฐ์
โ ์๋๋ฐ์ค ํ์ถexecve("./flag_reader-<md5>") (์๋ฒ cwd /home/pwn) โ flag์ ๋ถ ์์ฐ ํฐ์ด์ ์์ ๋์ํด์ผ ํ์ง๋ง(์๊ฒฉ ์ ์ฝ), ๋ฐ์ฐ ์์ฒด๋ ์ด๋ฏธ ์์ฐ ํฐ์ด์ ์ผ๋ก ๊ฒ์ฆ๋๋ค. ๋งํ ๊ฑด ์ค์ง 1๋ฒ์ ์ ๊ตฌ๋ค.
| ํ๋ณด ์ถ๊ตฌ | ๋ ธ๋ฆฐ ์ฝ์ | ๋ง์ ๊ฒ |
|---|---|---|
a[i] ์ธ๋ฑ์ค | CheckBounds ์ ๊ฑฐ | kAbortOnOutOfBounds ํธ๋ฉ (2ํธ) |
indexOf/includes/at | foldable NumberLessThan | element-access์ ๋ ๋ฒ์งธ abort CheckBounds (๋ผ๊ณ ๋ดค๋ค โ 4ํธ์ ์ ์ ) |
fill/copyWithin | CheckBounds ๋ถ์ฌ | ToInteger+ํด๋จํ (OOB๋ก๋ ๋ ๋จ๋จ) |
Math.min/max/sign | NaN ๋ณด์ ์๋ต | ๊ฐ์ด typer ๋ฒ์ ์์ ๊ฐํ |
TypedArray/DataView |
ํ ์ค ๊ฒฐ๋ก : ์ธ๋ฑ์ค ์ถ๊ตฌ๋, ๊ธธ์ด ์ถ๊ตฌ๋, ๋นํ์ค ์ถ๊ตฌ๋ (์์ค ๊ธฐ์ค) ์ ๋ถ ๋ซํ๋ค. ๋จ โ ๋ ๋ฒ์งธ ํ์ at์ ๋ํ ํ์ ์ด ์ฑ๊ธํ๋ค๋ ๊ฒ 4ํธ์์ ๋๋ฌ๋๋ค.
3ํธ์ ๊ฒฐ๊ณผ๋ ์ด๋ ๋ค.
CheckBounds ์ ๊ฑฐ)๋ ๋ซํ๋ค โ 2ํธ์์ ์์ค๋ก ํ์ .new Array length-construction)๋ง์ ๋ซํ ์๋ค โ ํจ์น๊ฐ typer.cc๋ง ์ด๊ณ js-create-lowering์ ๊ธธ์ด ๋ณดํธ๋ ์ ํ์๊ธฐ ๋๋ฌธ.์ฆ ์ด๊ฑด "์ด๋ ต๋ค"๊ฐ ์๋๋ผ, ์ฌ๋ฃ๋ ์๋๋ฐ ๋ฐ์์ค ๊ทธ๋ฆ์ด (์์ค ๊ธฐ์ค์ผ๋ก) ์ ๋ถ ๋งํ ์ํ๋ค. ๊ทธ๋์ ๋ค์ ํ ์๋ ์ถ์ธก์ ๋๋ฆฌ๋ ๊ฒ ์๋๋ผ, ๋ฐฐํฌ ๋ฐ์ด๋๋ฆฌ์์ ๊ทธ ๊ทธ๋ฆ์ ๋ณดํธ๊ฐ ์ง์ง ๋ฐํ ์๋์ง ๋ถ๊ฒํ๋ ๊ฒ โ ๊ฑฐ๊ธฐ์ ์์ค์ ๋ฐ์ด๋๋ฆฌ๊ฐ ๊ฐ๋ฆฌ๋ฉด ๊ธธ์ด ๋ค์ ์ด๋ฆฐ๋ค.
ํ๋๊ทธ๋ ์์ง์ด๋ค. ํ์ง๋ง ๋งํ ์ง์ ์ด "๊ฐ"์ด ์๋๋ผ ํจ์น ํ์ผยท์์ค ์คยทd8 ์ค์ธกยท๋ฐ์ด๋๋ฆฌ ๋ถ๊ฒ ๋์์ผ๋ก ์ ํํ ์ขํ์ก๋ค. typer ๋ฒ๊ทธ๋ฅผ ๋๊น์ง ๋ฐ๋ผ๊ฐ๋ฉด, ํ๋ คํ ์ต์ค๋ณด๋ค ์ด๋ฐ ์ง๋๊ฐ ๋จผ์ ๋์จ๋ค.
๋ท์ด์ผ๊ธฐ (4ํธ): ์ด "์ ๋ถ ๋ซํ" ๊ฒฐ๋ก ์ ๊ตฌ๋ฉ์ด ํ๋ ์์๋ค โ ์ฌ๊ธฐ์ ๋ณธ ์ถ๊ตฌ๋ ์ ๋ถ
a[i](bracket)์CheckBounds๊ณ์ด์ด์๋๋ฐ, ๋นํธ์ธ reducer๋ก ๊ฐ๋ ๊ธธ์ ์ ๋๋ก ์ ๋ดค๋ค.Array.prototype.at()์CheckBounds๊ฐ ์๋๋ผ fold ๊ฐ๋ฅํNumberLessThan๋ ๊ฐ๋ก ๊ฒฝ๊ณ๋ฅผ ๊ฒ์ฌํ๋ค โ ๊ทธ๋์ ๋ฌด๊ฐ๋ OOB read๊ฐ ์ค์ ๋ก ์ด๋ฆฐ๋ค. ๊ทธ ๋ฐ์ ์ 4ํธ์์.
์ด ์ฑ๋ฆฐ์ง๋ ์ญ์ค์ ์ผ๋ก ๊ณ์ธต ๋ฐฉ์ด๊ฐ ์ค์ ๋ก ์๋ํ๋ค๋ ์ฆ๊ฑฐ๋ค.
Math.exp ์คํ์
)๋ ํ ์ค๋ก ๋ค์ด์์ง๋ง, ๊ทธ๊ฒ๋ง์ผ๋ก๋ ์๋ฌด๊ฒ๋ ์ ๋๋ค.CheckBounds๋ฅผ ์ ๋ ์ ๊ฑฐํ์ง ์๊ณ in-bounds ์ฆ๋ช
์ abort๋ก ๋ฐ๊พธ๋ ํ๋๋์ด ์ธ๋ฑ์ค ์ถ๊ตฌ๋ฅผ ๋ง๊ณ ,JSCreateArray์ length=capacity ๋ณดํธ๊ฐ ๊ธธ์ด ์ถ๊ตฌ๋ฅผ ๋ง๋๋ค.๊ณต๊ฒฉ์๊ฐ ํ ์ถ๊ตฌ๋ฅผ ์ด์ด๋(์ฌ๊ธฐ์ typer๋ฅผ ์์ด๋ ๋ฐ๊น์ง ์ฑ๊ณต) ๋ค์ ๊ณ์ธต์ด ๋ฉ๋ชจ๋ฆฌ ์นจ๋ฒ์ผ๋ก์ ์น๊ธ์ ๋๋๋ค. ๋ธ๋ผ์ฐ์ ๋ณด์์ "๋ฒ๊ทธ โ ์ต์คํ๋ก์" ์์น์ด ์์ค ๋ผ์ธ ๋จ์๋ก ๊ตฌํ๋ ๋ชจ์ต์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ถ์ ์ ์ ์ฅ์์ ๋ณด๋ฉด, ์ด ๋ฌธ์ ๊ฐ ๋ค์ด์ 1์ธ ์ด์ ๋ ์ฌ๊ธฐ ์๋ค โ ์๋๋ sink ๋ณดํธ๊น์ง ์ด์ ์์ด, ํธ๋ ์ชฝ์ ๋จ์ ๋จ ํ๋์ ๋นํ์ค ์ ๊ตฌ๋ฅผ ์ฐพ์์ผ ํ๋ค.
6538a20a (11.2.214.14) โ compiler/simplified-lowering.cc, compiler/js-native-context-specialization.cc, compiler/js-create-lowering.ccMath.exp NaN ๋ฒ๊ทธ๋ฅ์ length-construction ํด๋ฒ.at() ๋ฐ์ maxsignSilenceNaN| no-NaN ๋ฏฟ์ด NaN ๋ณด์ ์๋ต |
NumberSilenceNaN ์๋ต์ ์ค์ฌํ๋ Math.exp(NaN)์ด ํญ์ canonical 0x7ff8โฆ๋ผ ๋ฌดํด |
ํํํ ๋ณํ (cvttsd2si ๊ฒฐ๊ณผ์ ํ์ ์ง) | int์ด CheckBounds ์์ด ๋ฉ๋ชจ๋ฆฌ ์คํ์
์ด ๋๋ ๊ณณ | INT_MIN ๋จ์ผ ์์ ํจํด๋ฟ โ ์์ ์์ OOB ์ธ๋ฑ์ค ์์ฑ ๋ถ๊ฐ, ๋ฟ๋ sink๋ ๋ค CheckBounds |
TypedArrayยทDataView (set/subarray/get*/detach) | ๊ธธ์ด/์คํ์ ๊ฒ์ฌ ์ฐํยทdetach UAF | ๊ฒฝ๊ณ๊ฒ์ฌ ๊ทธ๋๋ก ์ ์ง, detach-ํ-์ ๊ทผ๋ ๊ฐ๋๋จ |
์์์ข
๋ฅ ์ ์ด / MaybeGrowFastElements | grow ์๋ตยทdoubleโtagged ํผ๋ | grow ๊ฒฝ๋ก๋ ๊ฒฐ๊ตญ CheckBounds ๊ฒฝ์ , no-NaN์ผ๋ก ์ข
๋ฅ ๊ฐ๋ ์ ๊นจ์ง |
| ๊ฒฝ๊ณ ์ฐํยทdetach UAF |
| ๊ฒฝ๊ณ๊ฒ์ฌยทdetach ๊ฐ๋ ์ ์ง |
| ์์์ข ๋ฅ ์ ์ด/grow | ์ข ๋ฅ ํผ๋ยทgrow ์๋ต | grow๋ CheckBounds ๊ฒฝ์ , ์ข
๋ฅ ๊ฐ๋ ๋ฌด๊ด |
new Array(v) ๊ธธ์ด ๊ตฌ์ฑ | length>capacity ๋ถ์ผ์น | js-create-lowering์ด length==capacity ๊ณ ์ |
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ