From 5561a85ba1bcd4f8b21501db2694f31ba8d17b6e Mon Sep 17 00:00:00 2001 From: qiwentaidi <104491549+qiwentaidi@users.noreply.github.com> Date: Fri, 27 Dec 2024 11:27:05 +0800 Subject: [PATCH] update 1.7.7 --- .github/workflows/build.yml | 6 +- core/dumpall/ds_store.go | 417 ++++++++++++++++++++ core/jsfind/async.go | 31 ++ core/jsfind/jsfind_test.go | 68 ++-- core/jsfind/parameter.go | 44 +++ core/portscan/portscan.go | 17 +- core/space/quake.go | 2 +- core/tools/druid.go | 26 ++ core/tools/thinkdict.go | 122 ------ core/tools/urls.go | 9 + core/webscan/favicon.go | 44 ++- core/webscan/infoscan.go | 5 +- frontend/package-lock.json | 8 +- frontend/package.json | 2 +- frontend/package.json.md5 | 2 +- frontend/public/app/dict.png | Bin 1508 -> 0 bytes frontend/public/app/dropbox.png | Bin 0 -> 2970 bytes frontend/src/App.vue | 2 +- frontend/src/components/Update.vue | 4 +- frontend/src/global/index.ts | 3 +- frontend/src/i18n/en_US.ts | 3 +- frontend/src/i18n/zh_CN.ts | 1 + frontend/src/linkage/index.ts | 6 +- frontend/src/main.ts | 22 ++ frontend/src/router/menu.ts | 10 +- frontend/src/stores/validate.ts | 11 +- frontend/src/style/style.css | 43 +- frontend/src/usePagination.ts | 52 ++- frontend/src/util.ts | 24 +- frontend/src/views/AppStarter.vue | 224 ++++++----- frontend/src/views/Asset/Company.vue | 4 +- frontend/src/views/Asset/ISICollection.vue | 4 +- frontend/src/views/Asset/Subdomain.vue | 2 +- frontend/src/views/Permeation/Crack.vue | 2 +- frontend/src/views/Permeation/Dirsearch.vue | 83 +--- frontend/src/views/Permeation/Dumpall.vue | 79 ++++ frontend/src/views/Permeation/Portscan.vue | 32 +- frontend/src/views/Permeation/Webscan.vue | 203 +++++----- frontend/src/views/PocManagement.vue | 6 +- frontend/src/views/SpaceEngine/FOFA.vue | 2 +- frontend/src/views/SpaceEngine/Quake.vue | 20 +- frontend/src/views/Tools/AKSK.vue | 12 +- frontend/src/views/Tools/DataHanding.vue | 45 ++- frontend/src/views/Tools/Fscan.vue | 8 +- frontend/src/views/Tools/Thinkdict.vue | 89 ----- frontend/wailsjs/go/core/Tools.d.ts | 8 +- frontend/wailsjs/go/core/Tools.js | 16 +- frontend/wailsjs/go/models.ts | 4 + frontend/wailsjs/go/services/App.d.ts | 2 + frontend/wailsjs/go/services/App.js | 4 + frontend/wailsjs/go/services/File.d.ts | 4 + frontend/wailsjs/go/services/File.js | 8 + go.mod | 14 +- go.sum | 19 +- lib/clients/clients.go | 5 +- lib/structs/structs.go | 18 +- lib/update/update.go | 6 +- lib/util/regexp.go | 1 - main.go | 1 + services/app.go | 56 ++- services/file.go | 74 +++- 61 files changed, 1332 insertions(+), 707 deletions(-) create mode 100644 core/dumpall/ds_store.go create mode 100644 core/jsfind/async.go create mode 100644 core/jsfind/parameter.go create mode 100644 core/tools/druid.go delete mode 100644 core/tools/thinkdict.go create mode 100644 core/tools/urls.go delete mode 100644 frontend/public/app/dict.png create mode 100644 frontend/public/app/dropbox.png create mode 100644 frontend/src/views/Permeation/Dumpall.vue delete mode 100644 frontend/src/views/Tools/Thinkdict.vue diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 435062e4..6c4e5903 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,21 +49,21 @@ jobs: - name: Upload Artifacts macOS if: matrix.platform == 'macos-latest' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Slack-macos-${{ matrix.arch }} path: build/bin/* - name: Upload Artifacts Windows if: matrix.platform == 'windows-latest' && matrix.arch == 'amd64' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Slack-windows-amd64 path: build/bin/* - name: Upload Artifacts Linux if: matrix.platform == 'ubuntu-latest' && matrix.arch == 'amd64' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Slack-linux-amd64 path: build/bin/* diff --git a/core/dumpall/ds_store.go b/core/dumpall/ds_store.go new file mode 100644 index 00000000..a06c7ffb --- /dev/null +++ b/core/dumpall/ds_store.go @@ -0,0 +1,417 @@ +// https://github.com/gehaxelt/ds_store/blob/master/ds_store.go +package dumpall + +import ( + "bytes" + "encoding/binary" + "errors" + "slack-wails/lib/clients" + "strings" + "unicode/utf16" + "unicode/utf8" + "unsafe" +) + +type Block struct { + Allocator *Allocator + Offset uint32 + Size uint32 + Data []byte + Pos uint32 +} + +type Allocator struct { + Data []byte + Pos uint32 + Root *Block + Offsets []uint32 + Toc map[string]uint32 + FreeList map[uint32][]uint32 +} + +func NewBlock(a *Allocator, pos uint32, size uint32) (block *Block, err error) { + if len(a.Data) < int(pos+0x4+size) { + return nil, errors.New("not enought data") + } + block = &Block{Size: size, Allocator: a, Data: a.Data[pos+0x4 : pos+0x4+size]} + return block, nil +} + +func (block *Block) readUint32() (value uint32, err error) { + if block.Size-block.Pos < 4 { + return 0, errors.New("not enough bytes to read") + } + data := bytes.NewBuffer(block.Data) + data.Next(int(block.Pos)) + binary.Read(data, binary.BigEndian, &value) + block.Pos += 4 + return value, nil +} + +func (block *Block) readByte() (value byte, err error) { + if block.Size-block.Pos < 1 { + return 0, errors.New("not enough bytes to read") + } + data := bytes.NewBuffer(block.Data) + data.Next(int(block.Pos)) + binary.Read(data, binary.BigEndian, &value) + block.Pos += 1 + return value, nil +} + +func (block *Block) readBuf(length int) (buf []byte, err error) { + if int(block.Size)-int(block.Pos) < length { + return nil, errors.New("not enough bytes to read") + } + data := bytes.NewBuffer(block.Data) + data.Next(int(block.Pos)) + buf = make([]byte, length) + binary.Read(data, binary.BigEndian, &buf) + block.Pos += uint32(length) + return buf, nil +} + +func (block *Block) readFileName() (name string, err error) { + length, err := block.readUint32() + if err != nil { + return "", err + } + buf, err := block.readBuf(int(2 * length)) + if err != nil { + return "", err + } + + /* + sid, err := block.readUint32() + if err != nil { + return "", err + } + */ + block.skip(4) + + stype, err := block.readBuf(4) + if err != nil { + return "", err + } + + t := string(stype) + bytesToSkip := -1 + + switch { + case t == "bool": + bytesToSkip = 1 + case t == "type" || t == "long" || t == "shor": + bytesToSkip = 4 + case t == "comp" || t == "dutc": + bytesToSkip = 8 + case t == "blob": + blen, err := block.readUint32() + if err != nil { + break + } + bytesToSkip = int(blen) + case t == "ustr": + blen, err := block.readUint32() + if err != nil { + break + } + bytesToSkip = int(2 * blen) + default: + break + } + + if bytesToSkip <= 0 { + return "", errors.New("unknown file format") + } + block.skip(uint32(bytesToSkip)) + + name = utf16be2utf8(buf) + return name, nil +} + +func (block *Block) skip(i uint32) { + block.Pos += i +} + +func NewAllocator(data []byte) (a *Allocator, err error) { + a = &Allocator{Data: data} //bytes.NewBuffer(data)} + a.Toc = make(map[string]uint32) + a.FreeList = make(map[uint32][]uint32) + + offset, size, err := a.readHeader() + if err != nil { + return nil, err + } + + a.Root, err = NewBlock(a, offset, size) + if err != nil { + return nil, err + } + + err = a.readOffsets() + if err != nil { + return nil, err + } + + err = a.readToc() + if err != nil { + return nil, err + } + + err = a.readFreeList() + if err != nil { + return nil, err + } + + return a, err +} + +func (a *Allocator) GetBlock(bid uint32) (block *Block, err error) { + if len(a.Offsets) <= int(bid) { + return nil, errors.New("cannot find key in Offset-Table") + } + addr := a.Offsets[bid] + + offset := int(addr) & ^0x1f + size := 1 << (uint(addr) & 0x1f) + + block, err = NewBlock(a, uint32(offset), uint32(size)) ///+4?? + if err != nil { + return nil, errors.New("cannot create/read block") + } + return block, nil +} + +func (a *Allocator) TraverseFromRootNode() (filenames []string, err error) { + rootBlk, err := a.GetBlock(a.Toc["DSDB"]) + if err != nil { + return nil, err + } + rootNode, err := rootBlk.readUint32() + if err != nil { + return nil, err + } + /*height, err := rootBlk.readUint32() + if err != nil { + return nil, err + } + recordsCount, err := rootBlk.readUint32() + if err != nil { + return nil, err + } + nodesCount, err := rootBlk.readUint32() + if err != nil { + return nil, err + } + blksize, err := rootBlk.readUint32() + if err != nil { + return nil, err + }*/ + rootBlk.skip(4 * 4) + + return a.Traverse(rootNode) +} + +func (a *Allocator) Traverse(bid uint32) (filenames []string, err error) { + node, err := a.GetBlock(bid) + if err != nil { + return nil, err + } + nextPtr, err := node.readUint32() + if err != nil { + return nil, err + } + count, err := node.readUint32() + if err != nil { + return nil, err + } + if nextPtr > 0 { + //This may be broken + for i := 0; i < int(count); i++ { + next, err := node.readUint32() + if err != nil { + return nil, err + } + files, err := a.Traverse(next) + if err != nil { + return nil, err + } + filenames = append(filenames, files...) + f, err := node.readFileName() + if err != nil { + return nil, err + } + filenames = append(filenames, f) + } + files, err := a.Traverse(nextPtr) + if err != nil { + return nil, err + } + filenames = append(filenames, files...) + } else { + for i := 0; i < int(count); i++ { + f, err := node.readFileName() + if err != nil { + return nil, err + } + filenames = append(filenames, f) + } + } + + return filenames, nil +} + +func (a *Allocator) readFreeList() error { + for i := 0; i < 32; i++ { + blkcount, err := a.Root.readUint32() + if err != nil { + return err + } + if blkcount == 0 { + continue + } + a.FreeList[uint32(i)] = make([]uint32, 0) + for k := 0; k < int(blkcount); k++ { + val, err := a.Root.readUint32() + if err != nil { + return err + } + if val == 0 { + continue + } + a.FreeList[uint32(i)] = append(a.FreeList[uint32(i)], val) + } + } + return nil +} + +func (a *Allocator) readToc() error { + toccount, err := a.Root.readUint32() + if err != nil { + return err + } + for i := toccount; i > 0; i-- { + tlen, err := a.Root.readByte() + if err != nil { + return err + } + name, err := a.Root.readBuf(int(tlen)) + if err != nil { + return err + } + value, err := a.Root.readUint32() + if err != nil { + return err + } + a.Toc[string(name)] = value + } + return nil +} + +func (a *Allocator) readOffsets() error { + count, err := a.Root.readUint32() + if err != nil { + return err + } + a.Root.skip(4) + + for offcount := int(count); offcount > 0; offcount -= 256 { + for i := 0; i < 256; i++ { + val, err := a.Root.readUint32() + if err != nil { + return err + } + if val == 0 { + continue + } + a.Offsets = append(a.Offsets, val) + } + } + return nil +} + +func (a *Allocator) readHeader() (offset uint32, size uint32, err error) { + data := bytes.NewBuffer(a.Data) + if data.Len() < 32 { + return offset, size, errors.New("header not long enough") + } + var magic1, magic, offset2 uint32 + + binary.Read(data, binary.BigEndian, &magic1) + if magic1 != 1 { + return offset, size, errors.New("wrong magic bytes") + } + a.Pos += 4 + + binary.Read(data, binary.BigEndian, &magic) + if magic != 0x42756431 { + return offset, size, errors.New("wrong magic bytes") + } + a.Pos += 4 + + binary.Read(data, binary.BigEndian, &offset) + a.Pos += 4 + binary.Read(data, binary.BigEndian, &size) + a.Pos += 4 + binary.Read(data, binary.BigEndian, &offset2) + if offset != offset2 { + return offset, size, errors.New("offets do not match") + } + a.Pos += 4 + + return offset, size, nil +} + +func utf16be2utf8(utf16be []byte) string { + n := len(utf16be) + // 使用 unsafe.Slice 替代 reflect.SliceHeader + shorts := unsafe.Slice((*uint16)(unsafe.Pointer(&utf16be[0])), n/2) + // shorts may need byte-swapping + for i := 0; i < n; i += 2 { + shorts[i/2] = (uint16(utf16be[i]) << 8) | uint16(utf16be[i+1]) + } + + // Convert to []byte + count := 0 + for i := 0; i < len(shorts); i++ { + r := rune(shorts[i]) + if utf16.IsSurrogate(r) { + i++ + r = utf16.DecodeRune(r, rune(shorts[i])) + } + count += utf8.RuneLen(r) + } + buf := make([]byte, count) + bi := 0 + for i := 0; i < len(shorts); i++ { + r := rune(shorts[i]) + if utf16.IsSurrogate(r) { + i++ + r = utf16.DecodeRune(r, rune(shorts[i])) + } + bi += utf8.EncodeRune(buf[bi:], r) + } + return string(buf) +} + +func ExtractDSStore(url string) ([]string, error) { + _, body, err := clients.NewSimpleGetRequest(url, clients.DefaultClient()) + if err != nil { + return nil, err + } + a, err := NewAllocator(body) + if err != nil { + return nil, err + } + filenames, err := a.TraverseFromRootNode() + if err != nil { + return nil, err + } + + var urlRoot = strings.TrimSuffix(url, ".DS_Store") + var result = []string{} + for _, f := range filenames { + result = append(result, urlRoot+f) + } + return result, nil +} diff --git a/core/jsfind/async.go b/core/jsfind/async.go new file mode 100644 index 00000000..60fb61b4 --- /dev/null +++ b/core/jsfind/async.go @@ -0,0 +1,31 @@ +package jsfind + +import ( + "context" + "fmt" + + "github.com/chromedp/chromedp" +) + +// 加载网页全部内容,包括异步的js文件 +func asyncLoader(url string) { + // 创建 context + ctx, cancel := chromedp.NewContext(context.Background()) + defer cancel() + + // 用于存储页面内容 + var pageContent string + + // 运行任务 + err := chromedp.Run(ctx, + chromedp.Navigate(url), + chromedp.WaitVisible(`#target-element`, chromedp.ByID), // 等待目标元素加载完成 + chromedp.OuterHTML("html", &pageContent), // 获取页面内容 + ) + if err != nil { + fmt.Println("chromedp 错误:", err) + return + } + + fmt.Println("页面内容:", pageContent) +} diff --git a/core/jsfind/jsfind_test.go b/core/jsfind/jsfind_test.go index b2e39ca5..c67a8005 100644 --- a/core/jsfind/jsfind_test.go +++ b/core/jsfind/jsfind_test.go @@ -1,52 +1,38 @@ package jsfind import ( - "context" "fmt" "net/url" - "strings" - "sync" + "slack-wails/lib/clients" "testing" ) func TestFindInfo(t *testing.T) { - var fs *FindSomething - target := "https://www.baidu.com/" - jsLinks := ExtractJS(context.TODO(), target) - fmt.Printf("jsLinks: %v\n", jsLinks) - var wg sync.WaitGroup - limiter := make(chan bool, 100) - wg.Add(1) - limiter <- true - go func() { - fs = FindInfo(context.TODO(), target, limiter, &wg) - }() - wg.Wait() - u, _ := url.Parse(target) - fs.JS = *AppendSource(target, jsLinks) - host := u.Scheme + "://" + u.Host - for _, jslink := range jsLinks { - wg.Add(1) - limiter <- true - go func(js string) { - var newURL string - if strings.HasPrefix(js, "http") { - newURL = js - } else { - newURL = host + "/" + js - } - fs2 := FindInfo(context.TODO(), newURL, limiter, &wg) - fs.IP_URL = append(fs.IP_URL, fs2.IP_URL...) - fs.ChineseIDCard = append(fs.ChineseIDCard, fs2.ChineseIDCard...) - fs.ChinesePhone = append(fs.ChinesePhone, fs2.ChinesePhone...) - fs.SensitiveField = append(fs.SensitiveField, fs2.SensitiveField...) - fs.APIRoute = append(fs.APIRoute, fs2.APIRoute...) - }(jslink) + +} + +// 发送 GET 请求,直到参数补全 +func sendRequest(apiURL string, params url.Values) url.Values { + // 构造完整 URL + fullURL := fmt.Sprintf("%s?%s", apiURL, params.Encode()) + + // 发送 GET 请求 + _, body, err := clients.NewSimpleGetRequest(fullURL, clients.DefaultClient()) + if err != nil { + fmt.Println("请求失败:", err) + return params + } + + // 提取缺失参数 + missingParam := extractMissingParams(string(body)) + if missingParam != nil { + // 生成默认值并补全参数 + defaultValue := generateDefaultValue(missingParam.Type) + params.Set(missingParam.Name, fmt.Sprint(defaultValue)) + // 递归调用,直到所有参数补全 + return sendRequest(apiURL, params) } - wg.Wait() - fs.APIRoute = RemoveDuplicatesInfoSource(fs.APIRoute) - fs.ChineseIDCard = RemoveDuplicatesInfoSource(fs.ChineseIDCard) - fs.ChinesePhone = RemoveDuplicatesInfoSource(fs.ChinesePhone) - fs.SensitiveField = RemoveDuplicatesInfoSource(fs.SensitiveField) - fs.IP_URL = RemoveDuplicatesInfoSource(fs.IP_URL) + // 如果没有缺失参数提示,返回当前参数集 + fmt.Println(fullURL) + return params } diff --git a/core/jsfind/parameter.go b/core/jsfind/parameter.go new file mode 100644 index 00000000..14fc101f --- /dev/null +++ b/core/jsfind/parameter.go @@ -0,0 +1,44 @@ +package jsfind + +import "regexp" + +type Parameter struct { + Name string `json:"name"` + Type string `json:"type"` +} + +var extractMissingRegex = regexp.MustCompile(`Required (String|Int|Long|Double|Boolean|Date).*?'([^']+)'`) + +// 从错误信息中提取缺失参数的名称 +func extractMissingParams(message string) *Parameter { + // 提取匹配内容 + matches := extractMissingRegex.FindStringSubmatch(message) + // 输出结果 + if len(matches) > 2 { + return &Parameter{ + Name: matches[2], + Type: matches[1], + } + } + return nil +} + +// 根据参数类型生成默认值 +func generateDefaultValue(paramType string) interface{} { + switch paramType { + case "String": + return "" + case "Int": + return 0 + case "Long": + return int64(0) + case "Double": + return 0.0 + case "Boolean": + return false + case "Date": + return "1970-01-01" + default: + return "defaultValue" + } +} diff --git a/core/portscan/portscan.go b/core/portscan/portscan.go index cf701615..c97410a1 100644 --- a/core/portscan/portscan.go +++ b/core/portscan/portscan.go @@ -5,8 +5,8 @@ import ( "fmt" "net" "slack-wails/lib/clients" + "slack-wails/lib/gologger" "slack-wails/lib/gonmap" - "slack-wails/lib/util" "sync" "sync/atomic" "time" @@ -31,6 +31,7 @@ func TcpScan(ctx context.Context, addresses <-chan Address, workers, timeout int single := make(chan struct{}) retChan := make(chan PortResult) var wg sync.WaitGroup + openPorts := make(map[string]bool) // 记录开放的端口 go func() { for pr := range retChan { runtime.EventsEmit(ctx, "portScanLoading", pr) @@ -44,6 +45,16 @@ func TcpScan(ctx context.Context, addresses <-chan Address, workers, timeout int return } pr := Connect(add.IP, add.Port, timeout, proxy) + // 检查1-10端口的开放情况 + if pr.Port >= 1 && pr.Port <= 20 && pr.Status { + openPorts[pr.IP] = true // 记录该IP有开放端口 + gologger.IntervalError(ctx, fmt.Sprintf("[portscan] %s 疑似cdn地址,会对未识别到服务的端口进行过滤", pr.IP)) + } else { + // 如果该IP在1-10端口有开放,后续端口必须识别到服务 + if openPorts[pr.IP] && !pr.Status { + return // 如果没有识别到服务,则不返回 + } + } atomic.AddInt32(&id, 1) runtime.EventsEmit(ctx, "progressID", id) if pr.Status { @@ -103,8 +114,8 @@ func Connect(ip string, port, timeout int, proxy clients.Proxy) PortResult { if resp.StatusCode == 422 { pr.Status = false } - if match := util.RegTitle.FindSubmatch(b); len(match) > 1 { - pr.HttpTitle = util.Str2UTF8(string(match[1])) + if title := clients.GetTitle(b); title != "" { + pr.HttpTitle = title } else { pr.HttpTitle = "-" } diff --git a/core/space/quake.go b/core/space/quake.go index d9100dee..4f699546 100644 --- a/core/space/quake.go +++ b/core/space/quake.go @@ -39,7 +39,7 @@ func QuakeApiSearch(o *structs.QuakeRequestOptions) *structs.QuakeResult { "Content-Type": "application/json", "X-QuakeToken": o.Token, } - _, body, err := clients.NewRequest("POST", quakeServerApi, header, bytes.NewReader(bytesData), 10, false, clients.DefaultClient()) + _, body, err := clients.NewRequest("POST", quakeServerApi, header, bytes.NewReader(bytesData), 20, false, clients.DefaultClient()) if err != nil { return &structs.QuakeResult{ Code: 502, diff --git a/core/tools/druid.go b/core/tools/druid.go new file mode 100644 index 00000000..87a302ca --- /dev/null +++ b/core/tools/druid.go @@ -0,0 +1,26 @@ +package core + +import "regexp" + +var ( + regexAlibabaDruidWebSession = regexp.MustCompile(`"SESSIONID":"(?P[^"]+)"`) + regexAlibabaDruidWebURI = regexp.MustCompile(`"URI":"(?P[^"]+)"`) +) + +func (t *Tools) ExtractAlibabaDruidWebSession(input string) []string { + matches := regexAlibabaDruidWebSession.FindAllStringSubmatch(input, -1) + results := []string{} + for _, match := range matches { + results = append(results, match[regexAlibabaDruidWebSession.SubexpIndex("session")]) + } + return results +} + +func (t *Tools) ExtractAlibabaDruidWebURI(input string) []string { + matches := regexAlibabaDruidWebURI.FindAllStringSubmatch(input, -1) + results := []string{} + for _, match := range matches { + results = append(results, match[regexAlibabaDruidWebURI.SubexpIndex("uri")]) + } + return results +} diff --git a/core/tools/thinkdict.go b/core/tools/thinkdict.go deleted file mode 100644 index 3a71fcbf..00000000 --- a/core/tools/thinkdict.go +++ /dev/null @@ -1,122 +0,0 @@ -package core - -import ( - "slack-wails/lib/util" - "strconv" - "strings" - "time" - - "github.com/mozillazg/go-pinyin" -) - -var weakPasswordList = []string{"123456", "000000", "aa123456", "Aa123456", "Abc123!", "Abc123!@#", "abc123!", "abc1234!", "@bcd1234", "abc123!@#", "Abc123!@#", "666666", "888888", "88888888", "#EDC4rfv", "abcABC123", "1qaz!@#$", "admin@123", "Admin@123", "admin@1234", "Admin@1234", "QAZwsx123", "Pa$$w0rd", "P@ssw0rd", "P@$$word", "P@$$word123", "Abcd1234", "!QAZ2wsx", "!QAZ3edc", "2wsx#EDC", "1!qaz2@wsx", "1q2w3e4r", "1234abcd", "1234qwer", "1qaz!QAZ", "1qaz2wsx", "1qaz@WSX", "1qaz@WSX#EDC", "!q2w3e4r", "1234qwer", "1234QWER", "QWER!@#$", "Passwd@123", "Passwd12", "Passwd@123456", "P@ssw0rd", "1qaz@WSX#EDC", "p@ssw0rd", "qazasd123", "qazwsxedc123", "qweasdzxcqaz123", "asdf1234", "123456Aa", "Aa123456", "123456!Aa", "111111Aa", "111111"} - -func (t *Tools) GenerateDict(userNameCN, userNameEN, companyName, companyDomain, birthday, jobNumber, connectWord string, weakList []string) (dicts []string) { - names := []string{Chinese2PinyinQuanPin(userNameCN), Chinese2PinyinFirstLetter(userNameCN), Chinese2PinyinHalfQuanPin(userNameCN), userNameEN} - companyNames := []string{Chinese2PinyinQuanPin(companyName), Chinese2PinyinFirstLetter(companyName)} - year := time.Now().Year() - for i := 0; i <= 6; i++ { - weakList = append(weakList, strconv.Itoa(year-i)) - } - if birthday != "" { - for _, name := range names { - dicts = append(dicts, name+birthday) - dicts = append(dicts, name+"@"+birthday) - dicts = append(dicts, name+birthday[2:]) - dicts = append(dicts, name+"@"+birthday[2:]) - } - } - for _, weakpass := range weakList { - for _, name := range names { - dicts = append(dicts, name+weakpass) - dicts = append(dicts, name+"@"+weakpass) - } - // baidu.com [0] www.baidu.com [1] 1111.www.baidu.com [2] - if strings.Contains(companyDomain, ".") { - d := strings.Split(companyDomain, ".") - cd := d[len(d)-2] - dicts = append(dicts, cd+weakpass) - dicts = append(dicts, cd+"@"+weakpass) - } else { - dicts = append(dicts, companyDomain+weakpass) - dicts = append(dicts, companyDomain+"@"+weakpass) - } - } - for _, name := range names { - dicts = append(dicts, name+companyDomain) - dicts = append(dicts, name+"@"+companyDomain) - dicts = append(dicts, companyDomain+"@"+name) - dicts = append(dicts, companyDomain+name) - for _, company := range companyNames { - dicts = append(dicts, name+company) - dicts = append(dicts, name+"@"+company) - dicts = append(dicts, company+"@"+name) - dicts = append(dicts, company+name) - } - } - for _, company := range companyNames { - for _, weak := range weakList { - dicts = append(dicts, company+weak) - dicts = append(dicts, company+"@"+weak) - } - dicts = append(dicts, company+jobNumber) - dicts = append(dicts, company+"@"+jobNumber) - } - dicts = append(dicts, weakPasswordList...) - var connectWords []string - if connectWord != "" { - connectWords = append(connectWords, strings.Split(connectWord, ",")...) - } - for _, dict := range dicts { - if strings.Contains(dict, "@") { - for _, word := range connectWords { - dicts = append(dicts, strings.ReplaceAll(dict, "@", word)) - } - } - } - return util.RemoveDuplicates(dicts) -} - -func Chinese2PinyinFirstLetter(str string) (fl string) { - py := pinyin.NewArgs() - py.Style = pinyin.FirstLetter // 设置为获取拼音首字母(只包含首字母) - for _, v := range pinyin.Pinyin(str, py) { // 将字符串转换为拼音首字母 - fl += v[0] - } - if fl == "" { // 说明不是中文 - return str - } - return fl -} - -func Chinese2PinyinHalfQuanPin(str string) (fl string) { - a := pinyin.NewArgs() - dict := pinyin.Pinyin(str, a) - for _, qpzm := range dict { - fl += strings.Join(qpzm, "") - } - return fl -} - -func Chinese2PinyinQuanPin(str string) (qp string) { - // 默认 - a := pinyin.NewArgs() - for _, qpzm := range pinyin.Pinyin(str, a) { - for _, v := range qpzm { - qp += v - } - } - if qp == "" { // 说明不是中文 - return str - } - return qp -} - -func FirstUpper(str string) string { - bs := []byte(str) - if len(bs) == 0 { - return "" - } - bs[0] = byte(bs[0] - 32) - return string(bs) -} diff --git a/core/tools/urls.go b/core/tools/urls.go new file mode 100644 index 00000000..8b4e1885 --- /dev/null +++ b/core/tools/urls.go @@ -0,0 +1,9 @@ +package core + +import "regexp" + +var regexURL = regexp.MustCompile(`https?://[^\s"']+`) + +func (t *Tools) ExtractURLs(input string) []string { + return regexURL.FindAllString(input, -1) +} diff --git a/core/webscan/favicon.go b/core/webscan/favicon.go index 849f5ada..45f9db42 100644 --- a/core/webscan/favicon.go +++ b/core/webscan/favicon.go @@ -5,6 +5,7 @@ import ( "crypto/md5" "encoding/base64" "encoding/hex" + "errors" "fmt" "hash" "net/http" @@ -18,7 +19,8 @@ import ( ) var ( - iconRels = []string{"icon", "shortcut icon", "apple-touch-icon", "mask-icon"} + iconDesktopRels = []string{"icon", "shortcut icon"} // 桌面端 Logo 优先匹配 + iconMobileRels = []string{"apple-touch-icon", "mask-icon"} // 移动|其他端 Logo 其次 ) // 获取favicon Mmh3Hash32 和 MD5值 @@ -52,22 +54,60 @@ func FaviconHash(u *url.URL, client *http.Client) (string, string) { return "", "" } +func GetFaviconFullLink(u *url.URL, client *http.Client) (string, error) { + _, body, err := clients.NewSimpleGetRequest(u.String(), client) + if err != nil { + return "", err + } + doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body)) + if err != nil { + return "", errors.New("goquery failed to parse " + u.String() + " content") + } + iconLink := parseIcons(doc)[0] + var finalLink string + // 如果是完整的链接,则直接请求 + if strings.HasPrefix(iconLink, "http") { + finalLink = iconLink + // 如果为 // 开头采用与网站同协议 + } else if strings.HasPrefix(iconLink, "//") { + finalLink = u.Scheme + ":" + iconLink + } else { + finalLink = fmt.Sprintf("%s://%s/%s", u.Scheme, u.Host, iconLink) + } + return finalLink, nil +} + // parseIcons 解析HTML文档head中的标签中rel属性包含icon信息的href链接 func parseIcons(doc *goquery.Document) []string { var icons []string + // 桌面端 doc.Find("head link").Each(func(i int, s *goquery.Selection) { href, exists := s.Attr("href") if exists { // 匹配ICON链接 - if rel, exists := s.Attr("rel"); exists && util.ArrayContains(rel, iconRels) { + if rel, exists := s.Attr("rel"); exists && util.ArrayContains(rel, iconDesktopRels) { icons = append(icons, href) } } }) + // 移动端 + if len(icons) == 0 { + doc.Find("head link").Each(func(i int, s *goquery.Selection) { + href, exists := s.Attr("href") + if exists { + // 匹配ICON链接 + if rel, exists := s.Attr("rel"); exists && util.ArrayContains(rel, iconMobileRels) { + icons = append(icons, href) + } + } + }) + } + // 找不到自定义icon链接就使用默认的favicon地址 if len(icons) == 0 { icons = append(icons, "favicon.ico") } + return icons } diff --git a/core/webscan/infoscan.go b/core/webscan/infoscan.go index 42309b93..d9814bd4 100644 --- a/core/webscan/infoscan.go +++ b/core/webscan/infoscan.go @@ -25,7 +25,10 @@ import ( "github.com/wailsapp/wails/v2/pkg/runtime" ) -var ExitFunc = false +var ( + ExitFunc = false + IsRunning = false +) type WebInfo struct { Protocol string diff --git a/frontend/package-lock.json b/frontend/package-lock.json index af888c7b..a5cd42df 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,7 +14,7 @@ "async": "^3.2.5", "echarts": "^5.5.1", "element-plus": "^2.8.0", - "highlight.js": "^11.10.0", + "highlight.js": "^11.11.1", "marked": "^14.0.0", "monaco-editor": "^0.52.0", "nanoid": "^5.0.7", @@ -1418,9 +1418,9 @@ } }, "node_modules/highlight.js": { - "version": "11.10.0", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.10.0.tgz", - "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", + "version": "11.11.1", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", "engines": { "node": ">=12.0.0" } diff --git a/frontend/package.json b/frontend/package.json index 0562539b..737fd9c5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,7 @@ "async": "^3.2.5", "echarts": "^5.5.1", "element-plus": "^2.8.0", - "highlight.js": "^11.10.0", + "highlight.js": "^11.11.1", "marked": "^14.0.0", "monaco-editor": "^0.52.0", "nanoid": "^5.0.7", diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index cfb9fd66..6fdc9e1d 100644 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -b332002cbb4a15c7eb550caf4fdd5f5b \ No newline at end of file +44188a52e63d8ae5e381d192c474c1d1 \ No newline at end of file diff --git a/frontend/public/app/dict.png b/frontend/public/app/dict.png deleted file mode 100644 index 3570dc81df0de129f9c20cd65b4af2df493a488b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1508 zcmV)U9;yty(r{SIvi4)L8!*ZUB1-q-pfCx9j86^x$!_uTX@Efj|la^BbaA`v}32`o#} z+nYd2K|)#Bwjz}5vgk@G;@Cg%IN0|IxvodU^U>N+uQ%|0&E8&vkgxScOUilRXS5Ks zc=6KppBuwGk4P!FFm;A}ehAxEbdu|Otge>0d8>%lhJX;vuFOrpLxiM-fj4NnFm;B> z$q{T@(P_4=n4BEp!qgc;2Hv2Bq?O@in)3M}+NoAumX;r|wETc-)#bQMDmQ!3Rt4&6NodU_Hx+28)f!dW6D z5yGHovMfnEH*XbbG<-shhR@AgMUIPQNkUo(MT8{67=4zeR4U0~T-T#mtP+q?Af+Hs zELL${kK@qWo8U36$B2+TA;+npwIMJ%l45iuML=tV<5V~fQVQCUL>VzmQ+IcQ!xRc- zLYd3}nalv8LZQrYu)7o?0cj#6pJQyAlqG59x*i*wH3Cu!#>P%FHg*~*1%Zvt8m{Z{ ztR)f_A!FzxLh=M0r-Igo!01Q{+g8}NVss=$Kx>2JROlo{gaBpul#qf}g+iH-0Y3b2 zgMi^Pg+iHpeuz#{L>M?rQ(teADA)Da*sKvM7ONDCRa$Lq)^J@9+g3ywZCJarO|5po zQBtWSxm=p4-rgi3AvjBfA|}VFpta$PXl-zu3izXHgrpRNj6o5h zh^bI06B^G9(BI!p)XvUB?i9)d3WYNH{18!w_zdwWVsKrLjm;VXDFxHh-@vvNQLgK8 z_g)394I7&^T-U?46`_e-n!s-DfT%{p=c9jZ5gH!uBR4)sT#6VRr-Igo!01Q{+g7x) zZN=zFiuGa@tqqP-A)g;2Y9g1W)wOF5p{}IGYp;F_Aq82$8j=p% z-rnPb8|#G5of~F&IK|VFbdc|B{{G%wd|wk78tUZ-Q)l=xBpvj@jdjZ9hXfJ{$xnWq zK}f-uq3DqN_y5b%(gQ;M{oSmsZPQ9f7|#rH>QonTC_3c&!pB6FN_9%5I)_DIB zY_PVr&0~UPA{1?SzNS>FQ?2gf`zjB2ZiX*)6b({0q6iNz0{V7GSZSgE^l@IWJ zL#ws*J(ln85-_mKArx(#AMfMsKfZ)z3BD?CUcS%r-Cd3%X=CR6X)H@{(n+3klFtzi zP5=C82j4fGbb^Q%XBW~2vk533d12UQay*4)2|`^7LHcV+j>}g00ACvdt4^JjkLv^k zEDLGl&Q1RrC4LQTaRM13R*4X_ zk^j~Ewm2}~HHL98HlMM9VUEu_;L#)m-GBjh1uWj4n_lMs$^QUK#S$bkErY!P0000< KMNUMnLSTX-Y|M`U diff --git a/frontend/public/app/dropbox.png b/frontend/public/app/dropbox.png new file mode 100644 index 0000000000000000000000000000000000000000..4ffbe4bf0d294283e920e8fd8768636a72a2e165 GIT binary patch literal 2970 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 I>eJBw0hdj_umAu6 literal 0 HcmV?d00001 diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 58475405..423b7b07 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -40,7 +40,7 @@ useDark({ // 初始化语言 const locale = computed(() => (global.Language.value === 'zh' ? zhCn : en)) -const logArray = [] as string[] +var logArray = [] as string[] onMounted(async () => { // 初始化目录 diff --git a/frontend/src/components/Update.vue b/frontend/src/components/Update.vue index 6079efa1..711f23cc 100644 --- a/frontend/src/components/Update.vue +++ b/frontend/src/components/Update.vue @@ -178,7 +178,7 @@ function updateSuccess() {