Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
As I got one for Prime Day, I figured that I would write an Alexa Port Scanner.
I do not think that Alexa will replace many (or even any) of my day-to-day tools. That said, I still figured it would be fun to control some of my automation by voice.
My first idea was to develop a simple port scanner, turn it into an Alexa skill, and somehow load it onto my device. Unfortunately, I had no idea how Alexa actually worked outside of using my friend’s.
Developing skills for Alexa isn’t too difficult, and I followed the official Python tutorial. That said, I think it took me more than 5 minutes, as I did not have any of the prerequisite accounts created.
The tutorial has you build a simple application to set and retrieve your favorite color.
The code can also be found at this gist.
I ended up not even completing or testing the tutorial application, but modifying it as I went along.
For my port scanner, I knew that I would first need to change the Intents.
As the system needed to perform voice recognition, I setup a list of sites that the skill could scan. First, I modified the tutorial’s intent schema. I also added the StopIntent so that I could stop the skill using, “Alexa stop”.
{ "intents": [ { "intent": "PortScanIntent", "slots": [ { "name": "Site", "type": "LIST_OF_SITES" } ] }, { "intent": "AMAZON.StopIntent" }, { "intent": "AMAZON.HelpIntent" } ] }
With my schema modified, I also configured LIST_OF_SITES. For now, I only added sites under my control.
doyler.net r4y.pw
As I was the only one using this skill for now, I also only setup a few sample utterances.
PortScanIntent port scan {Site} PortScanIntent what ports are open on {Site} PortScanIntent scan the open ports on {Site}
Once the simple modifications to the intents were complete, it was time to change the actual application functionality.
The final code is in the next section, I just want to talk about what I changed.
First, I modified all the text and speech output to make sense for my application.
Then, I removed all the color saving and retrieving methods and added a scan_site() method of my own.
The basis for the port scanner was one that I shared on this site a bit back.
Other than that, I added some simple error checking, debug statements, and a way to handle the StopIntent.
You can find the final application below, and the GitHub link is at bottom of this post.
from __future__ import print_function import socket import sys # --------------- Helpers that build all of the responses ---------------------- def build_speechlet_response(title, output, reprompt_text, should_end_session): return { 'outputSpeech': { 'type': 'PlainText', 'text': output }, 'card': { 'type': 'Simple', 'title': "SessionSpeechlet - " + title, 'content': "SessionSpeechlet - " + output }, 'reprompt': { 'outputSpeech': { 'type': 'PlainText', 'text': reprompt_text } }, 'shouldEndSession': should_end_session } def build_response(session_attributes, speechlet_response): return { 'version': '1.0', 'sessionAttributes': session_attributes, 'response': speechlet_response } # --------------- Functions that control the skill's behavior ------------------ def get_welcome_response(): """ If we wanted to initialize the session to have some attributes we could add those here """ session_attributes = {} card_title = "Welcome" speech_output = "Welcome to the Alexa Port Scanner. "\ "Please scan a site by saying, "\ "Port Scan doyler.net" # If the user either does not reply to the welcome message or says something # that is not understood, they will be prompted again with this text. reprompt_text = "Please tell me a site you'd like to scan by saying, " \ "Port Scan doyler.net" should_end_session = False return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session)) def handle_session_end_request(): card_title = "Session Ended" speech_output = "Thank you for trying the Alexa Port Scanner. " \ "Have a nice day! " # Setting this to true ends the session and exits the skill. should_end_session = True return build_response({}, build_speechlet_response( card_title, speech_output, None, should_end_session)) def scan_site(intent, session): card_title = intent['name'] session_attributes = {} open_ports = [] should_end_session = False if 'Site' in intent['slots']: host = intent['slots']['Site']['value'] host = host.replace(" ", "") ports = [22, 23, 80, 443, 445, 3389] for port in ports: try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(5) result = s.connect_ex((host, port)) if result == 0: session_attributes[port] = 'open' open_ports.append(port) print(str(open_ports)) s.close() except: print("Unexpected error:" + str(sys.exc_info()[0])) pass speech_output = "The following ports are open on doyler.net " + \ str(open_ports) + \ "." reprompt_text = "You can ask me to scan a different site by saying, " \ "Port Scan r4y.pw" else: speech_output = "I'm not sure what site you want to scan. " \ "Please try again." reprompt_text = "I'm not sure what site you want to scan. " \ "You can tell me your favorite color by saying, " \ "Port Scan doyler.net" return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session)) # --------------- Events ------------------ def on_session_started(session_started_request, session): """ Called when the session starts """ print("on_session_started requestId=" + session_started_request['requestId'] + ", sessionId=" + session['sessionId']) def on_launch(launch_request, session): """ Called when the user launches the skill without specifying what they want """ print("on_launch requestId=" + launch_request['requestId'] + ", sessionId=" + session['sessionId']) # Dispatch to your skill's launch return get_welcome_response() def on_intent(intent_request, session): """ Called when the user specifies an intent for this skill """ print("on_intent requestId=" + intent_request['requestId'] + ", sessionId=" + session['sessionId']) intent = intent_request['intent'] intent_name = intent_request['intent']['name'] # Dispatch to your skill's intent handlers if intent_name == "PortScanIntent": return scan_site(intent, session) elif intent_name == "AMAZON.HelpIntent": return get_welcome_response() elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent": return handle_session_end_request() else: raise ValueError("Invalid intent") def on_session_ended(session_ended_request, session): """ Called when the user ends the session. Is not called when the skill returns should_end_session=true """ print("on_session_ended requestId=" + session_ended_request['requestId'] + ", sessionId=" + session['sessionId']) # add cleanup logic here # --------------- Main handler ------------------ def lambda_handler(event, context): """ Route the incoming request based on type (LaunchRequest, IntentRequest, etc.) The JSON body of the request is provided in the event parameter. """ print("event.session.application.applicationId=" + event['session']['application']['applicationId']) """ Uncomment this if statement and populate with your skill's application ID to prevent someone else from configuring a skill that sends requests to this function. """ # if (event['session']['application']['applicationId'] != # "amzn1.echo-sdk-ams.app.[unique-value-here]"): # raise ValueError("Invalid Application ID") if event['session']['new']: on_session_started({'requestId': event['request']['requestId']}, event['session']) if event['request']['type'] == "LaunchRequest": return on_launch(event['request'], event['session']) elif event['request']['type'] == "IntentRequest": return on_intent(event['request'], event['session']) elif event['request']['type'] == "IntentRequest": return on_session_ended(event['request'], event['session']) elif event['request']['type'] == "SessionEndedRequest": return on_session_ended(event['request'], event['session'])
While there was plenty of testing and debugging going on during the entire process, I ran into a few issues towards the end.
First, utilizing print statements can still help, and you can find the output in your Lambda logs.
Additionally, be prepared to see plenty of screens that look like this in your Alexa app.
That said, while it took me about an hour, I figured out what my biggest issue with the scanner was. While I was telling Alexa, “Port scan doyler.net”. Alexa was hearing “Port scan doyler .net”. Unfortunately, TCP connections don’t work very well with spaces in the middle, and my scanner code was throwing errors. This is the reasoning for the .strip() before actually scanning the provided site.
After all of the debugging was complete, I was able to successfully port scan this site using Alexa!
I even recorded a short video so that you can hear her as well.
While this isn’t the most useful security tool, it is still fun to do some enumeration from my couch.
Unfortunately, due to the way the voice recognition works, I am not sure how to scan a site without first adding it to my intents.
Other than that, I’m sure that I could have some more useful output or reporting with this.
Additionally, if you have any ideas what else I could/should automate with Alexa, then let me know!
Finally, you can find the code and updates in my GitHub repository.
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure page here. As an Amazon Associate, I earn from qualifying purchases.
Dude that was awesome!
Thanks man, it was surprisingly easy to setup!
Now I need to figure what else would be useful to control with my voice.
[…] Writing an Alexa Port Scanner for Couch Hacking […]