Frage zu Portscanner

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
pythonix
User
Beiträge: 10
Registriert: Samstag 29. März 2014, 16:40

Hi! Ich habe folgende Frage zu meinen Script: Ich habe ein kurzes Script zu einem Portscanner geschrieben.

Das script scheint nicht korrekt durchzulaufen...

Kann mir (blutiger Anfänger in python) sagen was ich falsch gemacht habe? Danke schon jetzt für eure Hilfe :)

Code: Alles auswählen

import socket, os, sys

def retBanner(ip, port):
    try:
        socket.setdefaulttimeout(2)
        s = socket.socket()
        s.connect((ip, port))
        banner = s.recv(1024)
        return banner
    
    except:
        return

def checkVulns(banner, filename):    
    with open('filename', 'r') as line:
        if(line.strip()) in banner:
            print('[+] Server is vulnerable: ' + banner.strip('\n'))
                
def main():
    if (len(sys.argv) == 2):
        filename = sys.argv[1]
        if not os.path.isfile(filename):
            print('[-]' + filename + ' does not exist')
        exit(0)
        if not(os.access(filename, os.R_OK)):
            print('[-]' + filename + ' access denied')
        exit(0)
    else:
        print('[-] Usage: ' + str(sys.argv[0]) + '<vuln filename>')
        exit(0)
        portList = [21, 22, 25, 80, 110, 443]
        for elem in range (30, 50):
            ip = '109.67.33.' + str(elem)
            for port in portList:
                banner = (retBanner(ip, port))
                if (banner):
                    print('[+] ' + ip + ': ' + banner)
                    checkVulns(banner, filename)               
                    
                
if __name__ == '__main__':
    main()     
In einer textdatei habe ich zeilenweise die verschiedenen Banner zum Einlesen und vergleichen.

Danke
Zuletzt geändert von Anonymous am Dienstag 28. März 2017, 20:56, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
TrayserCassa
User
Beiträge: 97
Registriert: Donnerstag 2. Mai 2013, 19:11

Morgen erstmal,

dein Script benutzt in Zeile 24 und 27 die sys.exit() Methode. Diese Methode schließt dein Programm und somit kann es nicht weiter durchlaufen.

In Python schreibt man Methoden namen wie 'receive_banner'. Also komplett klein und mit '_' zum trennen einzelner Wörter :)

MfG
Tray
beertonic
User
Beiträge: 37
Registriert: Montag 8. Mai 2017, 15:26

Ist das das Tutorial aus "Python Hacking" von O-Connor?
BlackJack

@pythonix: Was heisst denn „scheint nicht korrekt durchzulaufen“ genau? Gibt es eine Fehlermeldung? Falls ja welche? Am besten komletten Traceback zeigen. Falls keine Fehlermeldung kommt, was passiert denn und was hättest Du erwartet was passiert? Wo genau fängt das Programm an von Deinen Erwartungen abzuweichen?

Zu den Namen: Neben der Schreibweise sollte man auch keine Abkürzungen verwenden. Also zum Beispiel das `ret` in `retBanner`, da wäre so etwas wie `get_banner()` wesentlich verständlicher.

`socket.setdefaulttimeout()` sollte man nicht benutzen. Das gilt dann für alle Sockets in dem Programm. Wenn man eine Zeitüberschreitung für ein bestimmtes Socket-Objekt festlegen will, sollte man das auch nur für dieses Objekt machen.

Niemals ”nackte” ``except:``\s verwenden ohne anzugeben welche Ausnahme(n) man konkret erwartet. Bei den Socket-Operationen sollte man also nur `socket.error` behandeln, denn alles andere wäre ein Fehler den man besser nicht einfach ignorieren sollte. Da wird die Fehlersuche nämlich zum Abenteuer. Falls man andere Fehler an der Stelle dann überhaupt noch wahrnimmt.

Ausserdem sollte man vermeiden Ausnahmen durch spezielle Fehlerrückgabewerte zu ersetzen. Denn Ausnahmen sind ja gerade dazu da, das man keine speziellen Fehlerrückgabewerte mehr hat. Du willst also diese Ausnahme nicht in `retBanner()` behandeln, sondern dort wo die Funktion aufgerufen wird.

