Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: ci

on:
push:
branches:
- main
- master
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
basic:
name: basic checks (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.20.x'
cache: true

- name: Download dependencies
run: go mod download

- name: Test
run: go test -count=1 ./...

- name: Vet
run: go vet ./...

- name: Build packages
run: go build ./...
17 changes: 8 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test
*.dic
*_test.go
*.txt
*.tar.gz
*.so
*.dylib

# Test binary, built with `go test -c`
*.test
*.dic
*.txt
*.tar.gz

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
Expand Down
19 changes: 12 additions & 7 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/chainreactors/zombie/core"
"github.com/jessevdk/go-flags"
"io"
"io/ioutil"
"log"
"os"
Expand All @@ -17,12 +17,17 @@ func init() {
var ver = "dev"

func Zombie() {
defer os.Exit(0)
err := core.RunWithArgs(context.Background(), os.Args[1:], core.RunOptions{Version: ver})
os.Exit(Run(os.Args[1:], os.Stdout))
}

func Run(args []string, output io.Writer) int {
if output == nil {
output = os.Stdout
}
err := core.RunWithArgs(context.Background(), args, core.RunOptions{Output: output, Version: ver})
if err != nil {
if flagsErr, ok := err.(*flags.Error); !ok || flagsErr.Type != flags.ErrHelp {
fmt.Println(err.Error())
}
return
fmt.Fprintln(output, err.Error())
return 1
}
return 0
}
40 changes: 40 additions & 0 deletions cmd/cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cmd

import (
"bytes"
"strings"
"testing"
)

func TestRunReturnsZeroForVersion(t *testing.T) {
var out bytes.Buffer

if code := Run([]string{"--version"}, &out); code != 0 {
t.Fatalf("expected exit code 0, got %d", code)
}
if strings.TrimSpace(out.String()) != "dev" {
t.Fatalf("unexpected version output: %q", out.String())
}
}

func TestRunReturnsNonZeroForInvalidFlag(t *testing.T) {
var out bytes.Buffer

if code := Run([]string{"--definitely-not-a-flag"}, &out); code == 0 {
t.Fatal("expected non-zero exit code for invalid flag")
}
if !strings.Contains(out.String(), "unknown flag") {
t.Fatalf("expected unknown flag output, got %q", out.String())
}
}

func TestRunReturnsNonZeroForMissingInput(t *testing.T) {
var out bytes.Buffer

if code := Run(nil, &out); code == 0 {
t.Fatal("expected non-zero exit code for missing input")
}
if !strings.Contains(out.String(), "please input ip") {
t.Fatalf("expected missing input output, got %q", out.String())
}
}
5 changes: 3 additions & 2 deletions core/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package core

import (
"errors"
"github.com/chainreactors/files"
"github.com/chainreactors/utils/fileutils"
"github.com/chainreactors/words"
"github.com/chainreactors/zombie/pkg"
"io/ioutil"
Expand Down Expand Up @@ -44,6 +44,7 @@ func NewGeneratorWithFile(filename string) (*Generator, error) {
g := &Generator{
C: make(chan string),
Filename: filename,
File: f,
}

g.Word = words.NewWorderWithFile(f)
Expand Down Expand Up @@ -112,7 +113,7 @@ func (g *Generator) SetFile(filename string) error {
}

func (g *Generator) SetRuleFile(filename string) error {
if files.IsExist(filename) {
if fileutils.IsExist(filename) {
g.RuleFilename = filename
content, err := ioutil.ReadFile(filename)
if err != nil {
Expand Down
88 changes: 88 additions & 0 deletions core/generator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package core

import (
"os"
"path/filepath"
"testing"

"github.com/chainreactors/zombie/pkg"
)

func writeWordsFile(t *testing.T, words string) string {
t.Helper()

filename := filepath.Join(t.TempDir(), "words.txt")
if err := os.WriteFile(filename, []byte(words), 0600); err != nil {
t.Fatal(err)
}
return filename
}

func TestNewGeneratorWithFile(t *testing.T) {
g, err := NewGeneratorWithFile(writeWordsFile(t, "admin\nroot\n"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = g.File.Close() })

got := g.RunAsSlice()
if len(got) != 2 || got[0] != "admin" || got[1] != "root" {
t.Fatalf("unexpected generated words: %#v", got)
}
}

func TestNewGeneratorWithRule(t *testing.T) {
g, err := NewGeneratorWithFile(writeWordsFile(t, "admin\n"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = g.File.Close() })
if err := g.SetRuleFile(filepath.Join("..", "templates", "zombie", "rule", "weakpass.rule")); err != nil {
t.Fatal(err)
}

got := g.RunAsSlice()
if len(got) == 0 {
t.Fatal("expected weakpass rule to generate words")
}
}

func TestNewGeneratorWithWord(t *testing.T) {
g, err := NewGeneratorWithWord("{?#3}", nil, nil)
if err != nil {
t.Fatal(err)
}

_ = g
}

func TestKeyword(t *testing.T) {
if err := pkg.LoadKeyword(); err != nil {
t.Fatal(err)
}
g, err := NewGeneratorWithWord("{@mysql_pwd}", nil, nil)
if err != nil {
t.Fatal(err)
}

got := g.RunAsSlice()
if len(got) == 0 {
t.Fatal("expected mysql_pwd keyword to produce words")
}
}

func TestWeakPass(t *testing.T) {
g, err := NewGeneratorWithFile(writeWordsFile(t, "admin\n"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = g.File.Close() })
if err := g.SetRuleFile(filepath.Join("..", "templates", "zombie", "rule", "weakpass.rule")); err != nil {
t.Fatal(err)
}

got := g.RunAsSlice()
if len(got) == 0 {
t.Fatal("expected weakpass rule to generate words")
}
}
24 changes: 18 additions & 6 deletions core/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/chainreactors/files"
"github.com/chainreactors/logs"
"github.com/chainreactors/utils"
"github.com/chainreactors/utils/fileutils"
"github.com/chainreactors/zombie/pkg"
"io/ioutil"
"strings"
Expand Down Expand Up @@ -61,16 +61,27 @@ type MiscOptions struct {
Strict bool `long:"strict" description:"Bool, strict mode, when finger check pass will brute"`
Threads int `short:"t" default:"100" description:"Int, threads"`
Timeout int `long:"timeout" default:"5" description:"Int, timeout"`
Mod string `short:"m" default:"clusterbomb" description:"String, clusterbomb/sniper"`
Mod string `short:"m" default:"clusterbomb" description:"String, clusterbomb/pitchfork/sniper"`
ListService bool `short:"l" long:"list" description:"Bool, list all service"`
Bar bool `long:"bar" description:"Bool, enable bar"`
Version bool `long:"version" description:"Bool, show version"`
}

func (opt *Option) Validate() error {
if opt.Mod == "" {
opt.Mod = ModBomb
}
switch opt.Mod {
case ModBomb, ModPitchFork, ModSniper:
default:
return fmt.Errorf("unsupported mod %q, want clusterbomb, pitchfork, or sniper", opt.Mod)
}
if len(opt.IP) == 0 && opt.IPFile == "" && opt.JsonFile == "" && opt.GogoFile == "" && opt.CIDR == nil {
return errors.New("please input ip or or file or json file or gogo file")
}
if opt.Mod == ModPitchFork && opt.Auth == nil && opt.AuthFile == "" {
return errors.New("pitchfork mode requires auth, please set -a/-A")
}
if opt.WeakPassWord && (opt.Password == nil && opt.PasswordFile == "") {
return errors.New("use weak-password rule must set password, please set -p/-P")
}
Expand All @@ -87,16 +98,17 @@ func (opt *Option) Prepare() (*Runner, error) {
var err error
var targets []*Target

var file *files.File
var file *fileutils.File
var outfunc func(string)
if opt.OutputFile != "" {
file, err = files.NewFile(opt.OutputFile, false, false, true)
file, err = fileutils.NewFile(opt.OutputFile, fileutils.ModeAppend, false, false)
if err != nil {
return nil, err
}
outfunc = func(s string) {
file.SafeWrite(s)
file.SafeSync()
if err := file.SyncWrite(s); err != nil {
logs.Log.Warn(fmt.Sprintf("write output file failed: %v", err))
}
}
}

Expand Down
Loading
Loading