基於pikachu靶場的水平越權詳解

星海河發表於2024-09-13

1. pikachu靶場搭建

如果你在之前已經使用過phpstudy了,參考pikachu 靶場環境搭建
如果沒有,參考pikachu 靶場搭建
如果在靶場搭建中遇到一些問題,參考皮卡丘靶場搭建遇到的問題大全

2. 水平越權簡介

水平越權是指攻擊者透過獲取與自己擁有相同許可權級別的其他使用者的訪問許可權,從而訪問或操作這些使用者的資源。通常發生在許可權控制不足的場景中,例如,攻擊者在登入系統後,透過修改請求引數或URL,繞過身份驗證機制,訪問他人賬戶的資料。

3. pikachu靶場水平越權黑盒思路

以下為pikachu靶場水平越權的介面,點一下提示,可以看到三個人的使用者名稱與密碼。
不妨假設這麼一個情境:我的名字叫kobe,我擁有一個該網站的賬號,我希望利用該網站存在的水平越權漏洞來檢視lucy和lili的資訊。

先登陸我(kobe)的賬號,點選檢視個人資訊,可以看到我(kobe)自己的資訊。

再點一下檢視個人資訊,並使用burpsuite抓包。可以看到,該網站透過get方式向後端查詢了username=kobe的資料。

GET /pikachu-master/vul/overpermission/op1/op1_mem.php?username=kobe&submit=%E7%82%B9%E5%87%BB%E6%9F%A5%E7%9C%8B%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF HTTP/1.1
Host: pikachu
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;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, br
Connection: keep-alive
Referer: http://pikachu/pikachu-master/vul/overpermission/op1/op1_mem.php?username=kobe&submit=%E7%82%B9%E5%87%BB%E6%9F%A5%E7%9C%8B%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF
Cookie: PHPSESSID=os55u46tgc0km87ksvmp3iisal
Upgrade-Insecure-Requests: 1
Priority: u=0, i

修改包為username=lucy併發包,嘗試探測網站是否存在水平越權。此時,lucy的資訊被我(robe)給越權檢視了。

4. pikachu靶場水平越權白盒原理

在後端程式碼中,未對使用者身份進行校驗,而是直接接收了username並構造sql查詢語句,使得可以透過篡改資料包中的username引數來水平越權檢視其他人的資料。

if(isset($_GET['submit']) && $_GET['username']!=null){
    $username=escape($link, $_GET['username']);
    $query="select * from member where username='$username'";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)==1){
        $data=mysqli_fetch_assoc($result);
        $uname=$data['username'];
        $sex=$data['sex'];
        $phonenum=$data['phonenum'];
        $add=$data['address'];
        $email=$data['email'];
  
        $html.=<<<A
<div id="per_info">
   <h1 class="per_title">hello,{$uname},你的具體資訊如下:</h1>
   <p class="per_name">姓名:{$uname}</p>
   <p class="per_sex">性別:{$sex}</p>
   <p class="per_phone">手機:{$phonenum}</p>    
   <p class="per_add">住址:{$add}</p>
   <p class="per_email">郵箱:{$email}</p>
</div>
A;
    }
}

5. 防護建議

要解決這個問題,可以使用session會話進行身份驗證,並確保只能獲取當前登入使用者的個人資訊,而不是依賴於傳入的使用者名稱引數。

// 啟用會話
session_start();

// 檢查使用者是否已登入,並使用會話中的使用者名稱,而不是透過GET引數傳遞
if(isset($_SESSION['username'])){
    // 獲取當前登入使用者的使用者名稱
    $username = $_SESSION['username'];
    
    // 進行資料庫查詢,確保只能獲取當前登入使用者的資訊
    $username = escape($link, $username);
    $query = "SELECT * FROM member WHERE username='$username'";
    $result = execute($link, $query);
    
    if(mysqli_num_rows($result) == 1){
        $data = mysqli_fetch_assoc($result);
        $uname = $data['username'];
        $sex = $data['sex'];
        $phonenum = $data['phonenum'];
        $add = $data['address'];
        $email = $data['email'];
        
        $html .= <<<A
<div id="per_info">
   <h1 class="per_title">Hello, {$uname}, 你的具體資訊如下:</h1>
   <p class="per_name">姓名: {$uname}</p>
   <p class="per_sex">性別: {$sex}</p>
   <p class="per_phone">手機: {$phonenum}</p>    
   <p class="per_add">住址: {$add}</p> 
   <p class="per_email">郵箱: {$email}</p> 
</div>
A;
    }
} else {
    // 如果沒有登入,則重定向到登入頁面
    header("Location: login.php");
    exit();
}

相關文章