CVE-2026-47703: AdGuard Home Remote Code Execution Vulnerability

MODERATE
Advisory Identifier: GHSA-xgx4-4h9w-53pv
CWE-662 CWE-362 CWE-330
Published
Advisory Summary

CVE-2026-47703 is a MODERATE remote code execution vulnerability (CWE-662: Improper Synchronization) affecting AdGuard Home libraries in the Go ecosystem. The issue involves doq-to-udp state reduction and source-port oracle. Remediation is available by upgrading affected packages to version 0.107.75, 0.81.3.

CVE-2026-47703 at a Glance

CVE Identifier CVE-2026-47703
GHSA Identifier GHSA-xgx4-4h9w-53pv
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-662: Improper Synchronization
Affected Product AdGuard Home
Package Ecosystem Go
Published Date
Last Updated

How to Fix CVE-2026-47703

To remediate this vulnerability, upgrade all affected AdGuard Home dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/AdguardTeam/AdGuardHome fixed: 0.107.75
Upgrade github.com/AdguardTeam/AdGuardHome to 0.107.75
github.com/AdguardTeam/dnsproxy fixed: 0.81.3
Upgrade github.com/AdguardTeam/dnsproxy to 0.81.3
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/AdguardTeam/AdGuardHome

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-47703, inspect your project dependencies for AdGuard Home:

inspect dependencies for github.com/AdguardTeam/AdGuardHome

