Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vulnerability] Loop Request检测存在问题,可实现拒绝服务攻击 / Loop Request detection is too fragile and can realize denial of service attacks. #284

Closed
imlk0 opened this issue Dec 19, 2020 · 2 comments

Comments

@imlk0
Copy link

imlk0 commented Dec 19, 2020

English Version Report

漏洞危害

通过发送一个请求到服务器,就能实现持续的Loop request攻击,导致拒绝服务/服务缓慢

漏洞原理

通过精心构造的请求头,绕过Loop request防御,配合HTTP重定向(301/302),实现Loop request攻击。

该程序存在一个公开访问的外部API:

http://127.0.0.1:25500/sub?target=%TARGET%&url=%URL%&config=%CONFIG%

其中的一个名为url参数可以是一个任意的外部url。收到该请求后,服务器会发起一个对该urlGET请求。

  1. 构造Loop request

    我们可以构造这样的一个url,我们叫它BAD_URL_ONE

    http://127.0.0.1:25500/sub?target=clash&insert=false&url=BAD_URL_TWO
    

    其中BAD_URL_TWO也是一个url,在任何时候它都返回一个301/302响应,将请求重定向到BAD_URL_ONE(重定向可以用一个简单的nginx做到,或者一个带编辑功能的短链接服务做到)

    向该服务器发送一个GET请求,url是BAD_URL_ONE,服务器会请求BAD_URL_TWO。由于重定向,服务器会访问BAD_URL_ONE(自己访问自己),造成Loop request

image-20201219221139213

不幸的是,这种攻击已经被得到防御,但是我们发现了新的方法来绕过。

  1. 绕过服务端Loop request防御

    该程序通过检查请求头来实施对Loop request攻击的防御:

    在向外部发送请求时,会带上一个自定义的请求头SubConverter-Request: 1

    list = curl_slist_append(list, "SubConverter-Request: 1");

    通过检查请求头中是否包含SubConverter-Request,且它的值是否为"1"来拒绝Loop request

    const char *uri = req->uri, *internal_flag = evhttp_find_header(req->input_headers, "SubConverter-Request");

    if(internal_flag != NULL && strcmp(internal_flag, "1") == 0)

image-20201219214510521

但是该检测方式存在漏洞。

在服务器发出请求BAD_URL_TWO时,会顺带将攻击者请求BAD_URL_ONE时发送的所有HTTP请求头也带上,像下面这样:

image-20201219215030195

因此,我们可以用curl请求BAD_URL_ONE,并且包含一个叫做SubConverter-Request的请求头,但是值不是"1",而是"2",我们就可以得到,两个SubConverter-Request💥💥

image-20201219215510165

程序使用libev的evhttp_find_header函数获取请求中的header,这个函数在处理请求中多个相同的header时,只会返回第一个的结果,因此SubConverter-Request的值被我们覆盖成了"2",从而绕过了Loop request防御。

复现过程

  1. release页面下载发布的二进制文件,在本地启动一个服务程序:

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

    其中BAD_URL_TWO是一个短链接服务:https://t.xice.wang/v,它会重定向(301)到`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. 发出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'
    

    该请求发出后,程序开始进入无限的Loop Request

image-20201219223158674

此后新的请求到来时表现为请求缓慢,或者拒绝服务

修复建议

去掉strcmp()函数,将检测逻辑改成检测SubConverter-Request头是否存在

if(internal_flag != NULL && strcmp(internal_flag, "1") == 0)

@tindy2013
Copy link
Owner

感谢提醒。该请求头原意是防止用户将已经过 SubConverter 处理的订阅链接再次送入而做的简单处理,还未考虑过是否能防御真正的攻击。

@imlk0
Copy link
Author

imlk0 commented Dec 19, 2020

感谢提醒。该请求头原意是防止用户将已经过 SubConverter 处理的订阅链接再次送入而做的简单处理,还未考虑过是否能防御真正的攻击。

还有一问题,测试过程中通过观察日志,发现在两轮loop之后会停顿几秒钟(可能是timeout),然后再是两轮loop,停顿的期间所有的外部请求都会挂起(包括因为loop request而产生的请求),所以我猜测这里是否意味着程序同时最多只能处理两个外部请求,多余的会丢到队列里?这里是否是libevent的使用问题?(没有用过libevent,只是猜测哈)

@imlk0 imlk0 closed this as completed Dec 21, 2020
StarStar-Lab added a commit to StarStar-Lab/subconverter that referenced this issue Dec 21, 2020
LJason77 pushed a commit to LJason77/subconverter that referenced this issue Jan 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants