New Protocols Scans, SSH Fingerprinting

* SSH Fingerprint
* Page Snapshot
* A few new Protocol Tests (FTP, SMTP, Ricochet, IRC)
This commit is contained in:
Sarah Jamie Lewis 2016-04-24 19:46:28 -07:00
parent a0ae46ca31
commit 44e6d5c955
11 changed files with 192 additions and 3 deletions

View File

@ -84,7 +84,7 @@ or their users at risk of deanonymization.
## Server Fingerprint ## Server Fingerprint
Sometimes, even without mod_status we can determine if two sites are hosted on 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 * Server HTTP Header
* Technology Stack (e.g. php, jquery version etc.) * Technology Stack (e.g. php, jquery version etc.)

View File

@ -42,5 +42,21 @@ func (os *OnionScan) Scan(hiddenService string) (*report.OnionScanReport, error)
rps := new(protocol.RicochetProtocolScanner) rps := new(protocol.RicochetProtocolScanner)
rps.ScanProtocol(hiddenService, os.TorProxyAddress, report) 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 return report, nil
} }

View File

@ -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
}
}

24
protocol/ftp_scanner.go Normal file
View File

@ -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
}
}

View File

@ -22,6 +22,7 @@ func (hps * HTTPProtocolScanner) ScanProtocol(hiddenService string, proxyAddress
log.Printf("Failed to connect to service on port 80\n") log.Printf("Failed to connect to service on port 80\n")
} else { } else {
log.Printf("Found potential service on http(80)\n") log.Printf("Found potential service on http(80)\n")
report.WebDetected = true
dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, proxyAddress) dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, proxyAddress)
transportConfig := &http.Transport{ transportConfig := &http.Transport{
Dial: dialSocksProxy, Dial: dialSocksProxy,

25
protocol/irc_scanner.go Normal file
View File

@ -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
}
}

View File

@ -19,6 +19,7 @@ func (rps *RicochetProtocolScanner) ScanProtocol(hiddenService string, proxyAddr
} else { } else {
log.Printf("Detected possible ricochet instance\n") log.Printf("Detected possible ricochet instance\n")
// TODO: Actual Analysis // TODO: Actual Analysis
report.RicochetDetected = true
} }
} }

24
protocol/smtp_scanner.go Normal file
View File

@ -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
}
}

View File

@ -4,6 +4,11 @@ import (
"github.com/s-rah/onionscan/report" "github.com/s-rah/onionscan/report"
"h12.me/socks" "h12.me/socks"
"log" "log"
"golang.org/x/crypto/ssh"
"net"
"errors"
"crypto/md5"
"fmt"
) )
type SSHProtocolScanner struct { type SSHProtocolScanner struct {
@ -13,11 +18,35 @@ type SSHProtocolScanner struct {
func (sps *SSHProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) { func (sps *SSHProtocolScanner) ScanProtocol(hiddenService string, proxyAddress string, report *report.OnionScanReport) {
// SSH // SSH
log.Printf("Checking %s ssh(22)\n", hiddenService) 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 { if err != nil {
log.Printf("Failed to connect to service on port 22\n") log.Printf("Failed to connect to service on port 22\n")
} else { } else {
// TODO SSH Checking // 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)
} }
} }

View File

@ -2,6 +2,8 @@ package report
import ( import (
"encoding/json" "encoding/json"
"io/ioutil"
"github.com/s-rah/onionscan/utils"
) )
type ExifTag struct { type ExifTag struct {
@ -15,18 +17,43 @@ type ExifImage struct {
} }
type OnionScanReport 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"` HiddenService string `json:"hiddenService"`
ServerPoweredBy string `json:"serverPoweredBy"` ServerPoweredBy string `json:"serverPoweredBy"`
ServerVersion string `json:"serverVersion"` ServerVersion string `json:"serverVersion"`
FoundApacheModStatus bool `json:"foundApacheModStatus"` FoundApacheModStatus bool `json:"foundApacheModStatus"`
RelatedOnionServices []string `json:"relatedOnionServices"` RelatedOnionServices []string `json:"relatedOnionServices"`
RelatedClearnetDomains []string `json:"relatedOnionDomains"` RelatedClearnetDomains []string `json:"relatedOnionDomains"`
LinkedSites []string `json:"linkedSites"`
IP []string `json:"ipAddresses"` IP []string `json:"ipAddresses"`
OpenDirectories []string `json:"openDirectories"` OpenDirectories []string `json:"openDirectories"`
ExifImages []ExifImage `json:"exifImages"` ExifImages []ExifImage `json:"exifImages"`
InterestingFiles []string `json:"interestingFiles"` 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 { func NewOnionScanReport(hiddenService string) *OnionScanReport {
return &OnionScanReport{HiddenService: hiddenService} return &OnionScanReport{HiddenService: hiddenService}
} }
@ -51,6 +78,11 @@ func (osr *OnionScanReport) AddIPAddress(ip string) {
osr.IP = append(osr.IP, ip) 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) { func (osr *OnionScanReport) Serialize() (string, error) {
report,err := json.Marshal(osr) report,err := json.Marshal(osr)
if err != nil { if err != nil {

View File

@ -3,18 +3,25 @@ package scans
import ( import (
"github.com/s-rah/onionscan/report" "github.com/s-rah/onionscan/report"
"github.com/s-rah/onionscan/utils" "github.com/s-rah/onionscan/utils"
"net/url"
"log" "log"
"regexp" "regexp"
"strings" "strings"
"crypto/sha1"
"encoding/hex"
) )
func StandardPageScan(scan Scanner, page string, status int, contents string, report *report.OnionScanReport) { func StandardPageScan(scan Scanner, page string, status int, contents string, report *report.OnionScanReport) {
log.Printf("Scanning %s%s\n", report.HiddenService, page) log.Printf("Scanning %s%s\n", report.HiddenService, page)
if status == 200 { if status == 200 {
log.Printf("\tPage %s%s is Accessible\n", report.HiddenService, page) 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) domains := utils.ExtractDomains(contents)
for _,domain := range domains { for _,domain := range domains {
if !strings.HasPrefix(domain, "http://"+report.HiddenService) { if !strings.HasPrefix(domain, "http://"+report.HiddenService) {
log.Printf("Found Related URL %s\n", domain) 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 standard sites - google / bitpay etc.
// * Links to other onion sites // * Links to other onion sites
// * Links to obscure clearnet sites. // * Links to obscure clearnet sites.
baseUrl,_ := url.Parse(domain)
report.AddLinkedSite(baseUrl.Host)
} else { } else {
// * Process Internal links // * 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) log.Printf("\tPage %s%s is Does Not Exist\n", report.HiddenService, page)
} }
} }