本文轉載自 先知社群:https://xz.aliyun.com/t/2451
經紅日安全審計小組授權。
-----------------------------------------------------------------------------------
本文由紅日安全成員: 七月火 編寫,如有不當,還望斧正。
前言
大家好,我們是紅日安全-代碼審計小組。最近我們小組正在做一個PHP代碼審計的項目,供大家學習交流,我們給這個項目起了一個名字叫 PHP-Audit-Labs 。現在大家所看到的系列文章,屬于項目 第一階段 的内容,本階段的内容題目均來自 PHP SECURITY CALENDAR 2017 。對于每一道題目,我們均給出對應的分析,并結合實際CMS進行解說。在文章的最後,我們還會留一道CTF題目,供大家練習,希望大家喜歡。下面是 第1篇代碼審計文章:
Day 1 - Wish List
題目叫做願望清單,代碼如下:
漏洞解析 :
這一關卡考察的是一個任意檔案上傳漏洞,而導緻這一漏洞的發生則是不安全的使用 in_array() 函數來檢測上傳的檔案名,即上圖中的第12行部分。由于該函數并未将第三個參數設定為 true ,這導緻攻擊者可以通過構造的檔案名來繞過服務端的檢測,例如檔案名為 7shell.php 。因為PHP在使用 in_array() 函數判斷時,會将 7shell.php 強制轉換成數字7,而數字7在 range(1,24) 數組中,最終繞過 in_array() 函數判斷,導緻任意檔案上傳漏洞。(這裡之是以會發生強制類型轉換,是因為目标數組中的元素為數字類型)我們來看看PHP手冊對 in_array() 函數的定義。
in_array :(PHP 4, PHP 5, PHP 7)
功能 :檢查數組中是否存在某個值
定義 :
在 $haystack 中搜尋 $needle ,如果第三個參數 $strict 的值為 TRUE ,則 in_array()函數會進行強檢查,檢查 $needle 的類型是否和 $haystack 中的相同。如果找到 $haystack ,則傳回 TRUE,否則傳回 FALSE。
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
執行個體分析
本次執行個體分析,我們選取的是 piwigo2.7.1 版本。該版本由于SQL語句直接拼接 $rate 變量,而 $rate 變量也僅是用 in_array() 函數簡單處理,并未使用第三個參數進行嚴格比對,最終導緻sql注入漏洞發生。下面我們來看看具體的漏洞位置。漏洞的入口檔案在 include\functions_rate.inc.php 中,具體代碼如下:
當 $_GET['action'] 為 rate 的時候,就會調用檔案 include/functions_rate.inc.php 中的 rate_picture 方法,而漏洞便存在這個方法中。我們可以看到下圖第23行處直接拼接 $rate 變量,而在第2行使用 in_array()函數對 $rate 變量進行檢測,判斷 $rate 是否在 $conf['rate_items'] 中, $conf['rate_items'] 的内容可以在 include\config_default.inc.php 中找到,為
$conf['rate_items'] = array(0,1,2,3,4,5);
由于這裡(上圖第6行)并沒有将 in_array() 函數的第三個參數設定為 true ,是以會進行弱比較,可以繞過。比如我們将 $rate 的值設定成 1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));# 那麼SQL語句就變成:
INSERT INTO piwigo_rate (user_id,anonymous_id,element_id,rate,date)
VALUES (2,'192.168.2',1,1,1 and if(ascii(substr((select
database()),1,1))=112,1,sleep(3)));#,NOW()) ;
複制
這樣就可以進行盲注了,如果上面的代碼你看的比較亂的話,可以看下面簡化後的代碼:
漏洞利用
接下來我們直接用sqlmap進行驗證, payload 如下:
sqlmap -u "http://192.168.2.211/piwigo/picture.php?/1/category/1&action=rate" --data "rate=1" --dbs --batch
複制
修複建議
可以看到這個漏洞的原因是弱類型比較問題,那麼我們就可以使用強比對進行修複。例如将 in_array() 函數的第三個參數設定為 true ,或者使用 intval() 函數将變量強轉成數字,又或者使用正則比對來處理變量。這裡我将 in_array() 函數的第三個參數設定為 true ,代碼及防護效果如下:
結語
看完了上述分析,不知道大家是否對 in_array() 函數有了更加深入的了解,文中用到的CMS可以從 這裡(https://piwigo.org/download/dlcounter.php?code=2.7.1) 下載下傳,當然文中若有不當之處,還望各位斧正。如果你對我們的項目感興趣,歡迎發送郵件到 [email protected] 聯系我們。Day1 的分析文章就到這裡,我們最後留了一道CTF題目給大家練手,題目如下:
//index.php
<?php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("連接配接失敗: ");
}
$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
$whitelist = range(1, $row['COUNT(*)']);
}
$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
if (!in_array($id, $whitelist)) {
die("id $id is not in whitelist.");
}
$result = $conn->query($sql);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
echo "<center><table border='1'>";
foreach ($row as $key => $value) {
echo "<tr><td><center>$key</center></td><br>";
echo "<td><center>$value</center></td></tr><br>";
}
echo "</table></center>";
}
else{
die($conn->error);
}
?>
複制
複制
//config.php
<?php
$servername = "localhost";
$username = "fire";
$password = "fire";
$dbname = "day1";
function stop_hack($value){
$pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
$back_list = explode("|",$pattern);
foreach($back_list as $hack){
if(preg_match("/$hack/i", $value))
die("$hack detected!");
}
return $value;
}
?>
複制
複制
# 搭建CTF環境使用的sql語句
create database day1;
use day1;
create table users (
id int(6) unsigned auto_increment primary key,
name varchar(20) not null,
email varchar(30) not null,
salary int(8) unsigned not null );
INSERT INTO users VALUES(1,'Lucia','[email protected]',3000);
INSERT INTO users VALUES(2,'Danny','[email protected]',4500);
INSERT INTO users VALUES(3,'Alina','[email protected]',2700);
INSERT INTO users VALUES(4,'Jameson','[email protected]',10000);
INSERT INTO users VALUES(5,'Allie','[email protected]',6000);
create table flag(flag varchar(30) not null);
INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}');
複制
複制
題解我們會階段性放出,如果大家有什麼好的解法,可以在文章底下留言,祝大家玩的愉快!