initial commit

This commit is contained in:
MDK
2023-06-16 18:55:18 +08:00
parent 9e4182bced
commit b59fbce07c
10 changed files with 439 additions and 0 deletions

0
LICENSE Normal file
View File

46
cmd/root.go Normal file
View File

@@ -0,0 +1,46 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"os"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "dtool",
Short: "dns resolver probing tool",
Long: `A tool for probing open resolvers for various purpose. The following modules are implemented.
Modules:
upstream probe to get the upstream recursive resolvers of the given resolver as many as possible
ttl probe to test if resolvers on the resolution path change the authoritative TTL value
cache probe to enumerate the cache`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dtool.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

32
cmd/upstream.go Normal file
View File

@@ -0,0 +1,32 @@
package cmd
import (
_ "dtool/prober"
"dtool/utils"
"github.com/spf13/cobra"
)
var filename string
var thread_num int
var upstreamCmd = &cobra.Command{
Use: "upstream",
//Aliases: []string{"up", "stream"},
Short: "probe upstream recursive resolvers",
Long: `get the upstream recursive resolvers of the input resolvers
input target can be added as an argument or as a file
-f input file with limited ip addresses (limit=50)
-t number of goroutine`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
//prober.Get_upstream_ip(args[0])
utils.SendTencentHttpdnsQuery()
},
}
func init() {
upstreamCmd.Flags().StringVarP(&filename, "file", "f", "", "input filename")
upstreamCmd.Flags().IntVarP(&thread_num, "threads", "t", 10, "number of concurrent threads")
rootCmd.AddCommand(upstreamCmd)
}

15
go.mod Normal file
View File

@@ -0,0 +1,15 @@
module dtool
go 1.20
require github.com/spf13/cobra v1.7.0
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/miekg/dns v1.1.54 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/tools v0.3.0 // indirect
)

20
go.sum Normal file
View File

@@ -0,0 +1,20 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

11
main.go Normal file
View File

@@ -0,0 +1,11 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package main
import "dtool/cmd"
func main() {
cmd.Execute()
}

133
prober/rdns_prober.go Normal file
View File

@@ -0,0 +1,133 @@
package prober
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
"sync"
"time"
"dtool/utils"
)
type Data struct {
target string
dict map[string]bool
}
var dataset map[string][]string
func retrieve_ip(pool chan string, filename string) {
cnt := 0
f, err := os.Open(filename)
if err != nil {
fmt.Printf("cannot open file %s\n", filename)
return
}
fmt.Println("sending msg ...")
reader := bufio.NewReader(f)
for {
s, err := reader.ReadString('\n')
if err == io.EOF {
break
}
s = strings.Trim(s, "\n")
pool <- s
cnt++
if cnt%10 == 0 {
fmt.Println(cnt)
}
}
close(pool)
}
func active_probe(n int, addr string) Data {
target_ip := addr[:len(addr)-3]
data := Data{target_ip, make(map[string]bool)}
stop := 0
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
for i := 0; i < n; i++ {
subdomain := strings.Join([]string{strings.Replace(target_ip, ".", "-", -1), "echo", strconv.Itoa(i), timestamp}, "-")
rdns_ip, err := utils.SendQuery(addr, subdomain)
if err == nil {
data.dict[rdns_ip] = true
} else {
stop += 1
}
if stop == 3 {
break
}
}
return data
}
func upstream_prober(ip_pool chan string, data_pool chan Data, wg *sync.WaitGroup) {
for {
if s, ok := <-ip_pool; ok {
addr := s + ":53"
data := active_probe(20, addr)
if data.dict != nil {
data_pool <- data
}
} else {
break
}
}
wg.Done()
}
func create_probers(num int, ip_pool chan string, data_pool chan Data, wg *sync.WaitGroup) {
for i := 0; i < num; i++ {
wg.Add(1)
go upstream_prober(ip_pool, data_pool, wg)
}
}
func store_data(pool chan Data, wg *sync.WaitGroup) {
wg.Add(1)
for {
var temp []string
if data, ok := <-pool; ok {
if len(data.dict) > 0 {
for rdns := range data.dict {
temp = append(temp, rdns)
}
dataset[data.target] = temp
}
} else {
break
}
}
wg.Done()
}
func Get_upstream_file(filename string, prober_num int) {
dataset = map[string][]string{}
ip_pool := make(chan string, 500)
data_pool := make(chan Data, 200)
var probe_tasks sync.WaitGroup
var store_task sync.WaitGroup
go retrieve_ip(ip_pool, filename)
create_probers(prober_num, ip_pool, data_pool, &probe_tasks)
go store_data(data_pool, &store_task)
probe_tasks.Wait()
close(data_pool)
store_task.Wait()
utils.OutputJSON(dataset)
}
func Get_upstream_ip(ip string) {
dataset = make(map[string][]string)
var temp []string
addr := ip + ":53"
data := active_probe(10, addr)
for rdns := range data.dict {
temp = append(temp, rdns)
}
dataset[data.target] = temp
utils.OutputJSON(dataset)
}

