139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
package cookie
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func (r *RefreshService) getClearanceToken() (string, error) {
|
|
baseURL := fmt.Sprintf("https://%s/", r.domain)
|
|
req, _ := http.NewRequest("GET", baseURL, nil)
|
|
req.Header.Set("User-Agent", randomUserAgent())
|
|
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
|
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
|
|
req.Header.Set("Connection", "keep-alive")
|
|
|
|
resp, err := r.client.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
|
|
// Detect challenge (several patterns)
|
|
patterns := []*regexp.Regexp{
|
|
regexp.MustCompile(`<html[^>]*id=["']sssg["'][^>]*data-sssg-challenge=["']([^"']+)["'][^>]*data-sssg-difficulty=["'](\d+)["']`),
|
|
regexp.MustCompile(`<html[^>]*id=["']sssg["'][^>]*data-sssg-difficulty=["'](\d+)["'][^>]*data-sssg-challenge=["']([^"']+)["']`),
|
|
regexp.MustCompile(`data-sssg-challenge=["']([^"']+)["'][^>]*data-sssg-difficulty=["'](\d+)["']`),
|
|
}
|
|
var salt string
|
|
var difficulty int
|
|
found := false
|
|
for i, p := range patterns {
|
|
if m := p.FindStringSubmatch(string(body)); len(m) >= 3 {
|
|
if i == 1 {
|
|
difficulty, _ = strconv.Atoi(m[1])
|
|
salt = m[2]
|
|
} else {
|
|
salt = m[1]
|
|
difficulty, _ = strconv.Atoi(m[2])
|
|
}
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found || difficulty == 0 || salt == "" {
|
|
return "", nil
|
|
}
|
|
|
|
log.Printf("Solving KiwiFlare challenge (difficulty=%d)", difficulty)
|
|
time.Sleep(time.Duration(500+rand.Intn(750)) * time.Millisecond)
|
|
|
|
nonce, err := r.solvePoW(salt, difficulty)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
time.Sleep(time.Duration(700+rand.Intn(900)) * time.Millisecond)
|
|
|
|
submitURL := fmt.Sprintf("https://%s/.sssg/api/answer", r.domain)
|
|
form := url.Values{"a": {salt}, "b": {nonce}}
|
|
post, _ := http.NewRequest("POST", submitURL, strings.NewReader(form.Encode()))
|
|
post.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
post.Header.Set("User-Agent", randomUserAgent())
|
|
post.Header.Set("Origin", baseURL)
|
|
post.Header.Set("Referer", baseURL)
|
|
|
|
resp2, err := r.client.Do(post)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp2.Body.Close()
|
|
|
|
// Some deployments return JSON like {"auth":"..."}
|
|
var result map[string]any
|
|
_ = json.NewDecoder(resp2.Body).Decode(&result)
|
|
|
|
time.Sleep(time.Duration(1200+rand.Intn(800)) * time.Millisecond)
|
|
|
|
cookieURL, _ := url.Parse(baseURL)
|
|
for _, c := range r.client.Jar.Cookies(cookieURL) {
|
|
if c.Name == "sssg_clearance" {
|
|
log.Printf("✅ KiwiFlare clearance cookie confirmed: %s...", c.Value[:min(10, len(c.Value))])
|
|
return c.Value, nil
|
|
}
|
|
}
|
|
if v, ok := result["auth"].(string); ok && v != "" {
|
|
// Fallback: manually add
|
|
r.client.Jar.SetCookies(cookieURL, []*http.Cookie{{
|
|
Name: "sssg_clearance",
|
|
Value: v,
|
|
Path: "/",
|
|
Domain: r.domain,
|
|
}})
|
|
return v, nil
|
|
}
|
|
return "", fmt.Errorf("clearance cookie missing after solve")
|
|
}
|
|
|
|
func (r *RefreshService) solvePoW(salt string, difficulty int) (string, error) {
|
|
start := time.Now()
|
|
bytes := difficulty / 8
|
|
bits := difficulty % 8
|
|
|
|
for nonce := rand.Int63(); ; nonce++ {
|
|
sum := sha256.Sum256([]byte(fmt.Sprintf("%s%d", salt, nonce)))
|
|
ok := true
|
|
for i := 0; i < bytes; i++ {
|
|
if sum[i] != 0 {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
if ok && bits > 0 && bytes < len(sum) {
|
|
mask := byte(0xFF << (8 - bits))
|
|
if sum[bytes]&mask != 0 {
|
|
ok = false
|
|
}
|
|
}
|
|
if ok {
|
|
delay := time.Duration(2+rand.Intn(3))*time.Second - time.Since(start)
|
|
if delay > 0 {
|
|
time.Sleep(delay)
|
|
}
|
|
return fmt.Sprintf("%d", nonce), nil
|
|
}
|
|
}
|
|
}
|