PHP代碼安全雜談

2022-01-30 FreeBuf

雖然PHP是世界上最好的語言,但是也有一些因為弱類型語言的安全性問題出現。WordPress歷史上就出現過由於PHP本身的缺陷而造成的一些安全性問題,如CVE-2014-0166 中的cookie偽造就是利用了PHP Hash比較的缺陷。 當然一般這種情況實戰中用到的不是很多,但是在CTF競賽中卻是一個值得去考察的一個知識點,特此記錄總結之。

一、精度繞過缺陷理論

在用PHP進行浮點數的運算中,經常會出現一些和預期結果不一樣的值,這是由於浮點數的精度有限。儘管取決於系統,PHP 通常使用 IEEE 754 雙精度格式,則由於取整而導致的最大相對誤差為 1.11e-16。非基本數學運算可能會給出更大誤差,並且要考慮到進行複合運算時的誤差傳遞。下面看一個有趣的例子:

以十進位能夠精確表示的有理數如 0.1 或 0.7,無論有多少尾數都不能被內部所使用的二進位精確表示,因此不能在不丟失一點點精度的情況下轉換為二進位的格式。這就會造成混亂的結果:例如,floor((0.1+0.7)*10) 通常會返回 7 而不是預期中的 8,因為該結果內部的表示其實是類似 7.9999999999999991118…。

實踐

問鼎杯2017 老眼昏花網上很多write-up感覺就像是看著答案寫write-up,個人感覺真正的write-up中應該體現自己的思考在裡面。

題目描述

題目言簡意賅,讓我們把2017這個值傳遞給伺服器。

考察點write-up

what year is this?所以第一反應是直接給year參數賦值為2017:

?year=2017

然而結果如下:

有提示了,說明year這個參數是對的,但是2017中不可以出現7,這裡如果不了解php精度的話,肯定是對2017進行各種編碼繞過,但是這裡對編碼也進行過濾了:


所以最後一種可能就是利用PHP精度來繞過:

?year=2016.99999999999

二、類型轉換的缺陷理論

PHP提供了is_numeric函數,用來變量判斷是否為數字。PHP弱類型語言的一個特性,當一個整形和一個其他類型行比較的時候,會先把其他類型intval數位化再比。

實踐

is_numeric()用於判斷是否是數字,通常配合數值判斷。

案例代碼

考察點write-up

分析下代碼:首先對GET方式提交的參數id的值進行檢驗。id通過is_numeric函數來判斷是否為數字,如果為數字的話,GG。如果不是數字的話,和665進行比較,id的值大於665的時候輸出flag。
乍看上去又好像不可能這裡,但是如果知道PHP弱類型語言的一個特性,當一個整形和一個其他類型行比較的時候,會先把其他類型intval數位化再比。這個特性的話就可以很好的繞過。

http://localhost/?id=666gg

三、鬆散比較符的缺陷理論

php比較相等性的運算符有兩種,一種是嚴格比較,另一種是鬆散比較。

如果比較一個數字和字符串或者比較涉及到數字內容的字符串,則字符串會被轉換成數值並且比較按照數值來進行


嚴格比較符嚴格比較符,會先判斷兩種字符串的類型是否相等,再比較。

===   //全等!==   //不全等

鬆散比較符鬆散比較符,會先將字符串類型轉換成相同,再比較。

==   //等於!=   //不等


PHP 會根據變量的值,自動把變量轉換為正確的數據類型。這一點和C 和 C++ 以及 Java 之類的語言明顯不同。雖然這樣PHP方便了程式設計師,但是隨之而來卻會帶來一些安全性的問題。

一個簡單的例子

由於php對變量自動轉換的特性,這裡面的

$a==$b 與 $c==$d 均為真

所以頁面輸出的結果為:

一個深入的例子

下面結合PHP 相等性比較缺陷再解釋下會好懂一點:

var_dump(0=="gg");  //truevar_dump(0==="gg"); //falsevar_dump(1=="gg");  //false

0與gg進行鬆散性質的不嚴格比較,會將gg轉換為數值,強制轉換,由於gg是字符串,轉化的結果是0,所以 輸出 true

0與gg進行嚴格 性質的嚴格比較,這裡的gg是字符串類型,和int類型的0不相等,所以輸出 false

0與gg進行鬆散性質的不嚴格比較,會將gg轉換為數值,強制轉換,由於gg是字符串,轉化的結果是0,不等於1,所以輸出 false

