2

I have created a simple http server for my family on the local network, when i add a html file and png picture and tried to view the HTML file, my image cannot load.
It says:
"The image “http://...:255/header.png” cannot be displayed because it contains errors."
Here is a bit of my code

        elif self.path.endswith(".bm"):   #our dynamic content
            self.send_response(200)
            self.send_header('Content-type',    'text/html')
            self.end_headers()
            f= open(curdir + sep + self.path)
            ren = self.render(f.read())
            self.wfile.write(ren)
            return
        elif self.path.endswith('.png'):
            print "IMAGE WANTED!"
            self.send_response(200)
            self.send_header('Content-type',    'image/png')
            self.end_headers()
            f = open(curdir + sep + self.path)
            self.wfile.write(f.read())
            return
        elif self.path.endswith('.jpg'):
            print "IMAGE WANTED!"
            self.send_response(200)
            self.send_header('Content-type',    'image/jpeg')
            self.end_headers()
            f= open(curdir + sep + self.path)
            print f.read()
            self.wfile.write(f.read())
            return
        elif self.path.endswith(".esp"):
            self.send_response(200)
            self.send_header('Content-type',    'text/plain')
            self.end_headers()
            self.wfile.write("This Format Is Not Supported Any More, Upgrade To BM Script")
            return

They all work except for the png and jpeg section. BM script I made myself, same with esp so that is just nothing

bmandesign
  • 347
  • 1
  • 3
  • 11
  • 2
    Please tell me this will never be reachable from the internet, because you will have a wide security hole open there (you allow relative paths like `../../../etc/passwd\0`) – Niklas B. Jan 06 '12 at 01:09
  • 5
    Why don't you just use the built-in `python -m SimpleHTTPServer`. This will serve up the current directory. – wim Jan 06 '12 at 01:21

1 Answers1

7

The default mode of open is 'r', which stands for reading text data and does automatic EOL conversion on Windows. Replace f = open(curdir + sep + self.path); self.wfile.write(f.read()) with

fn = os.path.normpath(os.path.join(curdir, self.path))
if not fn.startswith(abspath + os.path.sep):
    raise Exception('Path traversal attempt')
with open(fn, 'rb') as f:
    self.wfile.write(f.read())

The with statement fixes the leak of file handles. Alternatively (on Python < 2.5), you can call f.close() manually.

os.path.join (for which you may need to import os.path at the beginning of the file) is a cleaner filename construction mechanism than string concatenation. The check that the resulting filename is in the directory you expect prevents the path traversal vulnerability that would allow anyone to read all the files on your system.

phihag
  • 278,196
  • 72
  • 453
  • 469
  • I may be missing something, but how does *normpath* prevent path traversal? My understanding is that it only "normalizes" a path, basically just cleaning it up. A//B, A/B/, A/./B and A/foo/../B all become A/B. – monkut Jan 06 '12 at 02:31
  • @monkut - You use normpath to join the web root with the user path and then ensure it actually starts with the webroot: `import os; root = "/web/root"; path_from_client = "../../etc/passwd"; os.path.normpath(os.path.join(root, path_from_client)).startswith(root)` – jdi Jan 06 '12 at 03:08
  • @jdi that makes sense, but it doesn't appear to be clear in the answer here. The call to *normpath* alone does not protect you from path traversal. – monkut Jan 06 '12 at 04:12
  • `import os` is enough to call functions from `os.path`. You certainly shouldn't do `import os.path.join` as that ends with an `ImportError: No module named join` error. – yak Jan 06 '12 at 05:15
  • @monkut @jdi Yeah, you're right. Corrected. Note that just testing for `startswith(root)` is still wrong, since it allows paths like '/path/to/root_backup/'. – phihag Jan 06 '12 at 10:21
  • what happens if you request files like member_ph%00txtpng? Do you get must be encoded string without NULL bytes, not Path? – Daviddd Feb 16 '16 at 12:45
  • @Daviddd `self.path` will be literally `member_ph%00txtpng` in that case. – phihag Feb 16 '16 at 13:50