代码审计初探与XDCMS审计

代码审计初探之XDCMS
一来总结一下最近所学习的代码审计的知识。
二来实战一下一个小CMS的代码审计。

代码审计

目的

查找并修复在开发阶段存在的一些漏洞或程序逻辑错误,避免漏洞被非法利用给企业带来风险。

应该让开发人员填写一个Checklist。它能比较直观的反映应用程序的信息和开发人员所做的编码安全,应该涵盖可能存在的严重漏洞模块,比如:数据验证、身份认证、会话管理、授权、加密、错误处理、日志、安全配置、网络架构。

关于审计

前提

刚开始审计,拿到一套源码。先查看数据库相关文件,如果直接拿到对方的数据库帐号密码那就先不用审计了。
也么直接丢到工具里边扫敏感函数,然后一个一个去回溯,找到入口点与可控的位置。这样对于快速寻找漏洞来说是高效的,但是如果是针对所分配任务(公司代码整体了解)这样是这样是浪费时间的,需要做的是去了解整个源码。

本质

①用户的一切输入都是有害的。②一切进入函数的变量都是有害的。

审计流程

拿到一套源码,先从最开始的地方(一般是根目录Index文件),按执行的顺序去读代码,一直到它的初始化内容,和基本功能完毕为止。
这样可以比较明确的了解整套源码的结构,哪一种函数文件放在哪个文件夹下;知道通用函数在哪个文件,之后在阅读可能有问题的代码,有比较大的帮助,比如在看到一个通用函数时,我们可以快速的切换到通用函数文件,查找其实现代码。

了解

  1. 阅读代码之前,了解整套代码的每一个功能点,每一个框以及他曾经出现的漏洞及相关修补方案。
  2. 了解源码的各个功能时候,注意观察url的编码,可以在后边的阅读代码过程跳过很多无用的分支
  3. 在测试每一个输入框时,仔细观察HTML源码中输入框的id或者name,可以在后边的审计中快速定位到利用点
    4、尝试了解这套代码曾经出现的漏洞,以及修补方案,一套源码可能不是一个人完成的,尝试了解可能会发现开发人员共有的陋习,若能了解漏洞修补的详细细节,因为随着平台的升级或者新技术的出现,修补或许就可能成为摆设。

计划

一、要找什么样的漏洞
明确要找什么样的漏洞,能方便我们在收集相关资料(引发问题的函数字典)时的目标更加精确,收集资料更加全面。

二、要花多长时间完成

漏洞挖掘


通过变量找函数(正向跟踪) 通过函数找变量(逆向跟踪)
人工代码审计的过程中,大多都会选择查找危险函数,根据危险函数的变量回溯到传入变量的方式。(如果有变量自动化跟踪工具,不用费很大精力便可完成)
跟踪函数,要收集可控变量(参数)的最终形态(用户通过各种方式传入进程序经过各种处理之后等待调用的形态)
对爆出的漏洞进行poc学习。
对已经修复的漏洞,进行补丁或修复方法的学习。

漏洞相关部分

输入验证和输出显示

较为严格的数据验证方式:

  1. 对数据进行精确匹配
  2. 接收白名单的数据
  3. 拒绝黑名单的数据
  4. 对匹配黑名单的数据进行编码

PHP可以由用户输入的变量列表:

1
2
3
4
5
6
7
8
9
10
* $_SERVER
* $_GET/_POST/COOKIE/REQUEST
* $_FILES
* $_ENV
* $_HTTP_COOKIE_VARS
* $_HTTP_ENV_VARS
* $_HTTP_GET_VARS
* $_HTTP_POST_FILES
* $_HTTP_POST_VARS
* $_HTTP_SERVER_VARS

命令注入

i. PHP执行系统命令可以使用

1
system、exec、passthru、``、shell_exec、popen、proc_open、pcntl_exec

ii. 通过在全部程序文件中搜索这些函数,确定函数的参数是否会因为外部提交而提交,检查是否经过安全处理
防范方法:
1) 使用自定义的函数或函数库来替代外部命令的功能
2) 使用escapeshellarg函数来处理命令函数
3) 使用safe_mode_exec_dir指定可执行文件的路径

跨站脚本

输出函数经常用:echo、print、printf、vprintf、<%=$test%>
防范方法:
1) 如果输入数据只包含字母和数字,那么任何特殊字符都应当组织
2) 对输入的数据经过严格匹配,比如邮件格式,用户名只包含英文或中文、下划线、连字符
3) 对输出进行HTML编码,编码规范

文件包含

PHP中可能出现的文件包含函数:

1
Include[_once]、require[_once]、show_source、highlight_file、readfile、file_get_contents、fopen、file

