AF_UNIX MSG_OOB UAF & SKB ๊ธฐ๋ฐ˜ ์ปค๋„ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

TL;DR

  • Linux >=6.9์—์„œ AF_UNIX MSG_OOB ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ manage_oob() ๋ฆฌํŒฉํ„ฐ(5aa57d9f2d53)์— ๊ฒฐํ•จ์ด ๋„์ž…๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฐ์†๋œ ์ œ๋กœ-๊ธธ์ด SKB๊ฐ€ u->oob_skb๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋กœ์ง์„ ์šฐํšŒํ•˜์—ฌ, ์ผ๋ฐ˜์ ์ธ recv()๊ฐ€ ํฌ์ธํ„ฐ๊ฐ€ ์—ฌ์ „ํžˆ ์‚ด์•„์žˆ๋Š” ๋™์•ˆ OOB SKB๋ฅผ freeํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ๊ทธ ๊ฒฐ๊ณผ CVE-2025-38236์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
  • recv(..., MSG_OOB)๋ฅผ ์žฌ์‹คํ–‰ํ•˜๋ฉด dangling struct sk_buff๋ฅผ ์—ญ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. MSG_PEEK๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ, ๊ฒฝ๋กœ unix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()๊ฐ€ ์•ˆ์ •์ ์ธ 1๋ฐ”์ดํŠธ ์ž„์˜ ์ปค๋„ ์ฝ๊ธฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค; MSG_PEEK๊ฐ€ ์—†์œผ๋ฉด ์ด ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋Š” ์žฌํ• ๋‹น๋œ ๊ฐ์ฒด์˜ offset 0x40์— ๋†“์ธ 64๋น„ํŠธ ๊ฐ’์˜ ์ƒ์œ„ dword์— +4 GiB๋ฅผ ๋”ํ•˜๋Š”, offset 0x44์˜ UNIXCB(oob_skb).consumed๋ฅผ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค.
  • order-0/1 unmovable pages๋ฅผ ์†Œ์ง„(page-table spray)ํ•˜๊ณ  SKB slab ํŽ˜์ด์ง€๋ฅผ buddy allocator๋กœ ๊ฐ•์ œ freeํ•œ ๋’ค ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ pipe buffer๋กœ ์žฌ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, ์ต์Šคํ”Œ๋กœ์ž‡์€ ์ œ์–ด๋œ ๋ฉ”๋ชจ๋ฆฌ์— SKB ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์œ„์กฐํ•ด dangling ํŽ˜์ด์ง€๋ฅผ ์‹๋ณ„ํ•˜๊ณ  read ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ .data, vmemmap, per-CPU, ํŽ˜์ด์ง€-ํ…Œ์ด๋ธ” ์˜์—ญ์œผ๋กœ ํ”ผ๋ฒ—์‹œํ‚ต๋‹ˆ๋‹ค โ€” usercopy hardening์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ .
  • ๋™์ผํ•œ ํŽ˜์ด์ง€๋Š” ์ดํ›„ ์ƒˆ๋กœ ํด๋ก ๋œ ์Šค๋ ˆ๋“œ์˜ ์ตœ์ƒ์œ„ ์ปค๋„ ์Šคํƒ ํŽ˜์ด์ง€๋กœ ์žฌํ™œ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CONFIG_RANDOMIZE_KSTACK_OFFSET๋Š” ์˜ค๋ผํด์ด ๋ฉ๋‹ˆ๋‹ค: pipe_write()๊ฐ€ ๋ธ”๋ก๋˜๋Š” ๋™์•ˆ ์Šคํƒ ๋ ˆ์ด์•„์›ƒ์„ ํƒ์ง€ํ•˜์—ฌ ๊ณต๊ฒฉ์ž๋Š” spilled copy_page_from_iter() ๊ธธ์ด(R14)๊ฐ€ offset 0x40์— ์œ„์น˜ํ•  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ํ›„ +4 GiB ์ฆ๊ฐ€๋ฅผ ๋ฐœ์‚ฌํ•ด ์Šคํƒ ๊ฐ’์„ ์†์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  • ์ž์ฒด ๋ฃจํ”„๋ฅผ ๋„๋Š” skb_shinfo()->frag_list๋Š” ํ˜‘๋ ฅ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹จ์ผ MADV_DONTNEED ํ™€์ด ์žˆ๋Š” VMA์— ๋Œ€ํ•ด mprotect()๋กœ copy_from_iter()๋ฅผ ๋ฉˆ์ถœ ๋•Œ๊นŒ์ง€ UAF syscall์„ ์ปค๋„ ๊ณต๊ฐ„์—์„œ ๋ฐ˜๋ณต์‹œํ‚ค๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํ”„๋ฅผ ๊นจ๋ฉด ์ฆ๊ฐ€๊ฐ€ ์Šคํƒ ํƒ€๊นƒ์ด ํ™œ์„ฑํ™”๋œ ์ •ํ™•ํ•œ ์‹œ์ ์— ํ•ด์ œ๋˜์–ด bytes ์ธ์ž๋ฅผ ๋ถ€ํ’€๋ฆฌ๊ณ  copy_page_from_iter()๊ฐ€ pipe buffer ํŽ˜์ด์ง€๋ฅผ ๋„˜์–ด ๋‹ค์Œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€์— ์“ฐ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • read ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ pipe-buffer PFN๊ณผ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•จ์œผ๋กœ์จ, ๊ณต๊ฒฉ์ž๋Š” ๋‹ค์Œ ํŽ˜์ด์ง€๊ฐ€ PTE ํŽ˜์ด์ง€์ž„์„ ํ™•์ธํ•˜๊ณ  OOB ๋ณต์‚ฌ๋ฅผ ์ž„์˜์˜ PTE ์“ฐ๊ธฐ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฌด์ œํ•œ ์ปค๋„ R/W/X๋ฅผ ํš๋“ํ•ฉ๋‹ˆ๋‹ค. Chrome์€ ๋ Œ๋”๋Ÿฌ์—์„œ MSG_OOB๋ฅผ ์ฐจ๋‹จํ•˜์—ฌ ์ ‘๊ทผ ๊ฐ€๋Šฅ์„ฑ์„ ์™„ํ™”ํ–ˆ์œผ๋ฉฐ(6711812), Linux๋Š” ๋กœ์ง ๊ฒฐํ•จ์„ 32ca245464e1์—์„œ ์ˆ˜์ •ํ•˜๊ณ  CONFIG_AF_UNIX_OOB๋ฅผ ๋„์ž…ํ•ด ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์„ ํƒํ˜•์œผ๋กœ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทผ๋ณธ ์›์ธ: manage_oob()๋Š” ํ•˜๋‚˜์˜ ์ œ๋กœ-๊ธธ์ด SKB๋งŒ ๊ฐ€์ •ํ•œ๋‹ค

