A Journey into Synology NAS — Part 4: HTTP Request Processing Flow and Vulnerability Analysis
Preface
The previous two articles analyzed the services available on Synology NAS device from the perspective of a local area network. However, in most scenarios, Synology NAS devices are used for remote access, with the primary entry point being through 5000/http (5001/https)
(excluding the use of QuickConnect
or other proxies for now). Therefore, this article will mainly analyze the HTTP request processing flow and its handling mechanism, then share several security issues discovered in some packages.
HTTP Request Processing Flow
During a regular login process, some http requests are captured. As we can see, these requests are sent to endpoints like /webapi/query.cgi
, /webapi/login.cgi
and /webapi/entry.cgi
. According to the Synology developer's document, the general routines to interact with the device are as follows:
- Retrieve API-related information through
query.cgi
; - Authenticate and obtain a session ID through
login.cgi
andencryption.cgi
; - Send requests and parse responses through
entry.cgi
; - Logout after completing the interaction.
An example of a specific request is as follows. For most requests, the endpoint is /webapi/entry.cgi
. As to the POST
data, it will contain some common parameters such as api
, method
, version
and so on. Among them, the api
parameter represents the requested API
name, the method
parameter indicates the method within the requested API
, and version
parameter represents the requested API
version.
For API
requests, a meta-data file named SYNO.***.***.lib
in json format is used in the back end to define information related to the API
. An example is as follows
{
"SYNO.Core.PersonalNotification.Event": { // API name
"allowUser": [ "admin.local"], // which group can access this API
"appPriv": "",
"authLevel": 1, // authentication is required or not (0 means no authentication)
"disableSocket": false,
"lib": "lib/SYNO.Core.PersonalNotification.so", // the file to handle this request
"maxVersion": 1,
"methods": { // which methods are available and its corresponding version
"1": [{
"fire": {
"allowUser": [ "admin.local","normal.local" ], // overwrite above definition
"grantByUser": false,
"grantable": true }
}]
},
"minVersion": 1,
"priority": 0,
"socket": ""
}
}
According to the above information, we know how to make a specific request to reach the target binary in the back end.
The following is a simple and high-level process flow. First, a request is sent to the device via port 5000. Then based on the request url
, nginx
will schedule the corresponding cgi
to handle the request, such as query.cgi
, login.cgi
, and entry.cgi
. Among them, entry.cgi
is the endpoint for most POST
requests. These cgi
may communicate with another two process: synocgid
and synoscgi
. synocgid
mainly handles session-related stuff. And synoscgi
is mainly responsible for dispatching specific requests to the target binary.
Security Issues
With a general understanding of the HTTP request processing flow and its handling mechanism, we can start to analyze those modules related to request processing on the Synology NAS device. In general, there are mainly two attack surfaces: the DSM
operating system itself and the various packages provided by Synology. Next, we will share some security issues found in detail.
Diagnosis Tool
As mentioned before, Diagnosis Tool
is a utility package provided by Synology, supporting packet capturing, debugging, and so on. The GUI of this tool and an example of a captured http request for this package are shown below.
The above request will be handled by the packet_capture.cgi
binary. Some code snippets are as follows. In handle_action_start()
, it will first obtain parameters, then pass them to another binary called tcpdump_wrapper
as parameters in json
string.
__int64 __fastcall handle_action_start(__int64 a1, __int64 a2, const char *a3, const char *a4)
{
// ...
Json::Value::Value((Json::Value *)&v39, (const std::string *)&v28);
v17 = Json::Value::operator[](&v35, "output_dir");
Json::Value::operator=(v17, &v39);
Json::Value::~Value((Json::Value *)&v39);
Json::Value::Value((Json::Value *)&v40, v4);
v18 = Json::Value::operator[](&v35, "expression");
Json::Value::operator=(v18, &v40);
Json::Value::~Value((Json::Value *)&v40);
Json::Value::Value((Json::Value *)&v41, v6);
v19 = Json::Value::operator[](&v35, "interface");
Json::Value::operator=(v19, &v41);
Json::Value::~Value((Json::Value *)&v41);
Json::FastWriter::write((Json::FastWriter *)&v33, (const Json::Value *)&v37);
std::string::assign((std::string *)&v29, (const std::string *)&v33);
// ...
if (SLIBCExec("/var/packages/DiagnosisTool/target/bin/tcpdump_wrapper", "--params", v29, 0LL, 0LL) == -1 )
// ...
The main()
function in binary tcpdump_wrapper
is shown below. As can be seen, sub_401F10()
is called to parse the output_dir
, expression
and interface
parameters, which are then passed to RunTcpDump()
. In RunTcpdump()
, it will ultimately invoke execve()
to execute the command "tcpdump -i <interface> -w <file> -C 10 -s 0 filter_expression"
.
__int64 __fastcall main(signed int a1, char **a2, char **a3)
{
if ( a1 > 1 )
{
// ...
if ( v3 != 2 && !strcmp(v4[1], "--params") )
{
std::string::string(&v11, v4[2], &v6);
// resolve parameters from json string
sub_401F10(&v11, &output_dir, &expression,&interface);
// ...
}
}
if (sub_4019D0(&output_dir) )
{
if (sub_401900() && !RunTcpdump(&output_dir, &expression, &interface) )
{
// ...
Calling execve()
is relatively safe to avoid command injection issues. However, the filter_expression
parameter is user-controlled. By examining the document of tcpdump
, it's found that there is a -z
option, when combined with -C
or -G
option, it can also lead to command execution.
Fortunately, the -C
option is already in the command "tcpdump -i <interface> -w <file> -C 10 -s 0 filter_expression"
. Therefore, by crafting a filter_expression
parameter like -z<path to your shell script>
, we can achieve code execution.
DS File
DS File
is a mobile application provided by Synology, facilitating access and management of files on DiskStation
from mobile devices. The process of accessing DiskStation
using this app is similar to the web-based process. When attempting to login into DiskStation
, the authentication process involves a PKI-based encryption mechanism. In certain situations, such as entering an incorrect target IP
or temporary network unavailability, normal requests may fail, leading DS File
to send additional requests.
By examining the third request, it’s observed that the request header contains the Authorization
information encoded in Base64
. In other words, it's equivalent to plaintext.
As a result, in an insecure network environment, attempting to access DiskStation
via the DS File
application could lead to the interception of the user's plaintext account information by MITM through simply discarding or redirecting the corresponding request.
Synology Calendar
This package is a web-based application designed for managing daily events and tasks. It supports features such as adding attachments to events and sharing schedules. In general, there are two ways to add attachments to events: uploading from a local device or uploading from the NAS
. The following is an example of a normal user creating an event and adding an attachment, along with some HTML code related to attachment links.
As we can see, the file name is concatenated to the href
link. Can we control the corresponding href
link by crafting a file name? After testing, it was found that due to the lack of file name validation, by forging a suitable file name, one can alter the corresponding href
link while making the displayed file name appear normal.
Furthermore, with the help of the event sharing feature, this event can be shared with the administrator group. When someone from the administrator group views the event and clicks on the corresponding attachment, the associated request will be executed. Therefore, by exploiting this vulnerability, a user with normal privileges can execute “arbitrary” requests with “administrator” permissions, such as adding themselves to the administrator group.
Media Server
Media Server
package provides services related to multimedia, allowing playback of multimedia content on the NAS
through DLNA/UPnP
. After installation, it initiates several custom services as follows.
Through simple analysis, it turns out that there are some accessible urls in ./sbin/dms
without the need for authentication.
The first interesting API
is related to videotranscoding.cgi
, and the corresponding request URL format is "http://%s:%d/transcoder/videotranscoding.cgi/%s/id=%d%s"
. The code responsible for handling this request is shown below. It can be observed that if the URL contains the strings "id="
and "?"
, the content between "id="
and "?"
will be copied to the dest
buffer. However, due to not considering the order of occurrence, if the request URL is "http://%s:%d/transcoder/videotranscoding.cgi/VideoStation?id=1"
, integer underflow would occur when calling strncpy()
later.
__int64 sub_406E80(__int64 a1)
{
// ...
v4 = getenv("REQUEST_URI");
snprintf(s, 0x800uLL, "%s", v4);
v99 = strstr(s, "id=");
if ( v99 )
{
v5 = strchr(s, '?');
if ( v5 )
strncpy(dest, v99 + 3, v5 - (v99 + 3)); // integer underflow
}
// ...
std::string::assign(v3, dest, strlen(dest));
// ...
sub_403F50(a1, v1, v3, (std::string *)(a1 + 136));
Assuming that the request URL format matches the program’s expectations, then sub_403F50()
will be invoked later, and its third parameter corresponds to the content between "id="
and "?"
copied from request URL. In sub_403F50()
, after a simple validation of parameter a2
, parameter a3
will be formatted as the id
parameter. Due to the lack of proper validation on parameter a3
, there exists SQL
injection vulnerability.
__int64 sub_403F50(__int64 a1, std::string *a2, _QWORD *a3, std::string *a4)
{
// ...
if ( !(unsigned int)std::string::compare(a2, "MediaServer") )
{
std::string::assign((std::string *)v32, "mediaserver", 0xBuLL);
std::string::assign((std::string *)&v34, "MediaServer", 0xBuLL);
std::string::assign((std::string *)v33, "video", 5uLL);
}
else
{
if ( (unsigned int)std::string::compare(a2, "VideoStation") )
goto LABEL_4;
std::string::assign((std::string *)v32, "video_metadata", 0xEuLL);
std::string::assign((std::string *)&v34, "VideoStation", 0xCuLL);
std::string::assign((std::string *)v33, "video_file", 0xAuLL);
}
snprintf(s, 0x100uLL, "SELECT * from %s where id = %s", v33[0], (const char *)*a3); // SQL injection
Another similar API
is jpegtnscaler.cgi
, and the corresponding request URL format is "http://%s:%d/transcoder/jpegtnscaler.cgi/%s/%d.%s"
. The code responsible for handling this request is as follows. It can be observed that there is no length parameter validation before calling strncpy()
. By constructing a request like "http://%s:%d/transcoder/jpegtnscaler.cgi/<a*0x450>/1"
, we can cause buffer overflow when calling strncpy()
.
__int64 main(__int64 a1, char **a2, char **a3)
{
// ...
v3 = getenv("REQUEST_URI");
// ...
v4 = strrchr(v3, '/');
v5 = v4;
// ...
v6 = strtol(v4 + 1, 0LL, 10);
bzero(s, 0x400uLL);
strncpy(s, v3, v5 - v3); // buffer overflow
Audio Station
The Audio Station
package provides features such as listening to radio programs, managing music libraries, creating personal playlists, and sharing with friends. After installing this package, there are some custom CGI
programs in its installation path, such as media_server.cgi
, web_player.cgi
, audiotransfer.cgi
, etc. Some requests captured when using this package are shown below.
According to the HTTP
request processing flow mentioned above, execl_cgi()
is responsible for handling custom CGI
requests. Importantly, in certain scenarios, authentication is handled by custom CGI
programs.
Upon analysis, the most interesting API
is audiotransfer.cgi
, with the corresponding request URL format "http://%s:%d/webman/3rdparty/AudioStation/webUI/audiotransfer.cgi/%s.%s"
. Part of code for handling this request is as follows. It can be observed that sub_402730()
is called at the beginning of the main()
function. In sub_402730()
, the last part of the request URL is obtained and passed to MediaIDDecryption()
. In MediaIDDecryption()
, after calculating the length of parameter a1
, it will copy the first 6 bytes, and then call snprintf()
. Due to the controllable size
parameter and string content in the snprintf()
call, there exists a buffer overflow vulnerability. Moreover, this process does not handle authentication, meaning no authentication is required when accessing this endpoint. Therefore, by sending a specific request, a remote unauthenticated user can trigger this buffer overflow vulnerability.
__int64 main(__int64 a1, char **a2, char **a3)
{
sub_402730((__int64)v5);
// ...
_BOOL8 sub_402730(__int64 a1)
{
// ...
v8 = getenv("REQUEST_URI");
snprintf(s, 0x400uLL, "%s", v8);
// ...
v11 = strrchr(s, '/');
v12 = v11;
if ( v11 )
{
// ...
v15 = MediaIDDecryption((__int64)(v12 + 1));
// ...
__int64 MediaIDDecryption(const char *a1)
{
// ...
v1 = strlen(a1);
if ( v1 > 5 )
{
v3 = (v1 - 6) >> 1;
snprintf(s, 7uLL, "%s", a1);
v14 = 0; v4 = s; v5 = (char *)&v14;
do
{
v6 = *v4; --v5; ++v4; v5[6] = v6;
}
while ( v5 != &v13 ); // copy first 6 bytes
__isoc99_sscanf(s, "%x", &v8);
__isoc99_sscanf(&v14, "%x", &v9);
snprintf(v17, v3 + 1, "%s", a1 + 6);
snprintf(v18, v3 + 1, "%s", &a1[v3 + 6]); // buffer overflow
// ...
Regarding vulnerability exploitation, @fenix
conducted analysis and testing based on DSM 5.2-5592
and Audio Station 5.4-2860
. The relevant contexts are x86 architecture
, NX protection
, and semi-random ASLR
. Here are some additional notes:
- For
x86
architecture, based onDSM 6.x
, even ifASLR
is fully random, stable exploitation can be achieved by finding suitable gadgets, without the need for heap spraying or brute-forcing. - On
DSM 6.x
, elevation of privileges is still required after obtaining a shell. - For
x86-64
architecture, due to the issue of address truncation, a suitable approach for exploitation has not been found yet.
One More Thing
The above only listed a few typical packages and some issues found within them. In reality, Synology DSM
has many features and numerous packages available for analysis. Synology officially releases security advisories for its products irregularly. Combined with Synology archive repository, it's convenient to conduct patch analysis and vulnerability exploration.
Summary
In this article, we focus on the remote scenario of Synology NAS, and analyze its process and handling mechanisms of web requests. Additionally, we share some security issues discovered within several typical packages.
This article concludes the series, hoping it brings insights to those interested in Synology NAS devices.