防范方法:
1) 对输入数据进行精确匹配,比如根据变量的值确定语言,比如根据变量的值确定语言en.php或cn.php,两个文件放在

1
'language/'.$_POST['lang'].'.php'

那么检查提交的数据是否是en或者cn是最严格的,检查是否只包含字母也不错
2) 过滤参数中的/、..等字符

代码注入

i. 将字符串按照PHP代码来执行。这里的例子是DVWA里边,本来是输入一个网页然后Ping,结果输入 网址 & net user可以执行代码
ii. PHP可能出现的代码注入函数:eval、preg_replace+/e、assert、call_user_func、call_user_func_array、create_function 查找使用这些函数的地方,检查用户是否可控并且有无做输入验证
防范方法:
1) 输入数据精确匹配
2) 白名单方式过滤可执行函数

SQL注入

SQL语句关键字:select、insert、delete、update,查看传递的变量参数是否用户可控,有无做过安全处理
防范方法:
1) 使用参数化查询
2) 过滤关键词
3) 预编译语句

Xpath注入

Xpath用于操控xml,通过搜索xpath来分析,提交给xpath函数的参数是否经过安全处理
防范方法:
1) 对数据进行精确匹配

HTTP响应拆分

i. 也叫做CRLF漏洞,CR和LF分别对应回车、换行字符。HTTP头又很多被CRLF组合分离的行构成,每行的结构都是“键:值”。如果用户输入部分注入了CRLF,有可能改变HTTP报头结构。可以注入自定义的HTTP头,比如注入cookie或者HTML代码,进行类似XSS或者会话固定攻击。

ii. PHP中可以导致HTTP响应拆分的情况为:使用header函数和使用&#36_SERVER变量。高版本PHP会禁止HTTP表头中出现换行字符。

防范方法:
1) 精确匹配数据
2) 检测输入如果有\r或\n,直接拒绝

文件管理

PHP用于文件管理的函数,如果输入变量用户可控,程序中也没有做数据验证,便可成为高危漏洞。
相关函数:
copy、rmdir、unlink、delete、fwrite、chmod、fgetc、fgetcsv、fgets、fgetss、file、file_get_contents、fread、readfile、ftruncate、file_put_contents、fputcsv、fputs,通常PHP中每一个文件操作函数都是危险的。
防范方法:
1) 对提交数据进行严格匹配
2) 限定文件可操作目录

文件上传

上传通常使用move_uploaded_file,也可以找到文件上传的程序进行具体分析
防范方法:
1) 使用白名单方式检测文件后缀
2) 上传之后按时间算法生成文件名称
3) 上传目录脚本文件不可执行
4) 注意%00截断

变量覆盖

i. 情况一、遍历初始化变量
1) foreach($_GET as $key => $value)
2) $$key = $value
ii. 情况二、函数覆盖变量:parse_str、mb_parse_str、import_request_variables
iii. 情况三、register_globals =ON,GET方式提交变量会直接覆盖

防范方法:
1) 设置register_globals=Off
2) 不要使用这些函数来获取变量

会话安全

  1. 会话固定攻击
    i. 利用session不变机制,借他人之手获得认证和授权,然后冒充他人。
    ii. 类型一、A打开一个网址http://unsafe,服务器回复一个session id(SID=QQQQQ)。A给B发了一个钓鱼邮件,http://unsafe/?SID=QQQQ,B被吸引了,点击了,像往常一样输入了自己的帐号和口令登录到银行网站。因为服务器session id不变,现在A点击http://unsafe/?SID=QQQ后,就拥有了B的身份。
    iii. 类型二、受害者打开站点获取SessionID,攻击者通过手段获取SessionID,然后通过获取的ID访问站点即可获得目标用户合法会话。
    1) 如何获取?
    爆破、窃取、预测
    
  2. HTTPonly设置
    i. session.cookie_httponly = ON时,客户端脚本(JS等)无法访问cookie,打开可以比较有效的防御XSS
  3. domain设置
    i. 检查session.cookie_domain是否只包含本域,如果是父域,则其他子域能够获取本域的cookie
  4. path设置
    i. 检查session.cookie_path,如果网站本身应用在/app,那么path必须设置为/app/,才能保证安全
  5. cookie持续时间
    i. 检查session.cookie_lifetime,如果时间设置过程过长,即使用户关闭浏览器,攻击者也会危害帐号安全
  6. secure设置
    i. 如果使用HTTPS,应该设置session.cookie_secure=ON,确保使用HTTPS来传输cookie
  7. session固定
    i. 如果权限级别改变时(如验证完用户名和密码后,普通用户提升到管理员),我们应该修改即将重新生成的会话ID,否则可能会面临会话固定攻击的风险。
  8. CSRF
    跨站请求伪造,攻击者伪造一个恶意请求连接,通过各种方式让正常用户访问后,会以用户的身份执行这些恶意请求。应该对比较重要的程序模块(如修改用户密码、添加用户等)功能进行审查,检察有无使用一次性令牌防御csrf攻击。

