/src/nighthawkresponse/audit/manifest.go

https://github.com/biggiesmallsAG/nightHawkResponse · Go · 257 lines · 178 code · 52 blank · 27 comment · 49 complexity · 9343e650cc01aa57cdd9da52e9ccbc1b MD5 · raw file

  1. /*
  2. *@package nighthawkresponse
  3. *@file manifest.go
  4. *@author roshan maskey <roshanmaskey@gmail.com>
  5. *
  6. *@description This file contains structure and functions to process Redline audit manifest file
  7. */
  8. //package nighthawkresponse
  9. package audit
  10. import (
  11. "encoding/json"
  12. "encoding/xml"
  13. "errors"
  14. "fmt"
  15. "io/ioutil"
  16. "os"
  17. "path/filepath"
  18. "regexp"
  19. "strings"
  20. nhlog "nighthawkresponse/log"
  21. nhs "nighthawkresponse/nhstruct"
  22. )
  23. type AuditResult struct {
  24. Payload string `json:"payload"`
  25. PayloadType string `json:"type"`
  26. }
  27. type AuditGenerator struct {
  28. Generator string `json:"generator"`
  29. GeneratorVersion string `json:"generatorVersion"`
  30. AuditResults []AuditResult `json:"results"`
  31. }
  32. type RlManifest struct {
  33. SysInfo nhs.RlSystemInfo `json:"sysinfo"`
  34. Type string `json:"type"`
  35. Version string `json:"version"`
  36. Audits []AuditGenerator `json:"audits"`
  37. }
  38. type RlAudit struct {
  39. AuditGenerator string
  40. AuditFile string
  41. }
  42. func (rlman *RlManifest) ParseAuditManifest(filename string) error {
  43. manifestData, err := ioutil.ReadFile(filename)
  44. if err != nil {
  45. return err
  46. }
  47. json.Unmarshal(manifestData, &rlman)
  48. return nil
  49. }
  50. /// This function returns the filename for the give audit generator
  51. /// It does not return full file path.
  52. /// This function ignore any file containing audit issues
  53. func (rlman *RlManifest) Payload(generator string) []string {
  54. var payload []string
  55. for _, auditgenerator := range rlman.Audits {
  56. if auditgenerator.Generator == generator {
  57. for _, p := range auditgenerator.AuditResults {
  58. // Only append the files without issue. We are going to ignore issue file
  59. if !strings.Contains(p.Payload, "issue") {
  60. payload = append(payload, p.Payload)
  61. }
  62. }
  63. }
  64. }
  65. return payload
  66. }
  67. /// This function returns all the payloads file from manifest
  68. func (rlman *RlManifest) Payloads(session_dir string) []string {
  69. var payload []string
  70. var w32system_file string
  71. for _, ag := range rlman.Audits {
  72. for _, p := range ag.AuditResults {
  73. if !strings.Contains(p.Payload, "issue") {
  74. payload = append(payload, p.Payload)
  75. if ag.Generator == "w32system" {
  76. w32system_file = p.Payload
  77. }
  78. }
  79. }
  80. }
  81. xmlData, _ := ioutil.ReadFile(filepath.Join(session_dir, w32system_file))
  82. xml.Unmarshal(xmlData, &rlman.SysInfo)
  83. return payload
  84. }
  85. func (rlman *RlManifest) Payloads2(session_dir string) []RlAudit {
  86. var rlaudits []RlAudit
  87. var w32system_file string = ""
  88. var sysinfo_file string = ""
  89. for _, ag := range rlman.Audits {
  90. for _, p := range ag.AuditResults {
  91. if !strings.Contains(p.Payload, "issue") && !strings.Contains(p.PayloadType, "issue") {
  92. var r = RlAudit{AuditGenerator: ag.Generator, AuditFile: p.Payload}
  93. rlaudits = append(rlaudits, r)
  94. if ag.Generator == "w32system" {
  95. w32system_file = p.Payload
  96. }
  97. if ag.Generator == "sysinfo" {
  98. sysinfo_file = p.Payload
  99. }
  100. }
  101. }
  102. }
  103. // Starting with HX3.5 w32system is replaced by sysinfo
  104. if w32system_file == "" && sysinfo_file != "" {
  105. w32system_file = sysinfo_file
  106. }
  107. xmlData, _ := ioutil.ReadFile(filepath.Join(session_dir, w32system_file))
  108. xml.Unmarshal(xmlData, &rlman.SysInfo)
  109. return rlaudits
  110. }
  111. /// Load SystemInformation
  112. /// This function returns manifest file in given session directory
  113. func GetAuditManifestFile(session_dir string) (string, error) {
  114. filelist, err := filepath.Glob(filepath.Join(session_dir, "*"))
  115. if err != nil {
  116. return "", err
  117. }
  118. var manifest_file string = ""
  119. for _, file := range filelist {
  120. manifest_file = ""
  121. _, filename := filepath.Split(file)
  122. lwrFilename := strings.ToLower(filename)
  123. // Checking keyword manifest in filename and file extension .json
  124. if strings.Contains(lwrFilename, "manifest") && strings.HasSuffix(lwrFilename, "json") {
  125. manifest_file = filename
  126. break
  127. } else if strings.HasSuffix(lwrFilename, "json") {
  128. // This is loose checking for manifest file. Just checking for json file if keyword is not found
  129. manifest_file = filename
  130. }
  131. }
  132. /// Something to do in future. Validate that json file is correct auditmanifest file by unmarshaling
  133. if len(manifest_file) > 2 {
  134. return manifest_file, nil
  135. }
  136. return manifest_file, errors.New("Error no manifest file")
  137. }
  138. /// This function generates manifest file. This function is
  139. /// typically used for audits generated using MIR (Mandiant Intelligence Response)
  140. /// The manifest file created by this function is different that Redline/HX manifest
  141. func GenerateAuditManifestFile(session_dir string) string {
  142. var manfilename string = "nh_manifest.json"
  143. var rlman RlManifest
  144. rlman.Type = "audit_manifest"
  145. rlman.Version = "1.0"
  146. filelist, err := filepath.Glob(filepath.Join(session_dir, "*"))
  147. if err != nil {
  148. panic(err.Error())
  149. }
  150. for _, file := range filelist {
  151. if IsRegularFile(file) {
  152. fh, err := os.Open(file)
  153. if err != nil {
  154. panic(err.Error())
  155. }
  156. defer fh.Close()
  157. buf := make([]byte, 500)
  158. fh.Read(buf)
  159. strbuf := string(buf)
  160. // No processing file containig Redline or MIR issues
  161. if strings.Contains(strbuf, "issue.xsd") {
  162. continue
  163. }
  164. /// Begin creating manifest audits
  165. var ar AuditResult
  166. var ag AuditGenerator
  167. ar.Payload = filepath.Base(file)
  168. ar.PayloadType = "application/xml"
  169. re := regexp.MustCompile("generator=\"(.*)\" generatorVersion=\"([0-9.]+)\" ")
  170. match := re.FindStringSubmatch(strbuf)
  171. if len(match) > 2 {
  172. ag.Generator = match[1]
  173. ag.GeneratorVersion = match[2]
  174. // HX audit failed audit contains "FireEye Agent" as generator.
  175. // Ignoring audits with issue.
  176. if ag.Generator != "FireEye Agent" {
  177. ag.AuditResults = append(ag.AuditResults, ar)
  178. rlman.Audits = append(rlman.Audits, ag)
  179. }
  180. }
  181. }
  182. }
  183. manJsonData, _ := json.MarshalIndent(&rlman, "", " ")
  184. nhlog.LogMessage("GenerateAuditManifestFile", "INFO", fmt.Sprintf("Generating manifest file %s", manfilename))
  185. err = ioutil.WriteFile(filepath.Join(session_dir, manfilename), manJsonData, 0644)
  186. if err != nil {
  187. nhlog.LogMessage("GenerateAuditManifestFile", "WARNING", fmt.Sprintf("Failed to write %s to session directory %s", manfilename, session_dir))
  188. return ""
  189. }
  190. return manfilename
  191. }
  192. //
  193. func IsRegularFile(file string) bool {
  194. fh, err := os.Open(file)
  195. if err != nil {
  196. nhlog.LogMessage("IsRegularFile", "WARNING", fmt.Sprintf("Failed to open file %s", file))
  197. return false
  198. }
  199. defer fh.Close()
  200. fi, _ := fh.Stat()
  201. if fi.Mode().IsRegular() {
  202. return true
  203. }
  204. return false
  205. }