├── .idea
├── .name
├── Ortau.iml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── README.md
├── conf
└── conf.go
├── go.mod
├── go.sum
├── main.go
├── reverseproxy
└── reverseproxy.go
└── static
├── banner.txt
├── cfg-tamper.ini
└── static.go
/.idea/.name:
--------------------------------------------------------------------------------
1 | main.go
--------------------------------------------------------------------------------
/.idea/Ortau.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 1639733404217
86 |
87 |
88 | 1639733404217
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | true
112 |
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Oratu
2 | >一个用于隐藏C2的、开箱即用的反向代理服务器。旨在省去繁琐的配置Nginx服务的过程。
3 |
4 | ## 0x01 使用
5 |
6 | ~~~shell
7 | ./Ortau
8 | ~~~
9 |
10 |
11 |
12 | 此时Ortau监听在8091端口,判断发送至8091端口的请求UA中是否包含`Ortau`(默认配置,可修改),如果包含则转发至8092,如果不包含则转发至jd.com。
13 |
14 |
15 |
16 | ## 0x02 其他配置
17 |
18 | 个人使用的Profile配置文件为:
19 |
20 | ~~~shell
21 | https://raw.githubusercontent.com/threatexpress/malleable-c2/master/jquery-c2.3.14.profile
22 |
23 | #修改http-config中的x-forwarded-for头,以配合反向代理设置。
24 | http-config {
25 | set trust_x_forwarded_for "true";
26 | header "Content-Type" "application/*; charset=utf-8";
27 | }
28 | #修改配置项set useragent
29 | set useragent "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Ortau";
30 | ~~~
31 |
32 |
33 |
34 | CS Listener配置为:
35 |
36 | 
37 |
38 |
39 |
40 | iptables禁掉8092端口,仅允许127.0.0.1访问:
41 |
42 | ~~~shell
43 | iptables -I INPUT -p tcp --dport 8092 -j DROP
44 | iptables -I INPUT -s 127.0.0.1 -p tcp --dport 8092 -j ACCEPT
45 | ~~~
46 |
47 |
48 |
49 |
50 | ## 0x03
51 |
52 | 
53 |
54 | 
55 |
56 |
--------------------------------------------------------------------------------
/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "Ortau/static"
5 | "gopkg.in/ini.v1"
6 | "log"
7 | "os"
8 | )
9 |
10 | func checkCfgFileIsExist(fileName string) bool {
11 | _, err := os.Stat(fileName)
12 | if err != nil {
13 | return false
14 | } else {
15 | return true
16 | }
17 | }
18 |
19 | func MakeCfg() bool {
20 | fileName := "config.ini"
21 | if checkCfgFileIsExist(fileName) == false {
22 | f, err := os.Create(fileName)
23 | if err != nil {
24 | log.Println("[Error] Error is : ", err)
25 | }
26 |
27 | defer f.Close()
28 | _, err = f.Write([]byte(static.Config))
29 | if err != nil {
30 | log.Println("[Error] Error is : ", err)
31 | }
32 |
33 | return false
34 | } else {
35 | return true
36 | }
37 | }
38 |
39 | func GetCfgSectionKey(section string, key string) string {
40 | var cfgValue string
41 |
42 | for {
43 | if MakeCfg() == false {
44 | log.Println("[Info] Not found cfg...Try to make config.ini...")
45 | } else {
46 | cfg, err := ini.Load("config.ini")
47 | if err != nil {
48 | log.Println("[Error] Fail to read file: %v", err)
49 | os.Exit(1)
50 | }
51 | cfgValue = cfg.Section(section).Key(key).String()
52 | break
53 | }
54 | }
55 |
56 | return cfgValue
57 | }
58 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module Ortau
2 |
3 | go 1.17
4 |
5 | require gopkg.in/ini.v1 v1.66.2
6 |
7 | require github.com/stretchr/testify v1.7.0 // indirect
8 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
7 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9 | gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
10 | gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
11 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
12 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "Ortau/conf"
5 | . "Ortau/reverseproxy"
6 | "Ortau/static"
7 | "fmt"
8 | "log"
9 | "net/http"
10 | "net/url"
11 | "os"
12 | "strings"
13 | )
14 |
15 | type ReverseProxy struct {
16 | RedirectUrl string
17 | Ua string
18 | }
19 |
20 | type Cfx struct {
21 | Host string
22 | Port string
23 | RedirectUrl string
24 | }
25 |
26 | func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
27 | //log.Println("[Info] UserAgent: ", r.UserAgent())
28 | remote, err := url.Parse(p.RedirectUrl)
29 | if err != nil {
30 | log.Println("[Error] Error is : ", err)
31 | }
32 |
33 | c2remote, err := url.Parse(conf.GetCfgSectionKey("default", "c2Url"))
34 | log.Println(c2remote)
35 |
36 | //log.Println(conf.GetCfgSectionKey("filter", "uaKey"))
37 | //根据uaKey判断转发
38 | if strings.Contains(r.UserAgent(), conf.GetCfgSectionKey("filter", "uaKey")) == true {
39 | log.Println("[Info] Try to redirect...")
40 | proxy := NewProxy(c2remote)
41 | proxy.ServeHTTP(w, r)
42 | } else {
43 | proxy := NewProxy(remote)
44 | proxy.ServeHTTP(w, r)
45 | }
46 |
47 | }
48 |
49 | func Runing() {
50 | fmt.Printf("\033[1;31;40m%s\033[0m", static.Banner)
51 | fmt.Println("\n\n")
52 |
53 | for {
54 | conf.MakeCfg()
55 | if conf.MakeCfg() == true {
56 | break
57 | }
58 | }
59 | ua := conf.GetCfgSectionKey("filter", "uaKey")
60 | if ua == "" {
61 | log.Println("[Error] UaKey is null,Please check config.ini ...")
62 | os.Exit(1)
63 | }
64 |
65 | cfx := &Cfx{
66 | Host: conf.GetCfgSectionKey("default", "host"),
67 | Port: conf.GetCfgSectionKey("default", "port"),
68 | RedirectUrl: conf.GetCfgSectionKey("default", "redirectUrl"),
69 | }
70 |
71 | localIpAddress := cfx.Host + ":" + cfx.Port
72 |
73 | proxyHandle := &ReverseProxy{RedirectUrl: cfx.RedirectUrl}
74 | log.Printf("[Info] Proxy Addr: %v, RedirectUrl: %v\n", localIpAddress, proxyHandle)
75 | err := http.ListenAndServe(localIpAddress, proxyHandle)
76 | if err != nil {
77 | log.Fatalln("ListenAndServe: ", err)
78 | }
79 |
80 | }
81 |
82 | func main() {
83 | Runing()
84 | }
85 |
--------------------------------------------------------------------------------
/reverseproxy/reverseproxy.go:
--------------------------------------------------------------------------------
1 | /*
2 | 重新修改一下net/http/httputil中的NewSingleHostReverseProxy函数,以解决无法反代至域名的问题。
3 | */
4 | package reverseproxy
5 |
6 | import (
7 | "net/http"
8 | "net/http/httputil"
9 | "net/url"
10 | "strings"
11 | )
12 |
13 | //CV from net/http/httputil NewSingleHostReverseProxy()
14 | func NewProxy(target *url.URL) *httputil.ReverseProxy {
15 | targetQuery := target.RawQuery
16 | director := func(req *http.Request) {
17 | req.Host = target.Host //<- add
18 | req.URL.Scheme = target.Scheme
19 | req.URL.Host = target.Host
20 | req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL)
21 | if targetQuery == "" || req.URL.RawQuery == "" {
22 | req.URL.RawQuery = targetQuery + req.URL.RawQuery
23 | } else {
24 | req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
25 | }
26 | if _, ok := req.Header["User-Agent"]; !ok {
27 | // explicitly disable User-Agent so it's not set to default value
28 | req.Header.Set("User-Agent", "")
29 | }
30 | }
31 | return &httputil.ReverseProxy{Director: director}
32 | }
33 |
34 | //CV from net/http/httputil
35 | func singleJoiningSlash(a, b string) string {
36 | aslash := strings.HasSuffix(a, "/")
37 | bslash := strings.HasPrefix(b, "/")
38 | switch {
39 | case aslash && bslash:
40 | return a + b[1:]
41 | case !aslash && !bslash:
42 | return a + "/" + b
43 | }
44 | return a + b
45 | }
46 | //CV from net/http/httputil
47 | func joinURLPath(a, b *url.URL) (path, rawpath string) {
48 | if a.RawPath == "" && b.RawPath == "" {
49 | return singleJoiningSlash(a.Path, b.Path), ""
50 | }
51 | // Same as singleJoiningSlash, but uses EscapedPath to determine
52 | // whether a slash should be added
53 | apath := a.EscapedPath()
54 | bpath := b.EscapedPath()
55 |
56 | aslash := strings.HasSuffix(apath, "/")
57 | bslash := strings.HasPrefix(bpath, "/")
58 |
59 | switch {
60 | case aslash && bslash:
61 | return a.Path + b.Path[1:], apath + bpath[1:]
62 | case !aslash && !bslash:
63 | return a.Path + "/" + b.Path, apath + "/" + bpath
64 | }
65 | return a.Path + b.Path, apath + bpath
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/static/banner.txt:
--------------------------------------------------------------------------------
1 | ___ ___ ___ ___
2 | / /\ / /\ ___ / /\ /__/\
3 | / /::\ / /::\ / /\ / /::\ \ \:\
4 | / /:/\:\ / /:/\:\ / /:/ / /:/\:\ \ \:\
5 | / /:/ \:\ / /:/~/:/ / /:/ / /:/~/::\ ___ \ \:\
6 | /__/:/ \__\:\ /__/:/ /:/___ / /::\ /__/:/ /:/\:\ /__/\ \__\:\
7 | \ \:\ / /:/ \ \:\/:::::/ /__/:/\:\ \ \:\/:/__\/ \ \:\ / /:/
8 | \ \:\ /:/ \ \::/~~~~ \__\/ \:\ \ \::/ \ \:\ /:/
9 | \ \:\/:/ \ \:\ \ \:\ \ \:\ \ \:\/:/
10 | \ \::/ \ \:\ \__\/ \ \:\ \ \::/
11 | \__\/ \__\/ \__\/ \__\/
--------------------------------------------------------------------------------
/static/cfg-tamper.ini:
--------------------------------------------------------------------------------
1 | [default]
2 | ;0.0.0.0表示接收任意地址
3 | host = 0.0.0.0
4 | ;监听8091端口,对8091端口传来的流量进行判断和转发
5 | port = 8091
6 | ;若不是CS的流量,则反代至jd.com
7 | redirectUrl = http://jd.com
8 | ;本地CS Linster 端口,配合iptables规则ban掉,只允许127.0.0.1访问
9 | c2Url = http://127.0.0.1:8092
10 |
11 |
12 | [filter]
13 | ;如果UA中包含uaKey,则将流量重定向至c2Url,否则反代至redirectUrl。
14 | ;因为ini文件以“;”做注释,且UA头内包含“;”,所以这里使用某个Key(默认Ortau),配合cs的Profile文件修改UA头即可。
15 | uaKey = Ortau
16 |
--------------------------------------------------------------------------------
/static/static.go:
--------------------------------------------------------------------------------
1 | package static
2 |
3 | import (
4 | _ "embed"
5 | )
6 |
7 | //go:embed banner.txt
8 | var Banner string
9 |
10 | //go:embed cfg-tamper.ini
11 | var Config string
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------