Skip to content

Instantly share code, notes, and snippets.

@nevikw39
Last active October 21, 2022 08:34
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save nevikw39/d7a08f637919348106f6a35d35a62d3f to your computer and use it in GitHub Desktop.
Save nevikw39/d7a08f637919348106f6a35d35a62d3f to your computer and use it in GitHub Desktop.
ptt IP

ptt IP

Detect whether an IP address on ptt belong to VPN or proxy networks

一款 userscript, 令您只要雙擊選取 pttIP 便可即時取得相關資訊.

適用於 www.ptt.cc, term.ptt.cc.

安裝步驟

  1. 首先安裝管理 userscript 的 extension. 您可以移駕至您瀏覽器的商店搜尋 userscript. 如果您使用 Chrome, 我推薦 TamperMonkey.
  2. 接著請點擊這個連結以安裝 userscript.
  3. 至此已完成惹,但首次點擊時,您可能必須允許 userscript 連線到 spur.us, 以俾提供 IP 相關資訊。

判斷結果

spur.us 的判斷結果如下:

  • XXX VPN: 很高機率就是 VPN
  • Open Proxy: 很高機率就是開放式 Proxy
  • Likely VPN/Proxy: 有可能是 VPN 或 Proxy
  • Residential/Call-back Proxy: 如上述,這個感覺怪怪的,有可能是誤判,可能須根據上線裝置數、port status 進一步判斷
  • Not Anonymous: 正常使用者

應用畫面

  • 抓到 VPN 跳板使用者惹!!
  • 這次是 Proxy 跳板呢
  • 一般使用者的顯示方式
  • 推文中的 IP 也不要放過喔 o'_'o

假使偵測到跳板 IP, 則在浮動提示出現時點擊左鍵會彈出對話方塊詢問是否要前往 spur.us 查看詳細資料。

注意事項

  • 由於 term.ptt.cc 會將不同頁、不同文章的內容放在相同的 element, 導致之前的浮動提示殘留在新的頁面。因此請務必特別注意選取的 IP 與浮動提示中的 IP 是否一致,以免鬧烏龍。面臨這種狀況時,可以按下 Crtl + Alt + D (macOS: Cmd + Alt + D) 刪除所有浮動提示實例。
  • IP 資訊來源為 spur.us, 資訊僅供參考,無法保證必定正確
  • 使用 VPN, Proxy 其實也不是甚麼滔天大罪,敝 userscript 旨在幫助使用者揪出惡意使用跳板以發布相關政治等內容的行為。我也曾使用過前揭相關服務,有時或許真的逼不得已必須使用,不過此時我會避免發文。因此,我想呼籲正常的跳板使用者,合理地揭露相關資訊。

另外,我在八卦板閒逛時,發現有些位於台灣的 IP 或被認為是 Likely VPN/Proxy, Residential/Call-Back Proxy. 而對於後者 spur.us 是這麼說的:

X.X.X.X proxies traffic for residential or call-back proxy networks. The owner of X.X.X.X is likely unaware of this activity... This means legitimate devices are unwittingly routing automated activity through their internet connection. This is likely a mix of anonymous activity and normal activity.

原先我追查跳板使用者皆以境外 IP 為目標,台灣 IP 我都認為應該正常無事。對於這樣的狀況,我其實持保留的態度。

為求謹慎我嘗試用 nmap 近一步掃描 ports, 大部分這類 IP 都得到 Host seems down 的結果,但不過真的有一個被我掃出來 port 1723 是開放的,即很有可能正運行 PPTP VPN. 總之,這一切變得真是耐人尋味啊!!至於實際真相就留給大家自行評判惹。

顯然這些所謂的跳板使用者終究只是少數,但希望各位鄉民們能提高警覺。

何謂 userscript

userscript 是指使用者對於各網站所自定義的 script, 如同一般使用者比較熟悉的 extension, 可以提供該網站許多額外附加功能。不同的是,userscript 相較之下往往更簡短、單純而方便開發且能輕易地跨瀏覽器。

