查看原文
其他

原创 | 记一次对VAuditDemo平台的代码审计(上)

禁忌 SecIN技术平台 2022-08-31

点击蓝字




关注我们



0x0 前言


搭建平台使用的架构是WAMP架构

操作系统为windows7

Apache版本为2.4.39

MySQL版本为8.0.12

PHP版本为5.2.17


0x1 信息收集


1.1 探测可能存在的漏洞类型

打开网站发现几个值得关注的功能,评论留言,搜索框,注册登录功能。

猜测可能存在的漏洞:

评论留言可能存在SQL注入、XSS漏洞。

搜索框可能存在SQL注入、XSS漏洞、命令执行漏洞。

注册登录可能存在暴力破解、SQL注入、XSS漏洞。

注册一个账号,登录查看是否存在其他的漏洞

在修改用户信息页面发现几个可能存在漏洞的点

用户名更新可能存在SQL注入漏洞、XSS漏洞。

更换头像功能可能存在文件上传漏洞。

修改密码处可能存在SQL注入漏洞


0x2  漏洞检测与利用


使用Seay源代码审计工具检测是否存在漏洞以及存在哪些漏洞

扫描结果

对扫描结果进行整理

可能存在的漏洞:

  1. SQL注入漏洞

  2. 文件包含漏洞

  3. XSS漏洞

  4. 命令执行漏洞

  5. 敏感信息泄露漏洞

  6. 任意文件操作漏洞

  7. 文件上传漏洞


2.1 SQL注入漏洞检测


2.1.1 留言处的SQL注入漏洞

在网站评论功能里面发现参数id,此处可能存在SQL注入漏洞

构造poc

?id=1'
页面回显

根据回显结果可知单引号被后端代码过滤了


2.1.1.1 代码审计

查找 messageDetail.php文件

后端处理前端传来的参数id的过程:先判断是否有GET传递参数id,如果有,就对id进一步处理,将传递来的id经过sqlwaf()函数处理,然后将处理后的id嵌入到sql执行语句中执行。我们要知道此处是否存在SQL注入漏洞就要知道sqlwaf()函数对传进来的参数 id做了哪些处理。


在网站文件夹中全局搜索sqlwaf()函数

在/sys/lib.php文件中找到了sqlwaf()函数

sqlwaf()函数中使用了正则匹配函数str_ireplace(),该函数的特点是不区分大小写。sqlwaf()函数过滤了很多关键字和特殊符号,由于str_ireplace()的原因无法使用大小写绕过,可以利用字符串替换绕过,例如将and改为a||nd,由于程序是由上至下执行的,所以可以绕过and关键字的检测,程序继续执行,正则匹配检测到了||,将||替换为空,所以传到后端的数据就变成了我们想要的and关键字,利用这个思路可以替换其它关键字。


2.1.1.2 漏洞检测

根据sql执行语句可以知道这是一个数字型注入,由此可以构造poc

?id=1 o||rder by 1


····

?id=1 o||rder by 4


?id=1 o||rder by 5


根据order by回显结果可以知道有四个回显字段

构造poc查看回显位置

?id=-1 u||nion s||elect 1,2,3,4


构造poc查看数据库中的留言

?id=-1 u||nion s||elect * fr||om comment


可以查询到数据库中的留言

由此可以判断此处存在SQL注入漏洞。

payload

?id=-1 u||nion s||elect *,4 fr||om admin



2.1.2 搜索框处的SQL注入漏洞检测

在搜索框中输入1'跳转到了留言处,上面在这里检测出了SQL注入漏洞。


2.1.3 登录处的SQL注入漏洞检测

在登录页面用户框中输入1',在密码框中随便输入一串字符,用BurpSuite抓包,放到Repeater模块中,发送数据包查看响应包

在响应数据包中没有发现有价值的信息

不抓取数据包测试一下,看看页面回显

无论怎么输入,只要用户名或密码不对页面就回显用户名或密码错误,在web网页得到有用信息微乎其微,下一步对源码进行审计。


2.1.3.1 代码审计

在logCheck.php文件中找到SQL语句,根据SQL语句中的变量回溯查找传递进来的参数,分析代码对传递进来的参数做了哪些处理。

