module.php 31 KB

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