Skip to content

Commit 2e89e19

Browse files
adding the LDAP server, also adjusting flags and printouts, as well as readme
1 parent 5e16183 commit 2e89e19

6 files changed

Lines changed: 101 additions & 33 deletions

File tree

README.md

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,26 @@ The tool does not send any exploits to the vulnerable hosts, and is designed to
2828
In this example we run the tool against the `192.168.1.59/29` subnet (which contains a vulnerable server).
2929

3030
The tools does the following:
31-
1. Open a TCP server on the default address (the local IP at port 5555)
32-
2. Adds the flag `--ports=top100` to adjust the scan to include the top 100 ports
31+
1. Open a server on the default address (the local IP at port 5555)
32+
2. POssibly, add the flag `--ports=top100` to adjust the scan to include the top 100 ports
3333
3. The tool then tries all ports on each of the IP addresses in the subnet. If a remote server responds at one of the ports, the request is sent to it.
3434
4. If the server is vulnerable, a callback is made to our server (created on step 1) and the IP address of the remote is logged
35-
5. After all IP addresses in the subnet are scanned, the TCP server waits 10s for any lingering connections and closes down
35+
5. After all IP addresses in the subnet are scanned, the server waits for a default duration of 10s for any lingering connections and closes down
3636
6. The tools displays the summary of the connections made:
3737
1. Requests sent to responding remote servers (and the status code they responded with)
38-
2. Any callback address made to our TCP server
38+
2. Any callback address made to our server
3939

4040
## Important Note about Assumptions
4141

4242
* If a callback happened, this means that a vulnerable server exists, the exploit worked and it initiated a callback.
43-
However, the logged IP address might not belong to the actual vulnerable server (it might be behind a NAT or a proxy)
43+
However.
4444
* A good rule of thumb, if the callback IP address is not in the subnet scanned, the vulnerable server is behind a NAT
4545
(e.g. a docker container responds with its own IP address, not the host running the docker)
4646
* The network traffic created by the tool might be classified as malicious by security products, or cause a lot of noise for monitoring services
47-
* The TCP server created by the tool assumes that it is open to receive inbound traffic. That means that opening a FW inbound rule on the host running the scan is needed.
48-
49-
### What to do if the vulnerable server is behind a NAT?
50-
51-
Let's assume that we are scannon `192.168.1.0/24` and a vulnerable application is running inside a docker container on the `192.168.1.2` host.
52-
53-
The tool will scan that host, sending requests to `192.168.1.2` but the callback we get will be from '172.10.0.1' (which is the internal docker subnet)
54-
55-
what we can do is minimize the search. we can get the list of all successful requests made by the scanner from the log, and the enumerate
56-
through them, one by one, to see which one is triggering the callback.
57-
58-
as this is a bit tedious, we plan on automating this if this becomes a real issue
59-
47+
* The server created by the tool assumes that it is open to receive inbound traffic. That means that opening a FW inbound rule on the host running the scan is needed.
6048

6149
## Basic usage
50+
6251
Download the tool for your specific platform (Windows, Linux or Mac), to run the tool, make sure port 5555 on the host is available (or change it via configuration),
6352
and specify the subnet to scan (it is possible to configure a separate server:port combination using the `--server` flag):
6453

@@ -90,7 +79,7 @@ if you wish to disable the callback server, use `--noserver`
9079
* `--nocolor` provide output without color
9180
* `--ports` either top10 (default) or top100 (list of the 100 most common web ports)
9281
* `--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
82+
* `--timeout=10` is setting the server shutdown timeout to 10 seconds
9483

9584
### Methods Used
9685

cmd/ldapserver.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ import (
1515

1616
var LDAPServer *Server
1717

18-
func StartServer(ctx context.Context, serverUrl string) {
18+
func StartServer(ctx context.Context, serverUrl string, serverTimeout int) {
1919
pterm.Info.Println("Server URL: " + serverUrl)
2020
log.Info("Server URL: " + serverUrl)
21-
2221
listenUrl, err := url.Parse("//" + serverUrl)
2322
if err != nil {
2423
pterm.Error.Println("Failed to parse server url")
@@ -31,6 +30,7 @@ func StartServer(ctx context.Context, serverUrl string) {
3130
log.Info("Starting LDAP server on ", listenUrl.Host)
3231
LDAPServer = NewServer()
3332
LDAPServer.sChan = make(chan string, 10000)
33+
LDAPServer.timeout = time.Duration(serverTimeout) * time.Second
3434

3535
go LDAPServer.server.ListenAndServe(listenUrl.Host)
3636
}
@@ -52,8 +52,9 @@ func (s *Server) ReportIP(vulnerableServiceLocation string) {
5252
}
5353

5454
type Server struct {
55-
server *ldap.Server
56-
sChan chan string
55+
server *ldap.Server
56+
sChan chan string
57+
timeout time.Duration
5758
}
5859

5960
func (s *Server) handleBind(w ldap.ResponseWriter, m *ldap.Message) {
@@ -65,7 +66,7 @@ func (s *Server) handleBind(w ldap.ResponseWriter, m *ldap.Message) {
6566
func (s *Server) handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
6667
r := m.GetSearchRequest()
6768

68-
pterm.Info.Println("Got LDAP search request: " + r.BaseObject())
69+
//pterm.Info.Println("Got LDAP search request: " + r.BaseObject())
6970
log.Info("Got LDAP search request: " + r.BaseObject())
7071

7172
vulnerableLocation := strings.ReplaceAll(string(r.BaseObject()), "_", ":")
@@ -96,7 +97,8 @@ func NewServer() *Server {
9697

9798
func (s *Server) Stop() {
9899
spinnerSuccess, _ := pterm.DefaultSpinner.Start("Stopping LDAP server")
99-
time.Sleep(10 * time.Second)
100+
timeout := LDAPServer.timeout
101+
time.Sleep(timeout)
100102
s.server.Stop()
101103
spinnerSuccess.Stop()
102104
}

cmd/scan.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ For example: log4jScanner scan --cidr "192.168.0.1/24`,
4242

