项目备份地址
https://github.dev/Sec-Fork/JNDIScan-1
main.go 分析
- 先打印了logo
core.PrintLogo(config.GetAuthors())
package core
import "fmt"
func PrintLogo(authors []interface{}) {
template := " ___ ______ _________ \n" +
" / / | / / __ \\/ _/ ___/_________ _____ \n" +
" __ / / |/ / / / // / \\__ \\/ ___/ __ `/ __ \\\n" +
"/ /_/ / /| / /_/ // / ___/ / /__/ /_/ / / / /\n" +
"\\____/_/ |_/_____/___//____/\\___/\\__,_/_/ /_/ \n" +
" coded by %s"
logo := fmt.Sprintf(template, authors...)
fmt.Println(logo)
}
package config
func GetAuthors() []interface{} {
return []interface{}{
"4ra1n",
}
}
- 在对传入的参数进行解析
- 初始化定义的输出结果的参数
ldap.go start fake reverse server分析
主程序中
go core.StartFakeServer(&ResultChan)
//启动一个goroutine启动反连平台
启动函数
数据处理函数
func acceptProcess(conn *net.Conn) {
buf := make([]byte, 1024)
num, err := (*conn).Read(buf)
if err != nil {
log.Error("accept data reading err: %s", err)
_ = (*conn).Close()
return
}
hexStr := fmt.Sprintf("%x", buf[:num])
// LDAP Protocol
// https://ldap.com/ldapv3-wire-protocol-reference-bind
if "300c020101600702010304008000" == hexStr {
data := []byte{
0x30, 0x0c, 0x02, 0x01, 0x01, 0x61, 0x07,
0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00,
}
_, _ = (*conn).Write(data)
_, _ = (*conn).Read(buf)
length := buf[8]
pathBytes := bytes.Buffer{}
for i := 1; i <= int(length); i++ {
temp := []byte{buf[8+i]}
pathBytes.Write(temp)
}
path := pathBytes.String()
res := &model.Result{
Host: (*conn).RemoteAddr().String(),
Name: "LDAP",
Finger: hexStr,
Path: path,
}
ResultChan <- res
_ = (*conn).Close()
return
}
// RMI Protocol
if checkRMI(buf) {
data := []byte{
0x4e, 0x00, 0x09, 0x31, 0x32,
0x37, 0x2e, 0x30, 0x2e, 0x30,
0x2e, 0x31, 0x00, 0x00, 0xc4, 0x12,
}
_, _ = (*conn).Write(data)
_, _ = (*conn).Read(buf)
_, _ = (*conn).Write([]byte{})
_, _ = (*conn).Read(buf)
var dataList []byte
flag := false
for i := len(buf) - 1; i >= 0; i-- {
if buf[i] != 0x00 || flag {
flag = true
dataList = append(dataList, buf[i])
}
}
var j int
for i := 0; i < len(dataList); i++ {
if int(dataList[i]) == i {
j = i
}
}
temp := dataList[0:j]
pathBytes := &bytes.Buffer{}
for i := len(temp) - 1; i >= 0; i-- {
pathBytes.Write([]byte{dataList[i]})
}
res := &model.Result{
Host: (*conn).RemoteAddr().String(),
Name: "RMI",
Finger: fmt.Sprintf("%x", buf[0:7]),
Path: pathBytes.String(),
}
ResultChan <- res
_ = (*conn).Close()
return
}
_ = (*conn).Close()
return
}
func checkRMI(data []byte) bool {
if data[0] == 0x4a &&
data[1] == 0x52 &&
data[2] == 0x4d &&
data[3] == 0x49 {
if data[4] != 0x00 {
return false
}
if data[5] != 0x01 && data[5] != 0x02 {
return false
}
if data[6] != 0x4b &&
data[6] != 0x4c &&
data[6] != 0x4d {
return false
}
lastData := data[7:]
for _, v := range lastData {
if v != 0x00 {
return false
}
}
return true
}
return false
}
总结而言,就是匹配数据包中的LDAP和RMI的特殊指纹
输出文件分析
主程序:
go core.StartOutput(&RenderChan)
package core
import (
"github.com/EmYiQing/JNDIScan/model"
"io/ioutil"
"sync"
)
var (
resultList []*model.Result
lock sync.Mutex
)
func StartOutput(renderChan *chan *model.Result) {
go listenData(renderChan)
}
func listenData(renderChan *chan *model.Result) {
for {
select {
case res := <-*renderChan:
lock.Lock()
resultList = append(resultList, res)
data := RenderHtml(resultList)
_ = ioutil.WriteFile("result.html", data, 0666)
lock.Unlock()
}
}
}
输出已经定义好的规格的result.html文件
package core
import (
"bytes"
"fmt"
"github.com/EmYiQing/JNDIScan/config"
"github.com/EmYiQing/JNDIScan/model"
)
func RenderHtml(resultList []*model.Result) []byte {
if len(resultList) == 0 {
return []byte("no result")
}
data := bytes.Buffer{}
data.Write([]byte(config.TemplatePrefix))
for i := 1; i < len(resultList)+1; i++ {
data.Write([]byte(fmt.Sprintf("<tr>\n "+
"<th scope=\"row\">%d</th>\n "+
"<td>%s</td>\n "+
"<td><span class=\"badge badge-danger\">%s</span></td>\n "+
"<td><span class=\"badge badge-warning\">%s</span></td>\n "+
"<td><span class=\"badge badge-success\">%s</span></td>\n "+
"</tr>", i, (resultList[i-1]).Host,
(resultList[i-1]).Name,
(resultList[i-1]).Finger,
(resultList[i-1]).Path)))
}
data.Write([]byte(config.TemplateSuffix))
return data.Bytes()
}
接收传输的数据
主程序
go startApp()
func startApp() {
for {
select {
case res := <-ResultChan:
info := fmt.Sprintf("%s->%s", res.Name, res.Host)
log.Info(info)
data := &model.Result{
Host: res.Host,
Name: res.Name,
Finger: res.Finger,
Path: res.Path,
}
RenderChan <- data
}
}
}
打印成功的payload
主程序
core.PrintPayload()
func PrintPayload() {
time.Sleep(time.Second * 3)
ipList := util.GetLocalIPs()
exIP := util.GetExternalIP()
if exIP != "" {
ipList = append(ipList, exIP)
}
port := config.Port
borderLen := 23
max := 0
for i := 0; i < len(ipList); i++ {
if len(ipList[i]) > max {
max = len(ipList[i])
}
}
borderLen += max + len(strconv.Itoa(port)) + 2
fmt.Print("|")
for i := 0; i < borderLen; i++ {
fmt.Print("-")
}
fmt.Println("|")
for i := 0; i < len(ipList); i++ {
tempLen := 18
fmt.Print("|--Payload: ldap://")
tempLen += len(ipList[i])
fmt.Print(ipList[i])
tempLen += 1
fmt.Print(":")
tempLen += len(strconv.Itoa(port))
fmt.Print(strconv.Itoa(port))
num := borderLen - tempLen
for j := 0; j < num; j++ {
fmt.Print("-")
}
fmt.Println("|")
}
for i := 0; i < len(ipList); i++ {
tempLen := 17
fmt.Print("|--Payload: rmi://")
tempLen += len(ipList[i])
fmt.Print(ipList[i])
tempLen += 1
fmt.Print(":")
tempLen += len(strconv.Itoa(port))
fmt.Print(strconv.Itoa(port))
tempLen += 4
fmt.Print("/xxx")
num := borderLen - tempLen
for j := 0; j < num; j++ {
fmt.Print("-")
}
fmt.Println("|")
}
fmt.Print("|")
for i := 0; i < borderLen; i++ {
fmt.Print("-")
}
fmt.Println("|")
}
优雅的退出程序
func wait() {
sign := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sign, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sign
fmt.Println()
fmt.Println(sig)
done <- true
}()
<-done
}