In case you’ve been wondering, the whole reason for the delay of this follow-up is that the first part was a good example on how to generally replace telnet and other shell tools programmatically, which, I might note, does not guarantee that the replacement is more stable or safer (I’m not using it for everyday stuff, for example). It is, however, a good opportunity to do some hacking and learn how things work.
In this part, I will narrow down to the actual initial problem. We previously made some nice wrappers over the python socket send() and recv() primitives and we called them sndshell() and rcvshell(). Now we’ll focus on sending and receiving some actual stuff. Remember we’re communicating with a jailed busybox command line, some version of it anyway.
First, we’ll define login data and the standard busybox strings:
15 16 | user = 'admin' password = 'blahyadda' |
21 22 23 24 | loginstr = 'Login: ' passstr = 'Password: ' shellstr = '> ' rootstr = '# ' |
What loginstr and passstr do is tell us what we shall expect at the login part of the session. In a similar fashion, shellstr and rootstr define what we will be expecting during the normal and root session respectively. Lots of data the normal user might need to give at a normal login, and notice the big security gap we create by providing a clear text password. That’s why it might be a good idea to store and run the script from somewhere safe, inside the local network. Bottom line, it’s telnet we’re talking about, so the password will be sent over the network unencrypted anyway.
As if the above variables weren’t enough, let’s add some more communication data, for the purpose of automating it later:
27 28 29 30 31 32 33 34 | shcomm = 'sh' iprule1 = 'iptables -t nat -A PREROUTING -d 92.86.141.4' iprule1 += ' -p tcp --dport 80 -j DNAT --to 192.168.1.2' iprule2 = 'iptables -t nat -A POSTROUTING -d 192.168.1.2 -s 192.168.1.0/24' iprule2 += ' -p tcp --dport 80 -j SNAT --to 192.168.1.1' exitcomm = 'exit' l = [shcomm,iprule1,iprule2,exitcomm] |
All we did was define a sequence of commands that we are going to run later on the router. This list could be (theoretically, at least) replaced with any list of commands. These ones update some iptables chains to provide NAT loopback and then exit the router shell. The sh command at the beginning opens a “root” shell or something similar.
Next, we’ll open the socket, connect to the router and begin sending and receiving. Since we handled the problem related to socket opening and closing in the first part, now we’ll do whatever is left.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | # the communication itself data = sock.recv(bufsize) #ignore this part # not ignoring characters in the login part rcvshell(sock,loginstr) sndshell(sock,user) rcvshell(sock,user + '\r\n') rcvshell(sock,passstr) sndshell(sock,password + '\r') rcvshell(sock,'\r\n') rcvshell(sock,shellstr) # here be commands for str in l: sndshell(sock,str) data = rcvshell(sock,None) print data |
The first sock.recv() receives an opening string from the router. Telnet doesn’t display it so we’ll ignore this one brutally. Next, the router sends us the login and password strings and we’ll send our part of the data, while expecting echo characters for the user name and a new line sequence (‘\r\n’) for both of them (only the latter for the password, considering that the router doesn’t echo this part for minimal security reasons).
Then we want to make sure we entered a shell, so we’ll expect the basic shell string. Now we can send commands. The router echoes back whatever we send so we’ll receive and not expect for something in particular. We however print the received data to the user in case he needs to do debugging on error reports or anything similar.
The script is far from perfect, but it seems to work. The reason I wanted it to look this way is because I felt like providing an abstraction – the two functions – for communicating with a shell over a remote (what was supposed to be a telnet) session. Also, it can be made to look more elegant (by integrating all the work into lambdas and using a map for example), but this will be left as spare-time work for the reader.
Or imagine you can implement a server on the other side. If you can and you want to, this could be a starting point. Anyway, here’s the entire script: link.