unix_stream_read_generic()๋Š” manage_oob()๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ชจ๋“  SKB๊ฐ€ unix_skb_len() > 0์ž„์„ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. 93c99f21db36 ์ดํ›„ manage_oob()๋Š” recv(MSG_OOB)๊ฐ€ ๋‚จ๊ธด ์ œ๋กœ-๊ธธ์ด SKB๋ฅผ ์ฒ˜์Œ ์ œ๊ฑฐํ•  ๋•Œ๋งˆ๋‹ค skb == u->oob_skb ์ •๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๊ฑด๋„ˆ๋›ฐ์—ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„์˜ ์ˆ˜์ •(5aa57d9f2d53)์€ ์—ฌ์ „ํžˆ ์ฒซ ๋ฒˆ์งธ ์ œ๋กœ-๊ธธ์ด SKB์—์„œ skb_peek_next()๋กœ ๋„˜์–ด๊ฐ€๋ฉด์„œ ๊ธธ์ด๋ฅผ ์žฌํ™•์ธํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ์—ฐ์†๋œ ์ œ๋กœ-๊ธธ์ด SKB๊ฐ€ ์žˆ์œผ๋ฉด ํ•จ์ˆ˜๋Š” ๋‘ ๋ฒˆ์งธ ๋นˆ SKB๋ฅผ ๋ฐ˜ํ™˜ํ–ˆ๊ณ , unix_stream_read_generic()๋Š” ๋‹ค์‹œ manage_oob()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์ด๋ฅผ ๊ฑด๋„ˆ๋›ฐ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ OOB SKB๊ฐ€ dequeue๋˜์–ด free๋˜์—ˆ๋Š”๋ฐ u->oob_skb๋Š” ์—ฌ์ „ํžˆ ์ด๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ตœ์†Œ ํŠธ๋ฆฌ๊ฑฐ ์‹œํ€€์Šค