In der `checkVulns()`-Funktion, die eigentlich `check_vulnerabilities()` heissen sollte, wird das `filename`-Argument nicht verwendet. Stattdessen hast Du einen literalen Dateinamen 'filename'. Ziemlich sicher nicht das was Du wolltest.

Du verwendest an einigen Stellen zu viele Klammern. Um Ausdrücke wo sie keinen Sinn machen, das sie keinen Effekt haben und auch nicht zur Lesbarkeit beitragen. Im Gegenteil — weil man dann immer erst einmal vermutet da würde ein Tupel erstellt, um dann das Komma zu suchen was aber nicht da ist. Im Bedingungen von ``if``, ``elif``, und ``while`` werden in Python ebenfalls keine Klammern gesetzt.

Da `print()` als Funktion geschrieben ist, also vermuten lässt das Python 3 verwendet wird: Sockets liefern Bytes und Du versuchst die mit Zeichenketten zu vergleichen. Das geht so nicht. Da müsstest Du eine Ausnahme bekommen. In Python 2 würde das funktionieren, allerdings sind dann entweder die Klammern bei ``print`` falsch, oder es fehlt der `__future__`-Import der aus der ``print``-Anweisung eine `print()`-Funktion macht.

Der Code in der `main()`-Funktion ist so ziemlich komplett falsch strukturiert. Neben den schon von TrayserCassa erwähnten `exit()`-Aufrufen, stimmt auch die Verteilung des Codes auf die einzelnen Zweige bei den Bedingungen so gar nicht. Wenn Du das korrigierst, solltest Du gleich alle `exit()`-Aufrufe rauswerfen, und den Code so strukturieren, dass er, egal aus welchem Grund er beendet werden soll, immer am Ende der Funktion ankommt.

Zwischenstand ohne die strukturellen Probleme in der `main()` gelöst zu haben:

Code: Alles auswählen

from __future__ import absolute_import, division, print_function
import os
import socket
import sys


def get_banner(ip, port):
    sock = socket.socket()
    sock.settimeout(2)
    sock.connect((ip, port))
    return sock.recv(1024)


def check_vulnerabilities(banner, filename):
    with open(filename, 'r') as line:
        if line.strip() in banner:
            print('[+] Server is vulnerable:', banner.strip('\n'))


def main():
    # 
    # TODO Control flow doesn't make sense at all here.  Restructure code
    #   and get rid of all `sys.exit()` calls.
    # 
    if len(sys.argv) == 2:
        filename = sys.argv[1]
        if not os.path.isfile(filename):
            print('[-]' + filename + ' does not exist')
        sys.exit(0)
        if not os.access(filename, os.R_OK):
            print('[-]' + filename + ' access denied')
        sys.exit(0)
    else:
        print('[-] Usage: ' + str(sys.argv[0]) + '<vuln filename>')
        sys.exit(0)
        port_list = [21, 22, 25, 80, 110, 443]
        for elem in range(30, 50):
            ip = '109.67.33.' + str(elem)
            for port in port_list:
                try:
                    banner = get_banner(ip, port)
                except socket.error:
                    # Not every port must have a server answering the request,
                    # so we ignore those which aren't open or refuse to answer.
                    pass  
                else:
                    if banner:
                        print('[+] ' + ip + ': ' + banner)
                        check_vulnerabilities(banner, filename)


if __name__ == '__main__':
    main()
Das Programm ist zudem übrigens noch nicht sehr robust weil `recv()` nur einen Teil der ersten 1024 Bytes liefern muss. Es kann also durchaus passieren das man an einen Dienst gerät der mit seinem Banner antwortet, man aber nur einen Teil davon auswertet und somit den vielleicht entscheidenden Teil nicht prüft, weil der erst mit einem nächsten `recv()`-Aufruf gelesen worden wäre.

Die Prüferei mit dem Dateinamen ist übrigens ”unpythonisch”. Da würde man einfach versuchen die Datei zu lesen und auf die Ausnahme reagieren, falls die nicht gelesen werden kann. Ein robustes Programm müsste das ja sowieso tun, denn die Datei wird ja immer wieder und wieder gelesen, und dabei kann ja jedes mal ein Problem auftauchen. Zum Beispiel mit den Zugriffsrechten, oder überhaupt mit der Existenz der Datei.
Antworten