# 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