Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Some hints:
##### 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 +27,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
Copy Markdown
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
Copy Markdown
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*
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

46 changes: 46 additions & 0 deletions pineapple/recon.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,52 @@ class Recon(Module):
def __init__(self, api):
super(Recon, self).__init__(api, 'Recon')
def getScanStatus(self, scanId, scanType):
""" This method is deprecated but left in for compatibility with older firmware """
return self.request('getScanStatus', {'scan': {'scanID': scanId, 'scanType': scanType}})
def startScan(self, scanType, scanDuration):
""" This method is deprecated but left in for compatibility with older firmware """
return self.request('startScan', {'scanType': scanType, 'scanDuration': scanDuration})
def startNormalScan(self, scanType, scanDuration):
""" Start a Normal Scan
Scan Type:
0 - 2.4 Ghz
1 - 5 Ghz
2 - Both
"""
def startLiveScan(self, scanType, scanDuration):
""" Start a Live Scan
Scan Type:
0 - 2.4 Ghz
1 - 5 Ghz
2 - Both
"""
return self.request('startNormalScan', { 'scanType': scanType, 'scanDuration': scanDuration})
def checkPineAPDaemon(self):
""" Errors on return but added to mirror recon api """
return self.request('checkPineAPDaemon')
def startPineAPDaemon(self):
""" Start Pine AP Daemon """
return self.request('startPineAPDaemon')
def getScans(self):
""" Return a list of Scans """
return self.request('getScans')
def downloadResults(self, scanId):
""" Download Scan Results by ID
Basically a limited stub right now,
Pending rewrite of Pineapple module to download data
"""
return self.request('downloadResults', { 'scanId': scanId })
def removeScan(self, scanId):
""" Delete a scan by ID """
return self.request('removeScan', { 'scanId': scanId})
def getCurrentScanID(self):
""" Return ID of Current Scan """
return self.request('getCurrentScanID')
def stopScan(self):
""" Stop currently running scan
Note: This is not by scan ID but rather just currently running scan
"""
return self.request('stopScan')
def loadResults(self, scanId):
return self.request('loadResults', { 'scanId': scanId })