Skip to content

Instantly share code, notes, and snippets.

@alwashali
Created July 12, 2025 11:17
Show Gist options
  • Save alwashali/d5aa08cb66053ebecb76d9edc5195653 to your computer and use it in GitHub Desktop.
Save alwashali/d5aa08cb66053ebecb76d9edc5195653 to your computer and use it in GitHub Desktop.
Discover the connected devices in the network via scraping the router devices page
// Vodafone router
// Firmware version: 3.6.12-IMS-KDG
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"strconv"
"time"
"golang.org/x/crypto/pbkdf2"
)
func pbkdf2Hex(input, salt string, iterations int, length int) string {
key := pbkdf2.Key([]byte(input), []byte(salt), iterations, length, sha256.New)
hexs := hex.EncodeToString(key)
return hexs[:32]
}
func postForm(client *http.Client, urlstr string, headers map[string]string, data url.Values) ([]byte, error) {
req, err := http.NewRequest(http.MethodPost, urlstr, bytes.NewBufferString(data.Encode()))
if err != nil {
return nil, err
}
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func getWithTs(client *http.Client, urlstr string, headers map[string]string) ([]byte, error) {
now := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
fullUrl := fmt.Sprintf("%s?_=%s", urlstr, now)
req, err := http.NewRequest(http.MethodGet, fullUrl, nil)
if err != nil {
return nil, err
}
for k, v := range headers {
req.Header.Set(k, v)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func main() {
router := "http://192.168.0.1"
username := "admin"
password := "password"
headers := map[string]string{
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"Referer": router + "/",
}
jar, _ := cookiejar.New(nil)
client := &http.Client{
Jar: jar,
}
// Step 1: get salt
data := url.Values{}
data.Set("username", username)
data.Set("password", "seeksalthash")
data.Set("logout", "True")
body, err := postForm(client, router+"/api/v1/session/login", headers, data)
if err != nil {
panic(err)
}
var saltResp struct {
Salt string `json:"salt"`
SaltWebUI string `json:"saltwebui"`
}
if err := json.Unmarshal(body, &saltResp); err != nil {
panic(err)
}
a := pbkdf2Hex(password, saltResp.Salt, 1000, 32)
b := pbkdf2Hex(a, saltResp.SaltWebUI, 1000, 32)
data = url.Values{}
data.Set("username", username)
data.Set("password", b)
_, err = postForm(client, router+"/api/v1/session/login", headers, data)
if err != nil {
panic(err)
}
cookieURL, _ := url.Parse(router)
client.Jar.SetCookies(cookieURL, append(client.Jar.Cookies(cookieURL), &http.Cookie{
Name: "cwd",
Value: "Yes",
}))
delete(headers, "Content-Type")
//There is a protection that detect if bsd_acl_rules.js and menu are not loaded, before any other API call
_, err = getWithTs(client, router+"/js/app/bsd_acl_rules.js", headers)
if err != nil {
panic(err)
}
_, err = getWithTs(client, router+"/api/v1/session/menu", headers)
if err != nil {
panic(err)
}
client.Jar.SetCookies(cookieURL, append(client.Jar.Cookies(cookieURL), &http.Cookie{
Name: "cwd",
Value: "No",
}))
respBody, err := getWithTs(client, router+"/api/v1/sta_lan_status", headers)
if err != nil {
panic(err)
}
fmt.Println("sta_lan_status cookies:", client.Jar.Cookies(cookieURL))
fmt.Println("sta_lan_status response:")
var obj interface{}
if err := json.Unmarshal(respBody, &obj); err != nil {
fmt.Println(string(respBody))
} else {
pretty, _ := json.MarshalIndent(obj, "", " ")
fmt.Println(string(pretty))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment