Share
## https://sploitus.com/exploit?id=2A6F1FD3-4D06-50EA-A607-1FBB54C574D2
# PoC โ€” `stb_image` v2.30 GIF `stbi__out_gif_code` ๋ฌดํ•œ ์žฌ๊ท€ (DoS)

๋Œ€์ƒ: [`nothings/stb`](https://github.com/nothings/stb) โ€” `stb_image.h` v2.30
์ทจ์•ฝ ์œ„์น˜: `stb_image.h` ๋ผ์ธ 6661 โ€” `stbi__out_gif_code()`
์นดํ…Œ๊ณ ๋ฆฌ: **CWE-674** (Uncontrolled Recursion) โ€” Denial of Service via stack exhaustion
์˜ํ–ฅ: ์ž‘์€ stack ํ™˜๊ฒฝ(Windows main thread 1 MB ๊ธฐ๋ณธ, pthread ์ถ•์†Œ stack, ์ž„๋ฒ ๋””๋“œ)์—์„œ SIGSEGV/access violation crash. ํฐ stack ํ™˜๊ฒฝ(Linux 8 MB)์—์„œ๋„ stack ์••๋ฐ• ๋ฐœ์ƒ.

## ๊ฒฐํ•จ ์š”์•ฝ

```c
static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
{
   ...
   if (g->codes[code].prefix >= 0)
      stbi__out_gif_code(g, g->codes[code].prefix);   // โ† ๋ฌดํ•œ ์žฌ๊ท€, ๊นŠ์ด ์ œํ•œ ์—†์Œ
   ...
}
```

LZW prefix chain์€ `avail โ‰ค 8192`๋กœ๋งŒ ์ œํ•œ๋จ โ†’ ๊ณต๊ฒฉ์ž๊ฐ€ ํ•˜๋‚˜์˜ ์„ ํ˜• chain์„ ์•ฝ **8190** entries๊นŒ์ง€ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ โ†’ `stbi__out_gif_code`๊ฐ€ ์•ฝ **8190 ๊นŠ์ด** ์žฌ๊ท€.

## ํ™˜๊ฒฝ๋ณ„ ์˜ํ–ฅ

| ํ™˜๊ฒฝ | ๊ธฐ๋ณธ stack | ๊ฒฐํ•จ ์˜ํ–ฅ |
|---|---|---|
| Linux x86_64 main thread | 8 MB | ์••๋ฐ•, ๋ณดํ†ต crash ์•ˆ ํ•จ |
| **Windows main thread** | **1 MB** | **stack overflow โ†’ access violation** |
| **pthread default** (`pthread_attr_init`) | **2 MB** | ๋‹ค๋ฅธ stack ์‚ฌ์šฉ๋Ÿ‰๊ณผ ํ•ฉ์‚ฐ ์‹œ crash ๊ฐ€๋Šฅ์„ฑ |
| **embedded RTOS / IoT** | 4-64 KB | **์ฆ‰์‹œ stack overflow** |
| Linux `ulimit -s 256` (256 KB) | 256 KB | ๊ฒฐ์ •๋ก ์  SIGSEGV (PoC๊ฐ€ ์ด๊ฑธ๋กœ ๊ฒ€์ฆ) |

## Trigger ๋ฉ”์ปค๋‹ˆ์ฆ˜

LZW chain ๊ตฌ์„ฑ:

1. `lzw_min_code_size = 2` โ†’ `clear=4`, `end=5`, `avail` ์‹œ์ž‘ 6.
2. literal `0` ์†ก์ถœ โ†’ `oldcode = 0`, `g->codes[0..3]`๋งŒ init (`prefix = -1`).
3. ๊ฐ iter์—์„œ ํ˜„์žฌ `avail` ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์†ก์ถœ โ†’ stb_image๊ฐ€ `codes[avail].prefix = oldcode`๋กœ set, ๊ทธ ํ›„ ์žฌ๊ท€ ํ˜ธ์ถœ. `oldcode`๋Š” ์ƒˆ code๋กœ ๊ฐฑ์‹ .
4. 8000+ iter ๋ฐ˜๋ณต โ†’ chain ๊ธธ์ด = iter ์ˆ˜ + 1.
5. end-of-stream `5` ์†ก์ถœ.

๊ฐ ์ƒˆ entry์˜ prefix๋Š” ์ง์ „ entry โ†’ chain ์„ ํ˜•. `out_gif_code(latest)` ํ˜ธ์ถœ ์‹œ latest โ†’ latest-1 โ†’ latest-2 โ†’ ... โ†’ 0 (prefix=-1) ๊นŒ์ง€ ์žฌ๊ท€.

## ํŒŒ์ผ ๊ตฌ์„ฑ

| ํŒŒ์ผ | ์„ค๋ช… |
|---|---|
| `generate_poc_gif.py` | 1ร—1 GIF ์ƒ์„ฑ๊ธฐ (`--depth N`๋กœ chain ๊ธธ์ด ์กฐ์ ˆ, ๊ธฐ๋ณธ ~8184) |
| `poc.c` | `stbi_load(...)` ํ˜ธ์ถœ์ž + RLIMIT_STACK ์ถœ๋ ฅ |
| `build_and_run.sh` | upstream fetch + ๋นŒ๋“œ + ๋‘ ๋ฒˆ ์‹คํ–‰ (๊ธฐ๋ณธ stack / `ulimit -s 256`) |
| `README.md` | ์ด ๋ฌธ์„œ |

## ์žฌํ˜„ (Linux)

```bash
chmod +x build_and_run.sh
./build_and_run.sh
```

์Šคํฌ๋ฆฝํŠธ๋Š” ๋‘ ๋ฒˆ ์‹คํ–‰:
1. **๊ธฐ๋ณธ stack**: ๋ณดํ†ต ์ •์ƒ ์ข…๋ฃŒ (chain ๋ชจ๋‘ ์ฒ˜๋ฆฌ๋˜๊ณ  GIF๋Š” 1ร—1๋กœ ๋””์ฝ”๋“œ โ€” stb์˜ raster boundary ๊ฒ€์‚ฌ๊ฐ€ ๋‹ค catch).
2. **`ulimit -s 256`**: SIGSEGV (exit 139). ๊ฒฐํ•จ ์ฆ๋ช….

## ์žฌํ˜„ (Windows)

```powershell
# wget์€ PowerShell ๋‚ด์žฅ alias
Invoke-WebRequest https://raw.githubusercontent.com/nothings/stb/master/stb_image.h -OutFile stb_image.h
python generate_poc_gif.py poc.gif
gcc -O0 -g poc.c -o poc.exe   # MinGW
.\poc.exe poc.gif
# -> EXCEPTION_STACK_OVERFLOW (Windows ๊ธฐ๋ณธ main thread stack 1 MB)
```

`/STACK:524288` ๊ฐ™์€ ์ž‘์€ stack์œผ๋กœ linkํ•˜๋ฉด ๋” ๊ฒฐ์ •๋ก ์ ์œผ๋กœ crash.

## ์˜ํ–ฅ ๋ถ„์„

- **Severity**: Medium-High.
- **๊ณต๊ฒฉ ๋ฒกํ„ฐ**: ์‹ ๋ขฐ๋˜์ง€ ์•Š์€ GIF๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ๋“  ์‚ฌ์šฉ์ฒ˜.
  - ๊ฒŒ์ž„ ๋กœ๋”ฉ ์‹œ ์‚ฌ์šฉ์ž mod์˜ GIF asset
  - chat/๋ฉ”์‹ ์ € thumbnail ์ƒ์„ฑ service
  - browser/๋ทฐ์–ด pre-render
  - ์ž„๋ฒ ๋””๋“œ IoT (e.g. e-paper display, ์นด๋ฉ”๋ผ)
- **Reliability**: ๋งค์šฐ ๋†’์Œ. ๊ตฌ์กฐ์  ๊ฒฐํ•จ (์žฌ๊ท€ ๊นŠ์ด ์ œํ•œ ์—†์Œ). ํ™•๋ฅ ์„ฑ ์—†์Œ.
- **Chain ๊ฐ€๋Šฅ์„ฑ**: stack overflow๊ฐ€ ์ธ์ ‘ ๋ฉ”๋ชจ๋ฆฌ(๋‹ค๋ฅธ thread stack, heap guard ๋“ฑ)์— ๋‹ฟ์œผ๋ฉด ์ถ”๊ฐ€ ์˜ํ–ฅ. ASLR/canary ์šฐํšŒ์™€ ๊ฒฐํ•ฉ ์‹œ RCE ๊ฐ€๋Šฅ์„ฑ๋„ ์ด๋ก ์ ์œผ๋กœ ์กด์žฌ (๋‹จ, ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” DoS).

## ํŒจ์น˜ ์ œ์•ˆ

**์˜ต์…˜ A** โ€” ์žฌ๊ท€๋ฅผ iterative๋กœ ๋ณ€ํ™˜ (๊ฐ€์žฅ ๊ฒฌ๊ณ ):

```c
static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
{
   /* The prefix chain is at most 8192 entries; resolve it into a
    * fixed-size buffer iteratively to avoid stack recursion. */
   stbi__uint16 stack[8192];
   int sp = 0;
   while (g->codes[code].prefix >= 0 && sp codes[code].prefix;
   }
   stack[sp++] = code;
   while (sp > 0) {
      stbi__uint16 cur = stack[--sp];
      /* original per-code rendering body, factored out: */
      stbi__render_gif_pixel(g, cur);
   }
}
```

**์˜ต์…˜ B** โ€” ๊นŠ์ด ์ œํ•œ:

```c
static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code, int depth)
{
   if (depth > 4096) return;   /* defensive cap */
   if (g->codes[code].prefix >= 0)
      stbi__out_gif_code(g, g->codes[code].prefix, depth + 1);
   ...
}
```

์˜ต์…˜ A๊ฐ€ ์ •์„. stb์˜ minimal-dependency ์ฒ ํ•™๊ณผ๋„ ๋งž์Œ (8192 short = 16 KB heap ๋˜๋Š” ํฐ stack 1ํšŒ).

## Disclosure

๋‹ค๋ฅธ ๋‘ ๊ฒฐํ•จ (`convert_format16` heap overflow, BMP palette uninit-read)๊ณผ ํ•จ๊ป˜ nothings/stb GitHub Security Advisory์— ๋ฌถ์–ด ๋ณด๊ณ  ๊ถŒ์žฅ.

## ๋ผ์ด์„ ์Šค

stb ๋ผ์ด์„ ์Šค(Public Domain / MIT-0)์™€ ํ˜ธํ™˜. ๋ณด์•ˆ ์—ฐ๊ตฌ ๋ชฉ์  ์ „์šฉ.