Analyze the terminal output to locate any version of AdGuard Home in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.107.75), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-662: Improper Synchronization, where the product utilizes multiple threads, processes, components, or systems to allow temporary access to a shared resource that can only be exclusive to one process at a time, but it does not properly synchronize these actions, which might cause simultaneous accesses of this resource by multiple threads or processes. In AdGuard Home, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary This report covers the client-triggered DoQ forwarding path in: - `dnsproxy` `v0.81.2` (`adguard/dnsproxy:v0.81.2`) - `AdGuard Home` `v0.107.74` (`adguard/adguardhome:latest`, image version label `v0.107.74`) The issue was reproduced on `2026-04-25` with the products configured through their documented DoQ listener and plain UDP upstream surfaces. The scope is the internal backend UDP hop created when a DoQ query is forwarded to a `udp://` upstream. On that path, the backend DNS `ID` is not preserved as an independent source of entropy. For both products, the backend observer saw `dns_id=0` for every sampled client-triggered query on the tested path. Repeated reruns then showed the same `txid=0` behavior and the same positive source-port oracle on every sampled run. A separate quoted-port ICMP oracle distinguished the correct backend UDP source port from a wrong one with a stable, client-visible behavior change. Attached evidence: - `dnsproxy` oracle path on `v0.81.2`: [attachments/artifacts/g03/20260425T141500Z-g03-v0812/summary.txt](attachments/artifacts/g03/20260425T141500Z-g03-v0812/summary.txt) - `dnsproxy` `v0.81.2` repeatability: [attachments/artifacts/g03/repeatability-v0812.md](attachments/artifacts/g03/repeatability-v0812.md) - `dnsproxy` steering follow-up on `v0.81.2`: [attachments/artifacts/g04/20260425T141900Z-g04-v0812/summary.txt](attachments/artifacts/g04/20260425T141900Z-g04-v0812/summary.txt) - `AdGuard Home` oracle path: [attachments/artifacts/g05/20260425T113000Z-g05/summary.txt](attachments/artifacts/g05/20260425T113000Z-g05/summary.txt) ## Root Cause Analysis The observable behavior is consistent across both products: 1. A DoQ client query is accepted on the frontend listener. 2. The query is forwarded over a backend UDP leg. 3. On that backend leg, the forwarded DNS `ID` collapses to `0` on the client-triggered path instead of remaining a fresh per-query variable. 4. The backend UDP source port is still allocated per query. 5. When an ICMP error quotes the actual backend source port, the forwarding path flips behavior in a way that does not occur for a wrong quoted port. That combination removes `txid` from the backend tuple on the tested path and leaves the UDP source port as the main remaining variable. In practical terms, the backend hop stops behaving like a fresh `(txid, source-port)` pair per forwarded query and instead becomes a one-variable state exposure. For `dnsproxy`, the correct quoted port does more than produce a failure signal: it can push resolution away from the primary UDP upstream and into the fallback upstream. For `AdGuard Home`, the same condition produces a fast `SERVFAIL`. ## Reproduce Prerequisites: - Docker and Docker Compose - OpenSSL - build the lab helper image used by the attached harness and observer The attached reproducer bundle contains only the files needed for this report: - scripts: `attachments/scripts/` - helper image build files: `attachments/docker/unbound-doq-attacker/` - compose files: `attachments/docker-compose.g03.yml`, `attachments/docker-compose.g04.yml`, `attachments/docker-compose.g05.yml` - shipped evidence: `attachments/artifacts/...` Build the helper image first: 1. `cd attachments` 2. `docker build -t unbound-doq-attacker:latest -f docker/unbound-doq-attacker/Dockerfile docker/unbound-doq-attacker` To rerun `dnsproxy`: 1. `cd attachments` 2. `bash scripts/repro-g03-dnsproxy-oracle.sh` 3. Inspect `artifacts/g03/<RUN_ID>/summary.txt` 4. Inspect `artifacts/g03/<RUN_ID>/entropy-backend.jsonl`, `txid_correct-backend.jsonl`, and `port_correct-backend.jsonl` To rerun the `dnsproxy` fallback-steering case: 1. `cd attachments` 2. `bash scripts/repro-g04-dnsproxy-steering.sh` 3. Inspect `artifacts/g04/<RUN_ID>/summary.txt` 4. Inspect `steering_correct-main.jsonl` and `steering_correct-fallback.jsonl` To rerun `AdGuard Home`: 1. `cd attachments` 2. `bash scripts/repro-g05-adguardhome-oracle.sh` 3. Inspect `artifacts/g05/<RUN_ID>/summary.txt` 4. Inspect `entropy-backend.jsonl`, `txid_correct-backend.jsonl`, and `port_correct-backend.jsonl` The attached evidence includes fresh `dnsproxy v0.81.2` reruns, one official- profile `AdGuard Home` run, and the minimal reproducer bundle used by both. ## Impact For both products, the tested DoQ-to-UDP path is no longer a full `(txid, source-port)` search surface: - `dnsproxy`: four of four sampled runs showed `txid=0` on the backend hop and a positive source-port oracle on `v0.81.2`. The remaining unknown is `port_only`. Median wrong/correct port latency was `327.99 ms / 40.93 ms`. - `AdGuard Home`: four of four sampled runs showed `txid=0` on the backend hop and a positive source-port oracle. The aggregate again classifies the remaining unknown as `port_only`. Median wrong/correct port latency was `319.14 ms / 37.02 ms`. Product-specific effects: - `dnsproxy`: a correct port guess produced an empty client-visible answer on the base oracle path, and in the fallback profile it steered all eight tested queries away from the main upstream and into the fallback upstream. - `AdGuard Home`: a correct port guess produced fast `SERVFAIL` and an extra backend query. This is the security-relevant point. On the tested official profiles, the backend hop no longer forces an off-path attacker to deal with two fresh random fields per forwarded DNS race. The DNS ID is already known: it is deterministically `0` on the client-triggered DoQ-to-UDP path. The only remaining backend tuple variable is the UDP source port, and the attached evidence shows a repeatable oracle for that remaining variable. That places the path in the same threat-model class as oracle-assisted DNS forgery work such as SAD DNS and TUdoor: the attack first uses an oracle to learn or validate the tuple state that protects an off-path response race, and only then attempts the forged response. This report stops short of a forgery demo, but the evidence already shows the crucial precondition on the tested backend hop: the tuple is not high-entropy anymore. It has been reduced from `(txid, source-port)` to `source-port` only. --- **Attachments** [attachments.zip](https://github.com/user-attachments/files/27227054/attachments.zip)

CVE-2026-47703 Affected AdGuard Home Versions

2 component(s)
Fixed Versions:
0.107.750.81.3
Product / Package Ecosystem Affected Versions Fixed Version
github.com/AdguardTeam/AdGuardHome
Go
< 0.107.75
0.107.75
github.com/AdguardTeam/dnsproxy
Go
< 0.81.3
0.81.3

References

Frequently Asked Questions

