By sending a request to the server, a denial of service/slow service can be achieved
With a carefully constructed request header, we can bypass the Loop request
defense. With HTTP redirection (301/302), Loop request
attack can be realized.
The program has a publicly accessible external API:
http://127.0.0.1:25500/sub?target=%TARGET%&url=%URL%&config=%CONFIG%
One of the parameters named url
can be an arbitrary external url. After receiving the request, the server will initiate a GET
request for the url
.
-
Construct
Loop request
We can construct such a url, and we call it
BAD_URL_ONE
:http://127.0.0.1:25500/sub?target=clash&insert=false&url=BAD_URL_TWO
Among them,
BAD_URL_TWO
is also a url. At any time it returns a301/302
response, redirecting the request toBAD_URL_ONE
(redirection can be done with a simple nginx server, or a short link Service with editing function)First, Send a GET
BAD_URL_ONE
request to the server. Then, the server will requestBAD_URL_TWO
. Due to the redirection, the server will requestBAD_URL_ONE
(request itself), causingLoop request
Unfortunately, this attack has been defended, but we have discovered new ways to bypass it.
-
Bypass server-side
Loop request
defenseThis server program implements defense against
Loop request
attacks by checking a request header:When the server sending a request, it will append a custom request header
SubConverter-Request: 1
:And it detects
Loop request
by checking whether the request header containsSubConverter-Request
and whether its value is"1"
:But this detection method has loopholes.
When the server sends a request
BAD_URL_TWO
, it will incidentally bring all the HTTP request headers sent when the attacker requestsBAD_URL_ONE
, like the following:Therefore, we can use
curl
to requestBAD_URL_ONE
, and include a request header calledSubConverter-Request
, but the value is not"1"
, but"2"
. We can get twoSubConverter -Request
onBAD_URL_TWO
request. 💥💥The program uses libev's evhttp_find_header function to get the header in the HTTP request. This function will only return the first one when processing multiple identical headers in the request. As a result, the value of
SubConverter-Request
was overwritten by us to"2"
, thus bypassing theLoop request
defense.
-
Download the released binary file from the release page, and start a service program locally:
chmod +x ./subconverter ./subconverter
-
Construct
BAD_URL_ONE
: http://127.0.0.1:25500/sub?target=clash&insert=false&url=https://t.xice.wang/vAmong them,
BAD_URL_TWO
is a short link service: https://t.xice.wang/v, it will redirect (301) toBAD_URL_ONE
:curl -v https://t.xice.wang/v <HTTP/2 301 <server: nginx/1.19.0 <content-type: text/html; charset=UTF-8 <location: http://127.0.0.1:25500/sub?target=clash&insert=false&url=https://t.xice.wang/v <cache-control: no-cache
-
Issue a request for
BAD_URL_ONE
:curl -H'SubConverter-Request: 2''http://127.0.0.1:25500/sub?target=clash&insert=false&url=https://t.xice.wang/v'
After the request is sent, the program starts to enter an infinite
Loop Request
After that, new incoming requests will appear to be slow. Multiple attacks can lead to denial of service
Remove the strcmp()
function and change the detection logic to detect the existence of the SubConverter-Request
header.
https://github.com/tindy2013/subconverter/blob/ab4d7543cec46cd9e45680f3b43d82de16f38a3d/src/webserver_libevent.cpp#L174