GoFiber Vulnerable to X-Real-IP Spoofing via Header.Add() in BalancerForward
Summary
The BalancerForward proxy helper in GoFiber uses Header.Add() instead of Header.Set() when injecting the X-Real-IP header. This appends the real client IP as a second header value rather than replacing any attacker-supplied value. Upstream servers that read the first X-Real-IP header (nginx, Express, most HTTP servers) use the attacker's spoofed IP for logging, rate limiting, and access control.
Vulnerable Code
File: middleware/proxy/proxy.go, lines 270-285
``go
func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler {
r := &roundrobin{
current: 0,
pool: servers,
}
return func(c fiber.Ctx) error {
server := r.get()
if !strings.HasPrefix(server, "http") {
server = "http://" + server
}
c.Request().Header.Add("X-Real-IP", c.IP()) // line 282: Add, not Set
return Do(c, server+c.OriginalURL(), clients...)
}
}
`
Data Flow
1. Attacker sends request with X-Real-IP: 10.0.0.1 (spoofed internal IP)
2. BalancerForward handler executes at line 282
3. c.Request().Header.Add("X-Real-IP", c.IP()) APPENDS the real IP as a second header
4. Upstream server receives: X-Real-IP: 10.0.0.1 AND X-Real-IP: <real-attacker-ip>
5. Most HTTP servers (nginx, Node.js, Apache) read the FIRST value
6. Upstream uses 10.0.0.1 for all IP-dependent logic
Impact
Rate limit bypass: IP-based rate limiting at the upstream uses the spoofed IP, allowing unlimited requests
IP ACL bypass: Internal IP allowlists (e.g., admin panels restricted to 10.0.0.0/8) can be bypassed
Audit log poisoning: Security logs record the spoofed IP, making incident investigation unreliable
Geolocation bypass: IP-based geofencing or region restrictions are circumvented
Fix
Replace Header.Add() with Header.Set() at line 282:
`go
c.Request().Header.Set("X-Real-IP", c.IP())
`
Header.Set()` replaces any existing header value, ensuring only the real client IP is forwarded.
golang.org/x/crypto: Invoking memory leak when rejecting channels can lead to DoS
An authenticated SSH client that repeatedly opened channels which were rejected by the server caused unbounded memory growth, eventually crashing the server process and affecting all connected users. Rejected channels are now properly removed from the connection's internal state and released for garbage collection.
golang.org/x/crypto is vulnerable to invoking server panic during CheckHostKey/Authenticate flow
SSH servers which use CertChecker as a public key callback without setting IsUserAuthority or IsHostAuthority could be caused to panic by a client presenting a certificate. CertChecker now returns an error instead of panicking when these callbacks are nil.
golang.org/x/crypto vulnerable to invoking bypass of certificate restrictions
When an SSH server authentication callback returned PartialSuccessError with non-nil Permissions, those permissions were silently discarded, potentially dropping certificate restrictions such as force-command after a second factor succeeded. Returning non-nil Permissions with PartialSuccessError now results in a connection error.
golang.org/x/crypto: Invoking pathological inputs can lead to client panic
For certain crafted inputs, a 'ed25519.PrivateKey' was created by casting malformed wire bytes, leading to a panic when used.
Go Net HTML parser is vulnerable to denial of service
In Go Net (golang.org/x/net) before verion 0.55.0, parsing arbitrary HTML can consume excessive CPU time, possibly leading to denial of service.
Fiber vulnerable to XSS in AutoFormat Content Negotiation
Summary
Description
A Cross-Site Scripting (CWE-79) vulnerability in Go Fiber allows a remote attacker to inject arbitrary HTML/JavaScript by supplying Accept: text/html on any request whose handler passes attacker-influenced data to the AutoFormat() feature. This affects github.com/gofiber/fiber/v3 (DefaultRes.AutoFormat) through version 3.1.0 and github.com/gofiber/fiber/v2 (Ctx.Format) through version 2.52.12.
The developer opts into content negotiation by calling AutoFormat(), but does not opt into raw HTML emission for a particular request; Fiber chooses that branch from attacker-controlled Accept. Five of the six branches of the same method already escape. JSON, XML, MsgPack, and CBOR all route through encoders that neutralize markup; the txt branch emits text/plain and cannot execute. The html branch is the sole outlier in a method whose name (AutoFormat) and symmetrical structure actively telegraph "safe, format-agnostic reply."
Details
The issue resides in res.go within (DefaultRes).AutoFormat(). The method negotiates against the request Accept header, selects one of html | json | txt | xml | msgpack | cbor, and serializes the caller-supplied body accordingly.
The "html" branch concatenates the stringified body directly into HTML markup with no output encoding:
accept comes from r.c.Accepts(...), i.e. is fully attacker-controlled. An attacker can force the "html" branch on any AutoFormat() call regardless of which format the developer tested against.
b is produced from body via direct assignment (string / []byte) or fmt.Sprintf("%v", body). No html.EscapeString is applied.
The resulting string is sent as text/html; charset=utf-8, so browsers render it as active HTML.
``go
// res.go
func (r DefaultRes) AutoFormat(body any) error {
accept := r.c.DefaultReq.Accepts("html", "json", "txt", "xml", "msgpack", "cbor")
r.Type(accept)
var b string
switch val := body.(type) {
case string:
b = val
case []byte:
b = r.c.app.toString(val)
default:
b = fmt.Sprintf("%v", val)
}
switch accept {
case "txt":
return r.SendString(b)
case "json":
return r.JSON(body)
case "xml":
return r.XML(body)
case "html":
return r.SendString("<p>" + b + "</p>")
case "msgpack":
return r.MsgPack(body)
case "cbor":
return r.CBOR(body)
}
return r.SendString(b)
}
`
Impact
This impacts all current v3 releases ≤ 3.1.0 containing DefaultRes.AutoFormat, and all current v2 releases ≤ 2.52.12 where the identical "<p>" + b + "</p>" construction exists in (Ctx).Format(). Exploitation requires that an application call c.AutoFormat(v) where v (or a field stringified by %v) contains request-influenced data.
A handler that uses AutoFormat() to serve multiple representations of the same data can be turned into an HTML XSS sink when the client sends Accept: text/html, even if the developer only tested the JSON path.
This may result in:
Reflected XSS in the application's origin via any request-derived value reaching AutoFormat.
Stored XSS where the reflected value originates from persisted input later passed to AutoFormat.
Proposed Patch
The injection surface is r.Type("html") followed by r.SendString(b) with unescaped caller data, where it constructs markup on the caller's behalf around a value whose HTML-ness the caller did not declare. A few options:
AutoFormat() should treat body as data, not markup, in the "html" branch and escape it before concatenating it into the framework-generated <p> wrapper. Callers that need raw negotiated HTML should use Format() with an explicit HTML handler.
Introduce a sibling method that escapes, leave AutoFormat alone for backward compatibility.
HTML-escape the value in the "html" branch before concatenating it into the <p> wrapper.
`go
import "html"
// ...
case "html":
return r.SendString("<p>" + html.EscapeString(b) + "</p>")
`
html.EscapeString escapes <, >, &, ', ", which is sufficient for an element-text context. Apply the same change to v2's (Ctx).Format().
Proof of Concept
`bash
Create project directory
mkdir fiber-xss-poc && cd fiber-xss-poc
Initialize Go module
go mod init fiber-xss-poc
Install Fiber v3
go get github.com/gofiber/fiber/v3
Create the PoC file
cat > main.go << 'EOF'
package main
import (
"github.com/gofiber/fiber/v3"
)
type User struct {
ID int json:"id"
Name string json:"name"
}
func main() {
app := fiber.New()
app.Get("/api/user", func(c fiber.Ctx) error {
user := User{
ID: 1,
Name: c.Query("name", "anonymous"),
}
return c.AutoFormat(user)
})
app.Listen(":3000")
}
EOF
Run it
go run main.go
}
`
Benign JSON
`bash
curl -s 'http://127.0.0.1:3000/api/user?name=Alice' -H 'Accept: application/json'
{"id":1,"name":"Alice"}
`
HTML sink enables XSS
`bash
curl -s 'http://127.0.0.1:3000/api/user?name=<script>alert(document.domain)</script>' -H 'Accept: text/html'
<p>{1 <script>alert(document.domain)</script>}</p>
``
golang.org/x/crypto/ssh/agent vulnerable to panic if message is malformed due to out of bounds read
SSH Agent servers do not validate the size of messages when processing new identity requests, which may cause the program to panic if the message is malformed due to an out of bounds read.
golang.org/x/crypto/ssh allows an attacker to cause unbounded memory consumption
SSH servers parsing GSSAPI authentication requests do not validate the number of mechanisms specified in the request, allowing an attacker to cause unbounded memory consumption.
golang.org/x/net vulnerable to Cross-site Scripting
The tokenizer incorrectly interprets tags with unquoted attribute values that end with a solidus character (/) as self-closing. When directly using Tokenizer, this can result in such tags incorrectly being marked as self-closing, and when using the Parse functions, this can result in content following such tags as being placed in the wrong scope during DOM construction, but only when tags are in foreign content (e.g. <math>, <svg>, etc contexts).
HTTP Proxy bypass using IPv6 Zone IDs in golang.org/x/net
Matching of hosts against proxy patterns can improperly treat an IPv6 zone ID as a hostname component. For example, when the NO_PROXY environment variable is set to "*.example.com", a request to "[::1%25.example.com]:80` will incorrectly match and not be proxied.
net/http, x/net/http2: close connections when receiving too many headers
An attacker may cause an HTTP/2 endpoint to read arbitrary amounts of header data by sending an excessive number of CONTINUATION frames. Maintaining HPACK state requires parsing and processing all HEADERS and CONTINUATION frames on a connection. When a request's headers exceed MaxHeaderBytes, no memory is allocated to store the excess headers, but they are still parsed. This permits an attacker to cause an HTTP/2 endpoint to read arbitrary amounts of header data, all associated with a request which is going to be rejected. These headers can include Huffman-encoded data which is significantly more expensive for the receiver to decode than for an attacker to send. The fix sets a limit on the amount of excess header frames we will process before closing a connection.
Prefix Truncation Attack against ChaCha20-Poly1305 and Encrypt-then-MAC aka Terrapin
Summary
Terrapin is a prefix truncation attack targeting the SSH protocol. More precisely, Terrapin breaks the integrity of SSH's secure channel. By carefully adjusting the sequence numbers during the handshake, an attacker can remove an arbitrary amount of messages sent by the client or server at the beginning of the secure channel without the client or server noticing it.
Mitigations
To mitigate this protocol vulnerability, OpenSSH suggested a so-called "strict kex" which alters the SSH handshake to ensure a Man-in-the-Middle attacker cannot introduce unauthenticated messages as well as convey sequence number manipulation across handshakes.
Warning: To take effect, both the client and server must support this countermeasure.
As a stop-gap measure, peers may also (temporarily) disable the affected algorithms and use unaffected alternatives like AES-GCM instead until patches are available.
Details
The SSH specifications of ChaCha20-Poly1305 (chacha20-poly1305@openssh.com) and Encrypt-then-MAC (-etm@openssh.com MACs) are vulnerable against an arbitrary prefix truncation attack (a.k.a. Terrapin attack). This allows for an extension negotiation downgrade by stripping the SSH_MSG_EXT_INFO sent after the first message after SSH_MSG_NEWKEYS, downgrading security, and disabling attack countermeasures in some versions of OpenSSH. When targeting Encrypt-then-MAC, this attack requires the use of a CBC cipher to be practically exploitable due to the internal workings of the cipher mode. Additionally, this novel attack technique can be used to exploit previously unexploitable implementation flaws in a Man-in-the-Middle scenario.
The attack works by an attacker injecting an arbitrary number of SSH_MSG_IGNORE messages during the initial key exchange and consequently removing the same number of messages just after the initial key exchange has concluded. This is possible due to missing authentication of the excess SSH_MSG_IGNORE messages and the fact that the implicit sequence numbers used within the SSH protocol are only checked after the initial key exchange.
In the case of ChaCha20-Poly1305, the attack is guaranteed to work on every connection as this cipher does not maintain an internal state other than the message's sequence number. In the case of Encrypt-Then-MAC, practical exploitation requires the use of a CBC cipher; while theoretical integrity is broken for all ciphers when using this mode, message processing will fail at the application layer for CTR and stream ciphers.
For more details see https://terrapin-attack.com.
Impact
This attack targets the specification of ChaCha20-Poly1305 (chacha20-poly1305@openssh.com) and Encrypt-then-MAC (-etm@openssh.com), which are widely adopted by well-known SSH implementations and can be considered de-facto standard. These algorithms can be practically exploited; however, in the case of Encrypt-Then-MAC, we additionally require the use of a CBC cipher. As a consequence, this attack works against all well-behaving SSH implementations supporting either of those algorithms and can be used to downgrade (but not fully strip) connection security in case SSH extension negotiation (RFC8308) is supported. The attack may also enable attackers to exploit certain implementation flaws in a man-in-the-middle (MitM) scenario.
HTTP/2 Stream Cancellation Attack
HTTP/2 Rapid reset attack
The HTTP/2 protocol allows clients to indicate to the server that a previous stream should be canceled by sending a RST_STREAM frame. The protocol does not require the client and server to coordinate the cancellation in any way, the client may do it unilaterally. The client may also assume that the cancellation will take effect immediately when the server receives the RST_STREAM frame, before any other data from that TCP connection is processed.
Abuse of this feature is called a Rapid Reset attack because it relies on the ability for an endpoint to send a RST_STREAM frame immediately after sending a request frame, which makes the other endpoint start working and then rapidly resets the request. The request is canceled, but leaves the HTTP/2 connection open.
The HTTP/2 Rapid Reset attack built on this capability is simple: The client opens a large number of streams at once as in the standard HTTP/2 attack, but rather than waiting for a response to each request stream from the server or proxy, the client cancels each request immediately.
The ability to reset streams immediately allows each connection to have an indefinite number of requests in flight. By explicitly canceling the requests, the attacker never exceeds the limit on the number of concurrent open streams. The number of in-flight requests is no longer dependent on the round-trip time (RTT), but only on the available network bandwidth.
In a typical HTTP/2 server implementation, the server will still have to do significant amounts of work for canceled requests, such as allocating new stream data structures, parsing the query and doing header decompression, and mapping the URL to a resource. For reverse proxy implementations, the request may be proxied to the backend server before the RST_STREAM frame is processed. The client on the other hand paid almost no costs for sending the requests. This creates an exploitable cost asymmetry between the server and the client.
Multiple software artifacts implementing HTTP/2 are affected. This advisory was originally ingested from the swift-nio-http2 repo advisory and their original conent follows.
swift-nio-http2 specific advisory
swift-nio-http2 is vulnerable to a denial-of-service vulnerability in which a malicious client can create and then reset a large number of HTTP/2 streams in a short period of time. This causes swift-nio-http2 to commit to a large amount of expensive work which it then throws away, including creating entirely new Channels to serve the traffic. This can easily overwhelm an EventLoop and prevent it from making forward progress.
swift-nio-http2 1.28 contains a remediation for this issue that applies reset counter using a sliding window. This constrains the number of stream resets that may occur in a given window of time. Clients violating this limit will have their connections torn down. This allows clients to continue to cancel streams for legitimate reasons, while constraining malicious actors.
Fiber unauthorized access vulnerability in `ctx.IsFromLocal()`
Impact
This vulnerability can be categorized as a security misconfiguration. It impacts users of our project who rely on the ctx.IsFromLocal() method to restrict access to localhost requests. If exploited, it could allow unauthorized access to resources intended only for localhost.
In it's implementation it uses c.IPs():
``go
// IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header.
// When IP validation is enabled, only valid IPs are returned.
func (c *Ctx) IPs() []string {
return c.extractIPsFromHeader(HeaderXForwardedFor)
}
`
Thereby, setting X-Forwarded-For: 127.0.0.1 in a request from a foreign host, will result in true for ctx.IsFromLocal()
Patches
This issue has been patched in v2.49.2` with commit b8c9ede6efa231116c4bd8bb9d5e03eac1cb76dc
Workarounds
Currently, there are no known workarounds to remediate this vulnerability without upgrading to the patched version. We strongly advise users to apply the patch as soon as it is released.
References
For further information and context regarding this security issue, please refer to the following resources:
Mozilla Developer Network - X-Forwarded-For
Improper rendering of text nodes in golang.org/x/net/html
Text nodes not in the HTML namespace are incorrectly literally rendered, causing text which should be escaped to not be. This could lead to an XSS attack.
Gin Web Framework does not properly sanitize filename parameter of Context.FileAttachment function
The filename parameter of the Context.FileAttachment function is not properly sanitized. A maliciously crafted filename can cause the Content-Disposition header to be sent with an unexpected filename value or otherwise modify the Content-Disposition header. For example, a filename of "setup.bat";x=.txt" will be sent as a file named "setup.bat".
If the FileAttachment function is called with names provided by an untrusted source, this may permit an attacker to cause a file to be served with a name different than provided. Maliciously crafted attachment file name can modify the Content-Disposition header.
Improper input validation in github.com/gin-gonic/gin
Versions of the package github.com/gin-gonic/gin before version 1.9.0 are vulnerable to Improper Input Validation by allowing an attacker to use a specially crafted request via the X-Forwarded-Prefix header, potentially leading to cache poisoning.
Note: Although this issue does not pose a significant threat on its own it can serve as an input vector for other more impactful vulnerabilities. However, successful exploitation may depend on the server configuration and whether the header is used in the application logic.