From e8919a2790b654d516e3e2f9bce9073c93e2535f Mon Sep 17 00:00:00 2001 From: "kihwan.song" Date: Mon, 13 Jul 2026 17:38:19 +0900 Subject: [PATCH] =?UTF-8?q?vpn=20base64=20otp=20seed=EB=A5=BC=20raw=20secr?= =?UTF-8?q?et=EC=9C=BC=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect-vpn/action.yml | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/connect-vpn/action.yml b/connect-vpn/action.yml index f66c244..2f94b77 100644 --- a/connect-vpn/action.yml +++ b/connect-vpn/action.yml @@ -28,7 +28,8 @@ inputs: default: "base64" description: > OTP 시드 포맷. base32=구글OTP류 문자열(대문자/공백 자동 정규화), - hex=16진수 문자열, base64=base64로 인코딩된 키. + hex=16진수 문자열, base64=raw secret bytes를 base64로 인코딩한 키, + base64-base32=base32 문자열을 base64로 감싼 키. otp-mode: required: false default: "form" @@ -163,15 +164,9 @@ runs: return base64.b32decode(normalized) if fmt == "base64": - decoded = base64.b64decode(seed) - try: - maybe_base32 = decoded.decode("ascii").replace(" ", "").upper() - if maybe_base32 and all(ch in "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=" for ch in maybe_base32): - raw = decode_base32(maybe_base32) - else: - raw = decoded - except UnicodeDecodeError: - raw = decoded + raw = base64.b64decode(seed) + elif fmt == "base64-base32": + raw = decode_base32(base64.b64decode(seed).decode("ascii")) elif fmt == "base32": raw = decode_base32(seed) elif fmt == "hex": @@ -192,12 +187,12 @@ runs: } build_otp_post_body() { - python3 - "$1" "$2" <<'PY' + python3 - "$1" "$2" "$3" "$4" <<'PY' from html.parser import HTMLParser import sys import urllib.parse - html_file, otp_code = sys.argv[1], sys.argv[2] + html_file, otp_code, action_file, body_file = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] class FormParser(HTMLParser): def __init__(self): @@ -252,7 +247,11 @@ runs: if "vhost" not in seen: data.append(("vhost", "standard")) - print(urllib.parse.urlencode(data)) + action = form["attrs"].get("action") or "/my.policy" + with open(action_file, "w", encoding="utf-8") as f: + f.write(action) + with open(body_file, "w", encoding="utf-8") as f: + f.write(urllib.parse.urlencode(data)) PY } @@ -321,11 +320,18 @@ runs: echo "DEBUG generated OTP offset=${otp_offset}: ${OTP_CODE} (unix-time=$(date +%s))" fi OTP_POST_BODY="$WORKDIR/03-otp-post-${otp_offset}.body" - build_otp_post_body "$WORKDIR/02-otp.html" "$OTP_CODE" > "$OTP_POST_BODY" + OTP_ACTION_FILE="$WORKDIR/03-otp-action-${otp_offset}.txt" + build_otp_post_body "$WORKDIR/02-otp.html" "$OTP_CODE" "$OTP_ACTION_FILE" "$OTP_POST_BODY" + OTP_ACTION="$(cat "$OTP_ACTION_FILE")" + case "$OTP_ACTION" in + http://*|https://*) OTP_URL="$OTP_ACTION" ;; + /*) OTP_URL="$VPN_HOST$OTP_ACTION" ;; + *) OTP_URL="$VPN_HOST/$OTP_ACTION" ;; + esac rm -f "$TOKEN_HEADERS" "$WORKDIR/04-token.body" curl_common -L -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ - -X POST "$VPN_HOST/my.policy" \ + -X POST "$OTP_URL" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-binary "@$OTP_POST_BODY" \ -o "$WORKDIR/03-webtop-${otp_offset}.html"