Skip to content

Instantly share code, notes, and snippets.

@imlk0
Last active August 1, 2023 06:43
Show Gist options
  • Save imlk0/150c3a52aead2741ae5d32930988861b to your computer and use it in GitHub Desktop.
Save imlk0/150c3a52aead2741ae5d32930988861b to your computer and use it in GitHub Desktop.

The harm of loopholes

By sending a request to the server, a denial of service/slow service can be achieved

Vulnerability details

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.

  1. 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 a 301/302 response, redirecting the request to BAD_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 request BAD_URL_TWO. Due to the redirection, the server will request BAD_URL_ONE (request itself), causing Loop request

    image-20201219221139213

    Unfortunately, this attack has been defended, but we have discovered new ways to bypass it.

  2. Bypass server-side Loop request defense

    This 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:

    https://github.com/tindy2013/subconverter/blob/ab4d7543cec46cd9e45680f3b43d82de16f38a3d/src/webget.cpp#L199

    And it detects Loop request by checking whether the request header contains SubConverter-Request and whether its value is "1":

    https://github.com/tindy2013/subconverter/blob/ab4d7543cec46cd9e45680f3b43d82de16f38a3d/src/webserver_libevent.cpp#L166

    https://github.com/tindy2013/subconverter/blob/ab4d7543cec46cd9e45680f3b43d82de16f38a3d/src/webserver_libevent.cpp#L174

    image-20201219214510521

    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 requests BAD_URL_ONE, like the following:

    image-20201219215030195

    Therefore, we can use curl to request BAD_URL_ONE, and include a request header called SubConverter-Request, but the value is not "1", but "2". We can get two SubConverter -Request on BAD_URL_TWO request. 💥💥

    image-20201219215510165

    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 the Loop request defense.

Recurrence process

  1. Download the released binary file from the release page, and start a service program locally:

    chmod +x ./subconverter
    ./subconverter
  2. Construct BAD_URL_ONE: http://127.0.0.1:25500/sub?target=clash&insert=false&url=https://t.xice.wang/v

    Among them, BAD_URL_TWO is a short link service: https://t.xice.wang/v, it will redirect (301) to BAD_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
    
  3. 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

    image-20201219223158674

    After that, new incoming requests will appear to be slow. Multiple attacks can lead to denial of service

Repair suggestions

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment