Skip to content

Updated to fix json stream bug in latest Pineapple Firmware. Updated … #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ Some hints:
- To find out more about the WiFi Pineapple API itself, set `debug = True` when instantiating your `Pineapple()`s and it will log the HTTP requests it makes
- Read the WiFi Pineapple php source located on your pineapple at `/pineapple/modules/$modulename/api/module.php` as well as the corresponding python files in this project

## Notes
This is currently undergoing a rewrite and some methods may appear to have duplicate functionality. This is being done to keep backward compatibility. Method names will mirror those shipped with Pineapple Firmware and associated Modules. Planning on adding some additional helpers. This README will be updated over time. Feel free to let me know if you want a specific feature.

## Examples:
##### Instantiate a Pineapple object:
<pre>
API_TOKEN = "xxxxxxxxxx..."
from pineapple.pineapple import Pineapple
fruit = Pineapple(API_TOKEN)
from pineapple import *
fruit = pineapple.Pineapple(API_TOKEN)
</pre>
##### Add a notification:
<pre>
Expand All @@ -27,5 +30,23 @@ fruit.getModule("pineap").enable()
<pre>
fruit.getModule("pineap").deauth('6e:74:64:69:63:6b', ['73:65:62:6b:69:6e', '6e:65:73:67:69:61'], 5, 1)
</pre>

##### Get SSID Pool
<pre>
p = fruit.getModule("pineap").getSSIDPool()
</pre>
Returns a dict. The pool is on the key "ssidPool" separated by newlines. To get a quick list, do the following:
<pre>
ssids = p['ssidPool'].split('\n')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't p['ssidPool'] return a list instead of a '\n'-joined string?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea probably but that's what's returned from the API raw. I've been using logging.getPineapLog() instead because it comes back structured with clients in json. I'll be publishing some more stuff and rewriting a bunch more of this library in my own repo as I get to it. I have to modify this PR most likely, as it wasn't backwards compatible with older firmware. The pineapple.api in mine is now but before submitting more PRs, I wanted to rewrite a few more things.

