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