加密

  1. 明文存储密码
  2. 密码弱加密
    i. 使用容易破解的加密算法
  3. 密码存储在攻击者能够访问的文件
    保存密码在txt、ini、conf等文件,或者直接写在HTML注释中

    认证与授权

  4. 用户认证
    i. 检查代码进行用户认证的位置,是否能够绕过认证,例如:登录代码可能存在表单注入
    ii. 检查登陆代码有无使用验证码,防止暴力破解的手段
  5. 函数或文件的未认证调用
    i. 一些管理页面是禁止普通用户访问的,有时候开发者忘记对这些文件进行权限验证,导致漏洞发生
    ii. 某些页面使用参数调用功能,没有经过权限验证,比如index.php?action=upload
  6. 密码硬编码
    有的程序会把数据库链接帐号和密码,直接写到数据库连接函数中

    随机函数

  7. rand()
    i. rand()最大随机数是32767,当使用rand处理session时,攻击者很容易破解出session,建议使用mt_rand()
    ii. mt_rand(),返回0~mt_getrandmax()之间的伪随机数。
    iii. 伪随机数:看似随机实际有固定的周期性序列。
    iv. mt_getrandmax() 显示随机数的最大可能值。
  8. mt_srand()和mt_rand()
    PHP4和PHP5<5.2.6,这两个函数处理数据是不安全的,在Web应用中很多mt_rand来处理随机的session,比如密码找回功能等,这样的后果就是被恶意利用直接修改密码。

PHP危险函数

  1. 缓冲区溢出 confirm_phpdoc_compiled
    i. 影响版本
    phpDocumentor phpDocumentor 1.3.1
    phpDocumentor phpDocumentor 1.3 RC4
    phpDocumentor phpDocumentor 1.3 RC3
    phpDocumentor phpDocumentor 1.2.3
    phpDocumentor phpDocumentor 1.2.2
    phpDocumentor phpDocumentor 1.2.1
    phpDocumentor phpDocumentor 1.2
    mssql_pconnect/mssql_connect
    影响版本:PHP < = 4.4.6
    crack_opendict
    影响版本:PHP = 4.4.6
    snmpget
    影响版本:PHP <= 5.2.3
    ibase_connect
    影响版本:PHP = 4.4.6
    unserialize
    影响版本:PHP 5.0.2、PHP 5.0.1、PHP 5.0.0、PHP 4.3.9、PHP 4.3.8、PHP 4.3.7、PHP 4.3.6、PHP 4.3.3、PHP 4.3.2、PHP 4.3.1、PHP 4.3.0、PHP 4.2.3、PHP 4.2.2、PHP 4.2.1、PHP 4.2.0、PHP 4.2-dev、PHP 4.1.2、PHP 4.1.1、PHP 4.1.0、PHP 4.1、PHP 4.0.7、PHP 4.0.6、PHP 4.0.5、PHP 4.0.4、PHP 4.0.3pl1、PHP 4.0.3、PHP 4.0.2、PHP 4.0.1pl2、PHP 4.0.1pl1、PHP 4.0.1
  2. session_destroy()删除文件漏洞
    i. 影响版本不详
    ii. 测试代码
    <?php
    session_save_path('./');
    session_start();
    if($_GET['del']) {
    session_unset();
    session_destroy();
    }else{
    $_SESSION['do']=1;
    echo(session_id());
    print_r($_SESSION);
    } ?>
    
    当提交cookieHPSESSIONID=././1.php,就相当于删除了此文件
  3. unset()-zend_hash_del_key_or_index漏洞
    zend_hash_del_key_or_index PHP小于4.4.3和PHP小于5.1.3,可能会导致zend_hash_del删除错误的元素。当PHP的unset()函数被调用时,它会阻止变量被Unset()

