fix path traversal ( ´_ゝ`)

This commit is contained in:
ed 2019-06-06 14:02:20 +02:00
parent af28a90e3d
commit 5414591362
3 changed files with 63 additions and 62 deletions

View file

@ -6,6 +6,7 @@ import os
import threading import threading
from .__init__ import * from .__init__ import *
from .util import undot
class VFS(object): class VFS(object):
@ -49,24 +50,9 @@ class VFS(object):
self.nodes[dst] = vn self.nodes[dst] = vn
return vn return vn
def undot(self, path):
ret = []
for node in path.split("/"):
if node in ["", "."]:
continue
if node == "..":
if ret:
ret.pop()
continue
ret.append(node)
return "/".join(ret)
def _find(self, vpath): def _find(self, vpath):
"""return [vfs,remainder]""" """return [vfs,remainder]"""
vpath = self.undot(vpath) vpath = undot(vpath)
if vpath == "": if vpath == "":
return [self, ""] return [self, ""]

View file

@ -22,17 +22,16 @@ class HttpCli(object):
def __init__(self, conn): def __init__(self, conn):
self.conn = conn self.conn = conn
self.s = conn.s self.s = conn.s
self.sr = conn.sr
self.addr = conn.addr self.addr = conn.addr
self.args = conn.args self.args = conn.args
self.auth = conn.auth self.auth = conn.auth
self.sr = conn.sr
self.bufsz = 1024 * 32
self.ok = True
self.log_func = conn.log_func self.log_func = conn.log_func
self.log_src = conn.log_src self.log_src = conn.log_src
self.ok = True
self.bufsz = 1024 * 32
def log(self, msg): def log(self, msg):
self.log_func(self.log_src, msg) self.log_func(self.log_src, msg)
@ -42,13 +41,12 @@ class HttpCli(object):
except: except:
return False return False
self.headers = {}
try: try:
mode, self.req, _ = headerlines[0].split(" ") mode, self.req, _ = headerlines[0].split(" ")
except: except:
self.log("bad headers:\n" + "\n".join(headerlines)) raise Pebkac("bad headers:\n" + "\n".join(headerlines))
return False
self.headers = {}
for header_line in headerlines[1:]: for header_line in headerlines[1:]:
k, v = header_line.split(":", 1) k, v = header_line.split(":", 1)
self.headers[k.lower()] = v.strip() self.headers[k.lower()] = v.strip()
@ -61,23 +59,34 @@ class HttpCli(object):
continue continue
v = unescape_cookie(v) v = unescape_cookie(v)
if v == "x": if v in self.auth.iuser:
break
if not v in self.auth.iuser:
msg = u'bad_cpwd "{}"'.format(v)
nuke = u"Set-Cookie: cppwd=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"
self.loud_reply(msg, headers=[nuke])
return True
self.uname = self.auth.iuser[v] self.uname = self.auth.iuser[v]
break
if self.uname: if self.uname:
self.rvol = self.auth.vfs.user_tree(self.uname, readable=True) self.rvol = self.auth.vfs.user_tree(self.uname, readable=True)
self.wvol = self.auth.vfs.user_tree(self.uname, writable=True) self.wvol = self.auth.vfs.user_tree(self.uname, writable=True)
self.log(self.rvol) self.log(self.rvol)
self.log(self.wvol) self.log(self.wvol)
# split req into vpath + args
args = {}
if not "?" in self.req:
vpath = undot(self.req)
else:
vpath, arglist = self.req.split("?", 1)
vpath = undot(vpath)
for k in arglist.split("&"):
if "=" in k:
k, v = k.split("=", 1)
args[k.lower()] = v.strip()
else:
args[k.lower()] = True
self.args = args
self.vpath = vpath
try: try:
if mode == "GET": if mode == "GET":
self.handle_get() self.handle_get()
@ -116,43 +125,28 @@ class HttpCli(object):
self.log("GET " + self.req) self.log("GET " + self.req)
# "embedded" resources # "embedded" resources
if self.req.startswith("/.cpr/"): if self.vpath.startswith(u".cpr"):
static_path = os.path.join(E.mod, "web", self.req.split("?")[0][6:]) static_path = os.path.join(E.mod, "web/", self.vpath[5:])
if os.path.isfile(static_path): if os.path.isfile(static_path):
return self.tx_file(static_path) return self.tx_file(static_path)
# split req into vpath + args
args = {}
vpath = self.req[1:]
if "?" in vpath:
vpath, arglist = vpath.split("?", 1)
for k in arglist.split("&"):
if "=" in k:
k, v = k.split("=", 1)
args[k.lower()] = v.strip()
else:
args[k.lower()] = True
# conditional redirect to single volumes # conditional redirect to single volumes
if vpath == "" and not args: if self.vpath == "" and not self.args:
nread = len(self.rvol) nread = len(self.rvol)
nwrite = len(self.wvol) nwrite = len(self.wvol)
if nread + nwrite == 1: if nread + nwrite == 1:
if nread == 1: if nread == 1:
vpath = self.rvol[0] self.vpath = self.rvol[0]
else: else:
vpath = self.wvol[0] self.vpath = self.wvol[0]
# go home if verboten # go home if verboten
readable = vpath in self.rvol readable = self.vpath in self.rvol
writable = vpath in self.wvol writable = self.vpath in self.wvol
if not readable and not writable: if not readable and not writable:
self.log("inaccessible: {}".format(vpath)) self.log("inaccessible: {}".format(self.vpath))
args = {"h"} self.args = {"h": True}
self.vpath = vpath
self.args = args
if "h" in self.args: if "h" in self.args:
self.vpath = None self.vpath = None
@ -190,12 +184,17 @@ class HttpCli(object):
def handle_login(self): def handle_login(self):
pwd = self.parser.require("cppwd", 64) pwd = self.parser.require("cppwd", 64)
if not pwd in self.auth.iuser: self.parser.drop()
h = [u"Set-Cookie: cppwd=x; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT"]
self.loud_reply(u'bad_ppwd "{}"'.format(pwd), headers=h) if pwd in self.auth.iuser:
msg = u"login ok"
else: else:
msg = u"naw dude"
pwd = u"x"
h = ["Set-Cookie: cppwd={}; Path=/".format(pwd)] h = ["Set-Cookie: cppwd={}; Path=/".format(pwd)]
self.loud_reply(u"login_ok", headers=h) html = u'<h1>{}<h2><a href="/">ack'.format(msg)
self.reply(html.encode("utf-8"), headers=h)
def handle_plain_upload(self): def handle_plain_upload(self):
nullwrite = self.args.nw nullwrite = self.args.nw

View file

@ -264,6 +264,22 @@ def read_header(sr):
return ret[:-4].decode("utf-8", "replace").split("\r\n") return ret[:-4].decode("utf-8", "replace").split("\r\n")
def undot(path):
ret = []
for node in path.split(u"/"):
if node in [u"", u"."]:
continue
if node == u"..":
if ret:
ret.pop()
continue
ret.append(node)
return u"/".join(ret)
def sanitize_fn(fn): def sanitize_fn(fn):
return fn.replace("\\", "/").split("/")[-1].strip() return fn.replace("\\", "/").split("/")[-1].strip()