├── LICENSE ├── Makefile ├── README.md ├── data └── google-analytics-id.yaml ├── go.mod ├── go.sum ├── runner └── runner.go ├── sources ├── hackertarget.go ├── osint.go ├── site-overview.go └── spyonweb.go ├── udon.go └── utils ├── banner.go ├── options.go ├── output.go ├── requests.go ├── types.go └── utils.go /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, dhn 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Go parameters 2 | GOCMD=go 3 | GOBUILD=$(GOCMD) build 4 | GOMOD=$(GOCMD) mod 5 | GOGET=$(GOCMD) get 6 | 7 | all: build 8 | build: 9 | $(GOBUILD) -v -ldflags="-extldflags=-static" -o "udon" udon.go 10 | tidy: 11 | $(GOMOD) tidy 12 | clean: 13 | rm -f udon 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # udon 2 | 3 | ``` 4 | 5 | 88 88 8888b. dP"Yb 88b 88 6 | 88 88 8I Yb dP Yb 88Yb88 7 | Y8 8P 8I dY Yb dP 88 Y88 8 | YbodP' 8888Y" YbodP 88 Y8 9 | 10 | 11 | made with <3 12 | ``` 13 | 14 | **udon**: A simple tool that helps to find domains based on the Google Analytics ID. 15 | 16 |

17 | 18 | 19 |