先梳理登陆验证过程,首先if语句判断是否有通过POST方式传递进来的参数submituserpass,然后对传递进来的userpass参数放到clean_input()函数中处理,将处理后的参数嵌入到sql语句中,然后执行sql语句在数据库中查询记录,如果数据库中没有记录就输出用户名或密码错,有记录就进入下一个if语句中,在第二个if语句中通过 get_client_ip()函数获取ip,然后将获取到的ip经过sqlwaf()函数进行过滤,将过滤后的ip嵌入到sql更新语句中并执行sql更新语句。


全局搜索clean_input()函数,在lib.php文件中找到定义的clean_input()函数

该函数中使用了stripslashes()函数和mysql_real_escape_string()函数


stripslashes()函数的作用是删除反斜杠\mysql_real_escape_string()函数的作用是转义SQL语句中使用的特殊字符,已知mysql_real_escape_string()函数存在宽字节注入漏洞,这里可用宽字节注入漏洞绕过。


假设第一个sql语句被绕过查询到了数据库中的记录进入到了第二个if语句,这里获取ip使用到了get_client_ip()函数,在lib.php文件中找到了该函数

该函数使用全局变量$_SERVER获取ip,使用strcasecmp()比较函数大小,在虚拟机中测试绕过strcasecmp()函数

poc

?a=unkonwn'


在后面随便加一些字符串也可以绕过

利用这点可以构造以unknown'开头的poc注入到请求包的XFF字段,继续分析代码,获取ip后对ipsqlwaf()函数进行了过滤,在上面提到了sqlwaf()函数,在这里不再赘述。经过 sqlwaf()函数后将处理过的参数嵌入到sql更新语句中,执行sql语句。分析了代码执行以及对传递进来的参数的处理流程,可以根据此构造poc。由于sql查询代码没有回显,所以只能借助第二个 if语句中的sql更新语句执行结果的回显判断是否注入成功。


检测思路:先在登录表单中传入poc1%df' union select * from users#,利用查询到的记录进入第二个if语句构造poc检测是否存在XFF注入


第一个数据包的poc

1%df' union select * from users#


第二个数据包的poc

 X-Forworded-For: unknown' u||nion s||elect * fr||om users#

2.1.3.2 漏洞检测

第一个数据包

在第二个数据包中添加 XFF字段

页面回显结果

注入失败,再测试一次,在源码中添加代码输出查询语句以及执行结果,查看未注入成功的原因。

再测试一次

发现宽字节注入没有成功,反斜杠\没有被吞掉,猜测应该与编码有关,查看数据库编码。

在config.php文件中发现数据库编码为utf8

在utf8编码中一个字节表示一个函数,而宽字节注入的条件是在GBK编码下一个汉字用两个字节表示,所以构造的poc并没有绕过mysql_real_escape_string()函数,所以这里不存在宽字节注入,由于无法查询到数据所以也就无法进行XFF注入,所以这里没有检测出SQL注入漏洞。


补充:

由于可以更改后台源码,这里将数据库编码设置为gbk以便测试在数据库编码为gbk的情况下是否存在SQL注入漏洞。

用BurpSuite抓包,利用上面的poc测试

第一个数据包

第二个数据包

页面回显

登录成功!!!

结论:在数据库编码为utf8的情况下不存在SQL注入漏洞,在数据库编码为gbk的情况下存在SQL注入漏洞。


2.1.4 注册处的SQL注入漏洞检测

regCheck.php

由于有strlen()函数的存在过滤了长度大于16的传递过来的参数user,在一定程度上限制住了绝大多数恶意sql语句的注入,看看其它参数

看到两条sql语句,由于对user参数的限制不能通过user参数传递恶意的sql语句,所以不能借助select查询语句进行SQL注入。在第17行代码中发现传递进来的pass参数,继续分析代码,在第32中发现被处理的参数pass出现在sql语句中,但是由于被当作参数进行执行,无法造成注入漏洞,所以此处不存在SQL注入漏洞。


2.1.5 用户名更新处的SQL注入漏洞检测

在updateName.php文件中查看源码