信息泄露

  1. phpinfo()
  2. 其他

    PHP环境

  3. open_basedir设置
    i. open_basedir能限制应用程序访问的目录,检查有没有对open_basedir进行设置,有的可以通过Web服务器来设置,例如apache的php_admin_value,nginx+fcgi通过conf来控制PHP设置
  4. all_url_fopen设置
    i. 如果为ON,PHP可以读取远程文件进行操作
  5. all_url_include设置
    i. 如果为ON,PHP可以远程包含文件,造成严重漏洞
  6. safe_mode_exec_dir设置
    i. 可以控制PHP调用外部命令的目录,如果PHP程序中有调用外部命令,通过指定外部命令的目录,控制程序的风险
  7. magic_quote_gpc
    i. 可以转移提交给参数的特殊字符,建议设置为ON
  8. register_globals设置
    i. 开启之后,导致PHP对所有外部提交的变量注册为全局变量,后果严重
  9. safe_mode设置
    i. 是PHP中的重要安全特性,建议开启。
  10. session_use_trans_dis设置
    i. 如果启用,会导致PHP通过URL传递会话ID,这样一来,攻击者很容易劫持当前回话,或者欺骗用户使用已经被攻击者控制的现有会话
  11. display_errors设置
    i. 如果启用,PHP将输出所有错误或警告信息,攻击者可以利用获取Web根路径等敏感信息
  12. expose_php设置
    如果启用,PHP解释器生成的每个响应都会包含主机系统上安装的PHP版本,了解到远程服务器上运行的PHP版本后,攻击者就能针对系统枚举已知的盗取手段,从而大大增加成功发动攻击的机会。

XDCMS审计

下载地址:http://www.mycodes.net/49/5240.htm

目录总览


首先寻找数据库文件,是否存在默认的下载路径,如果能直接下载到对方的数据库文件或者数据库配置文件拿到管理员帐号密码就不用审计了

里边没有默认的mdb和sql文件,唯一有用的就是config.inc.php,但因为是php所以不显示,这里的情况是我们下载了一套官方的与目标相同的CMS来审计,不是直接下了对方的所有源码….

查看目录

a. admin是管理员目录
b. cache是缓存目录,应该没什么用
c. data大约是存放数据库文件的地方
d. install为安装目录,经常出现的就是任意文件删除漏洞,利用漏洞删除install目录下的lock文件,就可以导致CMS重装
e. system应该是CMS的组成文件,是审计的重点
f. uploadfile上传目录,可以测试任意文件上传

开始审计

想起了开始的两种审计方法
一、直接丢到审计系统去扫一下,找到敏感函数之后回溯,然后找到入口点和可控的位置
二、从最开始的地方(index.php)读起,一直到程序初始化完成,这样可以比较明确的了解源码的结构。之后阅读到有问题的代码有较大的帮助

第一步骤

查看index.php文件

包含两个文件,第一个是数据库配置文件,默认字符集是utf8

宽字节注入

这里再巩固一下宽字节注入的绕过
默认情况下开启了GPC或用addslashes过滤GET/POST,当提交’时会变成\’
1) GBK里的宽字节
a) 当提交为%df’时,会转变为%df\’ - > %df%5c%27,由于是从GBK -> UTF8,%df%5c会变成一个汉字 縗,这样就造成了单引号逃逸
b) 现在基本都是使用set charset_set_client=binary来解决这个问题。
2) UTF8的宽字节
如果是UTF8 -> GBK,同样适用addslashes进行过滤,我们输入錦这个字,它的utf8编码是e98ca6,GBK编码是e55c,而反斜杠正好是5c,所以输入錦’,它们被转成GBK(%e5%5c%5c%27),两个反斜杠被转义成一个反斜杠,导致单引号逃逸

第二个文件:

应该是系统整体架构文件,发现在最下面一行又引入了一个文件fun.inc.php

从这个文件中我们发现了一个过滤函数,并发现其存在一些绕过,我们来找一下都是哪些页面调用了这个函数

1) 我们可以从中排除一些不必要的页面,比如管理员后台php,以及管理员才能接触的一些组件
来到member/index.php中

我们在登录或注册页面对其进行注入绕过

fuzz之后,m应该是目录,f所对应的是member/index.php里边的函数

单引号报错,根据源码对其进行显错注入的绕过

updatexml()

这里需要再学习一下MySQL updatexml() 函数的使用
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值

http://www.XXXIII.com/a.php?id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
通过查询@@version,返回版本。然后CONCAT将其字符串化。因为UPDATEXML第二个参数需要Xpath格式的字符串,所以不符合要求,然后报错。
错误大概会是:
ERROR 1105 (HY000): XPATH syntax error: ’:root@localhost’

构造语句得出所要信息,其他方法类似

其他


上传点还有几个,也不用进行绕过了,直接修改后缀上传木马即可。
上传上去是个txt文本

完了再搞,吃饭时间到了..

参考文章

https://www.cnblogs.com/MiWhite/p/6228491.html
http://foreversong.cn/archives/709#
https://github.com/wizardforcel/hacking-reading-list/blob/master/web/php/PHP%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1.md
http://www.cnseay.com/1051/
《代码审计 企业级Web代码安全架构》