</pre>
##### Download Scans
Some support for download files via token was added to api and recon
<pre>
fruit.getModule("recon").downloadResults(1)
</pre>
Will return a dict with key "download" and a unique download token for the results of Scan ID 1. So knowing that,
you can call api.download now to get the results
<pre>
myScan = fruit.api.download(fruit.getModule("recon").downloadResults(1)['download'])
</pre>
myScan will be the raw file text. In the case of a recon scan, it is json. So you can make it more usable with json.loads(myScan)
*To generate API tokens, use the [API Tokens](https://github.com/735tesla/Pineapple-API-Tokens-Module/) module*
23 changes: 23 additions & 0 deletions pineapple/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

class Advanced(Module):
def __init__(self, api):
"""
Methods this module should have:

getResources
dropCaches
getUSB
getFstab
saveFstab
getCSS
saveCSS
formatSDCard
formatSDCardStatus
checkForUpgrade
downloadUpgrade
getDownloadStatus
performUpgrade
getCurrentVersion
checkApiToken
addApiToken
getApiTokens
revokeApiToken
"""

super(Advanced, self).__init__(api, 'Advanced')
def getResources(self):
return self.request('getResources')
Expand Down
20 changes: 19 additions & 1 deletion pineapple/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,25 @@ def request(self, type, module, action, args = None):
payload = json.dumps(requestObject)
resp = requests.post(self.url, data=payload, headers=self.headers)
try:
return json.loads(resp.text)
# Update for latest firmware:
# The latest firmware puts 6 junk chars in the beginning of the json stream
# Filtering that out.
if resp.text[0:6] == ")]}',\n":
return json.loads(resp.text[6:])
else:
return json.loads(resp.text)

except ValueError as e:
print("Error decoding: %".format(repr(resp.text)))
print e

def download(self, dFile):
"""
Download a file by token. Just returning text instead of json as a just in case.
Easy to handle by just grabbing the response object and hitting it with json.loads
-- This was added to be able to download recon scans but I'm sure someone else might have another use for it
"""
requestObject = {'apiToken': self.apiToken, 'download': dFile }
resp = requests.get(self.url, requestObject)
return resp.text

9 changes: 9 additions & 0 deletions pineapple/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
class Clients(Module):
def __init__(self, api):
super(Clients, self).__init__(api, 'Clients')

def getClientData(self):
"""
Return List of Clients
"""
return self.request('getClientData')

def kickClient(self, mac):
"""
Kick a client by mac address
"""
return self.request('kickClient', {'mac': mac})

66 changes: 63 additions & 3 deletions pineapple/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,81 @@
class Filters(Module):
def __init__(self, api):
super(Filters, self).__init__(api, 'Filters')

def getSSIDMode(self):
"""
Return SSID Mode
Allow or Deny
"""
return self.request('getSSIDData')

def getClientMode(self):
"""
Return Client Mode
Allow or Deny
"""
return self.request('getClientMode')

def getSSIDFilters(self):
"""
Return SSID Filters from DB
"""
return self.request('getSSIDFilters')

def getClientFilters(self):
"""
Return Client Filters from DB
"""
return self.request('getClientFilters')

def getClientData(self):
"""
Return array of mode and clientFilters
"""
return self.request('getClientData')

def getSSIDData(self):
"""
Return array of mode and ssidFilters
"""
return self.request('getSSIDData')
def toggleClientMode(self, allow):
mode = 'Allow' if enabled else 'Disallow'

def toggleClientMode(self, mode):
"""
Toggle Client Mode
Allow or Deny are valid values.
Case Sensitive
"""
return self.request('toggleClientMode', {'mode': mode})

def toggleSSIDMode(self, allow):
mode = 'Allow' if enabled else 'Disallow'
"""
Toggle SSID Mode
Allow or Deny are valid values
Case Sensisitve
"""
return self.request('toggleSSIDMode', {'mode': mode})

def addClient(self, mac):
"""
Add Client by Mac to Filters
"""
return self.request('addClient', {'mac': mac})

def removeClient(self, mac):
"""
Remove Client by Mac from Filters
"""
return self.request('removeClient', {'mac': mac})

def addSSID(self, ssid):
"""
Add SSID to Filters
"""
return self.request('addSSID', {'ssid': ssid})

def removeSSID(self, ssid):
"""
Remove SSID from Filters
"""
return self.request('removeSSID', {'ssid': ssid})
44 changes: 44 additions & 0 deletions pineapple/logging.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,59 @@
from module import Module

class Logging(Module):

def __init__(self, api):
super(Logging, self).__init__(api, 'Logging')

def getSyslog(self):
"""
Return raw syslog
"""
return self.request('getSyslog')

def getDmesg(self):
"""
Return raw dmesg
"""
return self.request('getDmesg')

def getReportingLog(self):
"""
Return raw reporting log
"""
return self.request('getReportingLog')

def getPineapLog(self):
"""
Return PineAP Log in JSON Format
"""
return self.request('getPineapLog')

def clearPineapLog(self):
"""
Clear PineAP Log
"""
return self.request('clearPineapLog')

def getPineapLogLocation(self):
"""
Return the path where PineAP Log is written

default: /tmp/
"""
return self.request('getPineapLogLocation')

def setPineapLogLocation(self, location):
"""
Update the path where PineAP Log is written

default: /tmp/
"""
return self.request('setPineapLogLocation', { 'location': location })

def downloadPineapLog(self):
"""
Return the download token for PineAP Log
"""
return self.request('downloadPineapLog')

14 changes: 13 additions & 1 deletion pineapple/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ class Networking(Module):
def __init__(self, api):
super(Networking, self).__init__(api, 'Networking')
def getRoutingTable(self):
"""
Return Routing Table
"""
return self.request('getRoutingTable')
def restartDNS(self):
return self.request('restartDNS')
Expand All @@ -16,6 +19,9 @@ def setHostname(self, hostname):
def resetWirelessConfig(self):
return self.request('resetWirelessConfig')
def getInterfaceList(self):
"""
Return List of Interfaces
"""
return self.request('getInterfaceList')
def saveAPConfig(self, apConfig):
return self.request('saveAPConfig', {'apConfig': apConfig})
Expand All @@ -26,15 +32,21 @@ def getMacData(self):
def setMac(self, mac):
return self.request('setMac', {'mac': mac})
def setRandomMac(self):
"""
Set a random mac
"""
return self.request('setRandomMac')
def resetMac(self):
return self.request('resetMac')
def scanForNetworks(self, interface):
return self.request('scanForNetworks', {'interface': interface})
def getClientInterfaces(self):
"""
Return list of Client Interfaces
"""
return self.request('getClientInterfaces')
def connectToAP(self, interface, security, password = ''):
return self.request('connectToAP', {'interface': interface, security: security, key: password})
return self.request('connectToAP', {'interface': interface, 'security': security, 'key': password})
def checkConnection(self):
return self.request('checkConnection')
def disconnect(self, interface):
Expand Down
Loading