
Cendertron,動態爬蟲與敏感信息泄露檢測
Cendertron = Crawler + Rendertron
Cendertron https://url.wx-coder.cn/HinPM 是基于 Puppeteer 的 Web 2.0 動態爬蟲與敏感信息泄露檢測工具。其依托于 xe-crawler 的通用爬蟲、調度與緩存模型,新增了 Monkey Test 以及 Request Intercept 等特性,以期盡可能多地挖掘頁面與請求。同時針對滲透測試的場景,Cendertron 內置了目錄掃描、敏感文件掃描的能力,能夠模擬用戶實際在瀏覽器登錄狀態下的自定義字典爆破。Cendertron 在大量實踐的基礎上設置了自身的去重策略,能夠盡可能地避免重復爬取,加快掃描速度。Cendertron 同時也是正在閉源開發的 Chaos-Scanner 模塊化安全掃描解決方案的一部分,為基礎掃描與智能掃描提供前置輸入。

Usage | 使用
Locally Development | 本地開發
在本地開發中,我們只需要如正常的 Node 項目一樣啟動,其會使用 Puppeteer 內置的 Headless Chrome 來執行界面渲染操作:
$ git clone https://github.com/wx-chevalier/Chaos-Scanner
$ cd cendertron
$ yarn install
$ npm run dev
啟動之后可以按提示打開瀏覽器界面:

