From 44e6d5c9557497754e12f0882d9f763e915de4a9 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Sun, 24 Apr 2016 19:46:28 -0700 Subject: [PATCH] New Protocols Scans, SSH Fingerprinting * SSH Fingerprint * Page Snapshot * A few new Protocol Tests (FTP, SMTP, Ricochet, IRC) --- README.md | 2 +- onionscan.go | 16 ++++++++++++++++ protocol/bitcoin_scanner.go | 25 +++++++++++++++++++++++++ protocol/ftp_scanner.go | 24 ++++++++++++++++++++++++ protocol/http_scanner.go | 1 + protocol/irc_scanner.go | 25 +++++++++++++++++++++++++ protocol/ricochet_scanner.go | 1 + protocol/smtp_scanner.go | 24 ++++++++++++++++++++++++ protocol/ssh_scanner.go | 31 ++++++++++++++++++++++++++++++- report/onionscanreport.go | 32 ++++++++++++++++++++++++++++++++ scans/standard-page-scan.go | 14 +++++++++++++- 11 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 protocol/bitcoin_scanner.go create mode 100644 protocol/ftp_scanner.go create mode 100644 protocol/irc_scanner.go create mode 100644 protocol/smtp_scanner.go diff --git a/README.md b/README.md index d408064..92294ef 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ or their users at risk of deanonymization. ## Server Fingerprint Sometimes, even without mod_status we can determine if two sites are hosted on - the sam infrastructure. We can use the following attributes to make this distinction: + the same infrastructure. We can use the following attributes to make this distinction: * Server HTTP Header * Technology Stack (e.g. php, jquery version etc.) diff --git a/onionscan.go b/onionscan.go index f32c0fa..469a396 100644 --- a/onionscan.go +++ b/onionscan.go @@ -42,5 +42,21 @@ func (os *OnionScan) Scan(hiddenService string) (*report.OnionScanReport, error) rps := new(protocol.RicochetProtocolScanner) rps.ScanProtocol(hiddenService, os.TorProxyAddress, report) + // Bitcoin + bps := new(protocol.BitcoinProtocolScanner) + bps.ScanProtocol(hiddenService, os.TorProxyAddress, report) + + //IRC + ips := new(protocol.IRCProtocolScanner) + ips.ScanProtocol(hiddenService, os.TorProxyAddress, report) + + //FTP + fps := new(protocol.FTPProtocolScanner) + fps.ScanProtocol(hiddenService, os.TorProxyAddress, report) + + //SMTP + smps := new(protocol.SMTPProtocolScanner) + smps.ScanProtocol(hiddenService, os.TorProxyAddress, report) + return report, nil } diff --git a/protocol/bitcoin_scanner.go b/protocol/bitcoin_scanner.go new file mode 100644 index 0000000..1a48fad --- /dev/null +++ b/protocol/bitcoin_scanner.go @@ -0,0 +1,25 @@ +package protocol + +import ( + "github.com/s-rah/onionscan/report" + "h12.me/socks" + "log" +) + +type BitcoinProtocolScanner struct { + +} + +func (rps *BitcoinProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) { + // Bitcoin + log.Printf("Checking %s Bitcoin(8333)\n", hiddenService) + _, err := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)("", hiddenService+":8333") + if err != nil { + log.Printf("Failed to connect to service on port 8333\n") + } else { + log.Printf("Detected possible Bitcoin instance\n") + // TODO: Actual Analysis + report.BitcoinDetected = true + } + +} diff --git a/protocol/ftp_scanner.go b/protocol/ftp_scanner.go new file mode 100644 index 0000000..87e7621 --- /dev/null +++ b/protocol/ftp_scanner.go @@ -0,0 +1,24 @@ +package protocol + +import ( + "github.com/s-rah/onionscan/report" + "h12.me/socks" + "log" +) + +type FTPProtocolScanner struct { + +} + +func (sps *FTPProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) { + // FTP + log.Printf("Checking %s FTP(22)\n", hiddenService) + _, err := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)("", hiddenService+":21") + if err != nil { + log.Printf("Failed to connect to service on port 21\n") + } else { + // TODO FTP Checking + report.FTPDetected = true + } + +} diff --git a/protocol/http_scanner.go b/protocol/http_scanner.go index df1a496..30ea255 100644 --- a/protocol/http_scanner.go +++ b/protocol/http_scanner.go @@ -22,6 +22,7 @@ func (hps * HTTPProtocolScanner) ScanProtocol(hiddenService string, proxyAddress log.Printf("Failed to connect to service on port 80\n") } else { log.Printf("Found potential service on http(80)\n") + report.WebDetected = true dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, proxyAddress) transportConfig := &http.Transport{ Dial: dialSocksProxy, diff --git a/protocol/irc_scanner.go b/protocol/irc_scanner.go new file mode 100644 index 0000000..9d531bc --- /dev/null +++ b/protocol/irc_scanner.go @@ -0,0 +1,25 @@ +package protocol + +import ( + "github.com/s-rah/onionscan/report" + "h12.me/socks" + "log" +) + +type IRCProtocolScanner struct { + +} + +func (rps *IRCProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) { + // IRC + log.Printf("Checking %s IRC(6667)\n", hiddenService) + _, err := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)("", hiddenService+":6667") + if err != nil { + log.Printf("Failed to connect to service on port 6667\n") + } else { + log.Printf("Detected possible IRC instance\n") + // TODO: Actual Analysis + report.IRCDetected = true + } + +} diff --git a/protocol/ricochet_scanner.go b/protocol/ricochet_scanner.go index 1949d9a..7453410 100644 --- a/protocol/ricochet_scanner.go +++ b/protocol/ricochet_scanner.go @@ -19,6 +19,7 @@ func (rps *RicochetProtocolScanner) ScanProtocol(hiddenService string, proxyAddr } else { log.Printf("Detected possible ricochet instance\n") // TODO: Actual Analysis + report.RicochetDetected = true } } diff --git a/protocol/smtp_scanner.go b/protocol/smtp_scanner.go new file mode 100644 index 0000000..fe071ec --- /dev/null +++ b/protocol/smtp_scanner.go @@ -0,0 +1,24 @@ +package protocol + +import ( + "github.com/s-rah/onionscan/report" + "h12.me/socks" + "log" +) + +type SMTPProtocolScanner struct { + +} + +func (sps *SMTPProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) { + // SMTP + log.Printf("Checking %s SMTP(25)\n", hiddenService) + _, err := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)("", hiddenService+":25") + if err != nil { + log.Printf("Failed to connect to service on port 25\n") + } else { + // TODO SMTP Checking + report.SMTPDetected = true + } + +} diff --git a/protocol/ssh_scanner.go b/protocol/ssh_scanner.go index c4fb140..f99b89c 100644 --- a/protocol/ssh_scanner.go +++ b/protocol/ssh_scanner.go @@ -4,6 +4,11 @@ import ( "github.com/s-rah/onionscan/report" "h12.me/socks" "log" + "golang.org/x/crypto/ssh" + "net" + "errors" + "crypto/md5" + "fmt" ) type SSHProtocolScanner struct { @@ -13,11 +18,35 @@ type SSHProtocolScanner struct { func (sps *SSHProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) { // SSH log.Printf("Checking %s ssh(22)\n", hiddenService) - _, err := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)("", hiddenService+":22") + conn, err := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)("", hiddenService+":22") if err != nil { log.Printf("Failed to connect to service on port 22\n") } else { // TODO SSH Checking + report.SSHDetected = true + + config := &ssh.ClientConfig { + HostKeyCallback : func (hostname string, addr net.Addr, key ssh.PublicKey) error { + h := md5.New() + h.Write(key.Marshal()) + + fBytes := h.Sum(nil) + fingerprint := string("") + for i := 0; i < len(fBytes); i++ { + if i+1 != len(fBytes) { + fingerprint = fmt.Sprintf("%s%0.2x:", fingerprint, fBytes[i]) + } else { + fingerprint = fmt.Sprintf("%s%0.2x", fingerprint, fBytes[i]) + } + } + report.SSHKey = fingerprint + log.Printf("Found SSH Key %s\n", fingerprint) + // We don't want to continue + return errors.New("error") + }, + } + ssh.NewClientConn(conn,hiddenService+":22",config) + } } diff --git a/report/onionscanreport.go b/report/onionscanreport.go index c6d3ddc..22e0145 100644 --- a/report/onionscanreport.go +++ b/report/onionscanreport.go @@ -2,6 +2,8 @@ package report import ( "encoding/json" + "io/ioutil" + "github.com/s-rah/onionscan/utils" ) type ExifTag struct { @@ -15,18 +17,43 @@ type ExifImage struct { } type OnionScanReport struct { + + WebDetected bool `json:"webDetected"` + SSHDetected bool `json:"sshDetected"` + RicochetDetected bool `json:"ricochetDetected"` + IRCDetected bool `json:"ircDetected"` + FTPDetected bool `json:"ftpDetected"` + SMTPDetected bool `json:"smtpDetected"` + + BitcoinDetected bool `json:"bitcoinDetected"` + HiddenService string `json:"hiddenService"` ServerPoweredBy string `json:"serverPoweredBy"` ServerVersion string `json:"serverVersion"` FoundApacheModStatus bool `json:"foundApacheModStatus"` RelatedOnionServices []string `json:"relatedOnionServices"` RelatedClearnetDomains []string `json:"relatedOnionDomains"` + LinkedSites []string `json:"linkedSites"` IP []string `json:"ipAddresses"` OpenDirectories []string `json:"openDirectories"` ExifImages []ExifImage `json:"exifImages"` InterestingFiles []string `json:"interestingFiles"` + Hashes []string `json:"hashes"` + SSHKey string `json:"sshKey"` + Snapshot string `json:"snapshot"` } +func LoadReportFromFile(filename string) (OnionScanReport, error) { + dat, err := ioutil.ReadFile(filename) + if err != nil { + return OnionScanReport{}, err + } + res := OnionScanReport{} + err = json.Unmarshal(dat, &res) + return res, err +} + + func NewOnionScanReport(hiddenService string) *OnionScanReport { return &OnionScanReport{HiddenService: hiddenService} } @@ -51,6 +78,11 @@ func (osr *OnionScanReport) AddIPAddress(ip string) { osr.IP = append(osr.IP, ip) } +func (osr *OnionScanReport) AddLinkedSite(site string) { + osr.LinkedSites = append(osr.LinkedSites, site) + utils.RemoveDuplicates(&osr.LinkedSites) +} + func (osr *OnionScanReport) Serialize() (string, error) { report,err := json.Marshal(osr) if err != nil { diff --git a/scans/standard-page-scan.go b/scans/standard-page-scan.go index dd20684..3b78b18 100644 --- a/scans/standard-page-scan.go +++ b/scans/standard-page-scan.go @@ -3,18 +3,25 @@ package scans import ( "github.com/s-rah/onionscan/report" "github.com/s-rah/onionscan/utils" + "net/url" "log" "regexp" "strings" + "crypto/sha1" + "encoding/hex" ) func StandardPageScan(scan Scanner, page string, status int, contents string, report *report.OnionScanReport) { log.Printf("Scanning %s%s\n", report.HiddenService, page) if status == 200 { log.Printf("\tPage %s%s is Accessible\n", report.HiddenService, page) + + hash := sha1.Sum([]byte(contents)) + report.Hashes = append(report.Hashes, hex.EncodeToString(hash[:])) + report.Snapshot = contents domains := utils.ExtractDomains(contents) - + for _,domain := range domains { if !strings.HasPrefix(domain, "http://"+report.HiddenService) { log.Printf("Found Related URL %s\n", domain) @@ -22,8 +29,11 @@ func StandardPageScan(scan Scanner, page string, status int, contents string, re // * Links to standard sites - google / bitpay etc. // * Links to other onion sites // * Links to obscure clearnet sites. + baseUrl,_ := url.Parse(domain) + report.AddLinkedSite(baseUrl.Host) } else { // * Process Internal links + log.Printf("Found Internal URL %s\n", domain) } } @@ -40,3 +50,5 @@ func StandardPageScan(scan Scanner, page string, status int, contents string, re log.Printf("\tPage %s%s is Does Not Exist\n", report.HiddenService, page) } } + +