module.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. <?php namespace pineapple;
  2. require_once('/pineapple/modules/PineAP/api/PineAPHelper.php');
  3. class PineAP extends SystemModule
  4. {
  5. const EAP_USER_FILE = "/etc/pineape/hostapd-pineape.eap_user";
  6. private $pineAPHelper;
  7. private $dbConnection;
  8. public function __construct($request)
  9. {
  10. parent::__construct($request, __CLASS__);
  11. $this->pineAPHelper = new PineAPHelper();
  12. $this->dbConnection = false;
  13. $dbLocation = $this->uciGet("pineap.@config[0].ssid_db_path");
  14. if (file_exists($dbLocation)) {
  15. $this->dbConnection = new DatabaseConnection($dbLocation);
  16. }
  17. }
  18. public function route()
  19. {
  20. switch ($this->request->action) {
  21. case 'getPool':
  22. $this->getPool();
  23. break;
  24. case 'clearPool':
  25. $this->clearPool();
  26. break;
  27. case 'addSSID':
  28. $this->addSSID();
  29. break;
  30. case 'addSSIDs':
  31. $this->addSSIDs();
  32. break;
  33. case 'removeSSID':
  34. $this->removeSSID();
  35. break;
  36. case 'getPoolLocation':
  37. $this->getPoolLocation();
  38. break;
  39. case 'setPoolLocation':
  40. $this->setPoolLocation();
  41. break;
  42. case 'clearSessionCounter':
  43. $this->clearSessionCounter();
  44. break;
  45. case 'setPineAPSettings':
  46. $this->setPineAPSettings();
  47. break;
  48. case 'getPineAPSettings':
  49. $this->getPineAPSettings();
  50. break;
  51. case 'setEnterpriseSettings':
  52. $this->setEnterpriseSettings();
  53. break;
  54. case 'getEnterpriseSettings':
  55. $this->getEnterpriseSettings();
  56. break;
  57. case 'detectEnterpriseCertificate':
  58. $this->detectEnterpriseCertificate();
  59. break;
  60. case 'generateEnterpriseCertificate':
  61. $this->generateEnterpriseCertificate();
  62. break;
  63. case 'clearEnterpriseCertificate':
  64. $this->clearEnterpriseCertificate();
  65. break;
  66. case 'clearEnterpriseDB':
  67. $this->clearEnterpriseDB();
  68. break;
  69. case 'getEnterpriseData':
  70. $this->getEnterpriseData();
  71. break;
  72. case 'startHandshakeCapture':
  73. $this->startHandshakeCapture();
  74. break;
  75. case 'stopHandshakeCapture':
  76. $this->stopHandshakeCapture();
  77. break;
  78. case 'getHandshake':
  79. $this->getHandshake();
  80. break;
  81. case 'getAllHandshakes':
  82. $this->getAllHandshakes();
  83. break;
  84. case 'checkCaptureStatus':
  85. $this->checkCaptureStatus();
  86. break;
  87. case 'downloadHandshake':
  88. $this->downloadHandshake();
  89. break;
  90. case 'downloadAllHandshakes':
  91. $this->downloadAllHandshakes();
  92. break;
  93. case 'clearAllHandshakes':
  94. $this->clearAllHandshakes();
  95. break;
  96. case 'deleteHandshake':
  97. $this->deleteHandshake();
  98. break;
  99. case 'deauth':
  100. $this->deauth();
  101. break;
  102. case 'enable':
  103. $this->enable();
  104. break;
  105. case 'disable':
  106. $this->disable();
  107. break;
  108. case 'enableAutoStart':
  109. $this->enableAutoStart();
  110. break;
  111. case 'disableAutoStart':
  112. $this->disableAutoStart();
  113. break;
  114. case 'downloadPineAPPool':
  115. $this->downloadPineAPPool();
  116. break;
  117. case 'loadProbes':
  118. $this->loadProbes();
  119. break;
  120. case 'inject':
  121. $this->inject();
  122. break;
  123. case 'countSSIDs':
  124. $this->countSSIDs();
  125. break;
  126. case 'downloadJTRHashes':
  127. $this->downloadJTRHashes();
  128. break;
  129. case 'downloadHashcatHashes':
  130. $this->downloadHashcatHashes();
  131. break;
  132. }
  133. }
  134. private function toggleComment($fileName, $lineNumber, $comment)
  135. {
  136. $data = file_get_contents($fileName);
  137. $lines = explode("\n", $data);
  138. $line = $lines[$lineNumber - 1];
  139. if (substr($line, 0, 1) === "#") {
  140. if ($comment) {
  141. return;
  142. }
  143. $line = substr($line, 1);
  144. } else {
  145. if (!$comment) {
  146. return;
  147. }
  148. $line = '#' . $line;
  149. }
  150. $lines[$lineNumber - 1] = $line;
  151. file_put_contents($fileName, join("\n", $lines));
  152. }
  153. private function isCommented($fileName, $lineNumber)
  154. {
  155. $data = file_get_contents($fileName);
  156. $lines = explode("\n", $data);
  157. $line = $lines[$lineNumber - 1];
  158. $ret = substr($line, 0, 1) === "#";
  159. return $ret;
  160. }
  161. private function getDowngradeType()
  162. {
  163. if (!$this->isCommented(PineAP::EAP_USER_FILE, 6)) {
  164. return "MSCHAPV2";
  165. } else if (!$this->isCommented(PineAP::EAP_USER_FILE, 5)) {
  166. return "GTC";
  167. } else {
  168. return "DISABLE";
  169. }
  170. }
  171. private function enableMSCHAPV2Downgrade()
  172. {
  173. $this->toggleComment(PineAP::EAP_USER_FILE, 4, true);
  174. $this->toggleComment(PineAP::EAP_USER_FILE, 5, true);
  175. $this->toggleComment(PineAP::EAP_USER_FILE, 6, false);
  176. }
  177. private function enableGTCDowngrade()
  178. {
  179. $this->toggleComment(PineAP::EAP_USER_FILE, 4, true);
  180. $this->toggleComment(PineAP::EAP_USER_FILE, 5, false);
  181. $this->toggleComment(PineAP::EAP_USER_FILE, 6, true);
  182. }
  183. private function disableDowngrade()
  184. {
  185. $this->toggleComment(PineAP::EAP_USER_FILE, 4, false);
  186. $this->toggleComment(PineAP::EAP_USER_FILE, 5, true);
  187. $this->toggleComment(PineAP::EAP_USER_FILE, 6, true);
  188. }
  189. private function loadProbes()
  190. {
  191. if (!\helper\checkRunningFull("/usr/sbin/pineapd")) {
  192. $this->response = array('success' => false, 'reason' => "not running");
  193. return;
  194. }
  195. $mac = strtolower($this->request->mac);
  196. $probesArray = array();
  197. exec("/usr/bin/pineap list_probes ${mac}", $output);
  198. foreach ($output as $probeSSID) {
  199. array_push($probesArray, $probeSSID);
  200. }
  201. $this->response = array('success' => true, 'probes' => implode("\n", array_unique($probesArray)));
  202. }
  203. private function downloadPineAPPool()
  204. {
  205. $poolLocation = '/tmp/ssid_pool.txt';
  206. $data = $this->getPoolData();
  207. file_put_contents($poolLocation, $data);
  208. $this->response = array("download" => $this->downloadFile($poolLocation));
  209. }
  210. private function countSSIDs()
  211. {
  212. $this->response = array(
  213. 'SSIDs' => substr_count($this->getPoolData(), "\n"),
  214. 'newSSIDs' => substr_count($this->getNewPoolData(), "\n")
  215. );
  216. }
  217. private function enable()
  218. {
  219. $this->pineAPHelper->enablePineAP();
  220. $this->response = array("success" => true);
  221. }
  222. private function disable()
  223. {
  224. $this->pineAPHelper->disablePineAP();
  225. $this->response = array("success" => true);
  226. }
  227. private function enableAutoStart()
  228. {
  229. $this->uciSet("pineap.@config[0].autostart", 1);
  230. $this->response = array("success" => true);
  231. }
  232. private function disableAutoStart()
  233. {
  234. $this->uciSet("pineap.@config[0].autostart", 0);
  235. $this->response = array("success" => true);
  236. }
  237. private function checkPineAP()
  238. {
  239. if (!$this->checkRunningFull('/usr/sbin/pineapd')) {
  240. $this->response = array('error' => 'Please start PineAP', 'success' => false);
  241. return false;
  242. }
  243. return true;
  244. }
  245. private function deauth()
  246. {
  247. if ($this->checkPineAP()) {
  248. $sta = $this->request->sta;
  249. $clients = $this->request->clients;
  250. $multiplier = intval($this->request->multiplier);
  251. $channel = $this->request->channel;
  252. $success = false;
  253. if (empty($clients)) {
  254. $this->response = array('error' => 'This AP has no clients', 'success' => false);
  255. return;
  256. }
  257. foreach ($clients as $client) {
  258. $mac = $client;
  259. if (isset($client->mac)) {
  260. $mac = $client->mac;
  261. }
  262. $success = $this->pineAPHelper->deauth($mac, $sta, $channel, $multiplier);
  263. }
  264. if ($success) {
  265. $this->response = array('success' => true);
  266. }
  267. } else {
  268. $this->response = array('error' => 'Please start PineAP', 'success' => false);
  269. }
  270. }
  271. private function getPoolData()
  272. {
  273. $ssidPool = "";
  274. $rows = $this->dbConnection->query('SELECT * FROM ssids;');
  275. if (!isset($rows['databaseQueryError'])) {
  276. foreach ($rows as $row) {
  277. $ssidPool .= $row['ssid'] . "\n";
  278. }
  279. }
  280. return $ssidPool;
  281. }
  282. private function getNewPoolData()
  283. {
  284. $ssidPool = "";
  285. $rows = $this->dbConnection->query('SELECT * FROM ssids WHERE new_ssid=1;');
  286. if (!isset($rows['databaseQueryError'])) {
  287. foreach ($rows as $row) {
  288. $ssidPool .= $row['ssid'] . "\n";
  289. }
  290. }
  291. return $ssidPool;
  292. }
  293. private function getPool()
  294. {
  295. $this->response = array('ssidPool' => $this->getPoolData(), 'success' => true);
  296. }
  297. private function clearPool()
  298. {
  299. $this->checkPineAP();
  300. $this->dbConnection->query('DELETE FROM ssids;');
  301. $this->response = array('success' => true);
  302. }
  303. private function addSSID()
  304. {
  305. $this->checkPineAP();
  306. $ssid = $this->request->ssid;
  307. $created_date = date('Y-m-d H:i:s');
  308. if (strlen($ssid) < 1 || strlen($ssid) > 32) {
  309. $this->error = 'Your SSID must have a length greater than 1 and less than 32.';
  310. } else {
  311. @$this->dbConnection->query("INSERT INTO ssids (ssid, created_at) VALUES ('%s', '%s')", $ssid, $created_date);
  312. $this->response = array('success' => true);
  313. }
  314. }
  315. private function addSSIDs()
  316. {
  317. $this->checkPineAP();
  318. $ssidList = $this->request->ssids;
  319. $created_date = date('Y-m-d H:i:s');
  320. foreach ($ssidList as $ssid) {
  321. if (strlen($ssid) >= 1 && strlen($ssid) <= 32) {
  322. @$this->dbConnection->query("INSERT INTO ssids (ssid, created_at) VALUES ('%s', '%s');", $ssid, $created_date);
  323. }
  324. }
  325. $this->response = array('success' => true);
  326. }
  327. private function removeSSID()
  328. {
  329. $this->checkPineAP();
  330. $ssid = $this->request->ssid;
  331. if (strlen($ssid) < 1 || strlen($ssid) > 32) {
  332. $this->error = 'Your SSID must have a length greater than 1 and less than 32.';
  333. } else {
  334. $this->dbConnection->query("DELETE FROM ssids WHERE ssid='%s';", $ssid);
  335. $this->response = array('success' => true);
  336. }
  337. }
  338. private function getPoolLocation()
  339. {
  340. $dbBasePath = dirname($this->uciGet("pineap.@config[0].ssid_db_path"));
  341. $this->response = array('poolLocation' => $dbBasePath . "/");
  342. }
  343. private function setPoolLocation()
  344. {
  345. $dbLocation = dirname($this->request->location . '/fake_file');
  346. $this->uciSet("pineap.@config[0].ssid_db_path", $dbLocation . '/pineapple.db');
  347. $this->response = array('success' => true);
  348. }
  349. private function clearSessionCounter()
  350. {
  351. $ret = 0;
  352. $output = array();
  353. exec('/usr/sbin/resetssids', $output, $ret);
  354. if ($ret !== 0) {
  355. $this->error = "Could not clear SSID session counter.";
  356. } else {
  357. $this->response = array('success' => true);
  358. }
  359. }
  360. private function getPineAPSettings()
  361. {
  362. $sourceMAC = $this->pineAPHelper->getSource();
  363. $sourceMAC = $sourceMAC === false ? '00:00:00:00:00:00' : $sourceMAC;
  364. $targetMAC = $this->pineAPHelper->getTarget();
  365. $targetMAC = $targetMAC === false ? 'FF:FF:FF:FF:FF:FF' : $targetMAC;
  366. $settings = array(
  367. 'pineAPDaemon' => $this->checkRunning("pineapd"),
  368. 'autostartPineAP' => $this->uciGet("pineap.@config[0].autostart"),
  369. 'allowAssociations' => $this->pineAPHelper->getSetting("karma"),
  370. 'logEvents' => $this->pineAPHelper->getSetting("logging"),
  371. 'beaconResponses' => $this->pineAPHelper->getSetting("beacon_responses"),
  372. 'captureSSIDs' => $this->pineAPHelper->getSetting("capture_ssids"),
  373. 'broadcastSSIDs' => $this->pineAPHelper->getSetting("broadcast_ssid_pool"),
  374. 'connectNotifications' => $this->pineAPHelper->getSetting("connect_notifications"),
  375. 'disconnectNotifications' => $this->pineAPHelper->getSetting("disconnect_notifications"),
  376. 'broadcastInterval' => $this->pineAPHelper->getSetting("beacon_interval"),
  377. 'responseInterval' => $this->pineAPHelper->getSetting("beacon_response_interval"),
  378. 'monitorInterface' => $this->pineAPHelper->getSetting("pineap_interface"),
  379. 'sourceInterface' => $this->uciGet("pineap.@config[0].pineap_source_interface"),
  380. 'sourceMAC' => strtoupper($sourceMAC),
  381. 'targetMAC' => strtoupper($targetMAC),
  382. );
  383. $this->response = array('settings' => $settings, 'success' => true);
  384. return $settings;
  385. }
  386. private function setPineAPSettings()
  387. {
  388. $settings = $this->request->settings;
  389. if ($settings->allowAssociations) {
  390. $this->pineAPHelper->enableAssociations();
  391. $this->uciSet("pineap.@config[0].karma", 'on');
  392. } else {
  393. $this->pineAPHelper->disableAssociations();
  394. $this->uciSet("pineap.@config[0].karma", 'off');
  395. }
  396. if ($settings->logEvents) {
  397. $this->pineAPHelper->enableLogging();
  398. $this->uciSet("pineap.@config[0].logging", 'on');
  399. } else {
  400. $this->pineAPHelper->disableLogging();
  401. $this->uciSet("pineap.@config[0].logging", 'off');
  402. }
  403. if ($settings->beaconResponses) {
  404. $this->pineAPHelper->enableResponder();
  405. $this->uciSet("pineap.@config[0].beacon_responses", 'on');
  406. } else {
  407. $this->pineAPHelper->disableResponder();
  408. $this->uciSet("pineap.@config[0].beacon_responses", 'off');
  409. }
  410. if ($settings->captureSSIDs) {
  411. $this->pineAPHelper->enableHarvester();
  412. $this->uciSet("pineap.@config[0].capture_ssids", 'on');
  413. } else {
  414. $this->pineAPHelper->disableHarvester();
  415. $this->uciSet("pineap.@config[0].capture_ssids", 'off');
  416. }
  417. if ($settings->broadcastSSIDs) {
  418. $this->pineAPHelper->enableBeaconer();
  419. $this->uciSet("pineap.@config[0].broadcast_ssid_pool", 'on');
  420. } else {
  421. $this->pineAPHelper->disableBeaconer();
  422. $this->uciSet("pineap.@config[0].broadcast_ssid_pool", 'off');
  423. }
  424. if ($settings->connectNotifications) {
  425. $this->pineAPHelper->enableConnectNotifications();
  426. $this->uciSet("pineap.@config[0].connect_notifications", 'on');
  427. } else {
  428. $this->pineAPHelper->disableConnectNotifications();
  429. $this->uciSet("pineap.@config[0].connect_notifications", 'off');
  430. }
  431. if ($settings->disconnectNotifications) {
  432. $this->pineAPHelper->enableDisconnectNotifications();
  433. $this->uciSet("pineap.@config[0].disconnect_notifications", 'on');
  434. } else {
  435. $this->pineAPHelper->disableDisconnectNotifications();
  436. $this->uciSet("pineap.@config[0].disconnect_notifications", 'off');
  437. }
  438. $this->pineAPHelper->setBeaconInterval($settings->broadcastInterval);
  439. $this->uciSet("pineap.@config[0].beacon_interval", $settings->broadcastInterval);
  440. $this->pineAPHelper->setResponseInterval($settings->responseInterval);
  441. $this->uciSet("pineap.@config[0].beacon_response_interval", $settings->responseInterval);
  442. $this->pineAPHelper->setTarget($settings->targetMAC);
  443. $this->uciSet("pineap.@config[0].target_mac", $settings->targetMAC);
  444. $this->pineAPHelper->setSource($settings->sourceMAC);
  445. $this->uciSet("pineap.@config[0].pineap_mac", $settings->sourceMAC);
  446. $this->uciSet("pineap.@config[0].pineap_source_interface", $settings->sourceInterface);
  447. $this->pineAPHelper->setPineapInterface($settings->monitorInterface);
  448. $this->uciSet("pineap.@config[0].pineap_interface", $settings->monitorInterface);
  449. $this->response = array("success" => true);
  450. }
  451. private function detectEnterpriseCertificate()
  452. {
  453. if (file_exists('/etc/pineape/certs/server.crt')) {
  454. $this->response = array("installed" => true);
  455. } else {
  456. $this->response = array("installed" => false);
  457. }
  458. }
  459. private function generateEnterpriseCertificate()
  460. {
  461. $params = $this->request->certSettings;
  462. $state = $params->state;
  463. $country = $params->country;
  464. $locality = $params->locality;
  465. $organization = $params->organization;
  466. $email = $params->email;
  467. $commonname = $params->commonname;
  468. if ((strlen($state) < 1 || strlen($state) > 32) ||
  469. (strlen($country) < 2 || strlen($country) > 2) ||
  470. (strlen($locality) < 1 || strlen($locality) > 32) ||
  471. (strlen($organization) < 1 || strlen($organization) > 32) ||
  472. (strlen($email) < 1 || strlen($email) > 32) ||
  473. (strlen($commonname) < 1 || strlen($commonname) > 32)) {
  474. $this->error = "Invalid settings provided.";
  475. return;
  476. }
  477. $state = escapeshellarg($params->state);
  478. $country = escapeshellarg($params->country);
  479. $locality = escapeshellarg($params->locality);
  480. $organization = escapeshellarg($params->organization);
  481. $email = escapeshellarg($params->email);
  482. $commonname = escapeshellarg($params->commonname);
  483. exec("cd /etc/pineape/certs && ./clean.sh");
  484. exec("/etc/pineape/certs/configure.sh -p pineapplesareyummy -c ${country} -s ${state} -l ${locality} -o ${organization} -e ${email} -n ${commonname}");
  485. $this->execBackground("/etc/pineape/certs/bootstrap.sh");
  486. $this->response = array("success" => true);
  487. }
  488. private function clearEnterpriseCertificate()
  489. {
  490. exec("cd /etc/pineape/certs && ./clean.sh");
  491. $this->uciSet("wireless.@wifi-iface[2].disabled", "1");
  492. $this->execBackground("wifi down radio0 && wifi up radio0");
  493. $this->response = array("success" => true);
  494. }
  495. private function clearEnterpriseDB()
  496. {
  497. $dbLocation = "/etc/pineapple/pineape.db";
  498. $this->dbConnection = new DatabaseConnection($dbLocation);
  499. $this->dbConnection->exec("DELETE FROM chalresp; DELETE FROM basic;");
  500. $this->response = array("success" => true);
  501. }
  502. private function getEnterpriseSettings()
  503. {
  504. $settings = array(
  505. 'enabled' => $this->getEnterpriseRunning(),
  506. 'enableAssociations' => $this->getEnterpriseAllowAssocs(),
  507. 'ssid' => $this->uciGet('wireless.@wifi-iface[2].ssid'),
  508. 'mac' => $this->uciGet('wireless.@wifi-iface[2].macaddr'),
  509. 'encryptionType' => $this->uciGet('wireless.@wifi-iface[2].encryption'),
  510. 'downgrade' => $this->getDowngradeType(),
  511. );
  512. $this->response = array("settings" => $settings);
  513. }
  514. private function setEnterpriseSettings()
  515. {
  516. $settings = $this->request->settings;
  517. if ((strlen($settings->ssid) < 1 || strlen($settings->ssid) > 32) ||
  518. (strlen($settings->mac) < 17 || strlen($settings->mac) > 17)) {
  519. $this->error = "Invalid settings provided.";
  520. return;
  521. }
  522. $this->uciSet("wireless.@wifi-iface[2].ssid", $settings->ssid);
  523. $this->uciSet("wireless.@wifi-iface[2].macaddr", $settings->mac);
  524. $this->uciSet("wireless.@wifi-iface[2].encryption", $settings->encryptionType);
  525. if ($settings->enabled) {
  526. $this->uciSet("wireless.@wifi-iface[2].disabled", "0");
  527. } else {
  528. $this->uciSet("wireless.@wifi-iface[2].disabled", "1");
  529. }
  530. if ($settings->enableAssociations) {
  531. $this->uciSet("pineap.@config[0].pineape_passthrough", "on");
  532. } else {
  533. $this->uciSet("pineap.@config[0].pineape_passthrough", "off");
  534. }
  535. switch (strtoupper($settings->downgrade)) {
  536. case "MSCHAPV2":
  537. $this->enableMSCHAPV2Downgrade();
  538. break;
  539. case "GTC":
  540. $this->enableGTCDowngrade();
  541. break;
  542. case "DISABLE":
  543. default:
  544. $this->disableDowngrade();
  545. }
  546. $this->execBackground("wifi down radio0 && wifi up radio0");
  547. $this->response = array("success" => true);
  548. }
  549. private function getEnterpriseData()
  550. {
  551. $dbLocation = "/etc/pineapple/pineape.db";
  552. $this->dbConnection = new DatabaseConnection($dbLocation);
  553. $chalrespdata = array();
  554. $rows = $this->dbConnection->query("SELECT type, username, hex(challenge), hex(response) FROM chalresp;");
  555. foreach ($rows as $row) {
  556. $x = array();
  557. $x['type'] = $row['type'];
  558. $x['username'] = $row['username'];
  559. $x['challenge'] = $row['hex(challenge)'];
  560. $x['response'] = $row['hex(response)'];
  561. array_push($chalrespdata, $x);
  562. }
  563. $basicdata = array();
  564. $rows = $this->dbConnection->query("SELECT type, identity, password FROM basic;");
  565. foreach ($rows as $row) {
  566. $x = array();
  567. $x['type'] = $row['type'];
  568. $x['username'] = $row['identity'];
  569. $x['password'] = $row['password'];
  570. array_push($basicdata, $x);
  571. }
  572. $this->response = array("success" => true, "chalrespdata" => $chalrespdata, "basicdata" => $basicdata);
  573. }
  574. private function downloadJTRHashes()
  575. {
  576. $jtrLocation = '/tmp/enterprise_jtr.txt';
  577. $dbLocation = "/etc/pineapple/pineape.db";
  578. $this->dbConnection = new DatabaseConnection($dbLocation);
  579. $data = array();
  580. $rows = $this->dbConnection->query("SELECT type, username, hex(challenge), hex(response) FROM chalresp;");
  581. foreach ($rows as $row) {
  582. if (strtoupper($row['type']) !== "MSCHAPV2" && strtoupper($row['type']) != "EAP-TTLS/MSCHAPV2") {
  583. continue;
  584. }
  585. $data[] = $row['username'] . ':$NETNTLM$' . $row['hex(challenge)'] . '$' . $row['hex(response)'];
  586. }
  587. file_put_contents($jtrLocation, join("\n", $data));
  588. $this->response = array("download" => $this->downloadFile($jtrLocation));
  589. }
  590. private function downloadHashcatHashes()
  591. {
  592. $hashcatLocation = '/tmp/enterprise_hashcat.txt';
  593. $dbLocation = "/etc/pineapple/pineape.db";
  594. $this->dbConnection = new DatabaseConnection($dbLocation);
  595. $data = array();
  596. $rows = $this->dbConnection->query("SELECT type, username, hex(challenge), hex(response) FROM chalresp;");
  597. foreach ($rows as $row) {
  598. if (strtoupper($row['type']) !== "MSCHAPV2" && strtoupper($row['type']) != "EAP-TTLS/MSCHAPV2") {
  599. continue;
  600. }
  601. $data[] = $row['username'] . '::::' . $row['hex(response)'] . ':' . $row['hex(challenge)'];
  602. }
  603. file_put_contents($hashcatLocation, join("\n", $data));
  604. $this->response = array("download" => $this->downloadFile($hashcatLocation));
  605. }
  606. private function getEnterpriseRunning()
  607. {
  608. exec("hostapd_cli -i wlan0-2 pineape_enable_status", $statusOutput);
  609. if ($statusOutput[0] == "ENABLED") {
  610. return true;
  611. }
  612. return false;
  613. }
  614. private function getEnterpriseAllowAssocs()
  615. {
  616. exec("hostapd_cli -i wlan0-2 pineape_auth_passthrough_status", $statusOutput);
  617. if ($statusOutput[0] == "ENABLED") {
  618. return true;
  619. }
  620. return false;
  621. }
  622. private function startHandshakeCapture()
  623. {
  624. $bssid = $this->request->bssid;
  625. $channel = $this->request->channel;
  626. // We already set $this->response in checkPineAP() if it isnt running.
  627. if ($this->checkPineAP()) {
  628. $this->execBackground("pineap /etc/pineap.conf handshake_capture_start ${bssid} ${channel}");
  629. $this->response = array('success' => true);
  630. }
  631. }
  632. private function stopHandshakeCapture()
  633. {
  634. $this->execBackground('pineap /tmp/pineap.conf handshake_capture_stop');
  635. $this->response = array('success' => true);
  636. }
  637. private function getHandshake()
  638. {
  639. $bssid = str_replace(':', '-', $this->request->bssid);
  640. if (file_exists("/tmp/handshakes/{$bssid}_full.pcap")) {
  641. $this->response = array('handshakeExists' => true, 'partial' => false);
  642. } else if (file_exists("/tmp/handshakes/{$bssid}_partial.pcap")) {
  643. $this->response = array('handshakeExists' => true, 'partial' => true);
  644. } else {
  645. $this->response = array('handshakeExists' => false);
  646. }
  647. }
  648. private function getAllHandshakes()
  649. {
  650. $handshakes = array();
  651. foreach (glob("/tmp/handshakes/*.pcap") as $handshake) {
  652. $handshake = str_replace(["/tmp/handshakes/", "_full.pcap", "_partial.pcap"], "", $handshake);
  653. $handshake = str_replace('-', ':', $handshake);
  654. $handshakes[] = $handshake;
  655. }
  656. $this->response = array("handshakes" => $handshakes);
  657. }
  658. private function downloadAllHandshakes()
  659. {
  660. @unlink('/tmp/handshakes/handshakes.tar.gz');
  661. exec("tar -czf /tmp/handshakes/handshakes.tar.gz -C /tmp/handshakes .");
  662. $this->response = array("download" => $this->downloadFile("/tmp/handshakes/handshakes.tar.gz"));
  663. }
  664. private function clearAllHandshakes()
  665. {
  666. @unlink("/tmp/handshakes/handshakes.tar.gz");
  667. foreach (glob("/tmp/handshakes/*.pcap") as $handshake) {
  668. unlink($handshake);
  669. }
  670. $this->response = array("success" => true);
  671. }
  672. private function checkCaptureStatus()
  673. {
  674. $bssid = $this->request->bssid;
  675. exec("pineap /tmp/pineap.conf get_status", $status_output);
  676. if ($status_output[0] === "PineAP is not running") {
  677. $this->error = "PineAP is not running";
  678. return 0;
  679. } else {
  680. $status_output = implode("\n", $status_output);
  681. $status_output = json_decode($status_output, true);
  682. if ($status_output['captureRunning'] === true && $status_output['bssid'] === $bssid) {
  683. // A scan is running for the supplied BSSID.
  684. $this->response = array('running' => true, 'currentBSSID' => true, 'bssid' => $status_output['bssid']);
  685. return 3;
  686. } elseif ($status_output['captureRunning'] === true) {
  687. // A scan is running, but not for this BSSID.
  688. $this->response = array('running' => true, 'currentBSSID' => false, 'bssid' => $status_output['bssid']);
  689. return 2;
  690. } else {
  691. // No scan is running.
  692. $this->response = array('running' => false, 'currentBSSID' => false);
  693. return 0;
  694. }
  695. }
  696. }
  697. private function downloadHandshake()
  698. {
  699. $bssid = str_replace(':', '-', $this->request->bssid);
  700. $type = $this->request->type;
  701. // JTR and hashcat don't care whether the data came from a full or partial handshake
  702. if ($type === "pcap") {
  703. $suffix = "_";
  704. if (file_exists("/tmp/handshakes/{$bssid}_full.pcap")) {
  705. $suffix .= "full";
  706. } else {
  707. $suffix .= "partial";
  708. }
  709. $this->response = array("download" => $this->downloadFile("/tmp/handshakes/{$bssid}{$suffix}.pcap"));
  710. } else {
  711. $this->response = array("download" => $this->downloadFile("/tmp/handshakes/{$bssid}.{$type}"));
  712. }
  713. }
  714. private function deleteHandshake()
  715. {
  716. $bssid = str_replace(':', '-', $this->request->bssid);
  717. @unlink("/tmp/handshakes/${bssid}_full.pcap");
  718. @unlink("/tmp/handshakes/${bssid}_partial.pcap");
  719. @unlink("/tmp/handshakes/${bssid}.hccap");
  720. @unlink("/tmp/handshakes/${bssid}.txt");
  721. $this->response = array('success' => true);
  722. }
  723. private function inject()
  724. {
  725. $payload = preg_replace('/[^A-Fa-f0-9]/', '', $this->request->payload);
  726. if (hex2bin($payload) === false) {
  727. $this->error = 'Invalid hex';
  728. return;
  729. }
  730. if (!$this->checkRunningFull('/usr/sbin/pineapd')) {
  731. $this->error = 'Please start PineAP';
  732. return;
  733. }
  734. file_put_contents('/tmp/inject', $payload);
  735. $channel = intval($this->request->channel);
  736. $frameCount = intval($this->request->frameCount);
  737. $delay = intval($this->request->delay);
  738. $descriptorspec = array(
  739. 0 => array("pipe", "r"),
  740. 1 => array("pipe", "w"),
  741. 2 => array("pipe", "w"),
  742. );
  743. $cmd = "/usr/bin/pineap /tmp/pineap.conf inject /tmp/inject ${channel} ${frameCount} ${delay}";
  744. $process = proc_open($cmd, $descriptorspec, $pipes);
  745. if (!is_resource($process)) {
  746. $this->response = array('error' => "Failed to spawn process for command: ${cmd}", 'command' => $cmd);
  747. unlink('/tmp/inject');
  748. return;
  749. }
  750. fwrite($pipes[0], $payload);
  751. fclose($pipes[0]);
  752. $output = stream_get_contents($pipes[1]);
  753. $errorOutput = stream_get_contents($pipes[2]);
  754. $exitCode = proc_close($process);
  755. if (empty($output)) {
  756. $this->response = array(
  757. 'success' => true,
  758. 'request' => $this->request,
  759. 'payload' => json_encode($payload),
  760. 'command' => $cmd
  761. );
  762. } else {
  763. $this->response = array(
  764. 'error' => 'PineAP cli did not execute successfully',
  765. 'command' => $cmd,
  766. 'exitCode' => $exitCode,
  767. 'stdout' => $output,
  768. 'stderr' => $errorOutput
  769. );
  770. }
  771. unlink('/tmp/inject');
  772. }
  773. }