前言

前几天买了一个路由器(OpenWrt)想在学校用,就诞生了几个需求:

  1. 可以认证Giwifi上网
  2. 因为宿舍断电,需要来电开机后自动认证
  3. 最好能够占用平板端的闲置名额

此项目地址:Giwifi_autoLogin

原理

Giwifi认证时因为尚未联网,走的是http明文,只是对一些参数进行了加密

API接口

POST /gportal/Web/loginAction
Host: 10.100.100.2
X-Requested-With: XMLHttpRequest
Origin: http://10.100.100.2
Referer: http://10.100.100.2/gportal/web/login?has_reload=1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: ...

载荷明文

sign=xxxxx&
sta_vlan=xxx&
sta_port=xxx&
sta_ip=xxx&
nas_ip=xxx&
nas_name=xxx&
last_url=xxx&
request_ip=xxx&
device_mode=xxx&
device_type=xxx&
device_os_type=xxx&
is_mobile=xxx&
iv=xxxxxxxxxxxxxxxx&
login_type=xxx&
account_type=xxx&
user_account=你的账号&
user_password=你的密码

载荷加密

明文字符串
-> 转成字节
-> 按 16 字节补 0
-> AES-128-CBC
-> Base64
参数
  • 算法:AES-128-CBC
  • key:1234567887654321
  • iv:页面返回的 iv
  • padding:Zero Padding
  • 输出:Base64
enc = Base64(AES-CBC-Encrypt(zeropad(raw)))
模拟手机/平板/PC端

在编写此脚本时,UA使用的是Win,故此脚本模拟的是PC端。

而根据载荷参数,我们可以模拟其它端,只需要知道此端对应的device_mode=xxx&device_type=xxx&device_os_type=xxx&is_mobile=xxx&参数即可

后续有时间我会写一个第三方客户端,支持自定义模拟

更新:见第三方GiWifi客户端

脚本详细

内容

因为要在OpenWrt路由器上运行,故使用shell脚本

#!/bin/sh

# 用法:
#   sh giwifi.sh 账号 密码 [base_url]
# 例子:
#   sh giwifi.sh 12345678901 123456 http://10.100.100.2

UA='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36'
BASE_URL="${3:-http://10.100.100.2}"
LOGIN_URL="$BASE_URL/gportal/web/login?has_reload=1"
POST_URL="$BASE_URL/gportal/Web/loginAction"

TMP_DIR="/tmp/giwifi.$$"
COOKIE="$TMP_DIR/cookie.txt"
HTML="$TMP_DIR/login.html"
RAW="$TMP_DIR/raw.txt"
PADDED="$TMP_DIR/padded.bin"
RESP="$TMP_DIR/resp.txt"

cleanup() {
    rm -rf "$TMP_DIR"
}
trap cleanup EXIT INT TERM

mkdir -p "$TMP_DIR" || exit 1

if [ $# -lt 2 ]; then
    echo "Usage: sh $0 <user_account> <user_password> [base_url]" >&2
    exit 1
fi

USER_ACCOUNT="$1"
USER_PASSWORD="$2"

need_cmd() {
    command -v "$1" >/dev/null 2>&1 || {
        echo "missing command: $1" >&2
        exit 1
    }
}

need_cmd wget
need_cmd sed
need_cmd grep
need_cmd openssl
need_cmd hexdump
need_cmd awk
need_cmd tr

log() {
    echo "[giwifi] $*"
}

get_input_value() {
    name="$1"
    sed -n "s/.*name=\"$name\" value=\"\\([^\"]*\\)\".*/\\1/p" "$HTML" | head -n 1
}

urlencode() {
    local s="$1"
    local out=""
    local c
    while [ -n "$s" ]; do
        c="$(printf '%s' "$s" | cut -c1)"
        s="$(printf '%s' "$s" | cut -c2-)"
        case "$c" in
            [a-zA-Z0-9.~_-])
                out="${out}${c}"
                ;;
            ' ')
                out="${out}%20"
                ;;
            *)
                hex="$(printf '%s' "$c" | hexdump -ve '1/1 "%.2x"')"
                while [ -n "$hex" ]; do
                    out="${out}%$(printf '%s' "$hex" | cut -c1-2)"
                    hex="$(printf '%s' "$hex" | cut -c3-)"
                done
                ;;
        esac
    done
    printf '%s' "$out"
}

zeropad_file_16() {
    infile="$1"
    outfile="$2"
    size="$(wc -c < "$infile" | tr -d ' ')"
    rem=$((size % 16))
    cp "$infile" "$outfile"
    if [ "$rem" -ne 0 ]; then
        pad=$((16 - rem))
        i=0
        while [ "$i" -lt "$pad" ]; do
            printf '\000' >> "$outfile"
            i=$((i + 1))
        done
    fi
}

fetch_page() {
    log "fetch login page: $LOGIN_URL"
    wget -q -O "$HTML" \
        --save-cookies "$COOKIE" \
        --keep-session-cookies \
        --header="User-Agent: $UA" \
        "$LOGIN_URL"
}

