第一个dns查询
package main
import (
"fmt"
"github.com/miekg/dns"
)
func main() {
var msg dns.Msg
fqdn := dns.Fqdn("baidu.com")
msg.SetQuestion(fqdn,dns.TypeA)
in, err := dns.Exchange(&msg, "114.114.114.114:53")
if err!= nil{
panic(err)
}
fmt.Println(len(in.Answer))
fmt.Println(in.Answer)
for _,v := range (in.Answer){
fmt.Println(v.(*dns.A))
}
}
第19行是一个go的类型断言,确保v是dns.类型,否则就会panic
如我们修改为
fmt.Println(v.(*dns.CNAME))
实现一个子域名爆破
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"github.com/miekg/dns"
"os"
"text/tabwriter"
)
var (
Server string
Domain string
Wordlist string
WorkerCount int
)
type result struct {
Ipaddr string
Hostname string
}
type empty struct {}
func lookupA(domain string)([]string,error){
var ips []string
var msg dns.Msg
msg.SetQuestion(dns.Fqdn(domain),dns.TypeA)
in,err := dns.Exchange(&msg,Server)
if err !=nil{
return ips, err
}
if len(in.Answer)<1{
return ips,errors.New("no record")
}
for _,answer := range in.Answer{
if a,ok := answer.(*dns.A);ok{
ips = append(ips,a.A.String())
}
}
return ips,nil
}
func lookupCNAME(domain string)([]string,error) {
var domains []string
var msg dns.Msg
msg.SetQuestion(dns.Fqdn(domain),dns.TypeCNAME)
in,err := dns.Exchange(&msg,Server)
if err != nil{
return domains, err
}
for _,answer := range in.Answer{
if c,ok := answer.(*dns.CNAME);ok{
domains = append(domains,c.Target)
}
}
return domains,nil
}
func lookup(domain string) ([]result){
var results []result
var tmp_domain = domain
for{
cnames,err := lookupCNAME(tmp_domain)
if err == nil && len(cnames)>1{
tmp_domain = cnames[0]
continue
}
// 说明没有cname记录了
ips ,err := lookupA(tmp_domain)
if err != nil{
break
}
for _,ip := range ips{
results = append(results,result{Ipaddr: ip,Hostname: domain})
}
break
}
return results
}
func worker(tracer chan empty,domains chan string,gather chan []result) {
for domain := range domains{
results := lookup(domain)
if len(results)>0{
gather <- results
}
}
var e empty
tracer <- e
}
func init() {
flag.StringVar(&Domain,"d","","target")
flag.StringVar(&Wordlist,"w","dic/dics.txt","wordlist")
flag.IntVar(&WorkerCount,"n",200,"workercount")
flag.StringVar(&Server,"s","114.114.114.114:53","DNSServer")
flag.Parse()
if Domain == ""{
flag.Usage()
os.Exit(0)
}
}
func main() {
var results []result
domains := make(chan string,WorkerCount)
gather := make(chan []result)
tracer := make(chan empty)
dic ,err := os.Open(Wordlist)
if err !=nil{
panic(err)
}
defer dic.Close()
scanner := bufio.NewScanner(dic)
for i:=0;i<WorkerCount;i++{
go worker(tracer,domains,gather)
}
for scanner.Scan(){
domains <- fmt.Sprintf("%s.%s",scanner.Text(),Domain)
}
go func() {
for r := range gather{
results = append(results,r...) // gather类存储的为slice类型,...表示将slice类型打散进行传递,它是go的一个语法糖
}
var e empty
tracer <- e
}()
close(domains) // 因为所有子域名已经发送完毕,关闭通道
for j:=0;j<WorkerCount;j++{
<-tracer
}
close(gather)
<-tracer // 表示gather完成
w := tabwriter.NewWriter(os.Stdout,0,8,4,' ', 0)
for _, r := range results {
fmt.Fprintf(w, "%s\t%s\n", r.Hostname, r.Ipaddr)
}
w.Flush()
}
这段代码可能是写到现在较复杂的一段代码了。我们从main函数开始读起。首先创建了三个chan来处理数据,后面具体在看。接着打开用户指定的子域名字典按行读取。开启WorkerCount个子线程进行子域名爆破。但此时还没向chan注入数据,处于阻塞状态,再接下来开始注入数据。
domains <- fmt.Sprintf("%s.%s",scanner.Text(),Domain)
先不急着往下看,先看一下worker函数
for domain := range domains{
使用循环来依次从chan获取域名进行爆破。需要注意的是每次只能从chan里循环获取一个数据。那么它什么时候会结束循环呢,这里我们看到主函数在141行调用close函数关闭chan。本以为这里的close调用后会发送信号使循环停止,但在进行单独测试时发现不调用close,在无数据输入时循环也可以结束。调用close和不调用close的区别在于循环结束后是否执行循环体后的代码。这里就是
var e empty
tracer <- e
tracer在本程序的作用是同步各线程使程序恰当结束。如果我们没有close,在主函数143-145就会发生阻塞程序无法退出。133-139行并发程序集合数据,146行关gather,完成后注入一个empty到tracer。148行如果
<-tracer // 阻塞
实现一个dns rebinding
package main
import (
"github.com/miekg/dns"
"log"
"net"
)
var count int
func main() {
count = 0
pcount := &count
dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
if count%2 == 0 {
var resp dns.Msg
resp.SetReply(r)
for _,q := range r.Question{
a := dns.A{
Hdr: dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 0,
},
A:net.ParseIP("127.0.0.1").To4(),
}
resp.Answer = append(resp.Answer,&a)
}
w.WriteMsg(&resp)
}else {
var resp dns.Msg
resp.SetReply(r)
for _,q := range r.Question{
a := dns.A{
Hdr: dns.RR_Header{
Name: q.Name,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: 0,
},
A:net.ParseIP("129.211.73.107").To4(),
}
resp.Answer = append(resp.Answer,&a)
}
w.WriteMsg(&resp)
}
*pcount++
})
log.Fatal(dns.ListenAndServe(":53","udp",nil))
}
最简单的方式实现了一个dns重绑定