/-----------------------------\ | Xine - issue #4 - Phile 111 | \-----------------------------/ automating things with expect by b0z0/ikx this text wants to be a very small introduction to expect, a very nice tcl extension that helps you automating scripts that do need interaction with other programs. with some basic knowledge of tcl you can actually create very funny tasks with expect without having to write from scratch things that are already supplied from other system programs or whatever. this can be of course very useful to automatize scanning and breaking-in for very common exploits or something like that. and, yes, what is important to say is that this language is quite portable, since tcl with expect nowadays runs on a very large number of unix like operatings systems and i think on windozes too (but i'm not sure, haven't checked... i mean, i just don't care about windoze anyway). but first of all what is expect: it is a tcl extension, that contains some functions that make the interaction of your script with other programs very easy. of course, since it is based on tcl you need to know tcl to actually do something with a tcl extension (you argued that before probably?) like expect. you won't find a full tutorial in the next part of the text, but rather a very fast overview of some basic things that may make you approach this language and write fastly some easy scripts. for more informations get a good tcl book/tutorial from somewhere or just read some manpages. most of the things will be introduced by examples, which i think is a very fast way to learn such an easy language. let's begin, first of all, let's see how variables are handled. variables don't need to be declared at some place and you don't need to declare which type they are. tcl will just do the necessary conversions of variables for you depending on operations you need to execute. so you can just think on everything being a string and when you will be going to make some arithmetics the interpreter will convert them to integer or whatever. so the usual sample: set ovar "world sux" puts $ovar the set command as you could argue is used to assign some value to a variable (named ovar in our example) which takes as arguments the variable name and its value. the second line will then display the variable value. you can now notice that when you prepend the dollar ($) symbol at the variable name you'll get the variable value. let's continue with the tcl syntax. it is very important the use of the brackets and braces. things included in brackets "[]" are interpreted like commands which value will be inserted instead of them. you could compare this to the "``" quotes under bash. the braces "{}" instead are used to unite the internal contents. so by examples: set ovar 36 set newvar [expr $ovar * 2] set secvar {expr $ovar * 2} so newvar will finally contain the value 72, that is $ovar * 2, while secvar will contain the string "expr $ovar * 2". here we saw another new command, expr. it just evaluates a given expression and returns its value. let's see some conditional tests and loops now. of course there is the normal if statement as in most hlls. shortly: if {$somevar == 40} {puts gewd} else {puts nogood} just put the expression you want to evalutate in the first braces and what to do if succedes in the second and if needed an alternativa after the else. as for the math and logical operators they are somewhat simillar to c ones (do a man expr for a list of all of them with precedence). very simillary you have the while, for and switch structures aswell. just by some samples: puts {meltdown countdown} for {set count 10} {$count} {incr count -1} { puts $count sleep 1 } puts {i don't care} puts {meltdown countdown} set count 10 while {$count > 0} { puts $count incr count -1 sleep 1 } puts {finally} you should have learned the incr command aswell now. simply it takes the variable to increment and the value the variable has to be incremented of. we have now all the strings functions of course, various comparations, comparations, searches, calculations and what you could think of. go read the tcl manpage about 'string' for a full list and description of all of them, it is just boring copy-n-pasting. you have also the usual support for arrays of course. what we'll look a little closer, since we use thins in the final example program, are lists. this is very used since you will get the command line arguments, that is the argv, in a list structure, where the element 0 is actually the first passed parameter (and not the program name like usual with argv). let's see by examples: set thelist [list el1 el2 el3 el4] set firstel [lindex $thelist 0] set thirdel [lindex $thelist 2] lappend thelist el5 set thelist [lreplace $thelist 1 2 new23] foreach element $thelist {puts $element} set theip 193.31.41.2 set iplist [split $theip .] set reip [join $iplist .] let's examine now the examples. we first just create, using the command list, a list called thelist that contains four elements (el1, el2, el3, el4). we then put in the variable firstel the first element of the list. this is actually returned by the function lindex that gives the element numbered by the second parameter from the list given as the first parameter. in the same way we put the third element, el3, in the variable thirdel. we then append, with the lappen command, a new element to our list. next we replace two elements, the second and third one with a new one, new23. this is achieved using the lreplace function. this works on the list given as the first parameter and replaces all the elements starting from the index given as the second parameter up to the index given as the third parameter with the contents of the fourth parameter. of course to replace just one element just put the starting and ending value of replacing to be the same. pay attention that lreplace doesn't work directly on the list, like for example lappend, so it returns a new list with the replaced element. so if you want to keep the changes in your list you must do something like in the example, using a set before with the substitution brackets. the next is an even simpler example, how to examine all the list elements with a foreach. it is too clear to write more about it, if you can't understand what it does you'd better be doing something else. continuing with list examples we then have a split, like the split function in perl, that splits a string, using the given delimiter, into a list. the same way you can join some list into a string with the join function. more about lists in manpages of course. now we came more or less at the end of this very compact introduction to tcl. of course there is a lot more to know, but it wasn't my goal to teach everything about tcl. you can work with files, with sockets, using regular expressions and whatever you would like to do that you could in another hll. using tk you can also fastly develope an easy interface for x. well... let's look now at the most interesting part, that is at the expect extension. this extension is actually composed or three main functions, expect, spawn and send. the expect function actually reads some interactive program output and makes some actions if a desired input was received. the spawn function, as the name says, starts a program with some given parameters which output can be then checked by expect. the send function finally sends interactive input to the program we are talking to. there are plenty of options and particularities for this three functions (rtfm!) but we will just proceed with some samples: set timeout 5 set users [list jesus christ me] spawn telnet mailhost.com 25 expect { "ESMTP" {} timeout {exit} } foreach check $users { send "EXPN $check\r" sleep 1 } expect eof here we first declare a list of user we would like to do EXPN about. we spawn then a normal telnet at some host on the smtp port. once we got the ESMTP string, that should tell us a good connection was done, we send the EXPN for each user we want to. finally we wait for the connection to be closed (if we get an eof or if the timeout expires). the timeout is actually carried in the timeout variable and is by default 10 seconds. one more sample, an automatized login over a telnet: set user 36degrees set pass borntodie set timeout 4 log_user 0 stty -echo spawn telnet smarthost.com 23 expect { "Last login:" {} "ogin:" {send "$user\r"; exp_continue} "ssword:" {send "$pass\r"; exp_continue} "incorrect" {puts "bad password supplied!\n"; exit} timeout {puts "timed out"; exit} } interact let's examine this one, we first set some variables like username and password we are gonna use. the "log_user 0" line means that the output of the expect session, that is the various prompts and command sent, won't be displayed. this is to make the solution neater. simillary the "stty -echo" won't display characters the user should try to type in during the execution (like when you type the password at your login). the sample script will then start a telnet session and then will expect a few things. if the line received contains a "Last login:" then we could argue we got in the site, so we can just do nothing (that is that braces) and exit from the expect loop. the next possible input we could get is a "ogin:", that could be a login prompt. if so just send the user name and then continue the expect loop (that is the exp_continue) since we should need to wait for a password prompt aswell. when we will get the password prompt, that is a "assword:", we will send at the same way the password and wait for a response, either a "Login incorrect" or the beginning "Last login:". we have just to handle the first one tho, so just add another expectation in the braces that will display an error message and exit. if for some reason a timeout will occour, let's tell about that and exit aswell. if a sucessfull login was done, so we came out of the expect braces, we will just leave the program interactively to the user, using the interact command. and now let's give a look to the final example for this tutorial. the code presented below will scan a given range of ips on a given port for some services. this was originally developed as a wingate scanner, but could be used to check for every kind of tcp services. it takes five arguments at the command line. the first is the starting host where to scan to, the second is just the fourth byte of the c class of the starting host where to stop the scanning, the third parameter is the port where to check, the fourth is the string we are looking for and the last is a timeout for connection. with an example: ./exscan 193.23.13.36 79 25 "8.8.6" 20 will scan the part of the c class starting from 193.23.13.36 to 193.23.13.79 on the smtp port for a string "8.8.6". this could be an example of searching for that given version of sendmail on a serie of machines. this script doesn't have many features, it was just written for fun as a useful (somewhat) example for this small tutorial. it can be easily modified to scan for b classes, to search regular expressions when connected or whatever. ---------------------------------------------------------------------- #!/usr/bin/expect if {$argc == 0} { puts "options: host endip port string timeout";exit } # read some arguments set host [lindex $argv 0] set endip [lindex $argv 1] set port [lindex $argv 2] set string [lindex $argv 3] set timeout [lindex $argv 4] # check a bit arguments and use default values if [expr {$endip == ""} || {$endip > 254}] { set endip 254 } if {$port == ""} { set port 23 } if {$string == ""} { set string "ingate>" } if {$timeout == ""} { set timeout 10 } # split ip in our list set hostbytes [split $host .] # check if ip is valid if {[lindex $hostbytes 3] == ""} { puts "invalid address\n" exit } foreach byte $hostbytes { if {$byte > 255 || $byte < 0} { puts "invalid address\n" exit } } stty -echo # loop through the desired range of ips while {[lindex $hostbytes 3] < [expr $endip + 1] } { log_user 0 # put the list of ip address bytes into a string set host [join $hostbytes "."] # start telnet spawn -noecho telnet $host $port # wait for output expect { # if got string then we founded what we were looking for $string { puts "$host: present" } # if timeout then our string wasn't received timeout { puts "$host: not present" } } # go to next ip set hostbytes [lreplace $hostbytes 3 3 [expr [lindex $hostbytes 3] + 1]] } ---------------------------------------------------------------------- well, that's all for this small text file. hope it was quite clear and that someone will find some use with this.