build_form() {
    sign="$(get_input_value sign)"
    sta_vlan="$(get_input_value sta_vlan)"
    sta_port="$(get_input_value sta_port)"
    sta_ip="$(get_input_value sta_ip)"
    nas_ip="$(get_input_value nas_ip)"
    nas_name="$(get_input_value nas_name)"
    last_url="$(get_input_value last_url)"
    request_ip="$(get_input_value request_ip)"
    device_mode="$(get_input_value device_mode)"
    device_type="$(get_input_value device_type)"
    device_os_type="$(get_input_value device_os_type)"
    is_mobile="$(get_input_value is_mobile)"
    iv="$(get_input_value iv)"
    login_type="$(get_input_value login_type)"
    account_type="$(get_input_value account_type)"

    [ -n "$sign" ] || { echo "failed to parse sign" >&2; exit 1; }
    [ -n "$iv" ] || { echo "failed to parse iv" >&2; exit 1; }
    [ "${#iv}" -eq 16 ] || { echo "bad iv length: ${#iv}" >&2; exit 1; }

    body="sign=$(urlencode "$sign")"
    body="$body&sta_vlan=$(urlencode "$sta_vlan")"
    body="$body&sta_port=$(urlencode "$sta_port")"
    body="$body&sta_ip=$(urlencode "$sta_ip")"
    body="$body&nas_ip=$(urlencode "$nas_ip")"
    body="$body&nas_name=$(urlencode "$nas_name")"
    body="$body&last_url=$(urlencode "$last_url")"
    body="$body&request_ip=$(urlencode "$request_ip")"
    body="$body&device_mode=$(urlencode "$device_mode")"
    body="$body&device_type=$(urlencode "$device_type")"
    body="$body&device_os_type=$(urlencode "$device_os_type")"
    body="$body&is_mobile=$(urlencode "$is_mobile")"
    body="$body&iv=$(urlencode "$iv")"
    body="$body&login_type=$(urlencode "$login_type")"
    body="$body&account_type=$(urlencode "$account_type")"
    body="$body&user_account=$(urlencode "$USER_ACCOUNT")"
    body="$body&user_password=$(urlencode "$USER_PASSWORD")"

    printf '%s' "$body" > "$RAW"
    printf '%s' "$iv" > "$TMP_DIR/iv.txt"
}

encrypt_body() {
    iv_ascii="$(cat "$TMP_DIR/iv.txt")"
    iv_hex="$(printf '%s' "$iv_ascii" | hexdump -ve '1/1 "%.2x"')"
    key_hex="$(printf '%s' '1234567887654321' | hexdump -ve '1/1 "%.2x"')"

    zeropad_file_16 "$RAW" "$PADDED"

    openssl enc -aes-128-cbc -K "$key_hex" -iv "$iv_hex" -nopad -base64 -A \
        -in "$PADDED"
}

do_login() {
    iv="$(cat "$TMP_DIR/iv.txt")"
    enc="$(encrypt_body)"

    log "post login action: $POST_URL"
    wget -q -O "$RESP" \
        --load-cookies "$COOKIE" \
        --keep-session-cookies \
        --header="User-Agent: $UA" \
        --header="X-Requested-With: XMLHttpRequest" \
        --header="Origin: $BASE_URL" \
        --header="Referer: $LOGIN_URL" \
        --header="Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
        --post-data="data=$(urlencode "$enc")&iv=$(urlencode "$iv")" \
        "$POST_URL"

    cat "$RESP"
    echo
}

fetch_page
build_form
do_login

介绍

这是一个能够自动登录GiWifi校园网的shell脚本,在原作者的基础上:

  1. 适配山东科技大学GIWIFI的接口、加密方式
  2. 增加适用于OpenWrt的开机自动运行配置

原作者: TwiceTry

使用

配置

路由器上面需要安装wget-ssl,bash(路由器自带的均为精简版,指令不全,会出现问题)和openssl-util

opkg update
opkg install bash
opkg install wget-ssl
opkg install openssl-util

运行

./giwifi.sh <username> <password> [baseUrl]

示例

chmod +x giwifi_new.sh
./giwifi_new.sh 12345678901 123456 http://10.100.100.2

开机运行

  1. giwifi.sh放到/mnt下,推荐的目录结构如下:
/mnt/giwifi/
 ├─ giwifi_new.sh
 └─ giwifi_log.txt
  1. 修改giwifi中的账号、密码

  2. 移动至/etc/init.d/giwifi

  3. 赋予权限chmod +x /etc/init.d/giwifi

  4. 加入开机自启动/etc/init.d/giwifi enable

  5. 直接运行测试/etc/init.d/giwifi start或重启设备测试reboot

其它

Giwifi加密方式

明文字符串
-> 转成字节
-> 按 16 字节补 0
-> AES-128-CBC
-> Base64

多端设备

根据登录接口来说,可以在登录时伪装giwifi终端类型,此脚本是伪装成PC登录。

实测使用浏览器的开发者选项可以伪装成ipad,但不能伪装成apad

Giwifi:是高贵的苹果人,放行!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