├── .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 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 28 | 29 | 30 | 31 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 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 | 90 | 91 | 92 | 93 | 95 | 96 | 105 | 107 | 108 | 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 | ![image-20211219220450991](https://typora-mine.oss-cn-beijing.aliyuncs.com/typora/image-20211219220450991.png) 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 | ![image-20211219220911877](https://typora-mine.oss-cn-beijing.aliyuncs.com/typora/image-20211219220911877.png) 53 | 54 | ![image-20211219221006180](https://typora-mine.oss-cn-beijing.aliyuncs.com/typora/image-20211219221006180.png) 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 | --------------------------------------------------------------------------------