out-of-tree kernel {module, exploit} development tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

debug.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2018 Mikhail Klementev. All rights reserved.
  2. // Use of this source code is governed by a AGPLv3 license
  3. // (or later) that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "strings"
  12. "time"
  13. "gopkg.in/logrusorgru/aurora.v1"
  14. "code.dumpstack.io/tools/out-of-tree/config"
  15. "code.dumpstack.io/tools/out-of-tree/qemu"
  16. )
  17. func firstSupported(kcfg config.KernelConfig, ka config.Artifact,
  18. kernel string) (ki config.KernelInfo, err error) {
  19. km, err := kernelMask(kernel)
  20. if err != nil {
  21. return
  22. }
  23. ka.SupportedKernels = []config.KernelMask{km}
  24. for _, ki = range kcfg.Kernels {
  25. var supported bool
  26. supported, err = ka.Supported(ki)
  27. if err != nil || supported {
  28. return
  29. }
  30. }
  31. err = errors.New("No supported kernel found")
  32. return
  33. }
  34. func handleLine(q *qemu.System) (err error) {
  35. fmt.Print("out-of-tree> ")
  36. rawLine := "help"
  37. fmt.Scanf("%s", &rawLine)
  38. params := strings.Fields(rawLine)
  39. cmd := params[0]
  40. switch cmd {
  41. case "h", "help":
  42. fmt.Printf("help\t: print this help message\n")
  43. fmt.Printf("log\t: print qemu log\n")
  44. fmt.Printf("clog\t: print qemu log and cleanup buffer\n")
  45. fmt.Printf("cleanup\t: cleanup qemu log buffer\n")
  46. fmt.Printf("ssh\t: print arguments to ssh command\n")
  47. fmt.Printf("quit\t: quit\n")
  48. case "l", "log":
  49. fmt.Println(string(q.Stdout))
  50. case "cl", "clog":
  51. fmt.Println(string(q.Stdout))
  52. q.Stdout = []byte{}
  53. case "c", "cleanup":
  54. q.Stdout = []byte{}
  55. case "s", "ssh":
  56. fmt.Println(q.GetSSHCommand())
  57. case "q", "quit":
  58. return errors.New("end of session")
  59. default:
  60. fmt.Println("No such command")
  61. }
  62. return
  63. }
  64. func interactive(q *qemu.System) (err error) {
  65. for {
  66. err = handleLine(q)
  67. if err != nil {
  68. return
  69. }
  70. }
  71. }
  72. func debugHandler(kcfg config.KernelConfig, workPath, kernRegex, gdb string,
  73. dockerTimeout time.Duration, yekaslr, yesmep, yesmap, yekpti,
  74. nokaslr, nosmep, nosmap, nokpti bool) (err error) {
  75. ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
  76. if err != nil {
  77. return
  78. }
  79. if ka.SourcePath == "" {
  80. ka.SourcePath = workPath
  81. }
  82. ki, err := firstSupported(kcfg, ka, kernRegex)
  83. if err != nil {
  84. return
  85. }
  86. kernel := qemu.Kernel{KernelPath: ki.KernelPath, InitrdPath: ki.InitrdPath}
  87. q, err := qemu.NewSystem(qemu.X86x64, kernel, ki.RootFS)
  88. if err != nil {
  89. return
  90. }
  91. if ka.Qemu.Cpus != 0 {
  92. q.Cpus = ka.Qemu.Cpus
  93. }
  94. if ka.Qemu.Memory != 0 {
  95. q.Memory = ka.Qemu.Memory
  96. }
  97. q.SetKASLR(false) // set KASLR to false by default because of gdb
  98. q.SetSMEP(!ka.Mitigations.DisableSmep)
  99. q.SetSMAP(!ka.Mitigations.DisableSmap)
  100. q.SetKPTI(!ka.Mitigations.DisableKpti)
  101. if yekaslr {
  102. q.SetKASLR(true)
  103. } else if nokaslr {
  104. q.SetKASLR(false)
  105. }
  106. if yesmep {
  107. q.SetSMEP(true)
  108. } else if nosmep {
  109. q.SetSMEP(false)
  110. }
  111. if yesmap {
  112. q.SetSMAP(true)
  113. } else if nosmap {
  114. q.SetSMAP(false)
  115. }
  116. if yekpti {
  117. q.SetKPTI(true)
  118. } else if nokpti {
  119. q.SetKPTI(false)
  120. }
  121. redgreen := func(name string, enabled bool) aurora.Value {
  122. if enabled {
  123. return aurora.BgGreen(aurora.Black(name))
  124. }
  125. return aurora.BgRed(aurora.Gray(name))
  126. }
  127. fmt.Printf("[*] %s %s %s %s\n",
  128. redgreen("KASLR", q.GetKASLR()),
  129. redgreen("SMEP", q.GetSMEP()),
  130. redgreen("SMAP", q.GetSMAP()),
  131. redgreen("KPTI", q.GetKPTI()))
  132. fmt.Printf("[*] SMP: %d CPUs\n", q.Cpus)
  133. fmt.Printf("[*] Memory: %d MB\n", q.Memory)
  134. q.Debug(gdb)
  135. coloredGdbAddress := aurora.BgGreen(aurora.Black(gdb))
  136. fmt.Printf("[*] gdb is listening on %s\n", coloredGdbAddress)
  137. err = q.Start()
  138. if err != nil {
  139. return
  140. }
  141. defer q.Stop()
  142. tmp, err := ioutil.TempDir("/tmp/", "out-of-tree_")
  143. if err != nil {
  144. return
  145. }
  146. defer os.RemoveAll(tmp)
  147. outFile, output, err := build(tmp, ka, ki, dockerTimeout)
  148. if err != nil {
  149. log.Println(err, output)
  150. return
  151. }
  152. remoteFile := "/tmp/exploit"
  153. if ka.Type == config.KernelModule {
  154. remoteFile = "/tmp/module.ko"
  155. }
  156. err = q.CopyFile("user", outFile, remoteFile)
  157. if err != nil {
  158. return
  159. }
  160. coloredRemoteFile := aurora.BgGreen(aurora.Black(remoteFile))
  161. fmt.Printf("[*] build result copied to %s\n", coloredRemoteFile)
  162. fmt.Printf("\n%s\n", q.GetSSHCommand())
  163. fmt.Printf("gdb %s -ex 'target remote %s'\n\n", ki.VmlinuxPath, gdb)
  164. // TODO set substitute-path /build/.../linux-... /path/to/linux-source
  165. err = interactive(q)
  166. return
  167. }