image.png

项目备份地址

https://github.dev/Sec-Fork/JNDIScan-1

main.go 分析

image.png
image.png
image.png
image.png

  1. 先打印了logo
  1. core.PrintLogo(config.GetAuthors())
  1. package core
  2. import "fmt"
  3. func PrintLogo(authors []interface{}) {
  4. template := " ___ ______ _________ \n" +
  5. " / / | / / __ \\/ _/ ___/_________ _____ \n" +
  6. " __ / / |/ / / / // / \\__ \\/ ___/ __ `/ __ \\\n" +
  7. "/ /_/ / /| / /_/ // / ___/ / /__/ /_/ / / / /\n" +
  8. "\\____/_/ |_/_____/___//____/\\___/\\__,_/_/ /_/ \n" +
  9. " coded by %s"
  10. logo := fmt.Sprintf(template, authors...)
  11. fmt.Println(logo)
  12. }
  1. package config
  2. func GetAuthors() []interface{} {
  3. return []interface{}{
  4. "4ra1n",
  5. }
  6. }
  1. 在对传入的参数进行解析
  2. 初始化定义的输出结果的参数

ldap.go start fake reverse server分析

主程序中

  1. go core.StartFakeServer(&ResultChan)
  2. //启动一个goroutine启动反连平台

启动函数

image.png

数据处理函数

  1. func acceptProcess(conn *net.Conn) {
  2. buf := make([]byte, 1024)
  3. num, err := (*conn).Read(buf)
  4. if err != nil {
  5. log.Error("accept data reading err: %s", err)
  6. _ = (*conn).Close()
  7. return
  8. }
  9. hexStr := fmt.Sprintf("%x", buf[:num])
  10. // LDAP Protocol
  11. // https://ldap.com/ldapv3-wire-protocol-reference-bind
  12. if "300c020101600702010304008000" == hexStr {
  13. data := []byte{
  14. 0x30, 0x0c, 0x02, 0x01, 0x01, 0x61, 0x07,
  15. 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00,
  16. }
  17. _, _ = (*conn).Write(data)
  18. _, _ = (*conn).Read(buf)
  19. length := buf[8]
  20. pathBytes := bytes.Buffer{}
  21. for i := 1; i <= int(length); i++ {
  22. temp := []byte{buf[8+i]}
  23. pathBytes.Write(temp)
  24. }
  25. path := pathBytes.String()
  26. res := &model.Result{
  27. Host: (*conn).RemoteAddr().String(),
  28. Name: "LDAP",
  29. Finger: hexStr,
  30. Path: path,
  31. }
  32. ResultChan <- res
  33. _ = (*conn).Close()
  34. return
  35. }
  36. // RMI Protocol
  37. if checkRMI(buf) {
  38. data := []byte{
  39. 0x4e, 0x00, 0x09, 0x31, 0x32,
  40. 0x37, 0x2e, 0x30, 0x2e, 0x30,
  41. 0x2e, 0x31, 0x00, 0x00, 0xc4, 0x12,
  42. }
  43. _, _ = (*conn).Write(data)
  44. _, _ = (*conn).Read(buf)
  45. _, _ = (*conn).Write([]byte{})
  46. _, _ = (*conn).Read(buf)
  47. var dataList []byte
  48. flag := false
  49. for i := len(buf) - 1; i >= 0; i-- {
  50. if buf[i] != 0x00 || flag {
  51. flag = true
  52. dataList = append(dataList, buf[i])
  53. }
  54. }
  55. var j int
  56. for i := 0; i < len(dataList); i++ {
  57. if int(dataList[i]) == i {
  58. j = i
  59. }
  60. }
  61. temp := dataList[0:j]
  62. pathBytes := &bytes.Buffer{}
  63. for i := len(temp) - 1; i >= 0; i-- {
  64. pathBytes.Write([]byte{dataList[i]})
  65. }
  66. res := &model.Result{
  67. Host: (*conn).RemoteAddr().String(),
  68. Name: "RMI",
  69. Finger: fmt.Sprintf("%x", buf[0:7]),
  70. Path: pathBytes.String(),
  71. }
  72. ResultChan <- res
  73. _ = (*conn).Close()
  74. return
  75. }
  76. _ = (*conn).Close()
  77. return
  78. }
  79. func checkRMI(data []byte) bool {
  80. if data[0] == 0x4a &&
  81. data[1] == 0x52 &&
  82. data[2] == 0x4d &&
  83. data[3] == 0x49 {
  84. if data[4] != 0x00 {
  85. return false
  86. }
  87. if data[5] != 0x01 && data[5] != 0x02 {
  88. return false
  89. }
  90. if data[6] != 0x4b &&
  91. data[6] != 0x4c &&
  92. data[6] != 0x4d {
  93. return false
  94. }
  95. lastData := data[7:]
  96. for _, v := range lastData {
  97. if v != 0x00 {
  98. return false
  99. }
  100. }
  101. return true
  102. }
  103. return false
  104. }

总结而言,就是匹配数据包中的LDAP和RMI的特殊指纹

输出文件分析

