#! /usr/bin/perl -w $version = '$Id: ciscopwreset,v 1.4 2005/12/07 11:54:08 andrewm Exp $'; # Copyright (c) 2001-2002 Andrew McGill and Leading Edge Business Solutions # (South Africa). This software may be redistributed and/or modified only # under the terms of the GNU General Public Licence, version 2 or any later # version, as published by the Free Software Foundation. use POSIX; use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); # We set the enable password to this ... $secret = 'pokpok'; $routermode = 'ready'; # routermode modes: # ready: press RETURN to continue # normal: enter commands # rommon: rommon mode # hacked: press RETURN to continue (no enable password) # enable: ready for enable-mode commands # $confreg = '0x2102'; sub recv_tty { my $the_timeout = shift; $timeout=0; if (defined $the_timeout) { # SLURP until timeout expires my $rin = ''; my $rout; my $timeleft; my $nfound; vec($rin,fileno(MODEMRECV),1) = 1; ($nfound,$timeleft) = select($rout=$rin, undef, undef, $the_timeout); if ($nfound == 0) { # print "(timeout)"; print "\e[31m(timeout)\e[0m"; $timeout = 1; #return value in global var -- whee! return ''; } } my $c=''; my $nread = read(MODEMRECV,$c,1000); print "\e[33m$c\e[0m"; return $c; } sub flush_tty($) { my $timeout = shift; # Flush the incoming buffers ... for (;;) { my $rin = ''; my $rout; vec($rin,fileno(MODEMRECV),1) = 1; my ($nfound,$timeleft) = select($rout=$rin, undef, undef, $timeout); if ($nfound) { my $recvd = ''; for (my $i=0; $i<$nfound; $i++) { $recvd .= getc(MODEMRECV); } print "\e[33m$recvd\e[0m"; } else { last; } } } sub send_tty($) { my $sendstr=shift; flush_tty(0.4); # Slow send .. for (my $i=0; $i\e[0m"; } else { print STDOUT "\e[34m$p\e[0m"; } select undef, undef, undef, rand(0.1)+0.1; } } $modem="/dev/ttyS0"; $timeout = 0 ; sub pre_exit { my $signame = shift; if (defined $confreg) { print STDERR "configuration-register 0x$confreg\n"; } exit } sub catch_zap { my $signame = shift; print STDERR "Timer expired\n"; $timeout = 1; $SIG{ALRM} = 'catch_zap'; # re-initialise - this does no good without alarm(), as it turns out. } sub enter_enable_mode($) { } # Wait for a single value (or a timeout) sub waitfor { my $stuff = shift; my $seconds = shift; $_=''; while ($_.=recv_tty($seconds)) { if (m/$stuff/) { return 1; } if ($timeout) { return 0; } } } sub rommon_to_hacked() { $_=''; local $SIG{ALRM} = 'catch_zap'; # could fail in modules send_tty( "confreg 0x2142\r"); # 1600 waitfor("rommon .*>",5); send_tty( "o/r 0x2142\r"); # 2500 while ($_.=recv_tty(15) || $timeout) { if (m/System Configuration Dialog|\[.*\]:|enable secret:|No defaulting allowed/m) { print "In system config dialog ... skipping\n"; $_ = ''; send_tty("\003"); $sentreturn=0; } if (m/rommon \d+\s*>/) { print "In rom monitor ... rebooting\n"; $_ = ''; send_tty( "i\r"); # 2500 flush_tty(5); $sentreturn=0; } if (m/ RETURN / || $timeout) { # Press RETURN to get started flush_tty(5); send_tty( "\r"); $_ = ''; $sentreturn=1; } if (m/>|#/ && $sentreturn) { $routermode = 'hacked'; # press RETURN is next prompt last; } } alarm 0; return 1; } sub get_initial_prompt() { print "Sending:: RETURN, waiting for prompt\n"; $_=''; send_tty "\r"; $sentreturn = 1; while ($_.=recv_tty(5) || $timeout) { if (m/rommon \d+\s*>/) { print "In rom monitor ... rebooting\n"; $_ = ''; send_tty( "i\r"); flush_tty(5); $sentreturn=0; } if (m/>/ && $sentreturn) { $routermode = 'running'; last; } if (m/#/ && $sentreturn) { $routermode = 'hacked'; last; } if (m/--More--/ && $sentreturn) { $_ = ''; send_tty( "\003"); $sentreturn = 1; } if (m/ RETURN / || $timeout) { $_ = ''; send_tty( "\r"); $sentreturn=1; } if (m/System Configuration Dialog|\[.*\]:|enable secret:|No defaulting allowed/m) { print "Skipping system config dialog\n"; $_ = ''; send_tty( "\003\r"); $routermode = 'hacked'; $sentreturn=0; } } return 1; } sub get_cisco_confreg() { $SIG{ALRM} = 'catch_zap'; # could fail in modules AGAIN: send_tty( "terminal length 0\r"); $_=''; while ($_.=recv_tty(50)) { if (m/>|#/) { last; } if ($timeout) { goto AGAIN; # die "Timeout? Where's the router!?\n"; } } AGAIN2: $_=''; send_tty( "show version\r"); while ($_.=recv_tty(5)) { # if (m/>/) { last; } if ($timeout) { goto AGAIN2; die "Timeout? Where's the router!?\n"; } elsif (m/Configuration register is 0x(\d+)\r/) { # Configuration register is 0x2102 $confreg=$1; print "***** confreg is $confreg\n"; last; } # Configuration register is 0x2142 (will be 0x2102 at next reload) elsif (m/will be 0x(\d+) at next reload/) { $confreg=$1; print "***** confreg is $confreg\n"; last; } } if ($confreg eq '2142') { print "WARNING: Guessing that confreg=2102!\n"; $confreg = "2102"; } return $confreg; } sub break_on_boot() { $_ = ''; while ($_.=recv_tty(60)) { if ($timeout) { print "Still waiting ..."; } if (m/System Boot/i) { last; } } print "Saw \"System Bootstrap\" notice (pause)\n"; flush_tty(3); print "Sending break\n"; tcsendbreak(fileno(MODEMSEND),0); return 1; } sub wait_for_rommon() { $_=''; while ($_.=recv_tty(5)) { if ($timeout) { print "Sending break\n"; tcsendbreak(fileno(MODEMSEND),0); } if (m/rommon \d+\s*>/) { $routermode='rommon'; last; } } return 1; } print "CISCO PASSWORD RECOVERY UTILITY\n". " 2005, Andrew McGill, www.ledge.co.za\n". " $version\n". "\n"; open MODEMRECV, "< $modem" or die "Can't read from $modem: $!\n"; open MODEMSEND, "> $modem" or die "Can't write to $modem: $!\n"; select MODEMSEND; $| = 1; select STDOUT; $| = 1; local $SIG{INT} = 'pre_exit'; # could fail in modules local $SIG{TERM} = 'pre_exit'; # could fail in modules $flags = fcntl(MODEMRECV, F_GETFL, 0) or die "Can't get flags for the socket: $!\n"; $flags = fcntl(MODEMRECV, F_SETFL, $flags | O_NONBLOCK) or die "Can't set flags for the socket: $!\n"; while (1) { # this is as hacky as state machines get ... if (1==0) { print "Don't panic\n"; } elsif ($routermode eq 'rommon' && ! defined $confreg) { # Reboot required print "STEP 0: confreg not known: boot the router\n"; send_tty("i\r"); waitfor("RETURN"); flush_tty(3); $routermode = 'ready'; } elsif ($routermode eq 'ready') { print "STEP 1: check that router is connected\n"; while (! get_initial_prompt()) { print "The router is not responding. Plug in the cable at $modem\n". "and press Enter on the keyboard\n"; my $whatever=; } } elsif ($routermode eq 'running' && !defined $confreg) { print "STEP 2: Get configuration register value\n"; if ( ! get_cisco_confreg()) { die "Can't get initial config register\n"; } } elsif ($routermode eq 'running' && defined $confreg) { # Reboot required print "STEP 3: Cycle the power on the router now (I'm waiting for you)\n"; $routermode = 'booting'; break_on_boot(); wait_for_rommon(); } elsif ($routermode eq 'rommon' && defined $confreg) { # Reboot required print "STEP 4: Set conf register and restart\n"; rommon_to_hacked(); } elsif ($routermode eq 'hacked') { # Reboot required print "STEP 5: enter enable mode\n"; send_tty("enable\r"); waitfor("#"); send_tty("copy start running\r"); waitfor("#"); send_tty("show ip interface brief\r"); waitfor("#"); @interfaces = (); #Interface IP-Address OK? Method Status Protocol #Ethernet0 196.15.159.213 YES NVRAM administratively down down #Serial0 196.15.159.213 YES unset administratively down down while (m/^(.*?)\s+\S+\s+\S+\s+\S+\s+administratively/mg) { push @interfaces,$1; } print "Interfaces: ".join(" ",@interfaces)."\n"; send_tty("config term\r"); waitfor("config.*#"); send_tty("enable secret $secret\r"); waitfor("config.*#"); if (defined $confreg) { send_tty("config-register 0x$confreg\r"); waitfor("config.*#"); } foreach $interface (@interfaces) { send_tty("interface $interface\r"); waitfor("config.*#"); send_tty("no shutdown\r"); waitfor("config.*#"); } send_tty("end\r"); waitfor("#"); send_tty("write memory\r"); waitfor("#"); send_tty("terminal length 0\r"); waitfor("#"); send_tty("write terminal\r"); waitfor("#"); open CONFFILE,"> config"; s/\n.*?#$//; print CONFFILE "$_"; close CONFFILE; $routermode='configured'; } elsif ($routermode eq 'configured') { print "\n\n". "We're done!\n". "The enable password is set to $secret\n"; exit; } }