char byte;
int socks[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
for (int i = 0; i < 2; ++i) {
send(socks[1], "A", 1, MSG_OOB);
recv(socks[0], &byte, 1, MSG_OOB);
}
send(socks[1], "A", 1, MSG_OOB);   // SKB3, u->oob_skb = SKB3
recv(socks[0], &byte, 1, 0);         // normal recv frees SKB3
recv(socks[0], &byte, 1, MSG_OOB);   // dangling u->oob_skb

unix_stream_recv_urg()๊ฐ€ ๋…ธ์ถœํ•˜๋Š” ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ

  1. 1-byte arbitrary read (repeatable): state->recv_actor()๋Š” ๊ฒฐ๊ตญ copy_to_user(user, skb_sourced_addr, 1)์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. dangling SKB๊ฐ€ ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ(๋˜๋Š” pipe ํŽ˜์ด์ง€ ๊ฐ™์€ ์ œ์–ด ๊ฐ€๋Šฅํ•œ alias)๋กœ ์žฌํ• ๋‹น๋˜๋ฉด, ๋งค๋ฒˆ recv(MSG_OOB | MSG_PEEK)๊ฐ€ __check_object_size()๋กœ ํ—ˆ์šฉ๋˜๋Š” ์ž„์˜์˜ ์ปค๋„ ์ฃผ์†Œ์—์„œ 1๋ฐ”์ดํŠธ๋ฅผ ์œ ์ € ๊ณต๊ฐ„์œผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค(ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฃฝ์ง€ ์•Š์Œ). MSG_PEEK์„ ์œ ์ง€ํ•˜๋ฉด dangling ํฌ์ธํ„ฐ๋ฅผ ๋ณด์กดํ•˜์—ฌ ๋ฌด์ œํ•œ์œผ๋กœ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. Constrained write: MSG_PEEK๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์„ ๋•Œ UNIXCB(oob_skb).consumed += 1์€ ์˜คํ”„์…‹ 0x44์˜ 32๋น„ํŠธ ํ•„๋“œ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค. 0x100 ์ •๋ ฌ๋œ SKB ํ• ๋‹น์—์„œ ์ด๊ฒƒ์€ 8๋ฐ”์ดํŠธ ์ •๋ ฌ๋œ ๋‹จ์–ด๋ณด๋‹ค 4๋ฐ”์ดํŠธ ์œ„์— ์œ„์น˜ํ•˜์—ฌ, ์›์‹œ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ์˜คํ”„์…‹ 0x40์— ํ˜ธ์ŠคํŒ…๋œ ์›Œ๋“œ์˜ +4 GiB ์ฆ๊ฐ€๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ปค๋„ ์“ฐ๊ธฐ๋กœ ๋ฐ”๊พธ๋ ค๋ฉด ํ•ด๋‹น ์˜คํ”„์…‹์— ๋ฏผ๊ฐํ•œ 64๋น„ํŠธ ๊ฐ’์„ ๋ฐฐ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Reallocating the SKB page for arbitrary read

  1. Drain order-0/1 unmovable freelists: ๊ฑฐ๋Œ€ํ•œ ์ฝ๊ธฐ ์ „์šฉ anonymous VMA๋ฅผ ๋งคํ•‘ํ•˜๊ณ  ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ fault ์‹œ์ผœ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ํ• ๋‹น์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค (order-0 unmovable). ๋Œ€๋žต RAM์˜ 10% ์ •๋„๋ฅผ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”๋กœ ์ฑ„์šฐ๋ฉด ์ดํ›„ skbuff_head_cache ํ• ๋‹น์ด order-0 ๋ฆฌ์ŠคํŠธ๊ฐ€ ๊ณ ๊ฐˆ๋œ ํ›„ ์‹ ๊ทœ buddy ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  2. Spray SKBs and isolate a slab page: ์—ฌ๋Ÿฌ ๊ฐœ์˜ stream socketpair๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์†Œ์ผ“๋‹น ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์ž‘์€ ๋ฉ”์‹œ์ง€(~0x100 ๋ฐ”์ดํŠธ per SKB)๋ฅผ ํ์ž‰ํ•˜์—ฌ skbuff_head_cache๋ฅผ ์ฑ„์›๋‹ˆ๋‹ค. ์„ ํƒํ•œ SKB๋“ค์„ ํ•ด์ œํ•ด ๋ชฉํ‘œ ์Šฌ๋žฉ ํŽ˜์ด์ง€๋ฅผ ๊ณต๊ฒฉ์ž ์ œ์–ด ํ•˜์— ์™„์ „ํžˆ ๋†“๊ณ , ์ฝ๊ธฐ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ struct page refcount๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
  3. Return the slab page to the buddy allocator: ํŽ˜์ด์ง€์˜ ๋ชจ๋“  ๊ฐ์ฒด๋ฅผ ํ•ด์ œํ•œ ๋’ค ์ถ”๊ฐ€์ ์ธ ํ• ๋‹น/ํ•ด์ œ๋ฅผ ์ถฉ๋ถ„ํžˆ ์ˆ˜ํ–‰ํ•ด SLUB per-CPU partial ๋ฆฌ์ŠคํŠธ์™€ per-CPU ํŽ˜์ด์ง€ ๋ฆฌ์ŠคํŠธ์—์„œ ํŽ˜์ด์ง€๊ฐ€ ๋ฐ€๋ ค๋‚˜๋„๋ก ํ•˜์—ฌ buddy freelist์˜ order-1 ํŽ˜์ด์ง€๊ฐ€ ๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  4. Reallocate as pipe buffer: ์ˆ˜๋ฐฑ ๊ฐœ์˜ pipe๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค; ๊ฐ pipe๋Š” ์ตœ์†Œ ๋‘ ๊ฐœ์˜ 0x1000-๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ ํŽ˜์ด์ง€(PIPE_MIN_DEF_BUFFERS)๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค. buddy allocator๊ฐ€ order-1 ํŽ˜์ด์ง€๋ฅผ ๋ถ„ํ• ํ•  ๋•Œ, ํ•œ ์ชฝ ์ ˆ๋ฐ˜์ด ํ•ด์ œ๋œ SKB ํŽ˜์ด์ง€๋ฅผ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค pipe์™€ ์–ด๋–ค ์˜คํ”„์…‹์ด oob_skb์™€ alias ๋˜๋Š”์ง€ ์ฐพ๊ธฐ ์œ„ํ•ด ํŒŒ์ดํ”„ ํŽ˜์ด์ง€ ์ „์—ญ์— ๊ฐ€์งœ SKB์— ๊ณ ์œ  ๋งˆ์ปค ๋ฐ”์ดํŠธ๋ฅผ ์จ๋„ฃ๊ณ  ๋ฐ˜๋ณต์ ์œผ๋กœ recv(MSG_OOB | MSG_PEEK)์„ ํ˜ธ์ถœํ•ด ๋งˆ์ปค๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  5. Forge a stable SKB layout: alias๋œ pipe ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์งœ struct sk_buff๋กœ ์ฑ„์›Œ ๊ทธ data/head ํฌ์ธํ„ฐ์™€ skb_shared_info ๊ตฌ์กฐ๊ฐ€ ๊ด€์‹ฌ ์žˆ๋Š” ์ž„์˜์˜ ์ปค๋„ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. x86_64์—์„œ copy_to_user() ๋‚ด๋ถ€๋กœ SMAP๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜๋ฏ€๋กœ, ์ปค๋„ ํฌ์ธํ„ฐ๊ฐ€ ์•Œ๋ ค์งˆ ๋•Œ๊นŒ์ง€ ์œ ์ € ๋ชจ๋“œ ์ฃผ์†Œ๋ฅผ ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  6. Respect usercopy hardening: ์ด ๋ณต์‚ฌ๋Š” .data/.bss, vmemmap ํ•ญ๋ชฉ, per-CPU vmalloc ๋ฒ”์œ„, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ์ปค๋„ ์Šคํƒ ๋ฐ ๊ณ ์ฐจ์ˆ˜ folio ๊ฒฝ๊ณ„๋ฅผ ๋„˜์ง€ ์•Š๋Š” direct-map ํŽ˜์ด์ง€์— ๋Œ€ํ•ด์„œ๋Š” ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค. .text๋‚˜ __check_heap_object()๊ฐ€ ๊ฑฐ๋ถ€ํ•˜๋Š” ํŠน์ˆ˜ ์บ์‹œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ์ฝ๊ธฐ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฃฝ์ด์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ -EFAULT๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Introspecting allocators with the read primitive

  • Break KASLR: CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000)์˜ ๊ณ ์ • ๋งคํ•‘์—์„œ ์–ด๋–ค IDT ๋””์Šคํฌ๋ฆฝํ„ฐ๋“  ์ฝ๊ณ  ์•Œ๋ ค์ง„ ํ•ธ๋“ค๋Ÿฌ ์˜คํ”„์…‹์„ ๋นผ๋ฉด ์ปค๋„ ๋ฒ ์ด์Šค๋ฅผ ๋ณต๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • SLUB/buddy state: ๊ธ€๋กœ๋ฒŒ .data ์‹ฌ๋ณผ์€ kmem_cache ๋ฒ ์ด์Šค๋ฅผ ๋“œ๋Ÿฌ๋‚ด๊ณ , vmemmap ํ•ญ๋ชฉ์€ ๊ฐ ํŽ˜์ด์ง€์˜ ํƒ€์ž… ํ”Œ๋ž˜๊ทธ, freelist ํฌ์ธํ„ฐ ๋ฐ ์†Œ์œ  ์บ์‹œ๋ฅผ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค. per-CPU vmalloc ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์Šค์บ”ํ•˜๋ฉด struct kmem_cache_cpu ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐพ์•„ ์ฃผ์š” ์บ์‹œ(skbuff_head_cache, kmalloc-cg-192 ๋“ฑ)์˜ ๋‹ค์Œ ํ• ๋‹น ์ฃผ์†Œ๋ฅผ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Page tables: mm_struct๋ฅผ ์ง์ ‘ ์ฝ๋Š” ๋Œ€์‹ (์œ ์ €์นดํ”ผ๋กœ ์ฐจ๋‹จ๋จ) ๊ธ€๋กœ๋ฒŒ pgd_list(struct ptdesc)๋ฅผ ๋”ฐ๋ผ ํ˜„์žฌ mm_struct๋ฅผ cpu_tlbstate.loaded_mm์œผ๋กœ ๋งค์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํŠธ pgd๋ฅผ ์•Œ๊ฒŒ ๋˜๋ฉด, ์ด ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋กœ ๋ชจ๋“  ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”์„ ์ˆœํšŒํ•˜์—ฌ pipe ๋ฒ„ํผ, ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”, ์ปค๋„ ์Šคํƒ์˜ PFN์„ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Recycling the SKB page as the top kernel-stack page

  1. ์ œ์–ดํ•˜๋˜ pipe ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ํ•ด์ œํ•˜๊ณ  vmemmap์œผ๋กœ ๊ทธ refcount๊ฐ€ 0์œผ๋กœ ๋Œ์•„์˜ค๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. ์ฆ‰์‹œ ๋„ค ๊ฐœ์˜ ํ—ฌํผ ํŒŒ์ดํ”„ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•œ ๋’ค ์—ญ์ˆœ์œผ๋กœ ํ•ด์ œํ•ด buddy allocator์˜ LIFO ๋™์ž‘์„ ๊ฒฐ์ •๋ก ์ ์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  3. clone()์œผ๋กœ ํ—ฌํผ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค; x86_64์—์„œ ์Šคํƒ์€ ๋„ค ํŽ˜์ด์ง€์ด๋ฏ€๋กœ ์ตœ๊ทผ์— ํ•ด์ œ๋œ ๋„ค ํŽ˜์ด์ง€๊ฐ€ ๊ทธ ์Šค๋ ˆ๋“œ์˜ ์Šคํƒ์ด ๋˜๋ฉฐ, ๋งˆ์ง€๋ง‰์œผ๋กœ ํ•ด์ œ๋œ ํŽ˜์ด์ง€(์ด์ „์˜ SKB ํŽ˜์ด์ง€)๋Š” ๋†’์€ ์ฃผ์†Œ์— ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค.
  4. ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ” ์›Œํฌ๋กœ ํ—ฌํผ ์Šค๋ ˆ๋“œ์˜ ์ตœ์ƒ์œ„ ์Šคํƒ PFN์ด ์žฌํ™œ์šฉ๋œ SKB PFN๊ณผ ๊ฐ™์€์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  5. arbitrary read๋กœ ์Šคํƒ ๋ ˆ์ด์•„์›ƒ์„ ๊ด€์ฐฐํ•˜๋ฉด์„œ ์Šค๋ ˆ๋“œ๋ฅผ pipe_write()๋กœ ์œ ๋„ํ•ฉ๋‹ˆ๋‹ค. CONFIG_RANDOMIZE_KSTACK_OFFSET๋Š” syscall๋งˆ๋‹ค RSP์—์„œ ์ •๋ ฌ๋œ 0x0โ€“0x3f0 ๋ฒ”์œ„์˜ ๋žœ๋ค ๊ฐ’์„ ๋นผ๋ฏ€๋กœ, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ poll()/read()์™€ ๊ฒฐํ•ฉํ•œ ๋ฐ˜๋ณต์ ์ธ ์“ฐ๊ธฐ๋กœ ์ž‘์„ฑ์ž๊ฐ€ ์›ํ•˜๋Š” ์˜คํ”„์…‹์—์„œ ๋ธ”๋ก๋  ๋•Œ๋ฅผ ์ฐพ์•„๋ƒ…๋‹ˆ๋‹ค. ์šด์ด ์ข‹์œผ๋ฉด, ํ˜๋Ÿฌ๋‚˜์˜จ copy_page_from_iter()์˜ bytes ์ธ์ž(R14)๊ฐ€ ์žฌํ™œ์šฉ๋œ ํŽ˜์ด์ง€ ๋‚ด ์˜คํ”„์…‹ 0x40์— ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค.