4343
disableServer, err := cmd.Flags().GetBool("noserver")
4444
if err != nil {
45-
log.Error("server flag error")
45+
pterm.Error.Println("server flag error")
4646
cmd.Usage()
4747
return
4848
}
4949
// TODO: add cancel context
5050
cidr, err := cmd.Flags().GetString("cidr")
5151
if err != nil {
52-
log.Error("CIDR flag error")
52+
pterm.Error.Println("CIDR flag error")
5353
cmd.Usage()
5454
return
5555
}
@@ -63,14 +63,14 @@ For example: log4jScanner scan --cidr "192.168.0.1/24`,
6363

6464
ports, err := cmd.Flags().GetString("ports")
6565
if err != nil || (ports != "top100" && ports != "slow" && ports != "top10") {
66-
fmt.Println("error in ports flag")
66+
pterm.Error.Println("error in ports flag")
6767
cmd.Usage()
6868
return
6969
}
7070

7171
serverUrl, err := cmd.Flags().GetString("server")
7272
if err != nil {
73-
fmt.Println("Error in server flag")
73+
pterm.Error.Println("Error in server flag")
7474
cmd.Usage()
7575
return
7676
}
@@ -82,15 +82,22 @@ For example: log4jScanner scan --cidr "192.168.0.1/24`,
8282

8383
csvPath, err = cmd.Flags().GetString("csv-output")
8484
if err != nil {
85-
fmt.Println("Error in csv-output flag")
85+
pterm.Error.Println("Error in csv-output flag")
8686
cmd.Usage()
8787
return
8888
}
8989
initCSV()
9090

91+
serverTimeout, err := cmd.Flags().GetInt("timeout")
92+
if err != nil {
93+
pterm.Error.Println("error in timeout flag")
94+
cmd.Usage()
95+
return
96+
}
97+
9198
ctx := context.Background()
9299
if !disableServer {
93-
StartServer(ctx, serverUrl)
100+
StartServer(ctx, serverUrl, serverTimeout)
94101
}
95102
ScanCIDR(ctx, cidr, ports, serverUrl)
96103
},
@@ -111,6 +118,7 @@ func init() {
111118
"Ports to scan. By default scans top 10 ports; 'top100' will scan the top 100 ports, 'slow' will scan all possible ports")
112119
scanCmd.Flags().String("csv-output", "log4jScanner-results.csv",
113120
"Set path (inc. filename) to save the CSV file containing the scan results (e.g /tmp/log4jScanner_results.csv). By default will be saved in the running folder.")
121+
scanCmd.Flags().Int("timeout", 10, "Duration of time to wait before closing the callback server, in secods")
114122
createPrivateIPBlocks()
115123
}
116124

@@ -201,7 +209,7 @@ func PrintResults(resChan chan string) {
201209
close(LDAPServer.sChan)
202210
for suc := range LDAPServer.sChan {
203211
fullSuc := strings.Split(suc, ",")
204-
msg := fmt.Sprintf("Summary: Callback from %s", fullSuc[1])
212+
msg := fmt.Sprintf("Summary: Callback from %s:%s", fullSuc[1], fullSuc[2])
205213
pterm.Info.Println(msg)
206214
log.Info(msg)
207215
}

cmd/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func init() {
5656
}
5757

5858
func ServerStart(serverUrl string) {
59-
StartServer(nil, serverUrl)
59+
StartServer(nil, serverUrl, 10)
6060
pterm.Info.Println("Press ctr-l-c to exit")
6161
for {
6262
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
2323
github.com/magiconair/properties v1.8.5 // indirect
2424
github.com/mattn/go-runewidth v0.0.13 // indirect
25+
github.com/maykonlf/semver-cli v1.0.2 // indirect
2526
github.com/mitchellh/mapstructure v1.4.3 // indirect
2627
github.com/pelletier/go-toml v1.9.4 // indirect
2728
github.com/rivo/uniseg v0.2.0 // indirect

0 commit comments

Comments
 (0)