What is CVE-2026-47703?
CVE-2026-47703 is a security vulnerability in AdGuard Home involving DoQ-to-UDP State Reduction and Source-Port Oracle under CWE-662: Improper Synchronization.
Which AdGuard Home versions are affected?
Affected versions include releases prior to 0.107.75, 0.81.3 across affected packages: github.com/AdguardTeam/AdGuardHome, github.com/AdguardTeam/dnsproxy.
What version fixes CVE-2026-47703?
Upgrading to 0.107.75, 0.81.3 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/AdguardTeam/AdGuardHome affected?
Yes, github.com/AdguardTeam/AdGuardHome is affected in releases prior to 0.107.75. Upgrade your dependency to 0.107.75 or later.
Is github.com/AdguardTeam/dnsproxy affected?
Yes, github.com/AdguardTeam/dnsproxy is affected in releases prior to 0.81.3. Upgrade your dependency to 0.81.3 or later.
What CWE is associated with CVE-2026-47703?
CVE-2026-47703 is classified under CWE-662: Improper Synchronization (The product utilizes multiple threads, processes, components, or systems to allow temporary access to a shared resource that can only be exclusive to one process at a time, but it does not properly synchronize these actions, which might cause simultaneous accesses of this resource by multiple threads or processes.).
How can I check Go dependencies for CVE-2026-47703?
Run the dependency tree command: 'inspect dependencies for github.com/AdguardTeam/AdGuardHome'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-xgx4-4h9w-53pv",
  "modified" : "2026-07-20T19:05:45Z",
  "published" : "2026-06-04T19:50:23Z",
  "aliases" : [ "CVE-2026-47703" ],
  "summary" : "AdGuard Home: DoQ-to-UDP State Reduction and Source-Port Oracle",
  "details" : "## Summary\n\nThis report covers the client-triggered DoQ forwarding path in:\n\n- `dnsproxy` `v0.81.2` (`adguard/dnsproxy:v0.81.2`)\n- `AdGuard Home` `v0.107.74` (`adguard/adguardhome:latest`, image version label `v0.107.74`)\n\nThe issue was reproduced on `2026-04-25` with the products configured through\ntheir documented DoQ listener and plain UDP upstream surfaces. The scope is the\ninternal backend UDP hop created when a DoQ query is forwarded to a `udp://`\nupstream.\n\nOn that path, the backend DNS `ID` is not preserved as an independent source of\nentropy. For both products, the backend observer saw `dns_id=0` for every\nsampled client-triggered query on the tested path. Repeated reruns then showed\nthe same `txid=0` behavior and the same positive source-port oracle on every\nsampled run. A separate quoted-port ICMP oracle distinguished the correct\nbackend UDP source port from a wrong one with a stable, client-visible behavior\nchange.\n\nAttached evidence:\n\n- `dnsproxy` oracle path on `v0.81.2`: [attachments/artifacts/g03/20260425T141500Z-g03-v0812/summary.txt](attachments/artifacts/g03/20260425T141500Z-g03-v0812/summary.txt)\n- `dnsproxy` `v0.81.2` repeatability: [attachments/artifacts/g03/repeatability-v0812.md](attachments/artifacts/g03/repeatability-v0812.md)\n- `dnsproxy` steering follow-up on `v0.81.2`: [attachments/artifacts/g04/20260425T141900Z-g04-v0812/summary.txt](attachments/artifacts/g04/20260425T141900Z-g04-v0812/summary.txt)\n- `AdGuard Home` oracle path: [attachments/artifacts/g05/20260425T113000Z-g05/summary.txt](attachments/artifacts/g05/20260425T113000Z-g05/summary.txt)\n\n## Root Cause Analysis\n\nThe observable behavior is consistent across both products:\n\n1. A DoQ client query is accepted on the frontend listener.\n2. The query is forwarded over a backend UDP leg.\n3. On that backend leg, the forwarded DNS `ID` collapses to `0` on the\n   client-triggered path instead of remaining a fresh per-query variable.\n4. The backend UDP source port is still allocated per query.\n5. When an ICMP error quotes the actual backend source port, the forwarding path\n   flips behavior in a way that does not occur for a wrong quoted port.\n\nThat combination removes `txid` from the backend tuple on the tested path and\nleaves the UDP source port as the main remaining variable. In practical terms,\nthe backend hop stops behaving like a fresh `(txid, source-port)` pair per\nforwarded query and instead becomes a one-variable state exposure.\n\nFor `dnsproxy`, the correct quoted port does more than produce a failure signal:\nit can push resolution away from the primary UDP upstream and into the fallback\nupstream. For `AdGuard Home`, the same condition produces a fast `SERVFAIL`.\n\n## Reproduce\n\nPrerequisites:\n\n- Docker and Docker Compose\n- OpenSSL\n- build the lab helper image used by the attached harness and observer\n\nThe attached reproducer bundle contains only the files needed for this report:\n\n- scripts: `attachments/scripts/`\n- helper image build files: `attachments/docker/unbound-doq-attacker/`\n- compose files: `attachments/docker-compose.g03.yml`,\n  `attachments/docker-compose.g04.yml`, `attachments/docker-compose.g05.yml`\n- shipped evidence: `attachments/artifacts/...`\n\nBuild the helper image first:\n\n1. `cd attachments`\n2. `docker build -t unbound-doq-attacker:latest -f docker/unbound-doq-attacker/Dockerfile docker/unbound-doq-attacker`\n\nTo rerun `dnsproxy`:\n\n1. `cd attachments`\n2. `bash scripts/repro-g03-dnsproxy-oracle.sh`\n3. Inspect `artifacts/g03/<RUN_ID>/summary.txt`\n4. Inspect `artifacts/g03/<RUN_ID>/entropy-backend.jsonl`,\n   `txid_correct-backend.jsonl`, and `port_correct-backend.jsonl`\n\nTo rerun the `dnsproxy` fallback-steering case:\n\n1. `cd attachments`\n2. `bash scripts/repro-g04-dnsproxy-steering.sh`\n3. Inspect `artifacts/g04/<RUN_ID>/summary.txt`\n4. Inspect `steering_correct-main.jsonl` and `steering_correct-fallback.jsonl`\n\nTo rerun `AdGuard Home`:\n\n1. `cd attachments`\n2. `bash scripts/repro-g05-adguardhome-oracle.sh`\n3. Inspect `artifacts/g05/<RUN_ID>/summary.txt`\n4. Inspect `entropy-backend.jsonl`, `txid_correct-backend.jsonl`, and\n   `port_correct-backend.jsonl`\n\nThe attached evidence includes fresh `dnsproxy v0.81.2` reruns, one official-\nprofile `AdGuard Home` run, and the minimal reproducer bundle used by both.\n\n## Impact\n\nFor both products, the tested DoQ-to-UDP path is no longer a full\n`(txid, source-port)` search surface:\n\n- `dnsproxy`: four of four sampled runs showed `txid=0` on the backend hop and\n  a positive source-port oracle on `v0.81.2`. The remaining unknown is\n  `port_only`. Median wrong/correct port latency was `327.99 ms / 40.93 ms`.\n- `AdGuard Home`: four of four sampled runs showed `txid=0` on the backend hop\n  and a positive source-port oracle. The aggregate again classifies the\n  remaining unknown as `port_only`. Median wrong/correct port latency was\n  `319.14 ms / 37.02 ms`.\n\nProduct-specific effects:\n\n- `dnsproxy`: a correct port guess produced an empty client-visible answer on\n  the base oracle path, and in the fallback profile it steered all eight tested\n  queries away from the main upstream and into the fallback upstream.\n- `AdGuard Home`: a correct port guess produced fast `SERVFAIL` and an extra\n  backend query.\n\nThis is the security-relevant point. On the tested official profiles, the\nbackend hop no longer forces an off-path attacker to deal with two fresh random\nfields per forwarded DNS race. The DNS ID is already known: it is\ndeterministically `0` on the client-triggered DoQ-to-UDP path. The only\nremaining backend tuple variable is the UDP source port, and the attached\nevidence shows a repeatable oracle for that remaining variable.\n\nThat places the path in the same threat-model class as oracle-assisted DNS\nforgery work such as SAD DNS and TUdoor: the attack first uses an oracle to\nlearn or validate the tuple state that protects an off-path response race, and\nonly then attempts the forged response. This report stops short of a forgery\ndemo, but the evidence already shows the crucial precondition on the tested\nbackend hop: the tuple is not high-entropy anymore. It has been reduced from\n`(txid, source-port)` to `source-port` only.\n\n\n---\n\n**Attachments**\n[attachments.zip](https://github.com/user-attachments/files/27227054/attachments.zip)",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/AdguardTeam/AdGuardHome"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.107.75"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 0.107.74"
    }
  }, {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/AdguardTeam/dnsproxy"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.81.3"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/AdguardTeam/AdGuardHome/security/advisories/GHSA-xgx4-4h9w-53pv"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-47703"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/AdguardTeam/dnsproxy/commit/f00d992ce9567a50f596853978ad6500acfdcf1d"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/AdguardTeam/AdGuardHome"
  }, {
    "type" : "WEB",
    "url" : "https://pkg.go.dev/vuln/GO-2026-5756"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-330", "CWE-362", "CWE-662" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-06-04T19:50:23Z",
    "nvd_published_at" : "2026-07-15T17:16:48Z"
  }
}