Placing fake SKB metadata on the stack

  • AF_UNIX datagram ์†Œ์ผ“์—์„œ sendmsg()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด: ์ปค๋„์€ ์œ ์ €์˜ sockaddr_un์„ ์ตœ๋Œ€ 108๋ฐ”์ดํŠธ๊นŒ์ง€ ์Šคํƒ ์ƒ์˜ sockaddr_storage๋กœ ๋ณต์‚ฌํ•˜๊ณ , ๋ถ€์ˆ˜ ๋ฐ์ดํ„ฐ(ancillary data)๋ฅผ syscall์ด ํ ๊ณต๊ฐ„์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ๋ธ”๋ก๋˜๊ธฐ ์ „์— ๋˜ ๋‹ค๋ฅธ ์˜จ-์Šคํƒ ๋ฒ„ํผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์— ์ •๋ฐ€ํ•œ ๊ฐ€์งœ SKB ๊ตฌ์กฐ๋ฅผ ์‹ฌ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณต์‚ฌ๊ฐ€ ๋๋‚œ ์‹œ์ ์„ ๊ฐ์ง€ํ•˜๋ ค๋ฉด unmapped ์œ ์ € ํŽ˜์ด์ง€์— ์œ„์น˜ํ•œ 1๋ฐ”์ดํŠธ ์ปจํŠธ๋กค ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ณตํ•˜์„ธ์š”; ____sys_sendmsg()๊ฐ€ ์ด๋ฅผ fault ์‹œํ‚ค๋ฏ€๋กœ ๊ทธ ์ฃผ์†Œ์— ๋Œ€ํ•ด mincore()๋ฅผ ํด๋งํ•˜๋Š” ํ—ฌํผ ์Šค๋ ˆ๋“œ๋Š” ๋ชฉ์ ์ง€ ํŽ˜์ด์ง€๊ฐ€ ์ค€๋น„๋์„ ๋•Œ๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • CONFIG_INIT_STACK_ALL_ZERO๋กœ ์ธํ•œ 0์œผ๋กœ ์ดˆ๊ธฐํ™”๋œ ํŒจ๋”ฉ์€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ ํ•„๋“œ๋ฅผ ์ฑ„์›Œ ์ถ”๊ฐ€ ์“ฐ๊ธฐ ์—†์ด๋„ ์œ ํšจํ•œ SKB ํ—ค๋”๋ฅผ ์™„์„ฑ์‹œ์ผœ ์ค๋‹ˆ๋‹ค.

