背景
想要在服务器上起一个http服务器,用来远程执行shell命令并且返回。
基础版
package main
import (
"io"
"log"
"net/http"
)
func main() {
// Hello world, the web server
helloHandler := func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Hello, world!\n")
}
http.HandleFunc("/hello", helloHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
编译完成之后,执行起一个http
服务器,可以在浏览器中使用localhost:8080/hello
发送请求。也可以在shell
命令行中使用curl
命令发送请求。
想要远程执行命令,肯定是希望用curl的方式远程执行命令。
上面已经简单的实现了一个http服务器,可以正常响应请求,我们希望的是执行shell命令,应该如何给http
服务器传shell
命令参数,一起看一下。
实战版
package main
import (
"bytes"
"fmt"
"net/http"
"os/exec"
)
func execShellCmdBlock(shellCmd string) (string, error) {
var out bytes.Buffer
cmd := exec.Command("/bin/bash", "-c", shellCmd)
cmd.Stdout = &out
err := cmd.Run()
return out.String(), err
}
func Register(requestPath string) {
http.HandleFunc(requestPath, func(writer http.ResponseWriter, request *http.Request) {
err := request.ParseForm()
if err != nil {
return
}
cmds := request.Form["cmd"]
if len(cmds) != 1 {
return
}
ret, err := execShellCmdBlock(cmds[0])
if err != nil {
panic(err)
}
fmt.Fprintf(writer, "%s", ret)
})
}
func main() {
Register("/execute")
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}
大概逻辑就是先注册回调,回调解析传入的参数,并且以阻塞的方式运行shell
命令。
curtis@curtis-Aspire-E5-471G:~$ curl "http://192.168.0.105:8080/execute?cmd=%20ls%20-l"
total 6244
-rw-rw-r-- 1 curtis curtis 28 12月 17 12:53 go.mod
-rwxrwxr-x 1 curtis curtis 6383925 12月 17 13:34 http_server
-rw-rw-r-- 1 curtis curtis 749 12月 17 13:34 main.go
踩过的坑记录以下:
1、假设要在192.168.0.106上通过curl下发命令,如果http
服务器所在的主机没有关闭防火墙的话,会报no route
错误。
2、有些服务器可能对某些特定端口有限制,需要使用iptables
服务器端口放开,如果端口未放开,会报timeout
。