这里对传递进来的参数username做了长度检测,然后经过clean_input()函数的处理,clean_input()函数在上面已经分析过,这里不再赘述。用正则表达式过滤参数 user,由于\w表示只允许数字、字母、_·,所以无法构造恶意的sql语句,无法利用 select语句进行SQL注入,在updatesql语句中除了$clean_username之外还有 $_SESSION['user_id'],由于$_SESSION['user_id']不可控,所以无法利用它构造恶意sql语句,这里没有检测到SQL注入漏洞。


2.1.6 修改密码处的SQL注入漏洞检测

分析代码:

处理参数的过程:首先判断参数passwd是否为空,不为空继续执行,用clean_input()处理传递进来的参数,函数的作用不再赘述,将处理过的参数嵌入到sql语句中执行。在上面我们知道,在数据库编码为gbk时存在宽字节注入,由于数据库编码为utf8,所以不存在宽字节注入漏洞。即使存在宽字节注入漏洞,该漏洞也无法利用,因为代码中没有对sql语句执行结果的回显,即使恶意的sql查询语句被正常执行,也无法回显出查询结果。


2.1.7 管理员登录处的SQL注入漏洞检测

随便输入一串含有单引号的字符串

页面回显

数据库没有报错,应该是后端屏蔽了数据库报错信息,查看源码。


2.1.7.1 代码审计

后端代码对通过POST传递过来的参数没有经过任何过滤直接嵌入到了sql查询语句中(声明:添加stripslashes()函数是因为通过POST传参之后输出的变量$name值中单引号前莫名其妙被添加了反斜杠\,查看了php.ini配置文件,GPC为Off,后来问了一些师傅可能是全局变量的原因,然后搜索全局变量也没有找到原因,无奈只能添加一行代码去掉反斜杠继续测试)。


由于后端代码没有对输入的参数进行任何过滤,这里直接可以构造万能密码登录。


payload:

admin ' or '1'='1'#


2.1.7.2 漏洞检测

执行结果

登录成功!!!

所以在此处检测出SQL注入漏洞。


2.1.8 二次截断注入漏洞

危险文件在messageSub.php文件中

在拼接SQL语句之前有clean_input()函数过滤

clean_input()函数中有两个函数,stripslashes() 函数删除反斜杠,mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。


cleanmessage的值通过POST传参传递进来,然后经过cleaninput函数过滤,最终赋值给clean_message变量

$_SESSION['username']来自logCheck.php在用户登陆时从数据库中获取

所以要构造payload需要从用户名入手

在用户注册时使用了clean_input函数

当输入用户名为xxx\的时候,被转义成xxx

转义相关处理代码在lib.php中

本地测试

这样处理可以转义相关的特殊字符,这种防护相当于GPC是永远打开的。


传递进来的参数被转义成了xxx\,然后经过clean_input的stripslashes变成xxx\,再经过clean_input的mysql_real_escape_string又变成xxx\,这是前面的\转义了后面的\,所以现在我们的用户名在数据库中就是xxx\。

可以利用\进行转义,在用户注册时构造恶意的用户名用于单引号转义,然后在用户留言处进行SQL语句拼接。


最终构造的payload为

username: xxx\comment: ,(select @@datadir),now());#


最终拼接的SQL语句为

 

INSERT INTO comment(user_name,comment_text,pub_date) VALUES ('xxx\',',(select @@datadir),now());#',now())

简化就是

INSERT INTO comment(user_name,comment_text,pub_date) VALUES ('xxx\',',(select @@datadir),now());


查出数据库路径!


2.1.9 其它的SQL注入漏洞检测

搜索select、update、insert、delete等与sql语句相关的关键字查询剩余可能存在SQL注入漏洞的点,在管理员对普通用户、评论等管理处都发现了sql语句,但是由于传递进去的参数都使用了clean_input()函数过滤,而数据库编码为utf8,所以无法造成SQL注入漏洞,所以利用clean_input()函数过滤传递进来的参数的地方均没有SQL注入漏洞。



往期推荐



原创 | 缓冲区溢出漏洞那些事:C -gets函数

原创|Windows下基础免杀技术

原创 |CodeQL与AST之间联系


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存