20 | 21 | # Building 22 | 23 | * Download & install Go: https://golang.org/doc/install 24 | 25 | ``` 26 | go install github.com/dhn/udon@latest 27 | ``` 28 | 29 | # Usage 30 | 31 | ``` 32 | Usage of udon: 33 | -json 34 | Print results as JSON 35 | -s string 36 | UA ID to find domains for 37 | -silent 38 | Show only domains in output 39 | -version 40 | Show version of udon 41 | ``` 42 | 43 | # Running udon 44 | 45 | **udon** can be used to find domains/subdomains for a given Google Analytics ID. In this example for "UA-33427076": 46 | 47 | ```json 48 | ➜ $ udon -silent -json -s UA-33427076 | jq -c 49 | {"domain":"soester-anzeiger.de","source":"site-overview"} 50 | {"domain":"wa.de","source":"site-overview"} 51 | {"domain":"suederlaender-tageblatt.de","source":"site-overview"} 52 | {"domain":"trauer.nrw","source":"site-overview"} 53 | {"domain":"come-on.de","source":"site-overview"} 54 | {"domain":"immobilien.wa.de","source":"spyonweb"} 55 | {"domain":"nrw-jobs.de","source":"spyonweb"} 56 | {"domain":"auto.wa.de","source":"hackertarget"} 57 | {"domain":"come-on.de","source":"hackertarget"} 58 | {"domain":"immobilien.wa.de","source":"hackertarget"} 59 | {"domain":"soester-anzeiger.de","source":"hackertarget"} 60 | {"domain":"trauer.nrw","source":"hackertarget"} 61 | {"domain":"wa-mediengruppe.de","source":"hackertarget"} 62 | {"domain":"wa.de","source":"hackertarget"} 63 | {"domain":"web.archive.org","source":"hackertarget"} 64 | {"domain":"www.come-on.de","source":"hackertarget"} 65 | {"domain":"www.soester-anzeiger.de","source":"hackertarget"} 66 | {"domain":"www.wa.de","source":"hackertarget"} 67 | {"domain":"wa.de","source":"osint.sh"} 68 | {"domain":"soester-anzeiger.de","source":"osint.sh"} 69 | {"domain":"come-on.de","source":"osint.sh"} 70 | {"domain":"dispspotegcred.tk","source":"osint.sh"} 71 | {"domain":"conmamanan.tk","source":"osint.sh"} 72 | ``` 73 | 74 | To find UA id's following simple `nuclei` [1] template can be used: 75 | 76 | ```yaml 77 | id: google-analytics-id 78 | 79 | info: 80 | name: Google Analytics ID Discovery 81 | author: dhn 82 | severity: info 83 | tags: tech 84 | 85 | requests: 86 | - method: GET 87 | 88 | path: 89 | - "{{BaseURL}}" 90 | 91 | extractors: 92 | - type: regex 93 | name: ua-id 94 | part: body 95 | regex: 96 | - "UA-[0-9]+(-[0-9]+)" 97 | 98 | redirects: true 99 | max-redirects: 5 100 | stop-at-first-match: true 101 | matchers-condition: and 102 | matchers: 103 | - type: regex 104 | part: body 105 | regex: 106 | - "UA-[0-9]+(-[0-9]+)" 107 | 108 | - type: status 109 | status: 110 | - 200 111 | ``` 112 | 113 | ## Disclaimer 114 | 115 | **udon** leverages multiple open APIs, it is developed for individuals to help them for research or internal work. If you wish to incorporate this tool into a commercial offering or purposes, you must agree to the Terms of the leveraged services: 116 | 117 | - Hackertarget - https://hackertarget.com 118 | - OSINT - https://osint.sh 119 | - Site-Overview - http://site-overview.com 120 | - SpyOnWeb - https://spyonweb.com 121 | 122 | --- 123 | You expressly understand and agree that **udon** (creators and contributors) shall not be liable for any damages or losses resulting from your use of this tool or third-party products that use it. 124 | 125 | Creators aren't in charge of any and have/has no responsibility for any kind of: 126 | 127 | - Unlawful or illegal use of the tool. 128 | - Legal or Law infringement (acted in any country, state, municipality, place) by third parties and users. 129 | - Act against ethical and / or human moral, ethic, and peoples and cultures of the world. 130 | - Malicious act, capable of causing damage to third parties, promoted or distributed by third parties or the user through this tool. 131 | 132 | This disclaimer was shameless stolen from: https://github.com/projectdiscovery/subfinder/blob/master/DISCLAIMER.md 133 | 134 | ## Reference 135 | 136 | - [1] https://github.com/projectdiscovery/nuclei -------------------------------------------------------------------------------- /data/google-analytics-id.yaml: -------------------------------------------------------------------------------- 1 | id: google-analytics-id 2 | 3 | info: 4 | name: Google Analytics ID Discovery 5 | author: dhn 6 | severity: info 7 | tags: tech 8 | 9 | requests: 10 | - method: GET 11 | 12 | path: 13 | - "{{BaseURL}}" 14 | 15 | extractors: 16 | - type: regex 17 | name: ua-id 18 | part: body 19 | regex: 20 | - "UA-[0-9]+(-[0-9]+)" 21 | 22 | redirects: true 23 | max-redirects: 5 24 | stop-at-first-match: true 25 | matchers-condition: and 26 | matchers: 27 | - type: regex 28 | part: body 29 | regex: 30 | - "UA-[0-9]+(-[0-9]+)" 31 | 32 | - type: status 33 | status: 34 | - 200 35 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dhn/udon 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.8.0 7 | github.com/corpix/uarand v0.2.0 8 | github.com/json-iterator/go v1.1.12 9 | github.com/projectdiscovery/gologger v1.1.4 10 | github.com/valyala/fasthttp v1.43.0 11 | ) 12 | 13 | require ( 14 | github.com/andybalholm/brotli v1.0.4 // indirect 15 | github.com/andybalholm/cascadia v1.3.1 // indirect 16 | github.com/klauspost/compress v1.15.9 // indirect 17 | github.com/logrusorgru/aurora v2.0.3+incompatible // indirect 18 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 19 | github.com/modern-go/reflect2 v1.0.2 // indirect 20 | github.com/valyala/bytebufferpool v1.0.0 // indirect 21 | golang.org/x/net v0.33.0 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= 2 | github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= 3 | github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= 4 | github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 5 | github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= 6 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= 7 | github.com/corpix/uarand v0.2.0 h1:U98xXwud/AVuCpkpgfPF7J5TQgr7R5tqT8VZP5KWbzE= 8 | github.com/corpix/uarand v0.2.0/go.mod h1:/3Z1QIqWkDIhf6XWn/08/uMHoQ8JUoTIKc2iPchBOmM= 9 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 14 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 15 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 16 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 17 | github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= 18 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 19 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 20 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 21 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 22 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 23 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 24 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 25 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 26 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 27 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 28 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 29 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 30 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 31 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 32 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 33 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 34 | github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= 35 | github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= 36 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 37 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 38 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 39 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 40 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 41 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 42 | github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g= 43 | github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= 44 | github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= 45 | golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 46 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 47 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 48 | golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 49 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 50 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 51 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 52 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 53 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 54 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 55 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 56 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 57 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 58 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 59 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 60 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 61 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 62 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 63 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 64 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 65 | -------------------------------------------------------------------------------- /runner/runner.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package runner 6 | 7 | import ( 8 | "github.com/dhn/udon/sources" 9 | "github.com/dhn/udon/utils" 10 | ) 11 | 12 | // Query several sources to reverse search 13 | func UA(id string, options utils.Options) { 14 | ua := utils.MergeChannels( 15 | sources.GetHDData(id), 16 | sources.GetSOData(id), 17 | sources.GetOSData(id), 18 | sources.GetSPData(id), 19 | ) 20 | ua = utils.RemoveDuplicates(ua) 21 | utils.PrintResults(options.JSON, ua) 22 | } 23 | -------------------------------------------------------------------------------- /sources/hackertarget.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sources 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "net/url" 11 | "strings" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/dhn/udon/utils" 15 | ) 16 | 17 | // GetHDData function returns all domains based on the given UA id name 18 | func GetHDData(ua string) <-chan utils.Result { 19 | results := make(chan utils.Result) 20 | 21 | go func() { 22 | getHDData(fmt.Sprintf("https://api.hackertarget.com/analyticslookup/?q=%s", 23 | url.QueryEscape(ua)), results) 24 | close(results) 25 | }() 26 | 27 | return results 28 | } 29 | 30 | // Send a HTTP request and parse the JSON response 31 | func getHDData(sourceURL string, results chan utils.Result) { 32 | resp := utils.GetHTTPRequest(sourceURL, map[string]string{}) 33 | 34 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body())) 35 | if err != nil { 36 | return 37 | } 38 | 39 | doc.Find("*").Each(func(i int, domain *goquery.Selection) { 40 | for _, result := range strings.Split(domain.Text(), "\n") { 41 | if len(strings.TrimSpace(result)) != 0 && !strings.Contains(result, "error getting results") { 42 | results <- utils.Result{Value: result, Source: "hackertarget"} 43 | } 44 | } 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /sources/osint.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sources 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "net/url" 11 | "strings" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/dhn/udon/utils" 15 | ) 16 | 17 | // GetOSData function returns all domains based on the given UA id name 18 | func GetOSData(ua string) <-chan utils.Result { 19 | results := make(chan utils.Result) 20 | 21 | go func() { 22 | getOSData("https://osint.sh/analytics/", ua, results) 23 | close(results) 24 | }() 25 | 26 | return results 27 | } 28 | 29 | // Send a HTTP request and parse the JSON response 30 | func getOSData(sourceURL string, ua string, results chan utils.Result) { 31 | data := []byte(fmt.Sprintf("code=%s", url.QueryEscape(ua))) 32 | resp := utils.PostHTTPRequest(sourceURL, data) 33 | 34 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body())) 35 | if err != nil { 36 | return 37 | } 38 | 39 | doc.Find("td").Each(func(i int, domain *goquery.Selection) { 40 | for _, result := range strings.Split(domain.Text(), "\n") { 41 | value, _ := domain.Attr("data-th") 42 | if value == "Domain" && len(strings.TrimSpace(result)) != 0 { 43 | results <- utils.Result{Value: strings.TrimSpace(result), Source: "osint.sh"} 44 | } 45 | } 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /sources/site-overview.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sources 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "net/url" 11 | "strings" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/dhn/udon/utils" 15 | ) 16 | 17 | // GetSOData function returns all domains based on the given UA id name 18 | func GetSOData(ua string) <-chan utils.Result { 19 | results := make(chan utils.Result) 20 | ua = strings.Replace(strings.ToUpper(ua), "UA", "", -1) 21 | 22 | go func() { 23 | getSOData(fmt.Sprintf("http://site-overview.com/website-report-search/analytics-account-id/%s", 24 | url.QueryEscape(ua)), ua, results) 25 | close(results) 26 | }() 27 | 28 | return results 29 | } 30 | 31 | // Send a HTTP request and parse the JSON response 32 | func getSOData(sourceURL string, ua string, results chan utils.Result) { 33 | resp := utils.GetHTTPRequest(sourceURL, map[string]string{}) 34 | 35 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body())) 36 | if err != nil { 37 | return 38 | } 39 | 40 | doc.Find("u").Each(func(i int, domain *goquery.Selection) { 41 | for _, result := range strings.Split(domain.Text(), "\n") { 42 | if len(strings.TrimSpace(result)) != 0 && !strings.Contains(result, ua) { 43 | results <- utils.Result{Value: result, Source: "site-overview"} 44 | } 45 | } 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /sources/spyonweb.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sources 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "net/url" 11 | "strings" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/dhn/udon/utils" 15 | ) 16 | 17 | // GetSPData function returns all domains based on the given UA id name 18 | func GetSPData(ua string) <-chan utils.Result { 19 | results := make(chan utils.Result) 20 | 21 | go func() { 22 | getSPData(fmt.Sprintf("https://spyonweb.com/%s", 23 | url.QueryEscape(ua)), ua, results) 24 | close(results) 25 | }() 26 | 27 | return results 28 | } 29 | 30 | // Send a HTTP request and parse the JSON response 31 | func getSPData(sourceURL string, ua string, results chan utils.Result) { 32 | resp := utils.GetHTTPRequest(sourceURL, map[string]string{}) 33 | 34 | doc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body())) 35 | if err != nil { 36 | return 37 | } 38 | 39 | doc.Find("a").Each(func(i int, domain *goquery.Selection) { 40 | for _, result := range strings.Split(domain.Text(), "\n") { 41 | value, _ := domain.Attr("href") 42 | if strings.Contains(value, "/go/") { 43 | results <- utils.Result{Value: result, Source: "spyonweb"} 44 | } 45 | } 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /udon.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/dhn/udon/runner" 9 | "github.com/dhn/udon/utils" 10 | ) 11 | 12 | func main() { 13 | options := utils.ParseOptions() 14 | 15 | if options.SearchString != "" { 16 | runner.UA(options.SearchString, *options) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /utils/banner.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package utils 6 | 7 | import "github.com/projectdiscovery/gologger" 8 | 9 | const banner = ` 10 | 11 | 88 88 8888b. dP"Yb 88b 88 12 | 88 88 8I Yb dP Yb 88Yb88 13 | Y8 8P 8I dY Yb dP 88 Y88 14 | YbodP' 8888Y" YbodP 88 Y8 15 | 16 | ` 17 | 18 | // Version is the current version of dnsx 19 | const Version = `0.0.1` 20 | 21 | // showBanner is used to show the banner to the user 22 | func ShowBanner() { 23 | gologger.Print().Msgf("%s\n", banner) 24 | gologger.Print().Msgf("\t\t made with <3\n\n") 25 | } 26 | -------------------------------------------------------------------------------- /utils/options.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package utils 6 | 7 | import ( 8 | "errors" 9 | "flag" 10 | "io" 11 | "os" 12 | "strings" 13 | 14 | "github.com/projectdiscovery/gologger" 15 | "github.com/projectdiscovery/gologger/levels" 16 | ) 17 | 18 | // Options 19 | type Options struct { 20 | SearchString string // UA id is the word to find for 21 | Silent bool // Silent suppresses any extra text and only writes the output to screen 22 | Stdin bool // Stdin specifies whether stdin input was given to the process 23 | Version bool // Version specifies if we should just show version and exit 24 | JSON bool // JSON output 25 | Output io.Writer 26 | } 27 | 28 | // ParseOptions parses the command line flags provided by a user 29 | func ParseOptions() *Options { 30 | options := &Options{} 31 | 32 | flag.StringVar(&options.SearchString, "s", "", "UA ID to find domains for") 33 | flag.BoolVar(&options.Silent, "silent", false, "Show only domains in output") 34 | flag.BoolVar(&options.Version, "version", false, "Show version of udon") 35 | flag.BoolVar(&options.JSON, "json", false, "Print results as JSON") 36 | flag.Parse() 37 | 38 | options.Output = os.Stdout 39 | options.Stdin = hasStdin() 40 | options.configureOutput() 41 | 42 | ShowBanner() 43 | 44 | if options.Version { 45 | gologger.Info().Msgf("Current Version: %s\n", Version) 46 | os.Exit(0) 47 | } 48 | 49 | // Validate the options passed by the user and if any 50 | // invalid options have been used, exit. 51 | err := options.validateOptions() 52 | if err != nil { 53 | gologger.Fatal().Msgf("Program exiting: %s\n", err) 54 | } 55 | 56 | return options 57 | } 58 | 59 | func hasStdin() bool { 60 | stat, err := os.Stdin.Stat() 61 | if err != nil { 62 | return false 63 | } 64 | 65 | isPipedFromChrDev := (stat.Mode() & os.ModeCharDevice) == 0 66 | isPipedFromFIFO := (stat.Mode() & os.ModeNamedPipe) != 0 67 | 68 | return isPipedFromChrDev || isPipedFromFIFO 69 | } 70 | 71 | // configureOutput configures the output on the screen 72 | func (options *Options) configureOutput() { 73 | if options.Silent { 74 | gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) 75 | } 76 | } 77 | 78 | // validateOptions validates the configuration options passed 79 | func (options *Options) validateOptions() error { 80 | if len(strings.TrimSpace(options.SearchString)) == 0 { 81 | return errors.New("please set the UA ID with '-s'") 82 | } 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /utils/output.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package utils 6 | 7 | import ( 8 | "os" 9 | 10 | jsoniter "github.com/json-iterator/go" 11 | "github.com/projectdiscovery/gologger" 12 | ) 13 | 14 | // JSON object domain 15 | type jsonDomain struct { 16 | Domain string `json:"domain"` 17 | Source string `json:"source"` 18 | } 19 | 20 | // Print results as JSON 21 | func WriteJSON(results <-chan Result) { 22 | encoder := jsoniter.NewEncoder(os.Stdout) 23 | var domain jsonDomain 24 | 25 | for result := range results { 26 | domain.Domain = result.Value 27 | domain.Source = result.Source 28 | err := encoder.Encode(&domain) 29 | if err != nil { 30 | gologger.Fatal().Msgf(err.Error()) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /utils/requests.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package utils 6 | 7 | import ( 8 | "net" 9 | "time" 10 | 11 | "github.com/corpix/uarand" 12 | "github.com/projectdiscovery/gologger" 13 | "github.com/valyala/fasthttp" 14 | ) 15 | 16 | // GET HTTP wrapper 17 | func GetHTTPRequest(url string, headers map[string]string) *fasthttp.Response { 18 | req := fasthttp.AcquireRequest() 19 | req.SetRequestURI(url) 20 | req.Header.Add("User-Agent", uarand.GetRandom()) 21 | 22 | for key, value := range headers { 23 | req.Header.Add(key, value) 24 | } 25 | 26 | resp := fasthttp.AcquireResponse() 27 | client := &fasthttp.Client{} 28 | 29 | err := client.Do(req, resp) 30 | if err != nil { 31 | gologger.Error().Msgf(err.Error()) 32 | } 33 | 34 | return resp 35 | } 36 | 37 | // POST HTTP wrapper 38 | func PostHTTPRequest(url string, data []byte) *fasthttp.Response { 39 | req := fasthttp.AcquireRequest() 40 | req.SetRequestURI(url) 41 | req.SetBody(data) 42 | 43 | req.Header.SetMethod("POST") 44 | req.Header.Add("User-Agent", uarand.GetRandom()) 45 | req.Header.Add("Content-Type", "application/x-www-form-urlencoded") 46 | 47 | resp := fasthttp.AcquireResponse() 48 | client := &fasthttp.Client{ 49 | Dial: func(addr string) (net.Conn, error) { 50 | return fasthttp.DialTimeout(addr, time.Second*10) 51 | }, 52 | } 53 | 54 | err := client.Do(req, resp) 55 | if err != nil { 56 | gologger.Error().Msgf(err.Error()) 57 | } 58 | 59 | return resp 60 | } 61 | -------------------------------------------------------------------------------- /utils/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package utils 6 | 7 | type Result struct { 8 | Value string 9 | Source string 10 | } 11 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 dhn. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package utils 6 | 7 | import ( 8 | "sync" 9 | 10 | "github.com/projectdiscovery/gologger" 11 | ) 12 | 13 | // Print results as JSON or plain 14 | func PrintResults(json bool, results <-chan Result) { 15 | if json { 16 | WriteJSON(results) 17 | } else { 18 | for result := range results { 19 | gologger.Silent().Msg(result.Value) 20 | } 21 | } 22 | } 23 | 24 | // Remove duplicate strings 25 | func RemoveDuplicateStr(strSlice []string) []string { 26 | allKeys := make(map[string]bool) 27 | list := []string{} 28 | 29 | for _, item := range strSlice { 30 | if _, value := allKeys[item]; !value { 31 | allKeys[item] = true 32 | list = append(list, item) 33 | } 34 | } 35 | return list 36 | } 37 | 38 | // Remove duplicates from a channel and return a channel from type Result 39 | func RemoveDuplicates(input <-chan Result) <-chan Result { 40 | output := make(chan Result) 41 | 42 | go func() { 43 | set := make(map[Result]struct{}) 44 | for index := range input { 45 | if _, ok := set[index]; !ok { 46 | set[index] = struct{}{} 47 | output <- index 48 | } 49 | } 50 | close(output) 51 | }() 52 | 53 | return output 54 | } 55 | 56 | // Merge multiple channels from type Result 57 | func MergeChannels(channels ...<-chan Result) <-chan Result { 58 | out := make(chan Result) 59 | wg := sync.WaitGroup{} 60 | wg.Add(len(channels)) 61 | 62 | for _, channel := range channels { 63 | go func(channel <-chan Result) { 64 | for value := range channel { 65 | out <- value 66 | } 67 | wg.Done() 68 | }(channel) 69 | } 70 | 71 | go func() { 72 | wg.Wait() 73 | close(out) 74 | }() 75 | return out 76 | } 77 | --------------------------------------------------------------------------------