Timing the +4 GiB increment with a self-looping frag list

  • skb_shinfo(fakeskb)->frag_list๋ฅผ ๋‘ ๋ฒˆ์งธ ๊ฐ€์งœ SKB(๊ณต๊ฒฉ์ž ์ œ์–ด ์œ ์ € ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ)๋กœ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ์œ„์กฐํ•˜๊ณ , ๊ทธ SKB๊ฐ€ len = 0 ๋ฐ next = &self๊ฐ€ ๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. __skb_datagram_iter() ๋‚ด๋ถ€์—์„œ skb_walk_frags()๊ฐ€ ์ด ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•  ๋•Œ, ์ดํ„ฐ๋ ˆ์ดํ„ฐ๊ฐ€ NULL์— ๋„๋‹ฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์‹คํ–‰์€ ์˜์›ํžˆ ํšŒ์ „ํ•˜๊ณ  ๋ณต์‚ฌ ๋ฃจํ”„๋Š” ์ง„ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๋‘ ๋ฒˆ์งธ ๊ฐ€์งœ SKB๊ฐ€ self-loopํ•˜๋Š” ํ•œ recv syscall์„ ์ปค๋„ ์•ˆ์—์„œ ์ง€์†์‹œํ‚ต๋‹ˆ๋‹ค. ์ฆ๊ฐ€๋ฅผ ๋ฐœ์‚ฌํ•  ์‹œ์ ์—๋Š” ๋‘ ๋ฒˆ์งธ SKB์˜ next ํฌ์ธํ„ฐ๋ฅผ ์œ ์ € ๊ณต๊ฐ„์—์„œ NULL๋กœ ๋ฐ”๊พธ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋ฃจํ”„๊ฐ€ ์ข…๋ฃŒ๋˜๊ณ  unix_stream_recv_urg()๋Š” ์ฆ‰์‹œ UNIXCB(oob_skb).consumed += 1์„ ์‹คํ–‰ํ•˜์—ฌ ์žฌํ™œ์šฉ๋œ ์Šคํƒ ํŽ˜์ด์ง€์˜ ์˜คํ”„์…‹ 0x40์— ํ˜„์žฌ ๋ฐฐ์น˜๋œ ๊ฐ์ฒด์— ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.