var_dump(1=="1gg"); //true var_dump(1=="gg1"); //false

1與1gg進行鬆散性質的不嚴格比較,這裡1gg被強制轉換為int類型的時候會從字符串的第一位開始做判斷進行轉換,這裡的1gg第一位是1,所以這裡1gg被轉換為1,所以輸出 true

1與gg1進行嚴格 性質的嚴格比較,字符串gg1的第一位不是數字,所以它被強制轉換為0,所以輸出 false

var_dump("0e123" == "0e456");  //truevar_dump("0e123" == "0eabc");  //flase

這裡比較特殊,字符串中出現了0e,PHP手冊介紹如下:

當一個字符串欸當作一個數值來取值,其結果和類型如下:如果該字符串沒有包含'.','e','E'並且其數值值在整形的範圍之內 該字符串被當作int來取值,其他所有情況下都被作為float來取值, 該字符串的開始部分決定了它的值,如果該字符串以合法的數值開始,則使用該數值,否則其值為0。

實踐

md5繞過(Hash比較缺陷)南京郵電大學網絡攻防訓練平臺中一道比較經典的md5 collision題,關於這道題目的WriteUp網上很多,但是真正深入分析的少之又少~~

題目描述

md5 collision源碼

考察點

簡單的PHP代碼審計

PHP弱類型的Hash比較缺陷

write-up

從源碼中可以得輸入一個a的參數的變量,a首先不等於QNKCDZO並且a得md5值必須等於QNKCDZO加密後的md5值。 乍一看好像不可能存在這樣的值,但是這裡QNKCDZO加密後的md5值為0e830400451993494058024219903391 這裡是0e開頭的,在進行等於比較的時候,PHP把它當作科學計數法,0的無論多少次方都是零。 所以這裡利用上面的弱類型的比較的缺陷來進行解題:?a=s155964671a

字符串加密後md5為0exxxx的字符串(x必須是10進位數字)列表

| 字符串
| md5
|
| —- | —- |
| QNKCDZO
| 0e830400451993494058024219903391
|
| 240610708
| 0e462097431906509019562988736854
|
| aabg7XSs | 0e087386482136013740957780965295 |
| aabC9RqS | 0e041022518165728065344349536299 |
| s878926199a | 0e545993274517709034328855841020 |

四、sha1() md5()加密函數漏洞缺陷理論

md5()和sha1()對一個數組進行加密將返回 NULL

實踐

Boston Key Party CTF 2015: Prudential

題目描述

I dont think sha1 isbroken.Prove me wrong.

題目給了一個登陸框:

考察點write-up

原始碼給出如下:

分析一下核心登錄代碼如下:

GET類型提交了兩個欄位name和password,獲得flag要求的條件是:

這個乍看起來這是不可能的,但是這裡利用sha1()函數在處理數組的時候由於無法處理將返回NULL可以繞過if語句的驗證,if條件成立將獲得flag。 構造語句如下:

?name[]=a&password[]=b

這裡符合了2個拿到flag的條件:

拿到flag: I_think_that_I_just_broke_sha1

拓展總結

經過驗證,不僅sha1()函數無法處理數組,這裡md5()函數也有同樣的問題,在處理數組的時候,都將返回NULL
測試代碼如下:

這裡面的核心代碼如下:

同樣利用md5()函數無法處理數組的這個漏洞,構造get請求拿到flag:

?username[]=a&password[]=b

五、字符串處理函數漏洞缺陷理論

用法如下:

int strcmp ( string $str1 , string $str2 )

具體的用法解釋如下:

參數 `str1`第一個字符串。參數 `str2`第二個字符串。如果 `str1` 小於 `str2` 返回 `< 0`;如果 `str1` 大於 `str2` 返回 `> 0`;如果兩者相等,返回 0。

這個函數接受到了不符合的類型,例如數組類型,函數將發生錯誤。但是在5.3之前的php中,顯示了報錯的警告信息後,將return 0 !!!! 也就是雖然報了錯,但卻判定其相等了。

這2個函數都是用來處理字符串的,但是在傳入數組參數後都將返回NULL。

實踐

Boston Key Party CTF 2015: Northeastern Univ

題目描述

Of course, a timing attack might be the answer, but Im quite sure that you can do better than that. 題目給了一個登陸框:

考察點write-up

給出原始碼如下:

分析一下核心登錄代碼如下:

