api_rest.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package api_rest
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "sync"
  7. "time"
  8. "github.com/bettercap/bettercap/session"
  9. "github.com/bettercap/bettercap/tls"
  10. "github.com/bettercap/recording"
  11. "github.com/gorilla/mux"
  12. "github.com/gorilla/websocket"
  13. "github.com/evilsocket/islazy/fs"
  14. )
  15. type RestAPI struct {
  16. session.SessionModule
  17. server *http.Server
  18. username string
  19. password string
  20. certFile string
  21. keyFile string
  22. allowOrigin string
  23. useWebsocket bool
  24. upgrader websocket.Upgrader
  25. quit chan bool
  26. recClock int
  27. recording bool
  28. recTime int
  29. loading bool
  30. replaying bool
  31. recordFileName string
  32. recordWait *sync.WaitGroup
  33. record *recording.Archive
  34. recStarted time.Time
  35. recStopped time.Time
  36. }
  37. func NewRestAPI(s *session.Session) *RestAPI {
  38. mod := &RestAPI{
  39. SessionModule: session.NewSessionModule("api.rest", s),
  40. server: &http.Server{},
  41. quit: make(chan bool),
  42. useWebsocket: false,
  43. allowOrigin: "*",
  44. upgrader: websocket.Upgrader{
  45. ReadBufferSize: 1024,
  46. WriteBufferSize: 1024,
  47. },
  48. recClock: 1,
  49. recording: false,
  50. recTime: 0,
  51. loading: false,
  52. replaying: false,
  53. recordFileName: "",
  54. recordWait: &sync.WaitGroup{},
  55. record: nil,
  56. }
  57. mod.State.Store("recording", &mod.recording)
  58. mod.State.Store("rec_clock", &mod.recClock)
  59. mod.State.Store("replaying", &mod.replaying)
  60. mod.State.Store("loading", &mod.loading)
  61. mod.State.Store("load_progress", 0)
  62. mod.State.Store("rec_time", &mod.recTime)
  63. mod.State.Store("rec_filename", &mod.recordFileName)
  64. mod.State.Store("rec_frames", 0)
  65. mod.State.Store("rec_cur_frame", 0)
  66. mod.State.Store("rec_started", &mod.recStarted)
  67. mod.State.Store("rec_stopped", &mod.recStopped)
  68. mod.AddParam(session.NewStringParameter("api.rest.address",
  69. "127.0.0.1",
  70. session.IPv4Validator,
  71. "Address to bind the API REST server to."))
  72. mod.AddParam(session.NewIntParameter("api.rest.port",
  73. "8081",
  74. "Port to bind the API REST server to."))
  75. mod.AddParam(session.NewStringParameter("api.rest.alloworigin",
  76. mod.allowOrigin,
  77. "",
  78. "Value of the Access-Control-Allow-Origin header of the API server."))
  79. mod.AddParam(session.NewStringParameter("api.rest.username",
  80. "",
  81. "",
  82. "API authentication username."))
  83. mod.AddParam(session.NewStringParameter("api.rest.password",
  84. "",
  85. "",
  86. "API authentication password."))
  87. mod.AddParam(session.NewStringParameter("api.rest.certificate",
  88. "",
  89. "",
  90. "API TLS certificate."))
  91. tls.CertConfigToModule("api.rest", &mod.SessionModule, tls.DefaultLegitConfig)
  92. mod.AddParam(session.NewStringParameter("api.rest.key",
  93. "",
  94. "",
  95. "API TLS key"))
  96. mod.AddParam(session.NewBoolParameter("api.rest.websocket",
  97. "false",
  98. "If true the /api/events route will be available as a websocket endpoint instead of HTTPS."))
  99. mod.AddHandler(session.NewModuleHandler("api.rest on", "",
  100. "Start REST API server.",
  101. func(args []string) error {
  102. return mod.Start()
  103. }))
  104. mod.AddHandler(session.NewModuleHandler("api.rest off", "",
  105. "Stop REST API server.",
  106. func(args []string) error {
  107. return mod.Stop()
  108. }))
  109. mod.AddParam(session.NewIntParameter("api.rest.record.clock",
  110. "1",
  111. "Number of seconds to wait while recording with api.rest.record between one sample and the next one."))
  112. mod.AddHandler(session.NewModuleHandler("api.rest.record off", "",
  113. "Stop recording the session.",
  114. func(args []string) error {
  115. return mod.stopRecording()
  116. }))
  117. mod.AddHandler(session.NewModuleHandler("api.rest.record FILENAME", `api\.rest\.record (.+)`,
  118. "Start polling the rest API periodically recording each sample in a compressed file that can be later replayed.",
  119. func(args []string) error {
  120. return mod.startRecording(args[0])
  121. }))
  122. mod.AddHandler(session.NewModuleHandler("api.rest.replay off", "",
  123. "Stop replaying the recorded session.",
  124. func(args []string) error {
  125. return mod.stopReplay()
  126. }))
  127. mod.AddHandler(session.NewModuleHandler("api.rest.replay FILENAME", `api\.rest\.replay (.+)`,
  128. "Start the rest API module in replay mode using FILENAME as the recorded session file, will revert to normal mode once the replay is over.",
  129. func(args []string) error {
  130. return mod.startReplay(args[0])
  131. }))
  132. return mod
  133. }
  134. type JSSessionRequest struct {
  135. Command string `json:"cmd"`
  136. }
  137. type JSSessionResponse struct {
  138. Error string `json:"error"`
  139. }
  140. func (mod *RestAPI) Name() string {
  141. return "api.rest"
  142. }
  143. func (mod *RestAPI) Description() string {
  144. return "Expose a RESTful API."
  145. }
  146. func (mod *RestAPI) Author() string {
  147. return "Simone Margaritelli <evilsocket@gmail.com>"
  148. }
  149. func (mod *RestAPI) isTLS() bool {
  150. return mod.certFile != "" && mod.keyFile != ""
  151. }
  152. func (mod *RestAPI) Configure() error {
  153. var err error
  154. var ip string
  155. var port int
  156. if mod.Running() {
  157. return session.ErrAlreadyStarted(mod.Name())
  158. } else if err, ip = mod.StringParam("api.rest.address"); err != nil {
  159. return err
  160. } else if err, port = mod.IntParam("api.rest.port"); err != nil {
  161. return err
  162. } else if err, mod.allowOrigin = mod.StringParam("api.rest.alloworigin"); err != nil {
  163. return err
  164. } else if err, mod.certFile = mod.StringParam("api.rest.certificate"); err != nil {
  165. return err
  166. } else if mod.certFile, err = fs.Expand(mod.certFile); err != nil {
  167. return err
  168. } else if err, mod.keyFile = mod.StringParam("api.rest.key"); err != nil {
  169. return err
  170. } else if mod.keyFile, err = fs.Expand(mod.keyFile); err != nil {
  171. return err
  172. } else if err, mod.username = mod.StringParam("api.rest.username"); err != nil {
  173. return err
  174. } else if err, mod.password = mod.StringParam("api.rest.password"); err != nil {
  175. return err
  176. } else if err, mod.useWebsocket = mod.BoolParam("api.rest.websocket"); err != nil {
  177. return err
  178. }
  179. if mod.isTLS() {
  180. if !fs.Exists(mod.certFile) || !fs.Exists(mod.keyFile) {
  181. cfg, err := tls.CertConfigFromModule("api.rest", mod.SessionModule)
  182. if err != nil {
  183. return err
  184. }
  185. mod.Debug("%+v", cfg)
  186. mod.Info("generating TLS key to %s", mod.keyFile)
  187. mod.Info("generating TLS certificate to %s", mod.certFile)
  188. if err := tls.Generate(cfg, mod.certFile, mod.keyFile, false); err != nil {
  189. return err
  190. }
  191. } else {
  192. mod.Info("loading TLS key from %s", mod.keyFile)
  193. mod.Info("loading TLS certificate from %s", mod.certFile)
  194. }
  195. }
  196. mod.server.Addr = fmt.Sprintf("%s:%d", ip, port)
  197. router := mux.NewRouter()
  198. router.Methods("OPTIONS").HandlerFunc(mod.corsRoute)
  199. router.HandleFunc("/api/file", mod.fileRoute)
  200. router.HandleFunc("/api/events", mod.eventsRoute)
  201. router.HandleFunc("/api/session", mod.sessionRoute)
  202. router.HandleFunc("/api/session/ble", mod.sessionRoute)
  203. router.HandleFunc("/api/session/ble/{mac}", mod.sessionRoute)
  204. router.HandleFunc("/api/session/hid", mod.sessionRoute)
  205. router.HandleFunc("/api/session/hid/{mac}", mod.sessionRoute)
  206. router.HandleFunc("/api/session/env", mod.sessionRoute)
  207. router.HandleFunc("/api/session/gateway", mod.sessionRoute)
  208. router.HandleFunc("/api/session/interface", mod.sessionRoute)
  209. router.HandleFunc("/api/session/modules", mod.sessionRoute)
  210. router.HandleFunc("/api/session/lan", mod.sessionRoute)
  211. router.HandleFunc("/api/session/lan/{mac}", mod.sessionRoute)
  212. router.HandleFunc("/api/session/options", mod.sessionRoute)
  213. router.HandleFunc("/api/session/packets", mod.sessionRoute)
  214. router.HandleFunc("/api/session/started-at", mod.sessionRoute)
  215. router.HandleFunc("/api/session/wifi", mod.sessionRoute)
  216. router.HandleFunc("/api/session/wifi/{mac}", mod.sessionRoute)
  217. mod.server.Handler = router
  218. if mod.username == "" || mod.password == "" {
  219. mod.Warning("api.rest.username and/or api.rest.password parameters are empty, authentication is disabled.")
  220. }
  221. return nil
  222. }
  223. func (mod *RestAPI) Start() error {
  224. if mod.replaying {
  225. return fmt.Errorf("the api is currently in replay mode, run api.rest.replay off before starting it")
  226. } else if err := mod.Configure(); err != nil {
  227. return err
  228. }
  229. mod.SetRunning(true, func() {
  230. var err error
  231. if mod.isTLS() {
  232. mod.Info("api server starting on https://%s", mod.server.Addr)
  233. err = mod.server.ListenAndServeTLS(mod.certFile, mod.keyFile)
  234. } else {
  235. mod.Info("api server starting on http://%s", mod.server.Addr)
  236. err = mod.server.ListenAndServe()
  237. }
  238. if err != nil && err != http.ErrServerClosed {
  239. panic(err)
  240. }
  241. })
  242. return nil
  243. }
  244. func (mod *RestAPI) Stop() error {
  245. if mod.recording {
  246. mod.stopRecording()
  247. } else if mod.replaying {
  248. mod.stopReplay()
  249. }
  250. return mod.SetRunning(false, func() {
  251. go func() {
  252. mod.quit <- true
  253. }()
  254. ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
  255. defer cancel()
  256. mod.server.Shutdown(ctx)
  257. })
  258. }