對于php代碼審計我也是從0開始學的,對學習程序進行整理輸出沉淀如有不足歡迎提出共勉,對學習能力有較高要求,整個系列主要是在作業中快速精通php代碼審計,整個學習周期5天 ,建議花一天時間熟悉php語法,
我是直接phpstudy2019一鍵搭建小白首選
所謂工欲善其事必先利其器,找一款適合自己的php開發工具自行百度搭建,我用的是phpstrom .
初級篇主要講
1. php歷史版本漏洞,
2. 變數配置缺陷,
3. 函式缺陷,
4. 弱語言缺陷,
PHP配置漏洞
我們要熟知php各版本漏洞
Register_globals 全域變數注冊開關
在該選項為on情況下,會直接把用戶GET,POST等方式提交的引數注冊成全域變數并初始化值為引數對應的值
Php小于4.2.3默認打開 5.3廢棄 5.4移除
Allow_url_include 是否允許包含遠程檔案
Php 5.2 后默認設定off
Magic_quotes_gpc 魔術引號自動過濾
會自動在get post,cookie 變數對單引號,雙引號,反斜杠,空字符的前面加反斜杠
缺陷不會過濾$_SERVER 變數
Php5.3后廢棄,5.4移除 小于4.2.3 默認打開
Magic_quotes_runtime 魔術引號自動過濾
和上面一樣 卻是對從資料庫取出的資料進行轉義 防止二次注入 同樣在5.4取消
Safe_mode 安全模式
在開啟時做命令執行操作會提示執行命令失敗,和一些敏感操作. 可以查看配置是否開啟,
Open_basedir 可訪問目錄
默認web目錄,查看配置是否出現全目錄可訪問情況
Disable_functions 禁用函式
禁用敏感函式如exec 可以通過dl()函式來繞過自定義php擴展方法實作命令執行,這個函式并不安全,
Display_errors 錯誤顯示
生產環境關閉錯誤回顯 display_errors=off
PHP漏洞函式
變數覆寫
extract()
該函式使用陣列鍵名作為變數名,使用陣列鍵值作為變數值,針對陣列中的每個元素,將在當前符號表中創建對應的一個變數,條件:若有EXTR_SKIP則不行,
<?php $a = "Original"; $my_array = array("a" => "Cat", "b" => "Dog", "c" => "Horse"); extract($my_array); echo "$a = $a; $b = $b; $c = $c"; ?> # 結果:$a = Cat; $b = Dog; $c = Horse
parse_str()
決議字串并注冊成變數
$b=1; Parse_str('b=2'); Print_r($b); # 結果: $b=2
import_request_variables()
將 GET/POST/Cookie 變數匯入到全域作用域中,全域變數注冊,在5.4之后被取消,只可在4-4.1.0和5-5.4.0可用,
//匯入POST提交的變數值,前綴為post_ import_request_variable("p", "post_"); //匯入GET和POST提交的變數值,前綴為gp_,GET優先于POST import_request_variable("gp", "gp_"); //匯入Cookie和GET的變數值,Cookie變數值優先于GET import_request_variable("cg", "cg_");
$$變數覆寫
## 提交引數chs,則可覆寫變數"$chs"的值,$key為chs時,$$key就變成$chs
<? $chs = ''; if($_POST && $charset != 'utf-8'){ $chs = new Chinese('UTF-8', $charset); foreach($_POST as $key => $value){ $$key = $chs->Convert($value); } unset($chs); }
繞過過濾的空白字符
" "%00" (ASCII 0 (0x00)),空位元組符, 制表符 "\t" (ASCII 9 (0x09)),水平制表符, 空白字符: "\n" (ASCII 10 (0x0A)),換行符, "\v" "\x0b" (ASCII 11 (0x0B)),垂直制表符, "\f" "%0c" 換頁符 "\r" "%0d"(ASCII 13 (0x0D)),回車符, 空格: " " "%20" (ASCII 32 (0x20)),普通空格符,
Trim 函式過濾 \t\n\r 缺少了\f(%0c)
從而繞過%0c過濾
截斷
Iconv 例外字符截斷問題
## 因iconv遇到例外字符就不轉后面的內容了,所以可以截斷, ## 這里chr(128)到chr(255)都可以截斷, $a='1'.char(130).'2'; echo iconv("UTF-8","gbk",$a); //將字串的編碼從UTF-8轉到gbk echo iconv('GB2312', 'UTF-8', $str); //將字串的編碼從GB2312轉到UTF-8
eregi、ereg可用%00截斷
功能:正則匹配過濾 條件:要求php<5.3.4可以把非法的資料放在%00后面進行繞過我們可以看一個ctf題就是考察了這個知識
## http://127.0.0.1/Php_Bug/05.php?password=1e9%00*-*
#GET方式提交password,然后用ereg()正則限制了password的形式,只能是一個或者多個數字、大小寫字母,繼續strlen()限制了長度小于8并且大小必須大于9999999,繼續strpos()對password進行匹配,必須含有-,最終才輸出flag
#因為ereg函式存在NULL截斷漏洞,導致了正則過濾被繞過,所以可以使用%00截斷正則匹配,
#對于另一個難題可以使用科學計數法表示,計算器或電腦表達10的的冪是一般是e,也就是1.99714e13=19971400000000,所以構造 1e8 即 100000000 > 9999999,在加上-,于是乎構造password=1e8%00*-*,成功得到答案
<?php if (isset ($_GET['password'])) { if (ereg ("^[a-zA-Z0-9]+$",$_GET['password']) === FALSE) { echo '<p>You password must be alphanumeric</p>'; } else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999) { if (strpos ($_GET['password'], '*-*') !== FALSE) { die('Flag: ' . $flag); } else { echo('<p>*-* have not been found</p>'); } } else { echo '<p>Invalid password</p>'; } }
move_uploaded_file 用\0截斷
5.4.x<= 5.4.39, 5.5.x<= 5.5.23, 5.6.x <= 5.6.7 原來在高版本(受影響版本中),PHP把長度比較的安全檢查邏輯給去掉了,導致了漏洞的發生
獲取檔案名不一致%00 \0 導致截斷后面的檔案名
inclue用?截斷
<?php $name=$_GET['name']; $filename=$name.'.php'; include $filename; ?>
當輸入的檔案名包含URL時,問號截斷則會發生,并且這個利用方式不受PHP版本限制,原因是Web服務其會將問號看成一個請求引數, 測驗POC: http://127.0.0.1/test/t1.php?name=http://127.0.0.1/test/secret.txt? 則會打開secret.txt中的檔案內容,本測驗用例在PHP5.5.38版本上測驗通過,
系統長度截斷
這種方式在PHP5.3以后的版本中都已經得到了修復, win260個字符,linux下4*1024=4096位元組
mysql長度截斷
mysql內的默認字符長度為255,超過的就沒了, 由于mysql的sql_mode設定為default的時候,即沒有開啟STRICT_ALL_TABLES選項時,MySQL對于插入超長的值只會提示warning
mysql中utf-8截斷
insert into dvwa.test values (14,concat("admin",0xc1,"abc"))寫入為admin
弱型別比較
以下等式會成立
'' == 0 == false '123' == 123 'abc' == 0 '123a' == 123 '0x01' == 1 '0e123456789' == '0e987654321' [false] == [0] == [NULL] == [''] NULL == false == 0 true == 1
==、>、<的弱型別比較
這里用到了PHP弱型別的一個特性,當一個整形和一個其他型別行比較的時候,會先把其他型別轉換成整型再比,
##方法1 ##$a["a1"]="1e8%00"; ##這里用%00繞過is_numeric,然后1e8可以比1336大,因此最后能$v1=1 ##方法2 ##$a["a1"]=["a"]; ##使用陣列,可以,因為陣列恒大于數字或字串 ##方法3 ##$a["a1"]=1337a; ##1337a過is_numeric,又由>轉成1337與1336比較 <?php is_numeric(@$a["a1"])?die("nope"):NULL; if(@$a["a1"]){ var_dump($a); ($a["a1"]>1336)?$v1=1:NULL; } var_dump($v1);
switch 弱型別
// 第一種:弱型別,1e==1 // $x1=1e // 第二種:利用陣列名字bypass // $x1=1[] // 傳入后為string(3) "1[]",但在switch那里為1 if (isset($_GET['x1'])) { $x1 = $_GET['x1']; $x1=="1"?die("ha?"):NULL; switch ($x1) { case 0: case 1: $a=1; break; } }
md5比較(0e相等、陣列為Null)
md5('240610708') //0e462097431906509019562988736854 md5('QNKCDZO') //0e830400451993494058024219903391 0e 純數字這種格式的字串在判斷相等的時候會被認為是科學計數法的數字,先做字串到數字的轉換, md5('240610708')==md5('QNKCDZO'); //True md5('240610708')===md5('QNKCDZO'); //False 這樣的對應數值還有: var_dump(md5('240610708') == md5('QNKCDZO')); var_dump(md5('aabg7XSs') == md5('aabC9RqS')); var_dump(sha1('aaroZmOk') == sha1('aaK1STfY')); var_dump(sha1('aaO8zKZF') == sha1('aa3OFF9m')); var_dump('0010e2' == '1e3'); var_dump('0x1234Ab' == '1193131'); var_dump('0xABCdef' == ' 0xABCdef');
可以用于登錄判斷
240610708
技巧:找出在某一位置開始是0e的,并包含“XXX”的字串
Md5 加密陣列都是空 可以利用繞過
json傳資料{“key”:0}
PHP將POST的資料全部保存為字串形式,也就沒有辦法注入數字型別的資料了而JSON則不一樣,JSON本身是一個完整的字串,經過決議之后可能有字串,數字,布爾等多種型別,
第一個application/x-www-form-urlencoded,是一般表單形式提交的content-type第二個,是包含檔案的表單,第三,四個,分別是json和xml,一般是js當中上傳的.
{“key”:”0″}
這是一個字串0,我們需要讓他為數字型別,用burp攔截,把兩個雙引號去掉,變成這樣:
{“key”:0}
十六進制與十進制比較
== 兩邊的十六進制與十進制比較,是可以相等的,
至此已經介紹完了php常規函式缺陷,版本漏洞,語言特性導致的漏洞,需要熟記這些缺陷的利用方法,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/136639.html
標籤:PHP