if (strcmp($_GET['password'], $flag) == 0)

這裡使用了==鬆散比較了$flag和通過GET方式提交的password的值,如果想等的話,拿到flag。這裡用的是==鬆散性質的比較,再利用字符串處理數組時將會報錯,在5.3之前的php中,顯示了報錯的警告信息後,將return 0。所有這裡將password參數指定為數組,利用函數漏洞拿到flag:

拓展總結

除了strcmp()函數外,ereg()和strpos()函數在處理數組的時候也會異常,返回NULL。測試代碼如下:

將參數password賦值一個數組傳遞進去:

http://localhost/?password[]=gg

ereg()函數是處理字符串的,傳入數組後返回NULL,NULL和 FALSE,是不恆等(===)的,滿足第一個if條件;而strpos()函數也是處理字符串的,傳入數組後返回NULL,NULL!==FALSE,滿足條件,拿到flag:

六、parse_str函數變量覆蓋缺陷理論

parse_str函數的作用就是解析字符串並註冊成變量,在註冊變量之前不會驗證當前變量是否存在,所以直接覆蓋掉已有變量。

void parse_str ( string $str [, array &$arr ] )

str 輸入的字符串。
arr 如果設置了第二個變量 arr,變量將會以數組元素的形式存入到這個數組,作為替代。

實踐

測試代碼:

考察點write-up

找到核心代碼:

因為這裡用到了parse_str函數來傳遞b,if的語句的條件是拿$a[0]來比較的,有因為這裡的變量a的值已經三是固定的了:

$a = "www.sqlsec.com";

這裡其實是我博客的地址~~ 不過不重要。 整體代碼乍看起來又不可能,但是利用變量覆蓋函數的缺陷這裡可以對a的變量進行重新賦值,後面的的if語句再利用本文前面提到的md5()比較缺陷進行繞過:

http://localhost/?b=a[0]=240610708

參考文獻

PHP 比較運算符

PHP Float 浮點型

PHP 類型比較表

PHP 弱類型總結

PHP Hash比較存在缺陷,影響大量Web網站登錄認證、忘記密碼等關鍵業務

PHP代碼審計片段講解(入門代碼審計、CTF必備)

淺談PHP弱類型安全

NJCTF2017 線上賽 web 題解

CTF之PHP黑魔法總結

Some features of PHP in CTF

PHP浮點數運算精度的問題

php strcmp()漏洞

危險的is_numeric——PHPYun 2015-06-26 二次注入漏洞分析

【代碼審計】變量覆蓋漏洞詳解

*本文作者:國光,轉載請註明FreeBuf.COM

