api_rest_controller.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. package api_rest
  2. import (
  3. "crypto/subtle"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "os"
  10. "strconv"
  11. "strings"
  12. "github.com/bettercap/bettercap/session"
  13. "github.com/gorilla/mux"
  14. )
  15. type CommandRequest struct {
  16. Command string `json:"cmd"`
  17. }
  18. type APIResponse struct {
  19. Success bool `json:"success"`
  20. Message string `json:"msg"`
  21. }
  22. func (mod *RestAPI) setAuthFailed(w http.ResponseWriter, r *http.Request) {
  23. mod.Warning("Unauthorized authentication attempt from %s to %s", r.RemoteAddr, r.URL.String())
  24. w.Header().Set("WWW-Authenticate", `Basic realm="auth"`)
  25. w.WriteHeader(401)
  26. w.Write([]byte("Unauthorized"))
  27. }
  28. func (mod *RestAPI) toJSON(w http.ResponseWriter, o interface{}) {
  29. w.Header().Set("Content-Type", "application/json")
  30. if err := json.NewEncoder(w).Encode(o); err != nil {
  31. mod.Debug("error while encoding object to JSON: %v", err)
  32. }
  33. }
  34. func (mod *RestAPI) setSecurityHeaders(w http.ResponseWriter) {
  35. w.Header().Add("X-Frame-Options", "DENY")
  36. w.Header().Add("X-Content-Type-Options", "nosniff")
  37. w.Header().Add("X-XSS-Protection", "1; mode=block")
  38. w.Header().Add("Referrer-Policy", "same-origin")
  39. w.Header().Set("Access-Control-Allow-Origin", mod.allowOrigin)
  40. w.Header().Add("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
  41. w.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
  42. }
  43. func (mod *RestAPI) checkAuth(r *http.Request) bool {
  44. if mod.username != "" && mod.password != "" {
  45. user, pass, _ := r.BasicAuth()
  46. // timing attack my ass
  47. if subtle.ConstantTimeCompare([]byte(user), []byte(mod.username)) != 1 {
  48. return false
  49. } else if subtle.ConstantTimeCompare([]byte(pass), []byte(mod.password)) != 1 {
  50. return false
  51. }
  52. }
  53. return true
  54. }
  55. func (mod *RestAPI) patchFrame(buf []byte) (frame map[string]interface{}, err error) {
  56. // this is ugly but necessary: since we're replaying, the
  57. // api.rest state object is filled with *old* values (the
  58. // recorded ones), but the UI needs updated values at least
  59. // of that in order to understand that a replay is going on
  60. // and where we are at it. So we need to parse the record
  61. // back into a session object and update only the api.rest.state
  62. frame = make(map[string]interface{})
  63. if err = json.Unmarshal(buf, &frame); err != nil {
  64. return
  65. }
  66. for _, i := range frame["modules"].([]interface{}) {
  67. m := i.(map[string]interface{})
  68. if m["name"] == "api.rest" {
  69. state := m["state"].(map[string]interface{})
  70. mod.State.Range(func(key interface{}, value interface{}) bool {
  71. state[key.(string)] = value
  72. return true
  73. })
  74. break
  75. }
  76. }
  77. return
  78. }
  79. func (mod *RestAPI) showSession(w http.ResponseWriter, r *http.Request) {
  80. if mod.replaying {
  81. if !mod.record.Session.Over() {
  82. from := mod.record.Session.Index() - 1
  83. q := r.URL.Query()
  84. vals := q["from"]
  85. if len(vals) > 0 {
  86. if n, err := strconv.Atoi(vals[0]); err == nil {
  87. from = n
  88. }
  89. }
  90. mod.record.Session.SetFrom(from)
  91. mod.Debug("replaying session %d of %d from %s",
  92. mod.record.Session.Index(),
  93. mod.record.Session.Frames(),
  94. mod.recordFileName)
  95. mod.State.Store("rec_frames", mod.record.Session.Frames())
  96. mod.State.Store("rec_cur_frame", mod.record.Session.Index())
  97. buf := mod.record.Session.Next()
  98. if frame, err := mod.patchFrame(buf); err != nil {
  99. mod.Error("%v", err)
  100. } else {
  101. mod.toJSON(w, frame)
  102. return
  103. }
  104. } else {
  105. mod.stopReplay()
  106. }
  107. }
  108. mod.toJSON(w, mod.Session)
  109. }
  110. func (mod *RestAPI) showBLE(w http.ResponseWriter, r *http.Request) {
  111. params := mux.Vars(r)
  112. mac := strings.ToLower(params["mac"])
  113. if mac == "" {
  114. mod.toJSON(w, mod.Session.BLE)
  115. } else if dev, found := mod.Session.BLE.Get(mac); found {
  116. mod.toJSON(w, dev)
  117. } else {
  118. http.Error(w, "Not Found", 404)
  119. }
  120. }
  121. func (mod *RestAPI) showHID(w http.ResponseWriter, r *http.Request) {
  122. params := mux.Vars(r)
  123. mac := strings.ToLower(params["mac"])
  124. if mac == "" {
  125. mod.toJSON(w, mod.Session.HID)
  126. } else if dev, found := mod.Session.HID.Get(mac); found {
  127. mod.toJSON(w, dev)
  128. } else {
  129. http.Error(w, "Not Found", 404)
  130. }
  131. }
  132. func (mod *RestAPI) showEnv(w http.ResponseWriter, r *http.Request) {
  133. mod.toJSON(w, mod.Session.Env)
  134. }
  135. func (mod *RestAPI) showGateway(w http.ResponseWriter, r *http.Request) {
  136. mod.toJSON(w, mod.Session.Gateway)
  137. }
  138. func (mod *RestAPI) showInterface(w http.ResponseWriter, r *http.Request) {
  139. mod.toJSON(w, mod.Session.Interface)
  140. }
  141. func (mod *RestAPI) showModules(w http.ResponseWriter, r *http.Request) {
  142. mod.toJSON(w, mod.Session.Modules)
  143. }
  144. func (mod *RestAPI) showLAN(w http.ResponseWriter, r *http.Request) {
  145. params := mux.Vars(r)
  146. mac := strings.ToLower(params["mac"])
  147. if mac == "" {
  148. mod.toJSON(w, mod.Session.Lan)
  149. } else if host, found := mod.Session.Lan.Get(mac); found {
  150. mod.toJSON(w, host)
  151. } else {
  152. http.Error(w, "Not Found", 404)
  153. }
  154. }
  155. func (mod *RestAPI) showOptions(w http.ResponseWriter, r *http.Request) {
  156. mod.toJSON(w, mod.Session.Options)
  157. }
  158. func (mod *RestAPI) showPackets(w http.ResponseWriter, r *http.Request) {
  159. mod.toJSON(w, mod.Session.Queue)
  160. }
  161. func (mod *RestAPI) showStartedAt(w http.ResponseWriter, r *http.Request) {
  162. mod.toJSON(w, mod.Session.StartedAt)
  163. }
  164. func (mod *RestAPI) showWiFi(w http.ResponseWriter, r *http.Request) {
  165. params := mux.Vars(r)
  166. mac := strings.ToLower(params["mac"])
  167. if mac == "" {
  168. mod.toJSON(w, mod.Session.WiFi)
  169. } else if station, found := mod.Session.WiFi.Get(mac); found {
  170. mod.toJSON(w, station)
  171. } else if client, found := mod.Session.WiFi.GetClient(mac); found {
  172. mod.toJSON(w, client)
  173. } else {
  174. http.Error(w, "Not Found", 404)
  175. }
  176. }
  177. func (mod *RestAPI) runSessionCommand(w http.ResponseWriter, r *http.Request) {
  178. var err error
  179. var cmd CommandRequest
  180. if r.Body == nil {
  181. http.Error(w, "Bad Request", 400)
  182. } else if err = json.NewDecoder(r.Body).Decode(&cmd); err != nil {
  183. http.Error(w, "Bad Request", 400)
  184. }
  185. for _, aCommand := range session.ParseCommands(cmd.Command) {
  186. if err = mod.Session.Run(aCommand); err != nil {
  187. http.Error(w, err.Error(), 400)
  188. return
  189. }
  190. }
  191. mod.toJSON(w, APIResponse{Success: true})
  192. }
  193. func (mod *RestAPI) getEvents(limit int) []session.Event {
  194. events := make([]session.Event, 0)
  195. for _, e := range mod.Session.Events.Sorted() {
  196. if mod.Session.EventsIgnoreList.Ignored(e) == false {
  197. events = append(events, e)
  198. }
  199. }
  200. nevents := len(events)
  201. nmax := nevents
  202. n := nmax
  203. if limit > 0 && limit < nmax {
  204. n = limit
  205. }
  206. return events[nevents-n:]
  207. }
  208. func (mod *RestAPI) showEvents(w http.ResponseWriter, r *http.Request) {
  209. q := r.URL.Query()
  210. if mod.replaying {
  211. if !mod.record.Events.Over() {
  212. from := mod.record.Events.Index() - 1
  213. vals := q["from"]
  214. if len(vals) > 0 {
  215. if n, err := strconv.Atoi(vals[0]); err == nil {
  216. from = n
  217. }
  218. }
  219. mod.record.Events.SetFrom(from)
  220. mod.Debug("replaying events %d of %d from %s",
  221. mod.record.Events.Index(),
  222. mod.record.Events.Frames(),
  223. mod.recordFileName)
  224. buf := mod.record.Events.Next()
  225. if _, err := w.Write(buf); err != nil {
  226. mod.Error("%v", err)
  227. } else {
  228. return
  229. }
  230. } else {
  231. mod.stopReplay()
  232. }
  233. }
  234. if mod.useWebsocket {
  235. mod.startStreamingEvents(w, r)
  236. } else {
  237. vals := q["n"]
  238. limit := 0
  239. if len(vals) > 0 {
  240. if n, err := strconv.Atoi(q["n"][0]); err == nil {
  241. limit = n
  242. }
  243. }
  244. mod.toJSON(w, mod.getEvents(limit))
  245. }
  246. }
  247. func (mod *RestAPI) clearEvents(w http.ResponseWriter, r *http.Request) {
  248. mod.Session.Events.Clear()
  249. }
  250. func (mod *RestAPI) corsRoute(w http.ResponseWriter, r *http.Request) {
  251. mod.setSecurityHeaders(w)
  252. w.WriteHeader(http.StatusNoContent)
  253. }
  254. func (mod *RestAPI) sessionRoute(w http.ResponseWriter, r *http.Request) {
  255. mod.setSecurityHeaders(w)
  256. if !mod.checkAuth(r) {
  257. mod.setAuthFailed(w, r)
  258. return
  259. } else if r.Method == "POST" {
  260. mod.runSessionCommand(w, r)
  261. return
  262. } else if r.Method != "GET" {
  263. http.Error(w, "Bad Request", 400)
  264. return
  265. }
  266. mod.Session.Lock()
  267. defer mod.Session.Unlock()
  268. path := r.URL.Path
  269. switch {
  270. case path == "/api/session":
  271. mod.showSession(w, r)
  272. case path == "/api/session/env":
  273. mod.showEnv(w, r)
  274. case path == "/api/session/gateway":
  275. mod.showGateway(w, r)
  276. case path == "/api/session/interface":
  277. mod.showInterface(w, r)
  278. case strings.HasPrefix(path, "/api/session/modules"):
  279. mod.showModules(w, r)
  280. case strings.HasPrefix(path, "/api/session/lan"):
  281. mod.showLAN(w, r)
  282. case path == "/api/session/options":
  283. mod.showOptions(w, r)
  284. case path == "/api/session/packets":
  285. mod.showPackets(w, r)
  286. case path == "/api/session/started-at":
  287. mod.showStartedAt(w, r)
  288. case strings.HasPrefix(path, "/api/session/ble"):
  289. mod.showBLE(w, r)
  290. case strings.HasPrefix(path, "/api/session/hid"):
  291. mod.showHID(w, r)
  292. case strings.HasPrefix(path, "/api/session/wifi"):
  293. mod.showWiFi(w, r)
  294. default:
  295. http.Error(w, "Not Found", 404)
  296. }
  297. }
  298. func (mod *RestAPI) readFile(fileName string, w http.ResponseWriter, r *http.Request) {
  299. fp, err := os.Open(fileName)
  300. if err != nil {
  301. msg := fmt.Sprintf("could not open %s for reading: %s", fileName, err)
  302. mod.Debug(msg)
  303. http.Error(w, msg, 404)
  304. return
  305. }
  306. defer fp.Close()
  307. w.Header().Set("Content-type", "application/octet-stream")
  308. io.Copy(w, fp)
  309. }
  310. func (mod *RestAPI) writeFile(fileName string, w http.ResponseWriter, r *http.Request) {
  311. data, err := ioutil.ReadAll(r.Body)
  312. if err != nil {
  313. msg := fmt.Sprintf("invalid file upload: %s", err)
  314. mod.Warning(msg)
  315. http.Error(w, msg, 404)
  316. return
  317. }
  318. err = ioutil.WriteFile(fileName, data, 0666)
  319. if err != nil {
  320. msg := fmt.Sprintf("can't write to %s: %s", fileName, err)
  321. mod.Warning(msg)
  322. http.Error(w, msg, 404)
  323. return
  324. }
  325. mod.toJSON(w, APIResponse{
  326. Success: true,
  327. Message: fmt.Sprintf("%s created", fileName),
  328. })
  329. }
  330. func (mod *RestAPI) eventsRoute(w http.ResponseWriter, r *http.Request) {
  331. mod.setSecurityHeaders(w)
  332. if !mod.checkAuth(r) {
  333. mod.setAuthFailed(w, r)
  334. return
  335. }
  336. if r.Method == "GET" {
  337. mod.showEvents(w, r)
  338. } else if r.Method == "DELETE" {
  339. mod.clearEvents(w, r)
  340. } else {
  341. http.Error(w, "Bad Request", 400)
  342. }
  343. }
  344. func (mod *RestAPI) fileRoute(w http.ResponseWriter, r *http.Request) {
  345. mod.setSecurityHeaders(w)
  346. if !mod.checkAuth(r) {
  347. mod.setAuthFailed(w, r)
  348. return
  349. }
  350. fileName := r.URL.Query().Get("name")
  351. if fileName != "" && r.Method == "GET" {
  352. mod.readFile(fileName, w, r)
  353. } else if fileName != "" && r.Method == "POST" {
  354. mod.writeFile(fileName, w, r)
  355. } else {
  356. http.Error(w, "Bad Request", 400)
  357. }
  358. }