Stalling copy_from_iter() without userfaultfd

  • ๊ฑฐ๋Œ€ํ•œ anonymous RW VMA๋ฅผ ๋งคํ•‘ํ•˜๊ณ  ์ „๋ถ€ fault ์‹œํ‚ต๋‹ˆ๋‹ค.
  • madvise(MADV_DONTNEED, hole, PAGE_SIZE)๋กœ ๋‹จ์ผ ํŽ˜์ด์ง€ ํ™€์„ ๋งŒ๋“ค๊ณ  ๊ทธ ์ฃผ์†Œ๋ฅผ write(pipefd, user_buf, 0x3000)์— ์‚ฌ์šฉ๋˜๋Š” iov_iter์— ๋„ฃ์Šต๋‹ˆ๋‹ค.
  • ๋ณ‘๋ ฌ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ VMA ์ „์ฒด์— ๋Œ€ํ•ด mprotect()๋ฅผ ํ˜ธ์ถœํ•˜์„ธ์š”. ํ•ด๋‹น syscall์€ mmap ์“ฐ๊ธฐ ๋ฝ์„ ์žก๊ณ  ๋ชจ๋“  PTE๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ดํ”„ ์ž‘์„ฑ์ž๊ฐ€ ํ™€์— ๋„๋‹ฌํ•˜๋ฉด ํŽ˜์ด์ง€ ํดํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” mprotect()๊ฐ€ ์žก๊ณ  ์žˆ๋Š” mmap ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋ฉฐ ์ฐจ๋‹จ๋˜๋ฏ€๋กœ copy_from_iter()๋Š” ๊ฒฐ์ •๋ก ์ ์ธ ์‹œ์ ์—์„œ ๋ฉˆ์ถ”๊ณ  ํ˜๋Ÿฌ๋‚˜์˜จ bytes ๊ฐ’์€ ์žฌํ™œ์šฉ๋œ SKB ํŽ˜์ด์ง€๊ฐ€ ํ˜ธ์ŠคํŒ…ํ•˜๋Š” ์Šคํƒ ์„ธ๊ทธ๋จผํŠธ์— ๋‚จ์Šต๋‹ˆ๋‹ค.

