CVE-2020-26273 (C++, published 2020-12-16T02:15Z)
Description: value: osquery is a SQL powered operating system instrumentation, monitoring, and analytics framework. In osquery before version 4.6.0, by using sqlite's ATTACH verb, someone with administrative access to osquery can cause reads and writes to arbitrary sqlite databases on disk. This _does_ allow arbitrary files to be created, but they will be sqlite databases. It does not appear to allow existing non-sqlite files to be overwritten. This has been patched in osquery 4.6.0. There are several mitigating factors and possible workarounds. In some deployments, the people with access to these interfaces may be considered administrators. In some deployments, configuration is managed by a central tool. This tool can filter for the `ATTACH` keyword. osquery can be run as non-root user. Because this also limits the desired access levels, this requires deployment specific testing and configuration.
Severity: LOW (CVSS v3 base score: 5.2)
Weakness: CWE-77 \u2014 Improper Neutralization of Special Elements used in a Command ('Command Injection')
The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
Repository: https://github.com/osquery/osquery
Files changed:
osquery/sql/sqlite_util.cpp
osquery/sql/sqlite_util.h
osquery/sql/tests/sqlite_util_tests.cpp
osquery/sql/virtual_sqlite_table.cpp
Commit message: Merge pull request from GHSA-4g56-2482-x7q8

* Proposed fix for attach tables vulnerability

* Add authorizer to ATC tables and cleanups

- Add unit test for authorizer function

Vulnerable code:
      "CREATE TABLE test_table (username varchar(30) primary key, age int)",
      "CREATE TABLE test_types_table (username varchar(30) primary key, age "

Fixed code:
#include <osquery/core/shutdown.h>
// This function is called by SQLite when a statement is prepared and we use
// it to allowlist specific actions.
int sqliteAuthorizer(void* userData,
                     int code,
                     const char* arg3,
                     const char* arg4,
                     const char* arg5,
                     const char* arg6) {
  if (kAllowedSQLiteActionCodes.count(code) > 0) {
    return SQLITE_OK;
  }
  LOG(ERROR) << "Authorizer denied action " << code << " "
             << (arg3 ? arg3 : "null") << " " << (arg4 ? arg4 : "null") << " "
             << (arg5 ? arg5 : "null") << " " << (arg6 ? arg6 : "null");
  return SQLITE_DENY;
}


  auto rc = sqlite3_set_authorizer(db, &sqliteAuthorizer, nullptr);
  if (rc != SQLITE_OK) {
    LOG(ERROR) << "Failed to set sqlite authorizer: " << sqlite3_errmsg(db);
    requestShutdown(rc);
  }

int sqliteAuthorizer(void* userData,
                     int code,
                     const char* arg3,
                     const char* arg4,
                     const char* arg5,
                     const char* arg6);

// Allowlist of SQLite actions. Any action not in this set is denied. See
// possible values in https://sqlite.org/c3ref/c_alter_table.html.
// ** Never allow SQLITE_ATTACH ** as it can be used to write arbitrary files.
const std::unordered_set<int> kAllowedSQLiteActionCodes = {
    // Enable basic functionality
    SQLITE_READ,
    SQLITE_SELECT,

    // Some extensions implement writeable tables
    SQLITE_INSERT,
    SQLITE_UPDATE,
    SQLITE_DELETE,

    // Allow virtual tables to be attached
    SQLITE_CREATE_VTABLE,
    SQLITE_DROP_VTABLE,

    // Users may sometimes want to create tables and views
    SQLITE_CREATE_VIEW,
    SQLITE_DROP_VIEW,
    SQLITE_CREATE_TABLE,
    SQLITE_DROP_TABLE,
    SQLITE_CREATE_TEMP_TABLE,
    SQLITE_DROP_TEMP_TABLE,
    SQLITE_CREATE_TEMP_VIEW,
    SQLITE_DROP_TEMP_VIEW,

    // Required to allow calling functions in SQL
    SQLITE_FUNCTION,

    // Required for recursive queries
    SQLITE_RECURSIVE,
};


      "CREATE TABLE test_table (username varchar(30), age int)",
      "CREATE TABLE test_types_table (username varchar(30), age "
TEST_F(SQLiteUtilTests, test_sqlite_authorizer) {
  auto rc = sqliteAuthorizer(
      nullptr, SQLITE_ATTACH, nullptr, nullptr, nullptr, nullptr);
  EXPECT_EQ(SQLITE_DENY, rc);

  rc = sqliteAuthorizer(nullptr, 534, nullptr, nullptr, nullptr, nullptr);
  EXPECT_EQ(SQLITE_DENY, rc);

  rc = sqliteAuthorizer(
      nullptr, SQLITE_SELECT, nullptr, nullptr, nullptr, nullptr);
  EXPECT_EQ(SQLITE_OK, rc);
}

  rc = sqlite3_set_authorizer(db, &sqliteAuthorizer, nullptr);
  if (rc != SQLITE_OK) {
    sqlite3_close(db);
    auto errMsg =
        std::string("Failed to set sqlite authorizer: ") + sqlite3_errmsg(db);
    return Status(1, errMsg);
  }