From 84c526384fced186d3ce6041ac7f74ba70f5928d Mon Sep 17 00:00:00 2001 From: mrozitron Date: Mon, 13 Dec 2021 08:40:02 +0100 Subject: [PATCH] simulator/tor: add new Tor simulator Makes use of https://metrics.torproject.org/onionoo.html to obtain active Tor relays. Underlying simulation is carried out by TCPConnectSimulator. --- README.md | 29 +++++------ cmd/run/run.go | 17 +++---- simulator/tor.go | 122 +++++++++++++++++++++-------------------------- 3 files changed, 77 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index fa0ebfb..5fe03f6 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ To run all available modules, call: Available modules: - c2, dga, imposter, miner, scan, sink, spambot, ssh-exfil, ssh-transfer, tunnel-dns, tunnel-icmp + c2, dga, imposter, miner, scan, sink, spambot, ssh-exfil, ssh-transfer, tor, tunnel-dns, tunnel-icmp Available flags: -dry @@ -138,16 +138,17 @@ All done! The modules packaged with the utility are listed in the table below. -| Module | Description | -| ------------- | ----------------------------------------------------------------------------- | -| `c2` | Generates both DNS and IP traffic to a random list of known C2 destinations | -| `dga` | Simulates DGA traffic using random labels and top-level domains | -| `imposter` | Generates DNS traffic to a list of imposter domains | -| `miner` | Generates Stratum mining protocol traffic to known cryptomining pools | -| `scan` | Performs a port scan of random RFC 5737 addresses using common TCP ports | -| `sink` | Connects to known sinkholed destinations run by security researchers | -| `spambot` | Resolves and connects to random Internet SMTP servers to simulate a spam bot | -| `ssh-exfil` | Simulates an SSH file transfer to a service running on a non-standard SSH port| -| `ssh-transfer`| Simulates an SSH file transfer to a service running on an SSH port | -| `tunnel-dns` | Generates DNS tunneling requests to \*.sandbox.alphasoc.xyz | -| `tunnel-icmp` | Generates ICMP tunneling traffic to an Internet service operated by AlphaSOC | +| Module | Description | +| ------------- | ----------------------------------------------------------------------------- | +| `c2` | Generates both DNS and IP traffic to a random list of known C2 destinations | +| `dga` | Simulates DGA traffic using random labels and top-level domains | +| `imposter` | Generates DNS traffic to a list of imposter domains | +| `miner` | Generates Stratum mining protocol traffic to known cryptomining pools | +| `scan` | Performs a port scan of random RFC 5737 addresses using common TCP ports | +| `sink` | Connects to known sinkholed destinations run by security researchers | +| `spambot` | Resolves and connects to random Internet SMTP servers to simulate a spam bot | +| `ssh-exfil` | Simulates an SSH file transfer to a service running on a non-standard SSH port | +| `ssh-transfer` | Simulates an SSH file transfer to a service running on an SSH port | +| `tor` | Simulates Tor relay connections | +| `tunnel-dns` | Generates DNS tunneling requests to \*.sandbox.alphasoc.xyz | +| `tunnel-icmp` | Generates ICMP tunneling traffic to an Internet service operated by AlphaSOC | diff --git a/cmd/run/run.go b/cmd/run/run.go index a6534a5..778f77d 100644 --- a/cmd/run/run.go +++ b/cmd/run/run.go @@ -260,16 +260,13 @@ var allModules = []Module{ Timeout: 1 * time.Second, }, Module{ - Module: simulator.NewTorSimulator(), - Name: "tor", - Pipeline: PipelineDNS, - Experimental: true, - NumOfHosts: 5, - HeaderMsg: "Preparing Tor connection", - HostMsg: "Connecting to %s", - SuccessMsg: "Tor use is permitted in this environment", - // FailMsg: "Couldn't contact Tor network", - Timeout: 10 * time.Second, + Module: simulator.NewTorSimulator(), + Name: "tor", + Pipeline: PipelineIP, + NumOfHosts: 5, + HeaderMsg: "Preparing Tor relays", + HostMsg: "Connecting to %s", + Timeout: 1 * time.Second, }, Module{ Module: simulator.NewICMPtunnel(), diff --git a/simulator/tor.go b/simulator/tor.go index 1ae8d54..5754fb5 100644 --- a/simulator/tor.go +++ b/simulator/tor.go @@ -2,94 +2,82 @@ package simulator import ( "context" - "errors" + "encoding/json" "fmt" - "math/rand" "net/http" - "os" - "os/exec" - - "github.com/cretz/bine/tor" + "time" ) -//Slice containing websites hosted by torproject -var torHosts = []string{"expyuzz4wqqyqhjn.onion", "qrmfuxwgyzk5jdjz.onion", "e4nybovdbcwaqlyt.onion", "52g5y5karruvc7bz.onion", "x3nelbld33llasqv.onion", "vijs2fmpd72nbqok.onion", - "z5tfsnikzulwicxs.onion", "icxe4yp32mq6gm6n.onion", "qigcb4g4xxbh5ho6.onion", "kkvj4mhsttfcrksj.onion", "3gldbgtv5e4god56.onion", "tgnv2pssfumdedyw.onion", - "5bam5t36aombgv76.onion", "sdscoq7snqtznauu.onion", "rqef5a5mebgq46y5.onion", "ruv6ue7d3t22el2a.onion", "zfu7x4fuagirknhb.onion", "klbl4glo2btuwyok.onion", - "ngp5wfw5z6ms3ynx.onion", "tngjm3owsslo3wgo.onion", "dccbbv6cooddgcrq.onion", "jqs44zhtxl2uo6gk.onion", "odz6noxeukaw43e7.onion", "54nujbl4qohb5qdp.onion", - "eibwzyiqgk6vgugg.onion", "f7lqb5oicvsahone.onion", "y7pm6of53hzeb7u2.onion", "n46o4uxsej2icp5l.onion", "rougmnvswfsmd4dq.onion", "l3xrunzkfufzvw2c.onion", - "kzcx36ytbsm5iogs.onion", "ebxqgaz3dwywcoxl.onion", "yz7lpwfhhzcdyc5y.onion", "tgel7v4rpcllsrk2.onion", "llhb3u5h3q66ha62.onion", "rh7jaux2r3tzrqp4.onion", - "sbe5fi5cka5l3fqe.onion", "koz2sqqf4w23qxw2.onion", "hyntj47ow4ermsrh.onion", "yabd3wlpvybdnvzg.onion", "c5qrls2slxqz6vdw.onion", "wcgqzqyfi7a6iu62.onion", - "6m6blys5mwg2jwex.onion", "fhny6b7b6sbslc2b.onion", "s2bweojt5vg52e5i.onion", "xlv5dckljs4vhmhm.onion", "lfdhmyq24uacliu5.onion", "vt5hknv6sblkgf22.onion", - "buqlpzbbcyat2jiy.onion", "bn6kma5cpxill4pe.onion", "4bflp2c4tnynnbes.onion", "2xcd24wfjiqwzwnr.onion", "dgvdmophvhunawds.onion", "fylvgu5r6gcdadeo.onion", - "2iqyjmvrkrq5h5mg.onion", "nraswjtnyrvywxk7.onion", "ea5faa5po25cf7fb.onion", "krkzagd5yo4bvypt.onion", "hzmun3rnnxjhkyhg.onion", "expyuzz4wqqyqhjn.onion", -} - type TorSimulator struct { - tor *tor.Tor + TCPConnectSimulator } -//NewTorSimulator returns new TorSimulator func NewTorSimulator() *TorSimulator { return &TorSimulator{} } -// Tor creates tor connector; -// There is no way to pass the bind IP to tor, so we ignore it. -func (t *TorSimulator) Init(_ BindAddr) error { - tor, err := tor.Start(nil, &tor.StartConf{ - TempDataDirBase: os.TempDir(), - RetainTempDataDir: false, - ExtraArgs: []string{"--quiet"}}, - ) - if err != nil { - if errors.Is(err, exec.ErrNotFound) { - err = fmt.Errorf("%w (make sure you have tor installed in the system)", err) - } - return err - } - tor.StopProcessOnClose = true - t.tor = tor - return nil +// Init initializes the underlying TCPConnectSimulator. +func (s *TorSimulator) Init(bind BindAddr) error { + return s.TCPConnectSimulator.Init(bind) } -func (t *TorSimulator) Cleanup() { - if t.tor != nil { - t.tor.Close() - } +func (s *TorSimulator) Cleanup() {} + +// DetailsResponse contains the Relays slice we're interested in. +type DetailsResponse struct { + Version string `json:"version"` + BuildRevision string `json:"build_revision"` + RelaysPublished string `json:"relays_published"` + Relays []Relay `json:"relays"` } -//Hosts returns random hosts from predefined set -func (t TorSimulator) Hosts(scope string, size int) ([]string, error) { - var hosts []string - for _, i := range rand.Perm(len(torHosts)) { - if len(hosts) >= size { - break - } - hosts = append(hosts, torHosts[i]) - } - return hosts, nil +// Relays gets us what we need via OrAddresses. There is far more information available +// if future needs expand. +type Relay struct { + Nickname string `json:"nickname"` + Fingerprint string `json:"fingerprint"` + OrAddrs []string `json:"or_addresses"` } -//Simulate connection to tor network -func (t TorSimulator) Simulate(ctx context.Context, dst string) error { - dialer, err := t.tor.Dialer(ctx, nil) +// Hosts obtains size number of Tor relays using the onionoo.torproject.org API. +func (s *TorSimulator) Hosts(scope string, size int) ([]string, error) { + // Setup the query such that we get size number of running relays, ordered by consensus + // weight from largest to smallest. For details, refer to: + // https://metrics.torproject.org/onionoo.html#parameters + // Note also that the 'details' API is queried in order to get the full (ie. ip:port) + // relay address. + queryURL := fmt.Sprintf( + "https://onionoo.torproject.org/details?limit=%v&running=true&order=-consensus_weight", + size) + // Allow 5 seconds for the query. + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + req, err := http.NewRequestWithContext(ctx, "GET", queryURL, nil) if err != nil { - return err + return nil, err } - - httpClient := &http.Client{Transport: &http.Transport{DialContext: dialer.DialContext}} - //req, err := http.NewRequestWithContext(ctx, "GET", "http://"+dst, nil) //works in go 1.13 - req, err := http.NewRequest("GET", "http://"+dst, nil) + client := http.Client{} + resp, err := client.Do(req) if err != nil { - return err + // response body closed automatically on error. + return nil, err } - req = req.WithContext(ctx) - resp, err := httpClient.Do(req) + defer resp.Body.Close() + details := DetailsResponse{} + err = json.NewDecoder(resp.Body).Decode(&details) if err != nil { - return err + return nil, err } - defer resp.Body.Close() - - return nil + relays := details.Relays + // Setup relayAddrs to be returned. Per + // https://metrics.torproject.org/onionoo.html#details_relay_or_addresses, the first + // addr is the primary onion-routing address used during relay registration. + var relayAddrs []string + for _, r := range relays { + // Paranoia: let's make sure the relay address field has at least 1 address. + if len(r.OrAddrs) > 0 { + relayAddrs = append(relayAddrs, r.OrAddrs[0]) + } + } + return relayAddrs, nil }