Turning the increment into arbitrary PTE writes

  1. Fire the increment: frag ๋ฃจํ”„๋ฅผ ํ•ด์ œํ•˜์—ฌ copy_from_iter()๊ฐ€ ๋ฉˆ์ถ˜ ๋™์•ˆ +4 GiB ์ฆ๊ฐ€๊ฐ€ bytes ๋ณ€์ˆ˜์— ์ ์šฉ๋˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  2. Overflow the copy: ๊ฒฐํ•จ์ด ์žฌ๊ฐœ๋˜๋ฉด copy_page_from_iter()๋Š” ํ˜„์žฌ ํŒŒ์ดํ”„ ํŽ˜์ด์ง€๋กœ >4 GiB๋ฅผ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค. ํ•ฉ๋ฒ•์ ์ธ 0x2000 ๋ฐ”์ดํŠธ(๋‘ ํŒŒ์ดํ”„ ๋ฒ„ํผ)๋ฅผ ์ฑ„์šด ๋’ค ์ถ”๊ฐ€ ๋ฐ˜๋ณต์„ ์‹คํ–‰ํ•ด ๋‚จ์€ ์œ ์ € ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ดํ”„ ๋ฒ„ํผ PFN ๋‹ค์Œ์— ์˜ค๋Š” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€์— ์”๋‹ˆ๋‹ค.
  3. Arrange adjacency: ํ• ๋‹น์ž ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ๋ฅผ ์ด์šฉํ•ด buddy allocator๊ฐ€ ํ”„๋กœ์„ธ์Šค ์†Œ์œ ์˜ PTE ํŽ˜์ด์ง€๋ฅผ ๋Œ€์ƒ ํŒŒ์ดํ”„ ๋ฒ„ํผ ํŽ˜์ด์ง€ ๋ฐ”๋กœ ๋‹ค์Œ์— ๋†“๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ํŒŒ์ดํ”„ ํŽ˜์ด์ง€๋ฅผ ๋ฒˆ๊ฐˆ์•„ ํ• ๋‹นํ•˜๊ณ  ์ƒˆ ๊ฐ€์ƒ ๋ฒ”์œ„๋ฅผ ํ„ฐ์น˜ํ•ด ํŽ˜์ด์ง€-ํ…Œ์ด๋ธ” ํ• ๋‹น์„ ํŠธ๋ฆฌ๊ฑฐํ•ด PFN๋“ค์ด ๊ฐ™์€ 2 MiB ํŽ˜์ด์ง€๋ธ”๋ก์— ์ •๋ ฌ๋˜๋„๋ก ํ•จ).
  4. Overwrite page tables: OOB copy_from_iter()๊ฐ€ ์ด์›ƒ ํŽ˜์ด์ง€๋ฅผ ๊ณต๊ฒฉ์ž๊ฐ€ ์„ ํƒํ•œ ์—”ํŠธ๋ฆฌ๋กœ ์ฑ„์šฐ๊ฒŒ ํ•˜๋ ค๋ฉด ์ถ”๊ฐ€ 0x1000 ๋ฐ”์ดํŠธ์˜ ์œ ์ € ๋ฐ์ดํ„ฐ์— ์›ํ•˜๋Š” PTE ์—”ํŠธ๋ฆฌ๋“ค์„ ์ธ์ฝ”๋”ฉํ•˜์„ธ์š”. ์ด๋ฅผ ํ†ตํ•ด ์ปค๋„ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ๋Œ€ํ•œ RW/RWX ์œ ์ € ๋งคํ•‘์„ ๋ถ€์—ฌํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ์—”ํŠธ๋ฆฌ๋ฅผ ๋ฎ์–ด์จ SMEP/SMAP์„ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Mitigations / hardening ideas

  • Kernel: 32ca245464e1479bfea8592b9db227fdc1641705 ์ ์šฉ( SKB๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์žฌ๊ฒ€์ฆ) ๋ฐ CONFIG_AF_UNIX_OOB(5155cbcdbf03)๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด AF_UNIX OOB๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜์„ธ์š”. manage_oob()๋ฅผ ์ถ”๊ฐ€์ ์ธ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์‚ฌ(์˜ˆ: unix_skb_len() > 0๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๋ฃจํ”„)๋กœ ๊ฐ•ํ™”ํ•˜๊ณ  ์œ ์‚ฌํ•œ ๊ฐ€์ •์ด ์กด์žฌํ•˜๋Š” ๋‹ค๋ฅธ ์†Œ์ผ“ ํ”„๋กœํ† ์ฝœ์„ ๊ฐ์‚ฌํ•˜์„ธ์š”.
  • Sandboxing: seccomp ํ”„๋กœํ•„์ด๋‚˜ ์ƒ์œ„ ๋ธŒ๋กœ์ปค API์—์„œ MSG_OOB/MSG_PEEK ํ”Œ๋ž˜๊ทธ๋ฅผ ํ•„ํ„ฐ๋งํ•˜์„ธ์š”(Chrome ๋ณ€๊ฒฝ 6711812๋Š” ๋ Œ๋”๋Ÿฌ ์ธก MSG_OOB๋ฅผ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค).
  • Allocator defenses: SLUB freelist ๋ฌด์ž‘์œ„ํ™” ๊ฐ•ํ™”๋‚˜ ์บ์‹œ๋ณ„ ํŽ˜์ด์ง€ ์ปฌ๋Ÿฌ๋ง ๊ฐ•์ œ๋Š” ๊ฒฐ์ •๋ก ์  ํŽ˜์ด์ง€ ์žฌํ™œ์šฉ์„ ๋ณต์žกํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค; ํŒŒ์ดํ”„ ๋ฒ„ํผ ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋Š” ๊ฒƒ๋„ ์žฌํ• ๋‹น ์‹ ๋ขฐ๋„๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค.
  • Monitoring: ๋†’์€ ๋น„์œจ์˜ ํŽ˜์ด์ง€-ํ…Œ์ด๋ธ” ํ• ๋‹น์ด๋‚˜ ๋น„์ •์ƒ์ ์ธ ํŒŒ์ดํ”„ ์‚ฌ์šฉ์„ ํ…”๋ ˆ๋ฉ”ํŠธ๋ฆฌ๋กœ ๋…ธ์ถœํ•˜์„ธ์š”โ€”์ด ์ต์Šคํ”Œ๋กœ์ž‡์€ ๋Œ€๋Ÿ‰์˜ ํŽ˜์ด์ง€ ํ…Œ์ด๋ธ”๊ณผ ํŒŒ์ดํ”„ ๋ฒ„ํผ๋ฅผ ์†Œ๋ชจํ•ฉ๋‹ˆ๋‹ค.

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