Researchers have found that several thousand Oracle NetSuite customers are inadvertently leaking sensitive data to unauthenticated users through externally facing stores built with NetSuite SuiteCommerce or NetSuite Site Builder. The exposure is likely caused by a deficient understanding of access controls for custom record types in NetSuite, one of the most popular SaaS enterprise resource planning (ERP) platforms in use today.
“Based on my initial investigations, several thousand live public SuiteCommerce websites are already affected,” Aaron Costello, chief of SaaS security research at security firm AppOmni, wrote in a report. “In many such cases, organizations using NetSuite that had no intention of deploying a commercial store were entirely unaware that a default stock website had been deployed publicly upon purchase of their instance. From my observations of these sites, the most commonly exposed form of sensitive data has been PII of registered customers, which included full addresses and mobile phone numbers.”
The attack vector found by Costello leverages legitimate NetSuite SuiteScript API functionality to access data in custom record types (CRTs) that NetSuite customers created but did not properly secure. The researcher previously uncovered other attack vectors that exposed data in other enterprise SaaS platforms such as Salesforce and ServiceNow.
Costello told CSO he shared his findings with NetSuite but was told the functionality was working as intended. NetSuite has not yet responded to a request for comment sent by CSO.
Technically these data exposures are the result of access control misconfigurations rather than a vulnerability, but the occurrence rate of these misconfigurations appears to be quite high, which could suggest that NetSuite access controls for CRTs are commonly misunderstood.
“My research found that thousands of these organizations are leaking sensitive customer data to the public through misconfigurations in their access controls,” Costello said. “The sheer scale at which I found these exposures to be occurring is significant.”
What are custom record types and how do they work
NetSuite provides a set of data tables that exist by default in every deployment. These are known as Standard Record Types (SRTs) and cannot be accessed by unauthenticated users. However, the platform also allows customers to create Custom Record Types (CRTs) for storing custom data, which is useful because they can be configured for unauthenticated access, for example on a public website.
NetSuite allows users to create online stores that are directly linked with their back-office operations, such as supply chain management, enabling consumers or other businesses to browse and order products directly in a way that streamlines processing, fulfillment, and inventory management.
CRTs have table-level access controls and field-level access controls. This gives a lot of flexibility, because it means someone can set the whole table to be accessible by unauthenticated users, but then set strict permissions on certain sensitive fields from the table that should require authentication.
CRTs can have their Access Type definition set to “Require Custom Record Entries Permission,” “Use Permission List,” or “No Permission Required.” Of these, only the “No Permission Required” setting enables unauthenticated access, the other two being role-based permissions that unauthenticated users can’t access. As a result, having CRTs with the “No Permission Required” Access Type is a common occurrence, if data in those tables is intended to be listed on a public store.
Individual custom fields created inside CRTs can also have their own permissions based on a Role, Department, or Subsidiary (RDS) list. However, since unauthenticated users do not have a role, department, or subsidiary, custom fields created in CRTs with the “No Permission Required” will automatically be granted the “Default Access Level” and “Default Level for Search / Reporting” permissions.
“This can catch many people out,” Costello said. “When a new field is created, the default level set for these two fields is set to the highest possible level — Edit.”
How does this lead to misconfigurations?
Let’s assume an administrator creates a CRT with “No Permissions Required.” In adding custom fields, he wants some fields to be readable by unauthenticated users, so he sets their Default Access Level to View; other fields that should not be readable, he sets Default Access Level to None, assuming the job is done.
This would be incorrect because the “Default Level for Search / Reporting” (DLSR) setting is still Edit, even if Default Access Level is set to None. And this, Costello shows, can be abused through the NetSuite API to read the data in that field. The confusion here could be caused by the fact that fields with Default Access Level set to None cannot have their data read through the SuiteScript API loadRecord function, which is part of the N/record module and contains the most popular functions for performing CRUD (create, read, update, delete) operations on individual records.
But there is a different API function called nlapiSearchRecord, part of the N/search module, that can also be used to read data from record fields, and the permission for this API is defined by the DLSR setting. The difference is that reading field values with nlapiSearchRecord requires knowing the field name, while reading data via loadRecord requires knowing the field ID. Luckily, the data obtainable from the two APIs complete each other.
In his proof-of-concept example, Costello first uses the nlapiSearchRecord to get a list of all field IDs inside a custom record, which is straightforward. He then uses loadRecord to query those field IDs for their data, but he won’t succeed for fields that have Default Access Level set to None. However, the response to loadRecord will contain all field names present in the record, including those with Access Level set to None inside the response in the sortedFields object. This is very helpful because he can then take the names of the inaccessible fields and try to access their data using nlapiSearchRecord.
Detection and mitigation
Organizations should first review which CRTs have a legitimate business reason to be left accessible to unauthenticated users rather than having their Access Type setting configured for role-based permissions.
For CRTs that need to be left with “No Permission Required,” individual fields should be evaluated and sensitive ones should have both their Default Access Level and Default Level for Search / Reporting set to None. This will make those fields inaccessible to all users, including administrators, so permissions will have to be reinstated using the RDS access control feature instead.
While performing this investigation and review, it’s a good idea to take the websites temporarily offline by using the NetSuite feature at Commerce > Websites > Websites List > Click Edit beside the site(s) you wish to take offline > Select Take Website Offline For Maintenance or Inactive. This will also disable the API endpoints that can be used to extract data.
“For this to work, all of the organization’s sites must be taken down,” Costello said. “If only one is taken down, the second site may be used to continue exfiltrating the same data. Additionally, password protecting the site will not prevent data exposure through the endpoints described in this blog post.”
According to the researcher, NetSuite does not currently provide readily available transaction logs that could be used by administrators to determine whether client-side APIs have been used maliciously. But log data might be available on request from NetSuite support if there is suspicion that an attack has occurred.
“Perhaps over time, NetSuite will introduce functionality which will freely allow export of this data in the same manner that Salesforce granted customers access to Aura API events shortly after I published my Salesforce research,” Costello said.