---
# System prepended metadata

title: WhoisTool

---

# WhoisTool




```go=
cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("whois '%s'", s.GetString("domain")))
```

```go=
func isValidDomain(domain string) bool {
	return !strings.Contains(domain, "'") && !strings.Contains(domain, "\\") && !strings.HasPrefix(domain, "-")
}
```

> 這是幹嘛的阿 直接 google.com;ls; 不行嗎?
> 不行 有試過
> 因為他有 ' 包起來
> 不要用 ' 不行嗎?
> 我找不太到不用 ' 閉合他的方法
> 我來跑跑看
> 感覺甚麼神奇 shellshock 之類的
> 我一直在看 command injection 相關的 bypass 沒看到，我也來看看你說的這個東東



* 感覺不太可行，' filter 繞不掉
* `google.com;'ls;'`
    * Invalid input
* `google.com;%27ls;%27`
    * urlencode 不行
    * Error() err: exit status 1


```go=
package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/kataras/go-sessions"
)

const (
	TEMPLATE_BASE_HEAD = `<!DOCTYPE html><html lang="en"><head><title>Whois Tool</title></head><body>`
	TEMPLATE_BASE_FOOT = `</body></html>`
	TEMPLATE_INDEX = TEMPLATE_BASE_HEAD + `<h3>Whois Tool</h3><form method="POST" action="/set"><label for="domain">Domain: </label><input type="text" name="domain"><br /><br /><button type="submit">submit</button></form>` + TEMPLATE_BASE_FOOT
	TEMPLATE_RESULT = TEMPLATE_BASE_HEAD + `<textarea cols="100" rows="30">%s</textarea><br /><br /><a href="/">Back</a>` + TEMPLATE_BASE_FOOT
	TEMPLATE_ERROR = TEMPLATE_BASE_HEAD + `<h3>Error</h3><p>%s</p><br /><br /><a href="/">Back</a>` + TEMPLATE_BASE_FOOT
)

func isValidDomain(domain string) bool {
	return !strings.Contains(domain, "'") && !strings.Contains(domain, "\\") && !strings.HasPrefix(domain, "-")
}

func main() {
	sess := sessions.New(sessions.Config{})

	f, err := os.OpenFile("query.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		panic("Can't open ./query.log")
	}
	log.SetOutput(f)
	defer f.Close()

	http.HandleFunc("/set", func (w http.ResponseWriter, req *http.Request) {
		s := sess.Start(w, req)

		if req.Method != http.MethodPost {
			return
		}

		s.Set("domain", req.PostFormValue("domain"))
		http.Redirect(w, req, "/result", http.StatusSeeOther)
	})

	http.HandleFunc("/result", func (w http.ResponseWriter, req *http.Request) {
		s := sess.Start(w, req)

		var html string

		if (!isValidDomain(s.GetString("domain"))) {
			html = fmt.Sprintf(TEMPLATE_ERROR, "Invalid input")
			w.Write([]byte(html))
			return
		}

		ip, _, _ := net.SplitHostPort(req.RemoteAddr)
		log.Printf("- %s - %s\n", ip, s.GetString("domain"))

		cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("whois '%s'", s.GetString("domain")))
		out, err := cmd.CombinedOutput()
		if err != nil {
			html = fmt.Sprintf(TEMPLATE_ERROR, fmt.Sprintf("Error() err: %v", err))
		} else {
			w.Header().Set("Content-Type", "text/html; charset=UTF-8")
			html = fmt.Sprintf(TEMPLATE_RESULT, out)
		}

		w.Header().Set("Content-Type", "text/html; charset=UTF-8")
		w.Write([]byte(html))
	})

	http.HandleFunc("/", func (w http.ResponseWriter, req *http.Request) {
		s := sess.Start(w, req)
		s.Clear()
		w.Header().Set("Content-Type", "text/html; charset=UTF-8")
		w.Write([]byte(TEMPLATE_INDEX))
	})

	http.ListenAndServe(":8000", nil)
}

```

* race condition