Description:
An attacker cannot directly trigger this vulnerability in the go-spew
library itself. However, if a developer uses go-spew
to dump data structures in an application, and the dumped data structure contains a type that panics within its Stringer
or Error
interface method, go-spew
's panic handling mechanism might expose sensitive information. When go-spew
recovers from a panic during Stringer
or Error
method invocation, it uses fmt.Fprintf(w, "%v", err)
to write the panic error to the output writer. If the panic error message contains sensitive information, and the developer does not properly sanitize the output of go-spew
, this sensitive information could be exposed. For example, a custom type's Stringer
method panicking due to accessing a nil pointer might reveal memory addresses, or a logic error could reveal internal configuration or data paths within the error string.
Impact:
Information Exposure. Sensitive information contained within panic error messages from Stringer
or Error
interfaces could be exposed to an attacker if the developer using go-spew
does not sanitize the output appropriately. This information could potentially aid in further attacks or compromise the application's security.
Vulnerability Rank: High
Currently Implemented Mitigations:
The catchPanic
function in common.go
attempts to handle panics from Stringer
and Error
methods gracefully, preventing application crashes. The README.md
warns developers to use html.EscapeString()
when displaying spew output in HTML for web applications. However, html.EscapeString()
only escapes HTML entities in the dumped value and does not sanitize the panic error message itself.
Missing Mitigations:
go-spew
itself lacks specific sanitization or redaction of panic error messages. The documentation could be improved to explicitly warn about potential sensitive information leakage through panic error messages and advise developers to sanitize go-spew
's output, specifically considering panic scenarios and the content of panic error messages, beyond just HTML escaping.
Preconditions:
- A developer uses
go-spew
to dump data structures in a publicly accessible application. - The dumped data structure contains a type with a
Stringer
orError
interface that can panic. - The panic error message generated by Go runtime contains sensitive information.
- The developer does not properly sanitize the output from
go-spew
, including panic error messages, before displaying it in a context accessible to an attacker.
Source Code Analysis:
- File:
/code/spew/common.go
- Function:
catchPanic(w io.Writer, v reflect.Value)
func catchPanic(w io.Writer, v reflect.Value) {
if err := recover(); err != nil {
w.Write(panicBytes) // Writes "(PANIC="
fmt.Fprintf(w, "%v", err) // Writes the panic error message AS IS
w.Write(closeParenBytes) // Writes ")"
}
}
The catchPanic
function recovers from panics and writes the panic error message err
directly to the output writer w
without sanitization using fmt.Fprintf(w, "%v", err)
.
- File:
/code/spew/common.go
- Function:
handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool)
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// ...
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v) // catchPanic is deferred for error interface
// ...
w.Write([]byte(iface.Error()))
// ...
case fmt.Stringer:
defer catchPanic(w, v) // catchPanic is deferred for Stringer interface
// ...
w.Write([]byte(iface.String()))
// ...
}
return false
}
handleMethods
uses defer catchPanic(w, v)
to handle panics in iface.Error()
or iface.String()
.
Security Test Case:
- Create a Go web application that imports
github.com/davecgh/go-spew/spew
. - Define a custom struct with a
Stringer
interface that panics and includes sensitive information in the panic message:
type SensitiveStringer struct {
Path string
}
func (s SensitiveStringer) String() string {
if s.Path == "" {
panic("Sensitive path information: /internal/config/secrets.json")
}
return "SensitiveStringer with path: " + s.Path
}
- In a web handler, create an instance of this struct and use
spew.Sdump
to dump it without sanitizing the panic error message:
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
sensitiveData := SensitiveStringer{} // Trigger panic in Stringer
dumpOutput := spew.Sdump(sensitiveData)
fmt.Fprintf(w, "<!--\n%s\n-->", html.EscapeString(dumpOutput))
}
- Run the web application and access it through a browser.
- Inspect the HTML source code.
- Verify that the comment section contains the output of
spew.Sdump
and the sensitive information "Sensitive path information: /internal/config/secrets.json" from the panic message is present, demonstrating information exposure despite usinghtml.EscapeString()
on the overall output.
Description:
The default configuration of go-spew
includes raw memory pointers (addresses) in the output of its dump and formatting functions (e.g., Dump
, Fdump
, or when using the %+v
and %#+v
format verbs). If an application using go-spew
with default settings inadvertently exposes debugging output, an attacker may see hexadecimal addresses indicating the process’ memory layout. This can happen when a debugging routine calls a dump function on an internal data structure and the output is made publicly available, for example, via an HTTP error page in production.
Impact:
Revealing pointer addresses and memory layout details may allow attackers to gain insight into the internal state of the running process. With this knowledge, they could devise further exploitation strategies, such as bypassing address space layout randomization (ASLR), against an application mistakenly exposing go-spew
output in production.
Vulnerability Rank: High
Currently Implemented Mitigations:
The package documentation strongly warns against using debugging output in production and emphasizes its intended use for development only. A configuration option, DisablePointerAddresses
in ConfigState
, exists for users to disable dumping pointer addresses.
Missing Mitigations:
By default, the global configuration (spew.Config
) does not disable pointer address printing. There is no runtime safeguard preventing the dumping of raw address information in a production build. An enforced "safe-by-default" mode to suppress internal memory addresses when deployed in production is missing.
Preconditions:
- The application uses
go-spew
’s default configuration in a publicly exposed environment, such as production. - An endpoint, error handler, or logging function prints a dump that includes pointer addresses without disabling this feature.
Source Code Analysis:
- File:
/code/spew/config.go
- Structure:
ConfigState
TheDisablePointerAddresses
flag inConfigState
controls address printing and is set tofalse
by default. - File:
/code/spew/common.go
and/code/spew/format.go
Functions likeprintHexPtr
incommon.go
and pointer handling informat.go
write raw pointer addresses ifDisablePointerAddresses
is not set. - File:
/code/spew/spew.go
- Variable:
Config
The globalspew.Config
instance is created with default settings that allow full disclosure of pointer information.
Security Test Case:
- Set up an application that calls
go-spew
’sDump
orPrintf
on internal data and exposes the output in HTTP responses. - With the default configuration (
DisablePointerAddresses
set tofalse
), trigger a request that causes a debugging dump to be sent. Verify that pointer addresses (in hexadecimal) appear in the output. - Change the configuration to set
spew.Config.DisablePointerAddresses = true
and repeat the test. Verify that pointer address information is no longer present. - Confirm that in a production environment, no full pointer addresses (or other sensitive layout details) are dumped.
Description:
go-spew
uses an internal helper, unsafeReflectValue
(in bypass.go
), to implement deep dumping, which prints complete data structures even for unexported fields. This function manipulates the internal flags of a Go reflect.Value
to bypass normal safety restrictions, making unexported (private) fields accessible and including their data in the debugging output. If a data structure contains unexported fields with sensitive information, using go-spew
's dump functions on this structure will reveal this sensitive data.
Impact: The compromised debugging output may reveal sensitive internal information, such as secret keys, internal configurations, or other confidential data not meant for exposure. This increases the risk of further attacks against the application.
Vulnerability Rank: High
Currently Implemented Mitigations:
The package documentation clearly states that deep-dumping functionality is for debugging only and should not be used in production. A "safe" build tag (-tags safe
) exists to compile the package without using the unsafe
package, but the default build enables unsafe behavior.
Missing Mitigations: There is no runtime check enforcing that unsafe reflection is disabled in a production context. The library relies on developer discipline and build-time choices. A misconfigured production system might mistakenly use the unsafe defaults and expose sensitive data.
Preconditions:
- An application in production inadvertently uses
go-spew
’s dumping functions, including internal objects with private fields. - The project is compiled without the "safe" build tag and with default settings, so unsafe reflection is active.
Source Code Analysis:
- File:
/code/spew/bypass.go
- Function:
unsafeReflectValue(v reflect.Value) reflect.Value
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() {
return reflect.Value{}
}
if v.CanAddr() {
return v
}
v = reflect.New(v.Type()).Elem() // Create a new addressable value
return v
}
(Note: The provided code snippet in the original description was incomplete and not accurately representing the unsafe bypass logic. A more accurate representation is that unsafeReflectValue
is intended to make a reflect.Value
addressable, potentially using unsafe operations internally, although the provided snippet doesn't fully illustrate the unsafe manipulation described in the vulnerability. The core idea is that by default reflection in Go prevents access to unexported fields, and go-spew
uses unsafe
package to bypass this limitation for debugging purposes. The details of the unsafe
operations are more complex and involve directly manipulating memory representation of reflect.Value
.)
In “bypass.go”, the function unsafeReflectValue
is used to bypass Go's reflection restrictions on unexported fields. It makes private fields accessible so that functions like Dump
can print the complete value, including unexported fields. No runtime checks are performed to ensure this level of detail is acceptable in the current environment.
Security Test Case:
- Create a test structure with an unexported field containing sensitive data (e.g., a field named "secret" holding a confidential string).
type SecretData struct {
PublicField string
secret string // unexported field
}
- Invoke the debugging dump function (e.g.,
spew.Sdump
) on an instance of this structure using the default configuration.
data := SecretData{PublicField: "public value", secret: "sensitive secret"}
dumpOutput := spew.Sdump(data)
fmt.Println(dumpOutput)
- Verify that the output contains the value of the unexported sensitive field "secret".
- Compile the package with the "safe" build tag (
go build -tags safe .
) or adjust configuration to a mode that does not use unsafe reflection. Re-run the test and verify that the unexported field is either not printed or is masked to protect the sensitive value.
// Example of disabling unsafe reflection programmatically if possible (configuration method may vary)
spew.Config.DisableUnsafeReflection = true // Hypothetical configuration option, check actual API
dumpOutputSafe := spew.Sdump(data)
fmt.Println(dumpOutputSafe)
Verify that in the safe mode the sensitive field is not exposed.