這里我們可以以 DVWA 作為測試目標,在輸入框內輸入 http://localhost:8082/
然后執行爬取,即可得到如下結果:
{"isFinished": true,"metrics": {"executionDuration": 116177,"spiderCount": 51,"depth": 4},"spiderMap": {"http://localhost:8082/vulnerabilities/csrf/": [{"url": "http://localhost:8082/vulnerabilities/view_source.php?id=csrf&security=low","parsedUrl": {"host": "localhost:8082","pathname": "/vulnerabilities/view_source.php","query": {"id": "csrf","security": "low"}},"hash": "localhost:8082#/vulnerabilities/view_source.php#idsecurity","resourceType": "document"}// ...]}
}
需要說明的是,因為 DVWA 是需要登錄后爬取,因此如果想進行完整的測試請參考下文的 POST 方式創建任務。
Deploy in Docker | 部署在 Docker 中
# build image
$ docker build -t cendertron .# run as contaner
$ docker run -it --rm -p 3033:3000 --name cendertron-instance cendertron# run as container, fix with Jessie Frazelle seccomp profile for Chrome.
$ wget https://raw.githubusercontent.com/jfrazelle/dotfiles/master/etc/docker/seccomp/chrome.json -O ~/chrome.json
$ docker run -it -p 3033:3000 --security-opt seccomp=$HOME/chrome.json --name cendertron-instance cendertron# or
$ docker run -it -p 3033:3000 --cap-add=SYS_ADMIN --name cendertron-instance cendertron# use network and mapping logs
$ docker run -d -p 3033:3000 --cap-add=SYS_ADMIN --name cendertron-instance --network wsat-network cendertron
Deploy as FC | 以函數式計算方式部署
Install cendertron from NPM:
# set not downloading chromium
$ PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true$ yarn add cendertron
# or
$ npm install cendertron -S
Import Crawler
and use in your code:
const crawler = new Crawler(browser, {onFinish: () => {callback(crawler.spidersRequestMap);}
});let pageUrl =evtStr.length !== 0 && evtStr.indexOf('{') !== 0? evtStr: 'https://www.aliyun.com';crawler.start(pageUrl);
If you want to use it in Alibaba Function Computing Service, cendertron-fc provides simple template.
Strategy | 策略
Cendertron 的內部架構如下所示:

Crawler Scheduler 會負責定期重啟 Headless Chrome 以控制緩存,并且針對待爬取的請求返回已緩存的內容。Crawler Scheduler 會為每個無緩存的目標創建 Crawler,Crawler 會根據策略創建不同的 Spider,每個 Spider 依次執行并且將結果推送到 Crawler 中;Crawler 在全部 Spider 執行完畢后會將結果推送到緩存并且通知 Crawler Scheduler:
export interface CrawlerOption {// 爬取深度,如果設置為 1 就是單頁面爬蟲depth: number;// 爬蟲的唯一編號uuid?: string;// 爬蟲緩存crawlerCache?: CrawlerCache;// 單頁面爬取出的最多的子節點數maxPageCount: number;// 總站點的總延時timeout: number;// 單頁面的延時pageTimeout: number;// 是否僅爬取同站內容isSameOrigin: boolean;// 是否忽略媒體資源isIgnoreAssets: boolean;// 是否設置為移動模式isMobile: boolean;// 是否開啟緩存useCache: boolean;// 是否使用弱口令掃描useWeakfile: boolean;// 頁面 Cookiecookie: string;// 頁面的 localStoragelocalStorage: object;
}
模擬操作

Cendertron 內置了 Click Monkey, Gremlins 等多種隨機執行器,會點擊按鈕并且執行一些隨機操作:
function initGermlins() {gremlins.createHorde().gremlin(gremlins.species.formFiller()).gremlin(gremlins.species.clicker().clickTypes(['click'])).gremlin(gremlins.species.toucher()).gremlin(gremlins.species.scroller()).gremlin(function() {if ('$' in window) {window.$ = function() {};}}).unleash();
}
請求監聽與提取
Cendertron 會監聽打開的頁面與所有的 Ajax 請求:
await page.setRequestInterception(true);// 設置目標監聽
const targetCreatedListener = (target: puppeteer.Target) => {const opener = target.opener();if (!opener) {return;}// 記錄所有新打開的界面opener.page().then(_page => {if (_page === page) {target.page().then(_p => {if (!_p.isClosed()) {openedUrls.push(target.url());_p.close();}});}});
};// 監聽所有當前打開的頁面
browser.on('targetcreated', targetCreatedListener);page.on('request', interceptedRequest => {// 屏蔽所有的圖片if (isMedia(interceptedRequest.url())) {interceptedRequest.abort();} else if (interceptedRequest.isNavigationRequest() &&interceptedRequest.redirectChain().length !== 0) {interceptedRequest.continue();} else {interceptedRequest.continue();}requests.push(transformInterceptedRequestToRequest(interceptedRequest));// 每次調用時候都會回調函數cb(requests, openedUrls, [targetCreatedListener]);
});
URL 歸一化與過濾
所謂的 URL 歸一化,就是將同一資源下被隨機串處理的 Path 們泛化成同一個 Pattern,從而減少重復爬取的數目;當然,在安全掃描的場景下我們需要進行盡可能地去重,而在數據爬取的場景下,則往往不需要進行過多的過濾。目前 Cendertron 只是采取了簡單的 UTL 歸一化算法,并且使用 Set 進行過濾,如果你想了解更復雜的 URL 歸一化與聚類算法,可以參考自然語言處理 https://url.wx-coder.cn/JcINy 或者哈希表實戰 https://url.wx-coder.cn/WfdWP 中的關聯章節。
export function hashUrl(url: string): string {// 將 URL 進行格式化提取const _parsedUrl = parse(url, url, true);let urlHash = '';if (!_parsedUrl) {return urlHash;}// 提取出 URL 中的各個部分const { host, pathname, query, hash } = _parsedUrl;// 判斷是否存在查詢參數const queryKeys = Object.keys(query).sort((k1, k2) => (k1 > k2 ? 1 : -1));if (queryKeys.length > 0) {// 如果存在查詢參數,則默認全路徑加查詢參數進行解析urlHash = `${host}#${pathname}#${queryKeys.join('')}`;} else {// 如果不存在查詢參數,則去除 pathname 的最后一位,并且添加進來const pathFragments = pathname.split('/');// 判斷路徑是否包含多個項目,如果包含,則將所有疑似 UUID 的替換為 IDif (pathFragments.length > 1) {urlHash = `${host}#${pathFragments.filter(frag => frag.length > 0).map(frag => (maybeUUID(frag) ? 'id' : frag)).join('')}`;} else {urlHash = `${host}#${pathname}`;}}if (hash) {const hashQueryString = hash.replace('#', '');const queryObj = parseQuery(hashQueryString);Object.keys(queryObj).forEach(n => {if (n) {urlHash += n;}});}return urlHash;
}
權限認證
以 DVWA 為例,可以用 Docker 快速開啟測試環境:docker run --rm -it -p 8082:80 vulnerables/web-dvwa
,然后向 /scrape
提交 POST 請求:
{"url": "http://localhost:8082/vulnerabilities/csrf/","cookies": "tid=xx; PHPSESSID=xx; security=low"
}
在 Cendertron 中,其會使用如下方式設置 Cookie:
const puppeteer = require('puppeteer');let rockIt = async () => {const browser = await puppeteer.launch({ headless: false });const page = await browser.newPage();var cookie = [// cookie exported by google chrome plugin editthiscookie{domain: 'httpbin.org',expirationDate: 1597288045,hostOnly: false,httpOnly: false,name: 'key',path: '/',sameSite: 'no_restriction',secure: false,session: false,storeId: '0',value: 'value!',id: 1}];await page.setCookie(...cookie);await page.goto('https://httpbin.org/headers');console.log(await page.content());await page.close();await browser.close();
};
rockIt();
未來也會支持 localStorage 等存儲方式:
await page.evaluate(() => {localStorage.setItem('token', 'example-token');
});