55
utils/dns_utils.go Normal file
View File

@@ -0,0 +1,55 @@
package utils
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/miekg/dns"
)
type WrongAnswerError struct {
Message string
}
func (e *WrongAnswerError) Error() string {
return fmt.Sprintf("Wrong Answer: %s", e.Message)
}
func SendQuery(addr string, dn string) (string, error) {
var (
domain string
rdns_ip string
)
if dn == "timestamp" {
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
domain = strings.Join([]string{timestamp, "-scan.echodns.xyz."}, "")
} else {
domain = strings.Join([]string{dn, ".echodns.xyz."}, "")
}
//fmt.Println(domain)
m := new(dns.Msg)
m.SetQuestion(domain, dns.TypeA)
m.RecursionDesired = true
res, err := dns.Exchange(m, addr)
if err == nil {
if len(res.Answer) == 1 {
if a, ok := res.Answer[0].(*dns.A); ok {
rdns_ip = a.A.String()
} else {
rdns_ip = ""
err = &WrongAnswerError{
Message: "Wrong Record Type",
}
}
} else {
rdns_ip = ""
err = &WrongAnswerError{
Message: "Wrong Answer Section",
}
}
}
return rdns_ip, err
}

112
utils/httpdns_utils.go Normal file
View File

@@ -0,0 +1,112 @@
package utils
import (
"bytes"
"crypto/des"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
)
func pkcs5Padding(plaintext []byte, blocksize int) []byte {
padding := blocksize - len(plaintext)%blocksize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(plaintext, padtext...)
}
func pkcs5Unpadding(plaintext []byte) []byte {
padding := plaintext[len(plaintext)-1]
return plaintext[:len(plaintext)-int(padding)]
}
func encryptDES(plaintext, key []byte) ([]byte, error) {
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
blocksize := block.BlockSize()
plaintext = pkcs5Padding(plaintext, blocksize)
ciphertext := make([]byte, len(plaintext))
for i := 0; i < len(plaintext); i += blocksize {
block.Encrypt(ciphertext[i:], plaintext[i:i+blocksize])
}
return ciphertext, nil
}
func decryptDES(ciphertext, key []byte) ([]byte, error) {
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
blocksize := block.BlockSize()
decryptedtext := make([]byte, len(ciphertext))
for i := 0; i < len(ciphertext); i += blocksize {
block.Decrypt(decryptedtext[i:], ciphertext[i:i+blocksize])
}
decryptedtext = pkcs5Unpadding(decryptedtext)
return decryptedtext, nil
}
func SendTencentHttpdnsQuery() {
client := &http.Client{}
domain := []byte("echo.echodns.xyz")
key := []byte("046Ju3Cw")
encrypted_bytes, err := encryptDES(domain, key)
if err != nil {
return
}
encrypted_domain := hex.EncodeToString(encrypted_bytes)
url := fmt.Sprintf("http://119.29.29.98/d?dn=%s&id=61188", encrypted_domain)
fmt.Println(url)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Printf("create new request failed. Error: %s\n", err)
}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("request went wrong. Error: %s\n", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("read content failed. Error: %s\n", err)
return
}
encrypted_response, _ := hex.DecodeString(string(body))
decrypted_response, err := decryptDES(encrypted_response, key)
if err != nil {
return
}
fmt.Println(string(decrypted_response))
}
func SendAlicloudHttpdnsQurey() {
client := &http.Client{}
req, err := http.NewRequest("GET", "http://203.107.1.33/149702/d?host=echo.echodns.xyz", nil)
if err != nil {
fmt.Printf("create new request failed. Error: %s\n", err)
}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("request went wrong. Error: %s\n", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("read content failed. Error: %s\n", err)
return
}
fmt.Println(string(body))
}

15
utils/output_utils.go Normal file
View File

@@ -0,0 +1,15 @@
package utils
import (
"encoding/json"
"fmt"
)
func OutputJSON(data map[string][]string) {
jsonstr, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println("JSON encoding error:", err)
return
}
fmt.Println(string(jsonstr))
}