相關焦點

  • PHP代碼安全
    }以上代碼如果沒有限制 pagesize 的範圍,惡意請求者請求把 pagesize 輸入 5000,10000 等甚至更大的數,會給資料庫帶來一定的壓力,localhost/api/articles?
  • PHP代碼安全有必要了解下
    }以上代碼如果沒有限制 pagesize 的範圍,惡意請求者請求把 pagesize 輸入 5000,10000 等甚至更大的數,會給資料庫帶來一定的壓力,localhost/api/articles?
  • PHP代碼審計要點
    前言隨著代碼安全的普及,越來越多的開發人員知道了如何防禦sqli、xss等與語言無關的漏洞,但是對於和開發語言本身相關的一些漏洞和缺陷卻知之甚少
  • 【代碼審計】PHP代碼審計之CTF系列(2)
    文中所涉及的技術、思路和工具僅供以安全為目的的學習交流使用,任何人不得將其用於非法用途以及盈利等目的,否則後果自行承擔!PS:接上篇PHP代碼審計之CTF系列(1)challenge 9訪問頁面,查看源碼<?
  • 安全從我做起--PHP安全編碼
    1.1 PHP中驗證用戶的輸入這裡特別要注意php.ini中的register_globals的設定,在早期的php版本中是默認開啟的,這會導致很嚴重的安全問題:更安全的代碼應該給$admin賦默認FALSE值:上面這段代碼看起來是安全的,但是如果register_globals開啟了的話,在訪問的url
  • PHP編碼安全:上傳文件安全
    1、文件上傳漏洞以下是一個不安全的上傳代碼示例,即文件上傳PHP接收代碼upload.php。<?>以下是文件上傳HTML代碼upload.html。以下代碼通過黑名單方式對文件類型進行限制。<?
  • PHP項目安全:使用PHP的訪問限制
    Warning:file_get_contents():open_basedir restriction in effect.File(/etc/passwd) is not within the allowed path(s):(/home/web/php/) in /home/web/php/index.php on line 3如果使用函數file_get_contents
  • 搜索 PHP代碼審計之旅
    0x01:從首頁獲取信息了解文件目錄後,就先從index.php文件入手,index.php一般是整個程序的入口,通過index文件可以知道程序的架構、運行流程、包含那些配置文件,包含哪些過濾文件以及包含那些安全過濾文件,了解程序的業務邏輯,所以從首頁入手是很有必要的。
  • PHP代碼審計中常用代碼調試函數與注釋
    出品|安全幫(www.secbang.com)概述:安全幫專注安全教育【本章目的】掌握代碼審計中常用的代碼調試函數
  • PHP代碼審計
    代碼審計顧名思義就是檢查原始碼中的缺點和錯誤信息,分析並找到這些問題引發的安全漏洞,並提供代碼修訂措施和建議
  • 代碼審計之php.ini配置詳解
    >)不安全範例: 過濾文件上傳時對內容檢測如果忽略了短標籤形式,採用<?php的形式判斷,就可被繞過。<?不安全範例:在後端該函數內傳入了可變變量時,未做好過濾就會產生命令執行漏洞<?php    $commend=$_GET['commend'];    system("{$commend}");    ?
  • 二.PHP代碼審計涉及到的超全局變量
    · 出品|安全幫(www.secbang.com)概述:安全幫專注安全教育大牛繞過 針對零基礎小白
  • 代碼執行、命令執行漏洞-PHP
    php preg_replace("/\[(.*)\]/e",'\\1',$_GET['str']); //從$_GET['str']變量裡搜索括號[]中間的內容作為第一組的結果,preg_replace()函數的第二個參數為'\\1'代表這裡用第一組結果填充,這裡可以直接執行代碼?
  • PHP代碼審計四
    通常結合程序的其它漏洞實現完整的攻擊,比如文件上傳頁面,覆蓋掉原來白名單的列表,導致任意文件上傳;用戶註冊頁面控制沒覆蓋的未初始化變量導致SQL思路$$extract()函數parse_str()函數執行import_request_variables()函數如下代碼
  • PHP代碼審計Day1 - in_array函數缺陷
    ---本文由紅日安全成員: 七月火 編寫,如有不當,還望斧正。前言大家好,我們是紅日安全-代碼審計小組。最近我們小組正在做一個PHP代碼審計的項目,供大家學習交流,我們給這個項目起了一個名字叫 PHP-Audit-Labs 。
  • php代碼審計總結
    "白帽子社區在線CTF靶場BMZCTF,歡迎各位在這裡練習、學習,BMZCTF全身心為網絡安全賽手提供優質學習環境,連結(http://
  • PHP8新特性解讀(開發代碼實例演示)
    匹配表達式3. null安全運算符4. 構造函數屬性提升5. 註解6. 命名參數前言PHP8在2020年11月26日正式發布,又一個裡程碑到來。註:以上是自己手動DIY,有些麻煩,為此php中文網開發了一個支持php8的集成環境工具,專為php中文網粉絲學習使用,下載地址:https://www.php.cn/xiazai/gongju/1532
  • PHP 8 所有新特性一覽和代碼示例
    我們將在這篇文章中給大家展示 PHP 8 的所有新特性和相應的代碼示例。編譯安裝 PHP 8為了方便運行示例代碼,開始之前,我們可以在本地編譯安裝 PHP 8 RC2 版本:// 0、下載解壓源碼wget https://downloads.php.net/~pollita/php-8.0.0RC2.tar.gztar zxvf php-8.0.0RC2.tar.gzcd php-8.0.0RC2/
  • 網站安全檢測 對帝國CMS代碼的後臺功能性安全測試
    最近我們SINE安全在對帝國CMS系統進行代碼安全審計的時候,發現該系統存在網站漏洞,受影響的版本是EmpireCMS V7.5,從帝國官方網站下載到本地,我們人工對其代碼進行詳細的漏洞檢測與安全代碼分析。共計發現三個高危漏洞,都是在網站的後臺管理頁面上的功能發現的。
  • PHP Document 代碼注釋規範
    首頁 > 語言 > 關鍵詞 > php最新資訊 > 正文 PHP Document 代碼注釋規範