主程序:

  1. go core.StartOutput(&RenderChan)
  1. package core
  2. import (
  3. "github.com/EmYiQing/JNDIScan/model"
  4. "io/ioutil"
  5. "sync"
  6. )
  7. var (
  8. resultList []*model.Result
  9. lock sync.Mutex
  10. )
  11. func StartOutput(renderChan *chan *model.Result) {
  12. go listenData(renderChan)
  13. }
  14. func listenData(renderChan *chan *model.Result) {
  15. for {
  16. select {
  17. case res := <-*renderChan:
  18. lock.Lock()
  19. resultList = append(resultList, res)
  20. data := RenderHtml(resultList)
  21. _ = ioutil.WriteFile("result.html", data, 0666)
  22. lock.Unlock()
  23. }
  24. }
  25. }

输出已经定义好的规格的result.html文件

  1. package core
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/EmYiQing/JNDIScan/config"
  6. "github.com/EmYiQing/JNDIScan/model"
  7. )
  8. func RenderHtml(resultList []*model.Result) []byte {
  9. if len(resultList) == 0 {
  10. return []byte("no result")
  11. }
  12. data := bytes.Buffer{}
  13. data.Write([]byte(config.TemplatePrefix))
  14. for i := 1; i < len(resultList)+1; i++ {
  15. data.Write([]byte(fmt.Sprintf("<tr>\n "+
  16. "<th scope=\"row\">%d</th>\n "+
  17. "<td>%s</td>\n "+
  18. "<td><span class=\"badge badge-danger\">%s</span></td>\n "+
  19. "<td><span class=\"badge badge-warning\">%s</span></td>\n "+
  20. "<td><span class=\"badge badge-success\">%s</span></td>\n "+
  21. "</tr>", i, (resultList[i-1]).Host,
  22. (resultList[i-1]).Name,
  23. (resultList[i-1]).Finger,
  24. (resultList[i-1]).Path)))
  25. }
  26. data.Write([]byte(config.TemplateSuffix))
  27. return data.Bytes()
  28. }

接收传输的数据

主程序

  1. go startApp()
  1. func startApp() {
  2. for {
  3. select {
  4. case res := <-ResultChan:
  5. info := fmt.Sprintf("%s->%s", res.Name, res.Host)
  6. log.Info(info)
  7. data := &model.Result{
  8. Host: res.Host,
  9. Name: res.Name,
  10. Finger: res.Finger,
  11. Path: res.Path,
  12. }
  13. RenderChan <- data
  14. }
  15. }
  16. }

打印成功的payload

主程序

  1. core.PrintPayload()
  1. func PrintPayload() {
  2. time.Sleep(time.Second * 3)
  3. ipList := util.GetLocalIPs()
  4. exIP := util.GetExternalIP()
  5. if exIP != "" {
  6. ipList = append(ipList, exIP)
  7. }
  8. port := config.Port
  9. borderLen := 23
  10. max := 0
  11. for i := 0; i < len(ipList); i++ {
  12. if len(ipList[i]) > max {
  13. max = len(ipList[i])
  14. }
  15. }
  16. borderLen += max + len(strconv.Itoa(port)) + 2
  17. fmt.Print("|")
  18. for i := 0; i < borderLen; i++ {
  19. fmt.Print("-")
  20. }
  21. fmt.Println("|")
  22. for i := 0; i < len(ipList); i++ {
  23. tempLen := 18
  24. fmt.Print("|--Payload: ldap://")
  25. tempLen += len(ipList[i])
  26. fmt.Print(ipList[i])
  27. tempLen += 1
  28. fmt.Print(":")
  29. tempLen += len(strconv.Itoa(port))
  30. fmt.Print(strconv.Itoa(port))
  31. num := borderLen - tempLen
  32. for j := 0; j < num; j++ {
  33. fmt.Print("-")
  34. }
  35. fmt.Println("|")
  36. }
  37. for i := 0; i < len(ipList); i++ {
  38. tempLen := 17
  39. fmt.Print("|--Payload: rmi://")
  40. tempLen += len(ipList[i])
  41. fmt.Print(ipList[i])
  42. tempLen += 1
  43. fmt.Print(":")
  44. tempLen += len(strconv.Itoa(port))
  45. fmt.Print(strconv.Itoa(port))
  46. tempLen += 4
  47. fmt.Print("/xxx")
  48. num := borderLen - tempLen
  49. for j := 0; j < num; j++ {
  50. fmt.Print("-")
  51. }
  52. fmt.Println("|")
  53. }
  54. fmt.Print("|")
  55. for i := 0; i < borderLen; i++ {
  56. fmt.Print("-")
  57. }
  58. fmt.Println("|")
  59. }

优雅的退出程序

  1. func wait() {
  2. sign := make(chan os.Signal, 1)
  3. done := make(chan bool, 1)
  4. signal.Notify(sign, syscall.SIGINT, syscall.SIGTERM)
  5. go func() {
  6. sig := <-sign
  7. fmt.Println()
  8. fmt.Println(sig)
  9. done <- true
  10. }()
  11. <-done
  12. }