Show version and haiku, но не только: ищем все скрытые команды Junos

    Всем привет!

    Эта статья — о скрытых командах операционной системы Junos. Тем, кто работал с оборудованием Juniper под управлением ОС Junos (сюда относятся серии MX, SRX, EX, QFX, T, J, и многие другие) наверняка известно, что кроме «официальных» (документированных) команд в системе имеются и недокументированные. Их особенность в том, что они не видны в интерфейсе командной строки по контекстной подсказке (это когда вводишь вопросительный знак) и для них не работает автозаполнение, то есть команду нужно знать и ввести полностью (все буквы). Видимо, самая известная (и самая бесполезная) из таких команд — show version and haiku, выдающая «японское» трехстишие о жизни сетевых инженеров.

    Скрытых команд, на самом деле, много. Вендор нигде не приводит их полного списка, но, например, на официальном форуме Juniper имеется прилепленный топик с довольно большим набором. Так что производитель не возражает против использования нами таких команд, просто на них не дается никакой гарантии — может работать, а может и положить ваше оборудование.

    В этой статье я расскажу о том, как можно получить список всех скрытых команд Junos операционного режима, в пределах какой-то начальной ветки. Метод основан на довольно простом наблюдении, но гуглением мне не удалось найти свидетельств того, что вопрос раньше ставился в такой форме. Пример скрипта на Python прилагается.



    Идея


    Основная идея подхода очень проста, но чтобы ее уяснить, все-таки хорошо иметь доступ к CLI какого-либо Junos-устройства.

    Рассмотрим, например, команду «show version». Если мы вводим «show version a» (в конце — всегда жмем Enter), то вывод команды — такой:
    lab@jsrxA-1> show version a
                               ^
    syntax error.
    

    А если «show version с», то
    lab@jsrxA-1> show version c   
                              ^
    syntax error, expecting <command>.
    

    В первом случае имеется скрытое продолжение (and haiku), во втором — нет. Как видно, реакция CLI при наличии скрытого продолжения отличается двумя аспектами:
    • «syntax error.» вместо «syntax error, expecting ‹command›.»;
    • шляпка (a.k.a. циркумфлекс) стоит под следующей позицией строки.

    Соответственно, перебирая буквы одну за другой (хоть и вручную, но лучше автоматизировать), мы можем найти спрятанные команды — Junos сам их подсказывает, хоть и не так явно, как с обычными командами!

    Предварительные замечания


    Прежде чем писать скрипт, я должен предупредить читателей, что скрытые команды были скрыты разработчиком не просто так. Некоторые из них могут нарушить работу устройства, повредить файловую систему, и т.п. Поэтому их, даже по одной, следует использовать с большой осторожностью. В нашем случае, когда делается поиск перебором, это предостережение возводится в такую степень, что ни в коем случае не следует запускать подобный скрипт на обрабатывающем пользовательский трафик оборудовании. Ведь мы перебираем все команды, среди которых могут содержаться и file delete, и request system zeroize, и restart routing, и еще много чего. Так что играйтесь только с неподключенными к сети железками, которые не жалко убить, а лучше с виртуальным SRX (a.k.a. Firefly Perimeter).

    Также следует пояснить, что хотя у Junos имеется очень удобный и продвинутый, основанный на XML, API, его использование для данной задачи не представляется возможным, так как наш подход поиска команд основан на особенностях работы CLI. Поэтому будем открывать обычную telnet-сессию, давать команды и парсить текстовый вывод.

    В данной статье я ограничусь поиском команд операционного режима. Есть еще конфигурация, и в ней тоже много всего интересного припрятано (тот же commit full). Поиск скрытых команд там можно проводить аналогично.

    Алгоритм


    Итак, начиная с определенной команды (commandStart в скрипте) мы будем обходить все возможные варианты команд, добавляя каждый раз по символу (из массива alphabet) и вводя Enter. Вывод, присылаемый Junos в ответ, может быть следующим:
    1. Ругань про «syntax error.» (и при этом шляпка указывает на наличие продолжения команды) — признак наличия hidden-команды, перебираем дальше, добавляя новые символы.
    2. Ругань про «syntax error, expecting ‹command›.» —
      здесь необходимо анализировать положение шляпки. Если она на текущей букве, как выше в примере «show version c», то далее не идем, скрытых команд нет.
      Если же она указывает на продолжение команды, как здесь:
      lab@jsrxA-1> show version and 
                                    ^
      syntax error, expecting <command>.
      

      то в этом случае команда имеет продолжение и необходимо перебирать дальше (команда тут может быть скрытой или нет, в зависимости от предыстории).
    3. Просто вывод команды, без ругани про синтаксические ошибки, но возможно с руганью про двусмысленный (ambiguous) ввод, например как здесь:
      lab@jlab-Firefly-3> show chassis cluster i
                                                ^
      'i' is ambiguous.
      Possible completions:
        interfaces           Display chassis cluster interfaces
        ip-monitoring        Display IP monitoring related information
      

      В этом случае перебор необходимо продолжать, т.к. далее может содержаться скрытая команда (в данном случае, show chassis cluster information).

    Также необходимо учитывать, что выводы некоторых команд могут занимать несколько экранов, что приводит к выдаче приглашения "---(more)---". В этом случае просто шлем пробел.

    Скрипт


    Собственно, вот он (или на github).
    Скрипт (Python 3)
    import telnetlib
    import re
    
    HOST = "192.168.65.161"
    user = "lab"
    password = "lab123"
    commandStart = "show version "     # note space at the end
    
    alphabet = "abcdefghijklmnopqrstuvwxyz-1234567890."
    PAUSE = 3 
    
    def SearchCommands (cmd, on_hidden_now=False):
        for nChar in range(0, len(alphabet)):
            char = str(alphabet[nChar])
            tn.write(cmd.encode('ascii') + char.encode('ascii') + b"\n")
        
            totData=""
            finished = False
            while (not finished):
                inpData = tn.read_until(prompt.encode('ascii'), PAUSE)
                totData = totData + inpData.decode('ascii')
                if "---(more" in inpData.decode('ascii'): 
                    tn.write(b" ")
                else:
                    finished = True
    
            cmdNext = cmd + str(char)
            synt_error_exp_cmd = False
            synt_error_period = False
            if "syntax error, expecting <command>." in totData:
                synt_error_exp_cmd = True
            if "syntax error." in totData:
                synt_error_period = True
            
            if not (synt_error_exp_cmd or synt_error_period):  # normal output or ambiguity
                if on_hidden_now:
                    print("hidden command >> " + cmdNext)
                else:
                    SearchCommands(cmdNext, on_hidden_now) # i.e. False
            else:
                l = re.findall(' *\^', totData)
                lenToHat = len(l[len(l)-1])            
                if synt_error_period:                         
                    if lenToHat > lenPrompt + len(cmdNext):
                        SearchCommands(cmdNext, True)        # Hidden command in progress
                if synt_error_exp_cmd:
                    if (lenToHat == 2 + lenPrompt + len(cmdNext)): 
                        if on_hidden_now:
                            print("hidden command >> " + cmdNext + "  (incomplete)")
                        # else: print("Entering: " + cmdNext)
                        SearchCommands(cmdNext+" ", on_hidden_now)
                    if lenToHat > 2 + lenPrompt + len(cmdNext):  
                        SearchCommands(cmdNext, on_hidden_now)                            
    
    tn = telnetlib.Telnet(HOST)
    tn.read_until(b"login: ")
    tn.write(user.encode('ascii') + b"\n")
    tn.read_until(b"Password:")
    tn.write(password.encode('ascii') + b"\n")
    
    loginText = tn.read_until(b"> ").decode('ascii')
    prompt = re.search(".*@.*", loginText).group()
    print("Working with prompt = " + prompt)
    lenPrompt = len(prompt)
    SearchCommands(commandStart)
    
    


    Примеры работы:
    Запуск для ветки show version
    hidden command >> show version and (incomplete)
    hidden command >> show version and blame
    hidden command >> show version and haiku
    hidden command >> show version extensive
    hidden command >> show version forwarding-context
    hidden command >> show version invoke-on (incomplete)
    hidden command >> show version invoke-on a
    hidden command >> show version invoke-on o
    hidden command >> show version no-forwarding
    hidden command >> show version scc-dont-forward
    hidden command >> show version sdk

    Запуск для ветки show chassis
    hidden command >> show chassis accurate-statistics
    hidden command >> show chassis beacon
    hidden command >> show chassis broadcom
    hidden command >> show chassis cfeb
    hidden command >> show chassis cip
    hidden command >> show chassis clocks
    hidden command >> show chassis cluster ethernet-switching (incomplete)
    hidden command >> show chassis cluster information
    hidden command >> show chassis cluster ip-monitoring (incomplete)
    hidden command >> show chassis craft-interface
    hidden command >> show chassis customer-id
    hidden command >> show chassis ethernet-switch
    hidden command >> show chassis fabric (incomplete)
    hidden command >> show chassis fchip
    hidden command >> show chassis feb
    hidden command >> show chassis fpc-feb-connectivity
    hidden command >> show chassis hsl (incomplete)
    hidden command >> show chassis hsr
    hidden command >> show chassis hss (incomplete)
    hidden command >> show chassis hst
    hidden command >> show chassis in-service-upgrade
    hidden command >> show chassis ioc-npc-connectivity
    hidden command >> show chassis lccs
    hidden command >> show chassis message-statistics (incomplete)
    hidden command >> show chassis message-statistics i
    hidden command >> show chassis network-services
    hidden command >> show chassis nonstop-upgrade
    hidden command >> show chassis power-budget-statistics
    hidden command >> show chassis psd
    hidden command >> show chassis redundancy (incomplete)
    hidden command >> show chassis redundant-power-system
    hidden command >> show chassis scb
    hidden command >> show chassis sfm
    hidden command >> show chassis sibs
    hidden command >> show chassis spmb
    hidden command >> show chassis ssb
    hidden command >> show chassis synchronization
    hidden command >> show chassis tfeb
    hidden command >> show chassis timers
    hidden command >> show chassis usb (incomplete)
    hidden command >> show chassis zones

    Запуск для ветки show security idp (на SRX240)
    hidden command >> show security idp active-policy
    hidden command >> show security idp application-ddos (incomplete)
    hidden command >> show security idp application-identification (incomplete)
    hidden command >> show security idp detector (incomplete)
    hidden command >> show security idp detector a
    hidden command >> show security idp detector c
    hidden command >> show security idp detector p
    hidden command >> show security idp ips-cache
    hidden command >> show security idp logical-system (incomplete)

    Как видно, скрипт помечает некоторые команды как incomplete — это те, у которых предполагается продолжение. В случае, если продолжение команды Junos уже не прячет, такая команда далее тоже находится скриптом, но выдается в сокращенном виде (show chassis message-statistics i — это show chassis message-statistics ipc).

    Цели обработать скриптом все возможные ошибки и ситуации не ставилось, поэтому если у вас в дескрипшенах интерфейсов содержится строка про synax error, на которую скрипт реагирует, или включен логгинг в терминал, логика работы может нарушиться.

    Еще одной проблемой являются команды, принимающие на вход любое имя, например show interfaces AnyInterfaceNameIsOKHere (в отсутствии такого интерфейса выдается ошибка, другие подобные команды могут не выдавать ничего). По понятной причине, скрипт при натравливании его на show interfaces, вылетает с ошибкой про maximum recursion depth exceeded. Зато поиск с commandStart = «show interfaces ge-0/0/0 » работает нормально:
    Запуск для show interfaces ge-0/0/0
    hidden command >> show interfaces ge-0/0/0 forwarding-context
    hidden command >> show interfaces ge-0/0/0 ifd-index
    hidden command >> show interfaces ge-0/0/0 ifl-index
    hidden command >> show interfaces ge-0/0/0 instance
    hidden command >> show interfaces ge-0/0/0 no-forwarding
    hidden command >> show interfaces ge-0/0/0 scc-dont-forward


    Заключение


    Надо понимать, что значительная часть hidden-команд скрыты по причине того, что не поддерживаются (или не имеют смысла) на данном оборудовании или в данной версии софта. Многие из них бесполезны, тем не менее, среди них встречаются и «самородки» (например, show chassis cluster information). Поскольку я работаю инструктором по Juniper, то довольно часто приходится слышать от студентов вопрос — где взять список всех скрытых команд. Так что буду теперь всех отсылать к этой статье. Надеюсь, что какая-то польза от данного рецепта кому-то будет.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 1

      0
      Вспоминается сразу
      service unsupported-transceiver
      у Cisco ;)

      Only users with full accounts can post comments. Log in, please.