倘若敝 userscript 能收到不錯的回響,我或會考慮將之開發為 extension.

關於 spur.us

spur.us 是一個提供十分詳盡的 IP 資訊的網站。網路上提供 IP 資訊的網站為數不少,但其最為直觀與美觀。起初我之所以接觸到此服務,是因為在追查桶尼的分身 (xampp, nini680102, kiki1215, gnilrad, yellow450807, xiaoaili, housebetty, ... 族繁不及備載) 時,他都使用 ExpressVPN.

如有其他好用的 API, 請不吝於底下 comment 推薦,感謝。

權限說明

selectionchange 事件為核心。


這裡很多截圖,是我之前很閒的時候稽查八卦板的紀錄

後來乾脆直接貼網址,以後應該會有更多人一起打擊惡意跳板仔

// ==UserScript==
// @name ptt IP
// @namespace https://nevikw39.cf/
// @version 0.2.1
// @description Detect whether an IP address on ptt belong to VPN or proxy networks
// @author nevikw39
// @match https://*.ptt.cc/*
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// @grant GM_openInTab
// @require https://unpkg.com/@popperjs/core@2
// @require https://unpkg.com/tippy.js@6
// @homepageURL https://gist.github.com/nevikw39/d7a08f637919348106f6a35d35a62d3f
// @downloadURL https://gist.githubusercontent.com/nevikw39/d7a08f637919348106f6a35d35a62d3f/raw/ptt_ip.user.js
// ==/UserScript==
/**
* Permissions:
* GM_xmlhttpRequest: To connect to spur.us in order to get the detail about the IP address
* GM_setClipboard: To paste IP address to clipboard
* GM_openInTab: To open spur.us in new tab for further details
* https://unpkg.com/tippy.js@6 A JavaScript library that provides tooltip
* https://unpkg.com/@popperjs/core@2 Dependency of Tippy.js
*/
(function() {
'use strict';
// Create CSS style for IPs which are suspicious
document.head.appendChild(document.createElement("style")).innerHTML = `
.tippy-box[data-theme~='suspicious'] {
background-color: #E9693D;
color: white;
}`
document.head.appendChild(document.createElement("style")).innerHTML = `
.tippy-box[data-theme~='residential'] {
background-color: #F0E87D;
color: gray;
}`
// Create DOMParser instance so that we can get the title fetch from spur.us
const parser = new window.DOMParser()
// Store Tippy.JS instances so that we can destroy them manually
let instances = []
// Watch whether the selection changes
document.addEventListener('selectionchange', () => {
const select = document.getSelection()
const txt = select.toString().trim()
const url = "https://spur.us/context/" + txt
const node = select.baseNode.parentNode // If use `baseNode` directly, it would become a `string`, quite ridiculous
if (/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/.test(txt)) {
// Well, I would like to use Fetch API indeed
GM_xmlhttpRequest({
url: url,
onload: r => {
// We only need title to determine whether an IP address is likely to be anonymous
const title = parser.parseFromString(r.responseText, "text/html").title
tippy([node], {
content: title,
followCursor: true,
theme: (title.includes("esidential") || title.includes("ikely")) ? "residential" : (title.includes("VPN") || title.includes("roxy")) ? "suspicious" : "",
}).forEach(i => { i.show(); instances.push(i); })
// Event should be watched after XHR is done, lest tab opens while each selection
if (title.includes("VPN") || title.includes("roxy"))
node.onclick = () => {
if (confirm("Open spur.us Context in New Tab for More Detail??"))
GM_openInTab(url)
}
},
})
GM_setClipboard(txt)
}
})
// Manually destroy all Tipp.JS instances
document.addEventListener('keydown', e => {
if (e.ctrlKey && e.altKey && e.key === 'd') {
instances.forEach(i => i.destroy())
instances = []
}
})
// document.body.innerHTML = document.body.innerHTML.replace(/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/, `<a href="https://spur.us/context/$1", target="_blank">$1</a>`)
})();
@allmwh
Copy link

allmwh commented Jun 10, 2021

安安 打球囉^^
很好用 謝謝拉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment