microsoft / microsoft/AdaptiveCards

Security: Unbounded ShowCard nesting depth allows stack overflow crash via malicious card payload

Open
#9,372 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
2k
Forks
595
Avg merge
1d 19h
Merged PRs (30d)
1

Description

Bug Description
ShowCardActionParser::Deserialize in the shared C++ object model calls AdaptiveCard::Deserialize recursively with no depth limit. A card payload with deeply nested Action.ShowCard actions creates an arbitrarily deep parse tree that overflows the stack when rendered.

There is zero depth tracking anywhere in ParseContext, ShowCardAction, or the renderer code paths. The only incidental barrier is jsoncpp's stackLimit (1000 JSON nesting levels), which still allows ~325 levels of ShowCard nesting to parse successfully.

Steps to Reproduce

  1. Generate a card with nested ShowCards (e.g. 200 levels):
{
  "type": "AdaptiveCard",
  "version": "1.5",
  "body": [{"type": "TextBlock", "text": "Level 200"}],
  "actions": [{
    "type": "Action.ShowCard",
    "title": "More",
    "card": {
      "type": "AdaptiveCard",
      "version": "1.5",
      "body": [{"type": "TextBlock", "text": "Level 199"}],
      "actions": [{
        "type": "Action.ShowCard",
        "title": "More",
        "card": { "...repeat 200 times..." }
      }]
    }
  }]
}

Generator script (Python):

import json

def nested_showcard(depth):
    if depth == 0:
        return {"type": "AdaptiveCard", "version": "1.5", "body": [{"type": "TextBlock", "text": "bottom"}]}
    return {
        "type": "AdaptiveCard", "version": "1.5",
        "body": [{"type": "TextBlock", "text": f"Level {depth}"}],
        "actions": [{"type": "Action.ShowCard", "title": "More", "card": nested_showcard(depth - 1)}]
    }

with open("nested_showcard_200.json", "w") as f:
    json.dump(nested_showcard(200), f)
  1. Parse with AdaptiveCard::DeserializeFromString succeeds with no error, creates 200 nested AdaptiveCard objects
  2. Render the parsed card stack overflow crash

Verified Behavior
Tested by building a standalone C++ binary against the ObjectModel library:

+----------------+-----------+--------------+--------------------------+
| ShowCard Depth | JSON Size | Parse Result | Parsed Tree Depth |
+----------------+-----------+--------------+--------------------------+
| 5 | 825 bytes | OK | 5 (full tree) |
| 50 | 7.5 KB | OK | 50 (full tree) |
| 200 | 30 KB | OK | 200 (full tree) |
| 325 | 49 KB | OK | 325 (full tree) |
| 350 | 53 KB | Fails | jsoncpp stackLimit hit |
+----------------+-----------+--------------+--------------------------+

Impact

  • All renderers using the shared C++ model are affected (UWP, WinUI3, Android, iOS)
  • In Windows Widgets: A 3rd-party widget can send this payload → WidgetBoard.exe crashes with STATUS_STACK_OVERFLOW (0xC00000FD)
  • The crash is 100% reproducible on any device at any DPI —> unlike #9343 which requires 150% display scale
  • The payload is small (~30-50 KB) and trivial to generate

Expected Behavior
The parser should enforce a maximum ShowCard nesting depth (e.g. 5 levels) and emit a parse warning when exceeded. No real-world Adaptive Card nests ShowCards deeper than 2-3 levels.

Environment

  • Component: Shared C++ ObjectModel (ObjectModel)
  • Affects: All platforms using the shared model (UWP, WinUI3, Android, iOS)
  • AdaptiveCards version: Current main branch (verified May 2026)

Related
#9343 —> Different root cause (Carousel + long text + 150% DPI → XAML measurement crash), but same general category: card payload crashing the renderer with no defensive limits

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at ShowCardActionParser::Deserialize and trace its recursive AdaptiveCard::Deserialize calls through ParseContext and the shared renderer paths. Reproduce the issue with the provided nested_showcard_200.json generator or standalone binary, then verify that excessive ShowCard nesting produces a parse warning and no longer reaches a stack overflow.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.