
php抓取网页连接函数
解决方案:php抓取网页连接函数(1)_parse_url实现跳转
网站优化 • 优采云 发表了文章 • 0 个评论 • 107 次浏览 • 2022-09-22 19:13
php抓取网页连接函数div[1]_parse_url()实现跳转,第一个参数是url,第二个参数是变量名比如抓取notes.php的页面会调用div[1]_parse_url();返回一个dict对象mysql查询脚本f12select*fromtext1;使用select函数不必使用div[1]_parse_url(),而可以使用forloop。
抓取淘宝网页不需要用到select函数,可以用httpget请求数据库。
谢邀,
做个统计工具,模拟用户在淘宝前两页打开。然后分析这些打开的网页,按照事务等级和原始页,计算转化率,跳转次数,跳转间隔。
加了一个select,
原生的select(id,object,*,*)通常效率比较差,不如采用更加通用的形式forloop。比如id大于某个值并且返回时为空,所有的请求都走forloop,因为id这样不能重复。
通过正则提取数据。
匿名用户说的对,
自己写一个抓取脚本,预加载内容,查询时判断值为空,直接进行正则提取就好了。加上正则表达式、json、格式转换等各种功能。 查看全部
解决方案:php抓取网页连接函数(1)_parse_url实现跳转
php抓取网页连接函数div[1]_parse_url()实现跳转,第一个参数是url,第二个参数是变量名比如抓取notes.php的页面会调用div[1]_parse_url();返回一个dict对象mysql查询脚本f12select*fromtext1;使用select函数不必使用div[1]_parse_url(),而可以使用forloop。
抓取淘宝网页不需要用到select函数,可以用httpget请求数据库。

谢邀,
做个统计工具,模拟用户在淘宝前两页打开。然后分析这些打开的网页,按照事务等级和原始页,计算转化率,跳转次数,跳转间隔。
加了一个select,

原生的select(id,object,*,*)通常效率比较差,不如采用更加通用的形式forloop。比如id大于某个值并且返回时为空,所有的请求都走forloop,因为id这样不能重复。
通过正则提取数据。
匿名用户说的对,
自己写一个抓取脚本,预加载内容,查询时判断值为空,直接进行正则提取就好了。加上正则表达式、json、格式转换等各种功能。
phpn实现一个简单网站的爬虫网页连接函数(简称phpn)
网站优化 • 优采云 发表了文章 • 0 个评论 • 125 次浏览 • 2022-09-11 04:08
php抓取网页连接函数(简称phpn)phpn实现对网页数据的响应抓取和处理功能,简单说,phpn主要就是利用了request对象的四个方法来处理网页url地址等等数据。4个方法分别为get,post,delete,head。网站请求数据包post请求分为数据输出到header方法,和数据包输入到header方法。
header方法需要传入headermethod=http_refererrequest_type=xxx或者method=(http_referer)或者request_type=()都可以,这三种都可以用来处理url地址输出到header方法输出的数据可以是字符串,比如text和json串,也可以是pathname,比如ajax请求就会输出相应pathname的值,也可以是其他的一些json输出方法。
其实就是web服务器在看到请求数据包后,对这个数据进行格式化,但是格式化是可以生成php格式化的格式。post请求顾名思义是将数据(类似text或者json)传递给服务器的。类似的还有get请求的post方法,post方法分为trace和content两种,trace直接是返回值,也就是返回响应的地址和响应header的数据内容,content是将响应值输出到目标url,并且传递到method参数中作为响应。
网站访问地址php/homec/shoplandingc/homep/internal/home我们可以这样写php.exe/home访问地址是/home所以我们访问一下/home。接下来我们详细来看看phpn实现一个简单网站的page爬虫抓取网页连接我们最常用的是url,url里面有参数类型可以控制请求数据包输出格式,比如post的post格式格式化过后一般是{requestheader:texttype:json},content格式化过后也是{requestheader:text},也就是text,json格式化后就是{}{post请求数据包参数}。
所以我们写一个简单的抓取网页的page爬虫,我们首先要定义一个请求url:phpn/home通过post把服务器上的url地址传给服务器,定义一个请求数据包,格式化地址后保存在我们自己定义的header里面。可以用请求地址也可以用header,也可以是其他的一些json格式化地址方法header/然后通过get请求把我们通过post保存在header里面的数据解析出来。
我们还可以这样写:phpn/home定义一个处理json的方法,可以定义一个方法用来处理json地址,这样就可以通过json格式化我们的数据了phpn/home/text.js。 查看全部
phpn实现一个简单网站的爬虫网页连接函数(简称phpn)
php抓取网页连接函数(简称phpn)phpn实现对网页数据的响应抓取和处理功能,简单说,phpn主要就是利用了request对象的四个方法来处理网页url地址等等数据。4个方法分别为get,post,delete,head。网站请求数据包post请求分为数据输出到header方法,和数据包输入到header方法。

header方法需要传入headermethod=http_refererrequest_type=xxx或者method=(http_referer)或者request_type=()都可以,这三种都可以用来处理url地址输出到header方法输出的数据可以是字符串,比如text和json串,也可以是pathname,比如ajax请求就会输出相应pathname的值,也可以是其他的一些json输出方法。
其实就是web服务器在看到请求数据包后,对这个数据进行格式化,但是格式化是可以生成php格式化的格式。post请求顾名思义是将数据(类似text或者json)传递给服务器的。类似的还有get请求的post方法,post方法分为trace和content两种,trace直接是返回值,也就是返回响应的地址和响应header的数据内容,content是将响应值输出到目标url,并且传递到method参数中作为响应。

网站访问地址php/homec/shoplandingc/homep/internal/home我们可以这样写php.exe/home访问地址是/home所以我们访问一下/home。接下来我们详细来看看phpn实现一个简单网站的page爬虫抓取网页连接我们最常用的是url,url里面有参数类型可以控制请求数据包输出格式,比如post的post格式格式化过后一般是{requestheader:texttype:json},content格式化过后也是{requestheader:text},也就是text,json格式化后就是{}{post请求数据包参数}。
所以我们写一个简单的抓取网页的page爬虫,我们首先要定义一个请求url:phpn/home通过post把服务器上的url地址传给服务器,定义一个请求数据包,格式化地址后保存在我们自己定义的header里面。可以用请求地址也可以用header,也可以是其他的一些json格式化地址方法header/然后通过get请求把我们通过post保存在header里面的数据解析出来。
我们还可以这样写:phpn/home定义一个处理json的方法,可以定义一个方法用来处理json地址,这样就可以通过json格式化我们的数据了phpn/home/text.js。
web开发实战python系列入门书籍学python前端开发全套书籍中文版
网站优化 • 优采云 发表了文章 • 0 个评论 • 85 次浏览 • 2022-08-20 04:06
php抓取网页连接函数javascript可以实现类似操作,但是网页制作也不复杂嘛,你可以用类似网页翻页函数javascript解析html代码,由此实现分页等功能,html一直都是基础,
这个要分很多情况。另外...也要看你想干嘛了。php的话...慢慢来吧,我基本一个月刷一个页面。静态的话...也得看你做什么了。我有个朋友靠这个吃饭。一个页面要赚1w人民币,那就得1个月或者2个月,得看你水平怎么样了。
一,学习php,毕竟现在快速发展,好上手,收入还比较可观,框架的话很多,你可以看看。可以和同行交流一下,私信我,我可以给你分享我的学习心得。二,学习爬虫。如果你想做h5的seo就不要学php。可以参考我之前的回答。
node.js+npm快速发布爬虫目前我目前的困境是只能在几十个网站爬虫上线工程后,偶尔发现一个有用的例子,大概过了个半年多再去用nodejs+npm发布,上线又成了问题目前只能让爬虫更好用,让它不需要客户端配置,
一个页面用最简单的语言是最高效的,建议你学python或者php。要想有大功率,就先要解决编程的效率问题,看看下面的书列表。web开发-深入浅出mvc,laravel开发实战python系列入门书籍学python前端开发全套书籍中文版。 查看全部
web开发实战python系列入门书籍学python前端开发全套书籍中文版
php抓取网页连接函数javascript可以实现类似操作,但是网页制作也不复杂嘛,你可以用类似网页翻页函数javascript解析html代码,由此实现分页等功能,html一直都是基础,

这个要分很多情况。另外...也要看你想干嘛了。php的话...慢慢来吧,我基本一个月刷一个页面。静态的话...也得看你做什么了。我有个朋友靠这个吃饭。一个页面要赚1w人民币,那就得1个月或者2个月,得看你水平怎么样了。
一,学习php,毕竟现在快速发展,好上手,收入还比较可观,框架的话很多,你可以看看。可以和同行交流一下,私信我,我可以给你分享我的学习心得。二,学习爬虫。如果你想做h5的seo就不要学php。可以参考我之前的回答。

node.js+npm快速发布爬虫目前我目前的困境是只能在几十个网站爬虫上线工程后,偶尔发现一个有用的例子,大概过了个半年多再去用nodejs+npm发布,上线又成了问题目前只能让爬虫更好用,让它不需要客户端配置,
一个页面用最简单的语言是最高效的,建议你学python或者php。要想有大功率,就先要解决编程的效率问题,看看下面的书列表。web开发-深入浅出mvc,laravel开发实战python系列入门书籍学python前端开发全套书籍中文版。
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多
网站优化 • 优采云 发表了文章 • 0 个评论 • 92 次浏览 • 2022-08-11 00:06
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多,此外函数对设计非常友好,并且现代javascript语言比之java、python、.net等较为容易和方便,因此可以尽可能快的得到需要的数据。
1、php到iframe
2、php到cookie,对象存储和事件也可以实现。example1:php打开浏览器,后台开启支付宝,并且post请求,输入你的名字和密码,完成后把这个post请求发送给iframe,修改好密码,登录支付宝后,再把这个post请求返回给php。php也可以抓去订单的信息,但是iframe和cookie比,安全性一般。
例如如果我们抓包的时候是iframe,我们看到浏览器有跳转,是因为在解析页面js脚本,所以无论你怎么做,可能php在解析的时候就隐式加载了一个页面js脚本,所以解析是没有问题的。例如如果你在解析页面js脚本的时候,php在做的js请求的过程中呢?此时php在进行解析页面js脚本,还要处理服务器的响应,要统计响应数据,所以解析就慢了。
还有一种情况,两个php就算做一个js了,和cookie一样,是可以同时抓取到同一个页面的多个线程池的,方法就是如果php抓到同一个页面后,直接解析并返回给js。php抓取网页数据要取决于你的业务逻辑。比如说你想要抓取百度首页后台点击购买的这个链接的一千条,这样的简单举例。并没有扩展性太高,也不适合多台服务器。
所以我们需要合理的配置机器的流量,比如说一台服务器可以跑http的一千个并发,两台机器可以跑一千个并发,一个web服务器可以跑一千个并发。可以这样理解,一千个并发服务器就是一千台服务器。然后运行这些服务器上面运行php,我们就可以抓取包括首页的一千个并发,这个确实比单台机器抓取的慢。当然php也可以做到并发很多,可以帮助我们做idc并发,调整流量到每台机器上,一个站点加上这些机器的服务器就是一万台。
但是对于一千个并发,这样做并不现实。不要操心idc机器的事,你是做快递的,不可能每一单都往idc过去送,就像快递单上面的字很小,不可能每一张都打印成xxx快递。所以那我们需要高配置的php,支持opennginx,支持workerprocessor,速度比php本身快的多。
2、php抓取网页连接函数php拿到本地文件位置有两种方法,一种是用php从网页上读取信息,一种是通过dns抓取,然后拼接字符串。这两种方法一定是比字符串要快很多的。当然,在寻找网页的时候,首先要查找ip,再去找域名。首先定位tomcat路径:dns(原字符串):。然后需要通过抓包命令查看一下dns解析的返回。 查看全部
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多,此外函数对设计非常友好,并且现代javascript语言比之java、python、.net等较为容易和方便,因此可以尽可能快的得到需要的数据。
1、php到iframe

2、php到cookie,对象存储和事件也可以实现。example1:php打开浏览器,后台开启支付宝,并且post请求,输入你的名字和密码,完成后把这个post请求发送给iframe,修改好密码,登录支付宝后,再把这个post请求返回给php。php也可以抓去订单的信息,但是iframe和cookie比,安全性一般。
例如如果我们抓包的时候是iframe,我们看到浏览器有跳转,是因为在解析页面js脚本,所以无论你怎么做,可能php在解析的时候就隐式加载了一个页面js脚本,所以解析是没有问题的。例如如果你在解析页面js脚本的时候,php在做的js请求的过程中呢?此时php在进行解析页面js脚本,还要处理服务器的响应,要统计响应数据,所以解析就慢了。
还有一种情况,两个php就算做一个js了,和cookie一样,是可以同时抓取到同一个页面的多个线程池的,方法就是如果php抓到同一个页面后,直接解析并返回给js。php抓取网页数据要取决于你的业务逻辑。比如说你想要抓取百度首页后台点击购买的这个链接的一千条,这样的简单举例。并没有扩展性太高,也不适合多台服务器。

所以我们需要合理的配置机器的流量,比如说一台服务器可以跑http的一千个并发,两台机器可以跑一千个并发,一个web服务器可以跑一千个并发。可以这样理解,一千个并发服务器就是一千台服务器。然后运行这些服务器上面运行php,我们就可以抓取包括首页的一千个并发,这个确实比单台机器抓取的慢。当然php也可以做到并发很多,可以帮助我们做idc并发,调整流量到每台机器上,一个站点加上这些机器的服务器就是一万台。
但是对于一千个并发,这样做并不现实。不要操心idc机器的事,你是做快递的,不可能每一单都往idc过去送,就像快递单上面的字很小,不可能每一张都打印成xxx快递。所以那我们需要高配置的php,支持opennginx,支持workerprocessor,速度比php本身快的多。
2、php抓取网页连接函数php拿到本地文件位置有两种方法,一种是用php从网页上读取信息,一种是通过dns抓取,然后拼接字符串。这两种方法一定是比字符串要快很多的。当然,在寻找网页的时候,首先要查找ip,再去找域名。首先定位tomcat路径:dns(原字符串):。然后需要通过抓包命令查看一下dns解析的返回。
php抓取网页连接函数看php_xpath获取全部元素
网站优化 • 优采云 发表了文章 • 0 个评论 • 153 次浏览 • 2022-08-02 07:03
php抓取网页连接函数看php_xpath获取全部元素functionfav(){return'';}varfavname=$_get['href'];varhomehtml=$_get['href'];vara=preg_encode(""+$_get['href'],'utf-8');vara=preg_encode(""+$_get['href']+">",'utf-8');varp=fav();p。
page=homehtml。encode(""+p);a。src=$_get['href']。string();returna;}。
php的话,你可以看看expansion或者argumenthook,具体的实现可以查阅对应的php代码;html的话,则看看w3chtmldocumentation,
$_get['href']?
具体看官方的文档吧,案例和代码都有。
一个简单点儿的fav,可以百度preg_encode:utf-8其实也不复杂,用通配符的话,估计比较麻烦。如果像我们公司现在自己干点php发布的业务,那后台可能有时候也要做各种高级php的preg_encode,但是相对post/get/put的话,还是非常好办的。前后端分离开来比较麻烦,可以部署下全站的php工程做一个类似xpath_encode的函数,w3c/w3cpreg_encode之类的,方便部署。 查看全部
php抓取网页连接函数看php_xpath获取全部元素
php抓取网页连接函数看php_xpath获取全部元素functionfav(){return'';}varfavname=$_get['href'];varhomehtml=$_get['href'];vara=preg_encode(""+$_get['href'],'utf-8');vara=preg_encode(""+$_get['href']+">",'utf-8');varp=fav();p。

page=homehtml。encode(""+p);a。src=$_get['href']。string();returna;}。
php的话,你可以看看expansion或者argumenthook,具体的实现可以查阅对应的php代码;html的话,则看看w3chtmldocumentation,

$_get['href']?
具体看官方的文档吧,案例和代码都有。
一个简单点儿的fav,可以百度preg_encode:utf-8其实也不复杂,用通配符的话,估计比较麻烦。如果像我们公司现在自己干点php发布的业务,那后台可能有时候也要做各种高级php的preg_encode,但是相对post/get/put的话,还是非常好办的。前后端分离开来比较麻烦,可以部署下全站的php工程做一个类似xpath_encode的函数,w3c/w3cpreg_encode之类的,方便部署。
php抓取网页连接函数:data,可以看出url结构上是匹配的
网站优化 • 优采云 发表了文章 • 0 个评论 • 88 次浏览 • 2022-07-09 21:02
php抓取网页连接函数:data,可以看出url结构上是匹配的,所以这个问题的关键是连接不上是出于什么问题导致的。比如说location?getesturl()可以直接返回/,除非想获取html格式的url,那可以用curl的options参数配置连接参数,参数里有:[scheme='/']。如果想获取/连接里的内容,可以用data.url拿出来。
比如说:url="-xxx-free-javascript/sharing/html/app/helloworld.html";@app(registereditor='jsp')curl(url,'/',cmd=false);location=data.url;不过这样挂在@app就作废了,因为@app不是任何app都可以上,上到@app就会挂掉php爬虫工程师二三事连接到对应的app可以设置@app参数,自定义爬虫名,做一些有特色的页面效果:打开html时需要flag=0,选择爬取方式js控制的页面效果:通过以上的抓取title,我们可以不断对headers做手脚,比如下面的部分:我们可以通过爬虫的post到目标页面,再post到收起来的websocket会话:if(!success&&no_timeout||endif){//definewebsocketsessionvarsess=session()varrequest=newrequest(streamname='https_post',success='success',end='end',fail='fail',success='error',end='end',success='fail',end='end',fail='fail',end='fail',success='fail',end='end',success='fail',end='end',fail='fail',end='fail',fail='fail',end='fail',success='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='f。 查看全部
php抓取网页连接函数:data,可以看出url结构上是匹配的

php抓取网页连接函数:data,可以看出url结构上是匹配的,所以这个问题的关键是连接不上是出于什么问题导致的。比如说location?getesturl()可以直接返回/,除非想获取html格式的url,那可以用curl的options参数配置连接参数,参数里有:[scheme='/']。如果想获取/连接里的内容,可以用data.url拿出来。

比如说:url="-xxx-free-javascript/sharing/html/app/helloworld.html";@app(registereditor='jsp')curl(url,'/',cmd=false);location=data.url;不过这样挂在@app就作废了,因为@app不是任何app都可以上,上到@app就会挂掉php爬虫工程师二三事连接到对应的app可以设置@app参数,自定义爬虫名,做一些有特色的页面效果:打开html时需要flag=0,选择爬取方式js控制的页面效果:通过以上的抓取title,我们可以不断对headers做手脚,比如下面的部分:我们可以通过爬虫的post到目标页面,再post到收起来的websocket会话:if(!success&&no_timeout||endif){//definewebsocketsessionvarsess=session()varrequest=newrequest(streamname='https_post',success='success',end='end',fail='fail',success='error',end='end',success='fail',end='end',fail='fail',end='fail',success='fail',end='end',success='fail',end='end',fail='fail',end='fail',fail='fail',end='fail',success='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='f。
php抓取网页连接函数arraydeliever要连接java的native数据库吗
网站优化 • 优采云 发表了文章 • 0 个评论 • 92 次浏览 • 2022-07-02 09:05
php抓取网页连接函数是post连接sqldml连接。而数据库连接函数是select连接。post连接函数是post方法,post是类名,post是方法名,post是参数,是动态数据库连接函数是dml方法,dml是类名,dml是方法名,dml是参数,是静态数据库连接函数是select方法,select是方法名,select是参数,是java反射数据库连接函数是extjava反射数据库连接函数是extjava反射。
是java反射数据库连接函数arraydeliever要连接java的native数据库吗?可以用反射java动态数据库,将post的数据(如查询表名)转换为java的静态数据库连接。不想用c++写arraydeliever连接的话,jdbc(javadriverbroker)本身内置的arraydeliever方法或用jdbcmsgen包定义的arraydeliever:arraydeliever(auto,java.util.*,jarjdbc);//java.util包的arraydeliever方法.socket(socket,connectionfactory,jdbc);//jdbc包的socket方法我自己先看过下面这个网站的,你看下能不能启用jdbc:jdbc安装教程:jdbc教程。
反射是gc算法,要求数据库连接必须一次性作数据库连接,而post是对应于执行方法的http方法,且两个方法可以有重叠, 查看全部
php抓取网页连接函数arraydeliever要连接java的native数据库吗

php抓取网页连接函数是post连接sqldml连接。而数据库连接函数是select连接。post连接函数是post方法,post是类名,post是方法名,post是参数,是动态数据库连接函数是dml方法,dml是类名,dml是方法名,dml是参数,是静态数据库连接函数是select方法,select是方法名,select是参数,是java反射数据库连接函数是extjava反射数据库连接函数是extjava反射。

是java反射数据库连接函数arraydeliever要连接java的native数据库吗?可以用反射java动态数据库,将post的数据(如查询表名)转换为java的静态数据库连接。不想用c++写arraydeliever连接的话,jdbc(javadriverbroker)本身内置的arraydeliever方法或用jdbcmsgen包定义的arraydeliever:arraydeliever(auto,java.util.*,jarjdbc);//java.util包的arraydeliever方法.socket(socket,connectionfactory,jdbc);//jdbc包的socket方法我自己先看过下面这个网站的,你看下能不能启用jdbc:jdbc安装教程:jdbc教程。
反射是gc算法,要求数据库连接必须一次性作数据库连接,而post是对应于执行方法的http方法,且两个方法可以有重叠,
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 86 次浏览 • 2022-06-25 12:32
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
【技术分享】梨子带你刷burpsuite靶场系列之高级漏洞篇
网站优化 • 优采云 发表了文章 • 0 个评论 • 107 次浏览 • 2022-06-20 17:27
高级漏洞篇介绍
相对于服务器端漏洞篇和客户端漏洞篇,高级漏洞篇需要更深入的知识以及更复杂的利用手段,该篇也是梨子的全程学习记录,力求把漏洞原理及利用等讲的通俗易懂。
Web缓存投毒专题
什么是Web缓存投毒?
首先了解一下什么是Web缓存,Web缓存就是服务器会先将之前没见过的请求对应的响应缓存下来,然后当有认为是相同请求的时候直接将缓存发给用户,这样可以减轻服务器的负荷。但是服务器端在识别的时候是根据特征来的,如果两个请求的特征相同即会认为是相同的请求,此时如果攻击者首先触发服务器缓存附有恶意payload的响应,当其他用户发送相同请求时即会接收到这个恶意的响应。从影响范围来看,一旦成功缓存被投毒的响应,会影响到大量的用户,比起以往某些只能针对某个用户发起的攻击,危害大很多很多。
Web缓存的流程是什么样的?
其实前面梨子已经讲了差不多了,这一小节主要是补充讲解。其实这个Web缓存不是永久的,它只保存固定的一段时间,过了这个时间以后就会重新生成缓存,所以这类攻击还是有一定难度的。那么服务器端怎么识别等效的请求呢,我们接下来介绍一下缓存键的概念。
缓存键
缓存键就是服务器端用来识别等效请求的一系列特征的统称。一般缓存键包括请求行和Host头。服务器端只识别设置为缓存键的特征是否相同,这也就导致了Web缓存投毒漏洞的产生。
如何构造Web缓存投毒攻击?
一般构造Web缓存投毒攻击需要以下几个步骤
识别并确认不会被缓存的输入
我们想要构造Web缓存投毒就需要我们的输入能够反馈在响应中,但是如果我们的输入被设置为缓存键,那就不可能有用户发出等效请求,所以我们需要不断调试直到找到我们的输入既不会是缓存键又可以被反馈在被缓存的响应中。这样才能保证被投毒的响应缓存被投放到受害者那里,burp推荐了一款插件Param Miner来辅助我们寻找这样的不会被缓存的字段。
Param Miner
这款插件会自动去检测不会被缓存的字段使用方法很简单,只需要右键选择Guess headers即可。并且为了不给真实用户造成困扰,可以开启cache buster(缓存粉碎机)。
从服务器诱发被投毒的响应
我们确认了不会被缓存的输入以后,我们就要看服务端是如何处理这个输入的,如果可以动态反馈到响应中,就是我们能够发动Web缓存投毒的关键。
得到被缓存的响应
我们的输入可以被反馈到响应中还不够,还得能够生成缓存,这样才可以真正地将恶意payload落地。所以我们为此还是要不断调试才能成功找到生成投毒缓存的操作。
基于缓存设计缺陷的Web缓存投毒攻击
使用Web缓存投毒发动XSS攻击
因为XSS攻击也是有一部分是输入被反馈在响应中,所以Web缓存投毒当然也可以用来发动XSS攻击。我们关注这样一对请求和响应
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: innocent-website.co.uk<br />HTTP/1.1 200 OKCache-Control: public
我们观察到X-Forwarded-Host指定的URL会代替Host的值被反馈在响应中,并且X-Forwarded-Host是不会被缓存的字段,但是Host和请求行是缓存键。所以所有Host为的用户请求/en?region=uk都会接收到被投毒的响应,像这样
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: a.">alert(1)"<br />HTTP/1.1 200 OKCache-Control: publicalert(1)"/cms/social.png" />
当然了,alert只是弹窗验证而已,攻击者可以构造更复杂的XSS payload获取更多的东西。
使用Web缓存投毒发动不安全的资源导入
有的应用程序会导入Host指定服务器的某个资源,比如JS资源,但是如果我们像上面一样通过X-Forwarded-Host代替Host进行导入,则可能导入同名但是内容为恶意payload的JS资源,例如
GET / HTTP/1.1Host: innocent-website.comX-Forwarded-Host: evil-user.netUser-Agent: Mozilla/5.0 Firefox/57.0<br />HTTP/1.1 200 OK
配套靶场:使用不被缓存头的Web缓存投毒
首先我们随便设置一个查询参数,然后随便设置一个值,只是为了测试Web缓存
从上图来看,现在是没有产生Web缓存的,然后我们插入到X-Forwarded-Host字段会替换掉本应是Host字段的值,这样就相当于resources/js/tracking.js这个文件是可以伪造的,然后我们再go一下看一下缓存之后是什么样子的
好,我们现在看到Web缓存已经生成了,这样,所有被识别为发出等效请求的用户都会接收到这个响应,就会访问到”中毒”后的网页,模拟结束,现在我们到Exploit Server把payload写入到这个同名文件下
然后随便找一个靶场里面的帖子进去,抓包改包,把Exploit Server的域名插到X-Forwarded-Host字段下,然后重放几次让Web缓存生成
然后在浏览器访问这个页面,就能实现弹窗了,如果没生效就再缓存一次就行
使用不被缓存Cookie的Web缓存投毒
像上面一样,我们观察这样的请求
GET /blog/post.php?mobile=1 HTTP/1.1Host: innocent-website.comUser-Agent: Mozilla/5.0 Firefox/57.0Cookie: language=pl;Connection: close
我们看到应用程序通过Cookie中的language的值来调整网站的语言,当该请求生成响应后,等效请求的用户收到的就是波兰语(pl)的页面了。当然了,这种攻击方式比较少,因为很容易因为影响到正常用户被发现。
配套靶场:使用不被缓存Cookie的Web缓存投毒
首先我们观察一下请求与响应的关系
我们可以看到Cookie中的fehost字段被自动拼接到响应中的script节点下,那么我们可以修改这个字段的值实现XSS攻击
我们可以看到现在Web缓存已经生成了,并且我们修改后的字段值也是直接拼接到响应里,为了防止直接插XSS语句不生效,我们在前后多加了一般的script标签分别把前面后面的script标签闭合掉,从而成功发动XSS
使用多重头发动Web缓存投毒攻击
如果我们想要发动更高级的攻击可以操纵多个请求头来实现,现在我们考虑这样的一个情况,就是应用程序默认是使用HTTPS协议传输的,但是如果我们使用HTTP协议访问会自动触发一个指向Host的重定向,但是我们可以通过X-Forwarded-Host代替Host的值重定向到恶意域,像这样
GET /random HTTP/1.1Host: innocent-site.comX-Forwarded-Proto: http<br />HTTP/1.1 301 moved permanentlyLocation: https://innocent-site.com/random
配套靶场:使用多重头的Web缓存投毒
首先我们还是把HTTP History中的首页的那个包发到Repeater里面,随便加一个X-Forwarded-Host,Go一下,发现并没有什么变化,而且/resources/js/tracking.js变成了相对路径,那么我们再观察一下是相对于谁呢?那么我们就想办法让页面重定向,这就需要X-Forwarded-Scheme字段了,这个字段作用和X-Forwarded-Proto的效果是一样的,如果请求方法是这个方法指定的则会重定向到用HTTPS请求主机名
我们发现当同时有X-Forwarded-Host和X-Forwarded-Scheme两个字段的时候,就会有组合效果,因为请求方法为HTTP,则会重定向HTTPS流去请求主机名,然后这时候我们已经将主机名转为我们指定的主机名,这样它的相对路径就可以生效了,我们先去Exploit Server构造Payload
然后我们在Repeater里面抓包改包,让服务器产生Web缓存
我们可以看到重定向成功了,会跳转到Exploit Server下的/resources/js/tracking.js
利用暴露大量信息的响应
有时,网站会泄露大量有关自身及其行为的信息,从而使自己更容易遭受Web缓存投毒攻击。### Cache-control指令 有的时候响应会暴露缓存有效期等敏感信息,例如
HTTP/1.1 200 OKVia: 1.1 varnish-v4Age: 174Cache-Control: public, max-age=1800
有效期可以帮助我们去计算时间从而达到某种恶意目的。### Vary头 Vary头指定了一些可以被视为缓存键的字段列表,常见的如User-Agent头,应用程序通过其可以仅向指定用户群投放响应,也可以利用这个特点向特定用户群发动Web缓存投毒攻击。
配套靶场:使用未知请求头的定向Web缓存投毒
找到隐藏字段X-Host,然后像之前那样生成Web缓存,直接放生成缓存的结果
然后我们发现与之前的不同是User-Agent也是缓存键,所以我们还要去留言板钓他们的User-Agent
进到Exploit Server查收钓到的User-Agent,然后再结合刚才的那个生成新的Web缓存
使用Web缓存投毒发动基于DOM的漏洞攻击
不仅可以通过Web缓存投毒导入恶意JS文件,还可以导入恶意的JSON字符串,例如
{"someProperty" : ""}
如果这条payload被传递到恶意的sink就可能触发基于DOM的漏洞,如果想要让网站加载恶意JSON就需要CORS授权允许跨站,例如
HTTP/1.1 200 OKContent-Type: application/jsonAccess-Control-Allow-Origin: *<br />{ "malicious json" : "malicious json"}
配套靶场:在严格可缓存性标准下使用Web缓存投毒发动基于DOM的漏洞攻击
首先我们还是,默认使用Param Miner扫到可伪造字段X-Forwarded-Host。然后我们把首页放到Repeater里看一下有没有什么新东西
这些应该都是json的东西,大概的行为就是以data.host下的/resources/json/geolocate.json为参数执行initGeoLocate函数,然后我们再追踪一下这个geolocate.js和geolocate.json
从图上来看,geolocate.js会把j.country变量直接拼接进来,所以我们看一下这个country变量在哪,应该是在geolocate.json文件里面
那么我们就需要在Exploit Server中伪造属于我们的geolocate.json文件,只是country的值换成xss语句
好的,然后我们就可以生成Web缓存了
刷新首页成功加载我们自己的geolocate.json
注:扫到字段以后要把Param Miner的cachebuster都关了,不然永远也不会生成缓存
Web缓存投毒链
如果将我们学过的几种漏洞与Web缓存投毒结合起来,可以发动更高级的攻击。下面我们直接通过一道靶场来深入理解。
Web缓存投毒链
我们识别到两个可用来投毒的字段:X-Forwarded-Host、X-Original-URL,然后分析一下/resources/js/translations.js这个文件,看一下initTranslations()这个函数是怎么运作的
首先我们来看一下提取现在网页的语言的函数
大概是这么一个流程,然后是翻译函数,因为只翻译首页,所以所谓的翻译就是一对一的替换,然后我们留意一下红框部分,就是说除了英语其他的都会执行翻译,也就是说除了英语以外的我们都能用来插入DOM-XSS语句,考虑到乱码问题,我们选择en-gb这个,虽然也是英语,但是因为代码里严格匹配en,所以这个也是可以用来插入的,而且也不用考虑乱码的问题,现在我们去Exploit Server写入DOM-XSS语句进去
然后我们需要抓取/?localized=1这个包,因为你选择翻译的时候会请求这个页面,然后修改X-Forwarded-Host字段为Exploit Server的,并产生Web缓存
但是这是翻译页面,我们要怎样才能让用户首先进到的就是我们的翻译页面呢,那就是在首页加入一个X-Original-URL字段指向翻译页面,这样访问首页的人就都会被重定向到这个页面了
因为需要生成两个Web缓存并且需要在靶场访问的时候同时在生效中,所以需要多试几次
基于缓存实现缺陷的Web缓存投毒攻击
缓存键缺陷
传统的攻击经常通过将payload注入查询字符串实现,但是请求行是缓存键,这就导致不可能会有用户发出等效请求,也就接收不到投毒响应缓存,但是如果有CDN的话,他们会将缓存键内容进行某些处理之后再存入缓存,例如
尤其是前两条,即使我们注入payload到查询字符串或参数中,用户也可能收到被投毒的响应缓存。
识别合适的缓存断言
首先我们要识别合适的缓存断言以测试缓存的过程。我们需要知道我们接收到的响应是来自缓存还是服务器,比如从以下地方可以得到反馈
有的时候应用程序会使用第三方的缓存组件,此时可以通过查阅相关文档的方式得知缓存的过程。例如,基于Akamai的网站可能支持Pragma:akamai-x-get-cache-key,它可以在响应标头中显示缓存键。
GET /?param=1 HTTP/1.1Host: innocent-website.comPragma: akamai-x-get-cache-key<br />HTTP/1.1 200 OKX-Cache-Key: innocent-website.com/?param=1
探测缓存键的处理过程
我们应该还要观察缓存是否对缓存键有其他的处理。比如剔除Host中的端口号等。下面我们来关注这样一对请求和响应。它会动态将Host值拼接到Location中
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com/enCache-Status: miss
然后我们在Host中随便加入一个端口,观察一下响应
GET / HTTP/1.1Host: vulnerable-website.com:1337<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: miss
我们再去掉端口号重新发送请求
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: hit
发现已经生成缓存,但是缓存是我们加了端口号发出时的响应版本。这就说明端口号是不会加入缓存键中的。
识别可利用的漏洞
讲了这么多,其实Web缓存投毒的危害程度完全取决于其能够用来发动的攻击,常见的有像反射型XSS、开放重定向等客户端漏洞。并且Web缓存投毒不需要用户点击任何链接,可能在发出正常的请求时也会接收到被投毒的响应缓存。
缓存键缺陷的利用
不被缓存的端口号
前面我们介绍过,缓存键有时候可能不会只会缓存域名或主机名而不缓存端口号。所以我们可以利用这个特点发动如DDOS(向任意端口号发出大量请求)、XSS(在端口号位置插入payload)等攻击。
不被缓存的查询字符串
探测不被缓存的查询字符串
有的应用程序并不会在响应中告知是否产生缓存。而且如果查询字符串不是缓存键的话,即使不同的查询字符串也会得到相同的响应缓存。那么我们可以在其他请求头中下手,例如加在Accept-Encoding字段中
Accept-Encoding: gzip, deflate, cachebusterAccept: */*, text/cachebusterCookie: cachebuster=1Origin: https://cachebuster.vulnerable-website.com
其实也可以在Param Miner开启动态的缓存粉碎机(cachebuster)。还有一种办法就是修改不同的路径,但是仍然可以的到相同的响应缓存。例如
Apache: GET //Nginx: GET /%2FPHP: GET /index.php/xyz.NET GET /(A(xyz)/
利用不被缓存的查询字符串
查询字符串不会被缓存可能会扩大XSS攻击面,因为附有XSS payload的查询字符串的请求在缓存看来与普通请求无异。但是普通用户可能就会接收到被投毒的响应缓存。
配套靶场:通过不被缓存的查询字符串的Web缓存响应
这一关考察就是常规的web缓存投毒,这一关并没有对查询字符串进行绑定,所以同一个路径下即识别为等效页面即投放缓存,那么我们直接在URL里面投毒
响应中的Age字段可以告诉我们它的缓存已经生效多长时间了,这一关设定的是35秒后重新缓存,可以作为我们投毒的一个信号标,于是我们看一下这时候访问根目录的响应是怎样的
我们得到的就是投毒后的缓存,这时访问首页的人都会接收到投毒的缓存
不被缓存的查询参数
有的时候缓存仅将某几个查询参数排除在缓存键中,例如utm_content 等utm参数。但是这种攻击方式会因为参数不太会被专门处理而没有那么大的危害。但是如果有功能点可以处理整个URL则可能会有转机。
配套靶场:通过不被缓存的查询参数的Web缓存响应
这一关考点是它只有某些查询参数不会作为缓存键,我们扫到utm_content这个查询参数不是缓存键,所以我们可以用这个参数实现Web缓存投毒
等到重新生成Web缓存的时候,成功通关
缓存参数隐藏
有的时候因为解析差异,可以将某些本来是缓存键的查询参数隐藏到非缓存键中。例如查询字符串中通过与(&)区分各个参数,通过问号(?)识别查询参数的开始。但是如果有多个问号,就会以第一个问号作为查询参数的开始,例如
GET /?example=123?excluded_param=bad-stuff-here
这时,excluded_param就不会被作为缓存键处理。
利用参数解析差异
有的应用程序有一些特殊的查询参数解析规则,比如Ruby on Rails框架将与(&)和分号 (;)作为参数分隔符。但是如果缓存不支持这样解析参数,就会造成差异。例如
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
如上所示,keyed_param就会被视为缓存键,而excluded_param不会,而且只会被缓存解析成两个参数
keyed_param=abcexcluded_param=123;keyed_param=bad-stuff-here
而被Ruby on Rails解析成三个参数
keyed_param=abcexcluded_param=123keyed_param=bad-stuff-here
此时keyed_param的值就会被覆盖为bad-stuff-here,也会得到由这个值生成的响应,但是在缓存看来只要该参数值为abc的都会被视为该响应的等效请求。利用这种特点可以发动JSONP攻击,它会将回调函数名附在查询参数中,例如
GET /jsonp?callback=innocentFunction
我们可以利用上面的特点替换成我们指定的函数名。
配套靶场:参数差异
这一关主要是考察缓存与后台对URL参数的处理方式不同导致的Web缓存投毒,通过扫描得知utm_content是可伪装的缓存键,后台将(&)和(;)识别为分界符,这样就可以利用这种差异覆盖回调参数的值为我们指定的函数
这样访问首页的人就也会接收到我们的投毒缓存了
利用支持fat GET请求
有的时候请求方法并不是缓存键,这时候如果支持fat GET请求方法就也可以用来进行Web缓存投毒。fat GET请求很特殊,拥有URL查询参数和正文参数,而只有请求行是缓存键,而且应用程序会从正文参数获取参数值,例如我们构造这样的请求
GET /?param=innocent HTTP/1.1…param=bad-stuff-here 查看全部
【技术分享】梨子带你刷burpsuite靶场系列之高级漏洞篇
高级漏洞篇介绍
相对于服务器端漏洞篇和客户端漏洞篇,高级漏洞篇需要更深入的知识以及更复杂的利用手段,该篇也是梨子的全程学习记录,力求把漏洞原理及利用等讲的通俗易懂。
Web缓存投毒专题
什么是Web缓存投毒?
首先了解一下什么是Web缓存,Web缓存就是服务器会先将之前没见过的请求对应的响应缓存下来,然后当有认为是相同请求的时候直接将缓存发给用户,这样可以减轻服务器的负荷。但是服务器端在识别的时候是根据特征来的,如果两个请求的特征相同即会认为是相同的请求,此时如果攻击者首先触发服务器缓存附有恶意payload的响应,当其他用户发送相同请求时即会接收到这个恶意的响应。从影响范围来看,一旦成功缓存被投毒的响应,会影响到大量的用户,比起以往某些只能针对某个用户发起的攻击,危害大很多很多。
Web缓存的流程是什么样的?
其实前面梨子已经讲了差不多了,这一小节主要是补充讲解。其实这个Web缓存不是永久的,它只保存固定的一段时间,过了这个时间以后就会重新生成缓存,所以这类攻击还是有一定难度的。那么服务器端怎么识别等效的请求呢,我们接下来介绍一下缓存键的概念。
缓存键
缓存键就是服务器端用来识别等效请求的一系列特征的统称。一般缓存键包括请求行和Host头。服务器端只识别设置为缓存键的特征是否相同,这也就导致了Web缓存投毒漏洞的产生。
如何构造Web缓存投毒攻击?
一般构造Web缓存投毒攻击需要以下几个步骤
识别并确认不会被缓存的输入
我们想要构造Web缓存投毒就需要我们的输入能够反馈在响应中,但是如果我们的输入被设置为缓存键,那就不可能有用户发出等效请求,所以我们需要不断调试直到找到我们的输入既不会是缓存键又可以被反馈在被缓存的响应中。这样才能保证被投毒的响应缓存被投放到受害者那里,burp推荐了一款插件Param Miner来辅助我们寻找这样的不会被缓存的字段。
Param Miner
这款插件会自动去检测不会被缓存的字段使用方法很简单,只需要右键选择Guess headers即可。并且为了不给真实用户造成困扰,可以开启cache buster(缓存粉碎机)。
从服务器诱发被投毒的响应
我们确认了不会被缓存的输入以后,我们就要看服务端是如何处理这个输入的,如果可以动态反馈到响应中,就是我们能够发动Web缓存投毒的关键。
得到被缓存的响应
我们的输入可以被反馈到响应中还不够,还得能够生成缓存,这样才可以真正地将恶意payload落地。所以我们为此还是要不断调试才能成功找到生成投毒缓存的操作。
基于缓存设计缺陷的Web缓存投毒攻击
使用Web缓存投毒发动XSS攻击
因为XSS攻击也是有一部分是输入被反馈在响应中,所以Web缓存投毒当然也可以用来发动XSS攻击。我们关注这样一对请求和响应
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: innocent-website.co.uk<br />HTTP/1.1 200 OKCache-Control: public
我们观察到X-Forwarded-Host指定的URL会代替Host的值被反馈在响应中,并且X-Forwarded-Host是不会被缓存的字段,但是Host和请求行是缓存键。所以所有Host为的用户请求/en?region=uk都会接收到被投毒的响应,像这样
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: a.">alert(1)"<br />HTTP/1.1 200 OKCache-Control: publicalert(1)"/cms/social.png" />
当然了,alert只是弹窗验证而已,攻击者可以构造更复杂的XSS payload获取更多的东西。
使用Web缓存投毒发动不安全的资源导入
有的应用程序会导入Host指定服务器的某个资源,比如JS资源,但是如果我们像上面一样通过X-Forwarded-Host代替Host进行导入,则可能导入同名但是内容为恶意payload的JS资源,例如
GET / HTTP/1.1Host: innocent-website.comX-Forwarded-Host: evil-user.netUser-Agent: Mozilla/5.0 Firefox/57.0<br />HTTP/1.1 200 OK
配套靶场:使用不被缓存头的Web缓存投毒
首先我们随便设置一个查询参数,然后随便设置一个值,只是为了测试Web缓存
从上图来看,现在是没有产生Web缓存的,然后我们插入到X-Forwarded-Host字段会替换掉本应是Host字段的值,这样就相当于resources/js/tracking.js这个文件是可以伪造的,然后我们再go一下看一下缓存之后是什么样子的
好,我们现在看到Web缓存已经生成了,这样,所有被识别为发出等效请求的用户都会接收到这个响应,就会访问到”中毒”后的网页,模拟结束,现在我们到Exploit Server把payload写入到这个同名文件下
然后随便找一个靶场里面的帖子进去,抓包改包,把Exploit Server的域名插到X-Forwarded-Host字段下,然后重放几次让Web缓存生成
然后在浏览器访问这个页面,就能实现弹窗了,如果没生效就再缓存一次就行
使用不被缓存Cookie的Web缓存投毒
像上面一样,我们观察这样的请求
GET /blog/post.php?mobile=1 HTTP/1.1Host: innocent-website.comUser-Agent: Mozilla/5.0 Firefox/57.0Cookie: language=pl;Connection: close
我们看到应用程序通过Cookie中的language的值来调整网站的语言,当该请求生成响应后,等效请求的用户收到的就是波兰语(pl)的页面了。当然了,这种攻击方式比较少,因为很容易因为影响到正常用户被发现。
配套靶场:使用不被缓存Cookie的Web缓存投毒
首先我们观察一下请求与响应的关系
我们可以看到Cookie中的fehost字段被自动拼接到响应中的script节点下,那么我们可以修改这个字段的值实现XSS攻击
我们可以看到现在Web缓存已经生成了,并且我们修改后的字段值也是直接拼接到响应里,为了防止直接插XSS语句不生效,我们在前后多加了一般的script标签分别把前面后面的script标签闭合掉,从而成功发动XSS
使用多重头发动Web缓存投毒攻击
如果我们想要发动更高级的攻击可以操纵多个请求头来实现,现在我们考虑这样的一个情况,就是应用程序默认是使用HTTPS协议传输的,但是如果我们使用HTTP协议访问会自动触发一个指向Host的重定向,但是我们可以通过X-Forwarded-Host代替Host的值重定向到恶意域,像这样
GET /random HTTP/1.1Host: innocent-site.comX-Forwarded-Proto: http<br />HTTP/1.1 301 moved permanentlyLocation: https://innocent-site.com/random
配套靶场:使用多重头的Web缓存投毒
首先我们还是把HTTP History中的首页的那个包发到Repeater里面,随便加一个X-Forwarded-Host,Go一下,发现并没有什么变化,而且/resources/js/tracking.js变成了相对路径,那么我们再观察一下是相对于谁呢?那么我们就想办法让页面重定向,这就需要X-Forwarded-Scheme字段了,这个字段作用和X-Forwarded-Proto的效果是一样的,如果请求方法是这个方法指定的则会重定向到用HTTPS请求主机名
我们发现当同时有X-Forwarded-Host和X-Forwarded-Scheme两个字段的时候,就会有组合效果,因为请求方法为HTTP,则会重定向HTTPS流去请求主机名,然后这时候我们已经将主机名转为我们指定的主机名,这样它的相对路径就可以生效了,我们先去Exploit Server构造Payload
然后我们在Repeater里面抓包改包,让服务器产生Web缓存
我们可以看到重定向成功了,会跳转到Exploit Server下的/resources/js/tracking.js
利用暴露大量信息的响应
有时,网站会泄露大量有关自身及其行为的信息,从而使自己更容易遭受Web缓存投毒攻击。### Cache-control指令 有的时候响应会暴露缓存有效期等敏感信息,例如
HTTP/1.1 200 OKVia: 1.1 varnish-v4Age: 174Cache-Control: public, max-age=1800
有效期可以帮助我们去计算时间从而达到某种恶意目的。### Vary头 Vary头指定了一些可以被视为缓存键的字段列表,常见的如User-Agent头,应用程序通过其可以仅向指定用户群投放响应,也可以利用这个特点向特定用户群发动Web缓存投毒攻击。
配套靶场:使用未知请求头的定向Web缓存投毒
找到隐藏字段X-Host,然后像之前那样生成Web缓存,直接放生成缓存的结果
然后我们发现与之前的不同是User-Agent也是缓存键,所以我们还要去留言板钓他们的User-Agent
进到Exploit Server查收钓到的User-Agent,然后再结合刚才的那个生成新的Web缓存
使用Web缓存投毒发动基于DOM的漏洞攻击
不仅可以通过Web缓存投毒导入恶意JS文件,还可以导入恶意的JSON字符串,例如
{"someProperty" : ""}
如果这条payload被传递到恶意的sink就可能触发基于DOM的漏洞,如果想要让网站加载恶意JSON就需要CORS授权允许跨站,例如
HTTP/1.1 200 OKContent-Type: application/jsonAccess-Control-Allow-Origin: *<br />{ "malicious json" : "malicious json"}
配套靶场:在严格可缓存性标准下使用Web缓存投毒发动基于DOM的漏洞攻击
首先我们还是,默认使用Param Miner扫到可伪造字段X-Forwarded-Host。然后我们把首页放到Repeater里看一下有没有什么新东西
这些应该都是json的东西,大概的行为就是以data.host下的/resources/json/geolocate.json为参数执行initGeoLocate函数,然后我们再追踪一下这个geolocate.js和geolocate.json
从图上来看,geolocate.js会把j.country变量直接拼接进来,所以我们看一下这个country变量在哪,应该是在geolocate.json文件里面
那么我们就需要在Exploit Server中伪造属于我们的geolocate.json文件,只是country的值换成xss语句
好的,然后我们就可以生成Web缓存了
刷新首页成功加载我们自己的geolocate.json
注:扫到字段以后要把Param Miner的cachebuster都关了,不然永远也不会生成缓存
Web缓存投毒链
如果将我们学过的几种漏洞与Web缓存投毒结合起来,可以发动更高级的攻击。下面我们直接通过一道靶场来深入理解。
Web缓存投毒链
我们识别到两个可用来投毒的字段:X-Forwarded-Host、X-Original-URL,然后分析一下/resources/js/translations.js这个文件,看一下initTranslations()这个函数是怎么运作的
首先我们来看一下提取现在网页的语言的函数
大概是这么一个流程,然后是翻译函数,因为只翻译首页,所以所谓的翻译就是一对一的替换,然后我们留意一下红框部分,就是说除了英语其他的都会执行翻译,也就是说除了英语以外的我们都能用来插入DOM-XSS语句,考虑到乱码问题,我们选择en-gb这个,虽然也是英语,但是因为代码里严格匹配en,所以这个也是可以用来插入的,而且也不用考虑乱码的问题,现在我们去Exploit Server写入DOM-XSS语句进去
然后我们需要抓取/?localized=1这个包,因为你选择翻译的时候会请求这个页面,然后修改X-Forwarded-Host字段为Exploit Server的,并产生Web缓存
但是这是翻译页面,我们要怎样才能让用户首先进到的就是我们的翻译页面呢,那就是在首页加入一个X-Original-URL字段指向翻译页面,这样访问首页的人就都会被重定向到这个页面了
因为需要生成两个Web缓存并且需要在靶场访问的时候同时在生效中,所以需要多试几次
基于缓存实现缺陷的Web缓存投毒攻击
缓存键缺陷
传统的攻击经常通过将payload注入查询字符串实现,但是请求行是缓存键,这就导致不可能会有用户发出等效请求,也就接收不到投毒响应缓存,但是如果有CDN的话,他们会将缓存键内容进行某些处理之后再存入缓存,例如
尤其是前两条,即使我们注入payload到查询字符串或参数中,用户也可能收到被投毒的响应缓存。
识别合适的缓存断言
首先我们要识别合适的缓存断言以测试缓存的过程。我们需要知道我们接收到的响应是来自缓存还是服务器,比如从以下地方可以得到反馈
有的时候应用程序会使用第三方的缓存组件,此时可以通过查阅相关文档的方式得知缓存的过程。例如,基于Akamai的网站可能支持Pragma:akamai-x-get-cache-key,它可以在响应标头中显示缓存键。
GET /?param=1 HTTP/1.1Host: innocent-website.comPragma: akamai-x-get-cache-key<br />HTTP/1.1 200 OKX-Cache-Key: innocent-website.com/?param=1
探测缓存键的处理过程
我们应该还要观察缓存是否对缓存键有其他的处理。比如剔除Host中的端口号等。下面我们来关注这样一对请求和响应。它会动态将Host值拼接到Location中
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com/enCache-Status: miss
然后我们在Host中随便加入一个端口,观察一下响应
GET / HTTP/1.1Host: vulnerable-website.com:1337<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: miss
我们再去掉端口号重新发送请求
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: hit
发现已经生成缓存,但是缓存是我们加了端口号发出时的响应版本。这就说明端口号是不会加入缓存键中的。
识别可利用的漏洞
讲了这么多,其实Web缓存投毒的危害程度完全取决于其能够用来发动的攻击,常见的有像反射型XSS、开放重定向等客户端漏洞。并且Web缓存投毒不需要用户点击任何链接,可能在发出正常的请求时也会接收到被投毒的响应缓存。
缓存键缺陷的利用
不被缓存的端口号
前面我们介绍过,缓存键有时候可能不会只会缓存域名或主机名而不缓存端口号。所以我们可以利用这个特点发动如DDOS(向任意端口号发出大量请求)、XSS(在端口号位置插入payload)等攻击。
不被缓存的查询字符串
探测不被缓存的查询字符串
有的应用程序并不会在响应中告知是否产生缓存。而且如果查询字符串不是缓存键的话,即使不同的查询字符串也会得到相同的响应缓存。那么我们可以在其他请求头中下手,例如加在Accept-Encoding字段中
Accept-Encoding: gzip, deflate, cachebusterAccept: */*, text/cachebusterCookie: cachebuster=1Origin: https://cachebuster.vulnerable-website.com
其实也可以在Param Miner开启动态的缓存粉碎机(cachebuster)。还有一种办法就是修改不同的路径,但是仍然可以的到相同的响应缓存。例如
Apache: GET //Nginx: GET /%2FPHP: GET /index.php/xyz.NET GET /(A(xyz)/
利用不被缓存的查询字符串
查询字符串不会被缓存可能会扩大XSS攻击面,因为附有XSS payload的查询字符串的请求在缓存看来与普通请求无异。但是普通用户可能就会接收到被投毒的响应缓存。
配套靶场:通过不被缓存的查询字符串的Web缓存响应
这一关考察就是常规的web缓存投毒,这一关并没有对查询字符串进行绑定,所以同一个路径下即识别为等效页面即投放缓存,那么我们直接在URL里面投毒
响应中的Age字段可以告诉我们它的缓存已经生效多长时间了,这一关设定的是35秒后重新缓存,可以作为我们投毒的一个信号标,于是我们看一下这时候访问根目录的响应是怎样的
我们得到的就是投毒后的缓存,这时访问首页的人都会接收到投毒的缓存
不被缓存的查询参数
有的时候缓存仅将某几个查询参数排除在缓存键中,例如utm_content 等utm参数。但是这种攻击方式会因为参数不太会被专门处理而没有那么大的危害。但是如果有功能点可以处理整个URL则可能会有转机。
配套靶场:通过不被缓存的查询参数的Web缓存响应
这一关考点是它只有某些查询参数不会作为缓存键,我们扫到utm_content这个查询参数不是缓存键,所以我们可以用这个参数实现Web缓存投毒
等到重新生成Web缓存的时候,成功通关
缓存参数隐藏
有的时候因为解析差异,可以将某些本来是缓存键的查询参数隐藏到非缓存键中。例如查询字符串中通过与(&)区分各个参数,通过问号(?)识别查询参数的开始。但是如果有多个问号,就会以第一个问号作为查询参数的开始,例如
GET /?example=123?excluded_param=bad-stuff-here
这时,excluded_param就不会被作为缓存键处理。
利用参数解析差异
有的应用程序有一些特殊的查询参数解析规则,比如Ruby on Rails框架将与(&)和分号 (;)作为参数分隔符。但是如果缓存不支持这样解析参数,就会造成差异。例如
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
如上所示,keyed_param就会被视为缓存键,而excluded_param不会,而且只会被缓存解析成两个参数
keyed_param=abcexcluded_param=123;keyed_param=bad-stuff-here
而被Ruby on Rails解析成三个参数
keyed_param=abcexcluded_param=123keyed_param=bad-stuff-here
此时keyed_param的值就会被覆盖为bad-stuff-here,也会得到由这个值生成的响应,但是在缓存看来只要该参数值为abc的都会被视为该响应的等效请求。利用这种特点可以发动JSONP攻击,它会将回调函数名附在查询参数中,例如
GET /jsonp?callback=innocentFunction
我们可以利用上面的特点替换成我们指定的函数名。
配套靶场:参数差异
这一关主要是考察缓存与后台对URL参数的处理方式不同导致的Web缓存投毒,通过扫描得知utm_content是可伪装的缓存键,后台将(&)和(;)识别为分界符,这样就可以利用这种差异覆盖回调参数的值为我们指定的函数
这样访问首页的人就也会接收到我们的投毒缓存了
利用支持fat GET请求
有的时候请求方法并不是缓存键,这时候如果支持fat GET请求方法就也可以用来进行Web缓存投毒。fat GET请求很特殊,拥有URL查询参数和正文参数,而只有请求行是缓存键,而且应用程序会从正文参数获取参数值,例如我们构造这样的请求
GET /?param=innocent HTTP/1.1…param=bad-stuff-here
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 97 次浏览 • 2022-06-13 16:08
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 97 次浏览 • 2022-05-28 07:03
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(

)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 158 次浏览 • 2022-05-25 13:19
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(

)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 101 次浏览 • 2022-05-25 03:29
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(

)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 面试PHP一般会考查你哪些内容呢?不妨看看
网站优化 • 优采云 发表了文章 • 0 个评论 • 75 次浏览 • 2022-05-21 18:52
状态码 401表示未授权;<br />header(“HTTP/1.0 404 Not Found”);<br />
扩展
8、把John新增到users阵列?
$users[] = ‘john’; <br />array_push($users,‘john’);<br />
9 、在PHP中error_reporting这个函数有什么作用?
error_reporting()设置PHP的报错级别并返回当前级别。
10、如何修改SESSION的生存时间.
方法1:将php.ini中的session.gc_maxlifetime设置为9999 重启apache<br /><br />方法2:<br />$savePath = "./session_save_dir/";<br />$lifeTime = 小时 * 秒;<br />session_save_path($savePath);<br />session_set_cookie_params($lifeTime);<br />session_start();<br /><br />方法3:<br />setcookie() and session_set_cookie_params($lifeTime);<br />
11、有一个网页地址,比如PHP自学中心的主页:,如何得到它的内容?
方法1(对于PHP5及更高版本):<br />$readcontents = fopen("http://www.startphp.cn/index.html", "rb");<br />$contents = stream_get_contents($readcontents);<br />fclose($readcontents);<br />echo $contents;<br /><br />方法2:<br />echo file_get_contents("http://www.startphp.cn/index.html");<br />
12、写一个函数,尽可能高效的,从一个标准url里取出文件的扩展名
例如:需要取出php或.php
方法1:<br />function getExt($url){<br /> $arr = parse_url($url);<br /> $file = basename($arr['path']);<br /> $ext = explode(".",$file);<br /> return $ext[1];<br />}<br /><br />方法2:<br />function getExt($url) {<br /> $url = basename($url);<br /> $pos1 = strpos($url,".");<br /> $pos2 = strpos($url,"?");<br /><br /> if(strstr($url,"?")){<br /> Return substr($url,$pos1 + 1,$pos2 – $pos1 – 1);<br /><br /> } else {<br /> return substr($url,$pos1);<br /> }<br />}<br />
13、使用五种以上方式获取一个文件的扩展名
要求:dir/upload.image.jpg,找出.jpg或者jpg,必须使用PHP自带的处理函数进行处理,方法不能明显重复,可以封装成函数get_ext1($file_name),get_ext2($file_name)
方法1:<br />function get_ext1($file_name){<br /> return strrchr($file_name, '.');<br />}<br />方法2:<br />function get_ext2($file_name){<br /> return substr($file_name,strrpos($file_name, '.'));<br />}<br />方法3:<br />function get_ext3($file_name){<br /> return array_pop(explode('.', $file_name));<br />}<br />方法4:<br />function get_ext4($file_name){<br /> $p = pathinfo($file_name);<br /> return $p['extension'];<br /><br />}<br />方法5:<br />function get_ext5($file_name){<br /> return strrev(substr(strrev($file_name), 0,strpos(strrev($file_name), '.')));<br />}<br />
14、PHP中的null与false,‘’与0有什么区别?
<p> 查看全部
php抓取网页连接函数 面试PHP一般会考查你哪些内容呢?不妨看看
状态码 401表示未授权;<br />header(“HTTP/1.0 404 Not Found”);<br />
扩展
8、把John新增到users阵列?
$users[] = ‘john’; <br />array_push($users,‘john’);<br />
9 、在PHP中error_reporting这个函数有什么作用?
error_reporting()设置PHP的报错级别并返回当前级别。
10、如何修改SESSION的生存时间.
方法1:将php.ini中的session.gc_maxlifetime设置为9999 重启apache<br /><br />方法2:<br />$savePath = "./session_save_dir/";<br />$lifeTime = 小时 * 秒;<br />session_save_path($savePath);<br />session_set_cookie_params($lifeTime);<br />session_start();<br /><br />方法3:<br />setcookie() and session_set_cookie_params($lifeTime);<br />
11、有一个网页地址,比如PHP自学中心的主页:,如何得到它的内容?
方法1(对于PHP5及更高版本):<br />$readcontents = fopen("http://www.startphp.cn/index.html", "rb");<br />$contents = stream_get_contents($readcontents);<br />fclose($readcontents);<br />echo $contents;<br /><br />方法2:<br />echo file_get_contents("http://www.startphp.cn/index.html");<br />
12、写一个函数,尽可能高效的,从一个标准url里取出文件的扩展名
例如:需要取出php或.php
方法1:<br />function getExt($url){<br /> $arr = parse_url($url);<br /> $file = basename($arr['path']);<br /> $ext = explode(".",$file);<br /> return $ext[1];<br />}<br /><br />方法2:<br />function getExt($url) {<br /> $url = basename($url);<br /> $pos1 = strpos($url,".");<br /> $pos2 = strpos($url,"?");<br /><br /> if(strstr($url,"?")){<br /> Return substr($url,$pos1 + 1,$pos2 – $pos1 – 1);<br /><br /> } else {<br /> return substr($url,$pos1);<br /> }<br />}<br />
13、使用五种以上方式获取一个文件的扩展名
要求:dir/upload.image.jpg,找出.jpg或者jpg,必须使用PHP自带的处理函数进行处理,方法不能明显重复,可以封装成函数get_ext1($file_name),get_ext2($file_name)
方法1:<br />function get_ext1($file_name){<br /> return strrchr($file_name, '.');<br />}<br />方法2:<br />function get_ext2($file_name){<br /> return substr($file_name,strrpos($file_name, '.'));<br />}<br />方法3:<br />function get_ext3($file_name){<br /> return array_pop(explode('.', $file_name));<br />}<br />方法4:<br />function get_ext4($file_name){<br /> $p = pathinfo($file_name);<br /> return $p['extension'];<br /><br />}<br />方法5:<br />function get_ext5($file_name){<br /> return strrev(substr(strrev($file_name), 0,strpos(strrev($file_name), '.')));<br />}<br />
14、PHP中的null与false,‘’与0有什么区别?
<p>
php抓取网页连接函数download_urls,传递server_id进去判断下
网站优化 • 优采云 发表了文章 • 0 个评论 • 109 次浏览 • 2022-05-10 07:02
php抓取网页连接函数download_urls,传递server_id进去判断下server_id是否在爬虫本地常驻,如果不常驻会建立临时线程下载连接。类似动态页面时会实时更新信息的做法。具体可以参考phptagcloud。php对apache的支持也很好。本人没有用过apache,只是使用过tengine,我猜apache在爬虫大部分任务上好用的多。
不要用php,这次应该是你配置问题。如果php的echo$url+:(但是php+symbian环境中的这个其实写错了。其实应该是),就可以抓取到浏览器上的加载链接,因为它返回的就是url标签下的标签。因为相比使用js,你的浏览器下载过程是不会立即完成的,而是类似exfat或formdata文件,使用stock文件。
实测为不可以,你要安装一个apache的插件apache-nginx,在你的php安装的目录下。这是我下的图apache插件安装完毕后,那个叫simpleurlhandler,下载地址:ocean'sblog,貌似现在都免费了apache-nginx安装完毕之后,你的php就可以抓取了,但是只能一个人用,并且要全局配置,为什么呢?因为这个simpleurlhandler是可选的,也就是你可以抓取网页的任何部分。
那么在大部分的小网站上(http请求量很小的除外)php可以实现你的目的。it'snotareadabilitytest.(译者注:语感很重要)所以,你的php环境真的很牛很牛,那么就安全,看不懂也没关系,反正你不懂php语法。 查看全部
php抓取网页连接函数download_urls,传递server_id进去判断下
php抓取网页连接函数download_urls,传递server_id进去判断下server_id是否在爬虫本地常驻,如果不常驻会建立临时线程下载连接。类似动态页面时会实时更新信息的做法。具体可以参考phptagcloud。php对apache的支持也很好。本人没有用过apache,只是使用过tengine,我猜apache在爬虫大部分任务上好用的多。
不要用php,这次应该是你配置问题。如果php的echo$url+:(但是php+symbian环境中的这个其实写错了。其实应该是),就可以抓取到浏览器上的加载链接,因为它返回的就是url标签下的标签。因为相比使用js,你的浏览器下载过程是不会立即完成的,而是类似exfat或formdata文件,使用stock文件。
实测为不可以,你要安装一个apache的插件apache-nginx,在你的php安装的目录下。这是我下的图apache插件安装完毕后,那个叫simpleurlhandler,下载地址:ocean'sblog,貌似现在都免费了apache-nginx安装完毕之后,你的php就可以抓取了,但是只能一个人用,并且要全局配置,为什么呢?因为这个simpleurlhandler是可选的,也就是你可以抓取网页的任何部分。
那么在大部分的小网站上(http请求量很小的除外)php可以实现你的目的。it'snotareadabilitytest.(译者注:语感很重要)所以,你的php环境真的很牛很牛,那么就安全,看不懂也没关系,反正你不懂php语法。
php抓取网页连接函数legionaproxy,mysql模式下各项事件触发函数
网站优化 • 优采云 发表了文章 • 0 个评论 • 86 次浏览 • 2022-05-07 22:01
php抓取网页连接函数legionaproxy,mysql连接函数hibernate函数proxy_transfer,mvc模式下各项事件触发函数。其中legionaproxy是最难用的,这个事件要封装各种方法,不是一般人能搞得定的,要不然他就不是php界的web开发大咖了。
在一些专门的开发框架里面,这种东西是可以嵌套的。比如说以前最流行的drupal就有一套组件可以实现。不过要看具体框架的设计者,有的是直接控制,有的是被block。
mysql方面看一下数据库事务管理;php方面看一下mvc模式中的某个组件或者是某个文件;
其实我觉得吧,问问题的人提的这些问题,建议最好还是自己写代码实现吧。有网站、app出现问题,你不先看看程序怎么跑,而是立马让答题者回答,然后你自己思考,
php撸了一天,
官方的github就有这样的库,
我自己的话是去看一下thefivewaystogenerateaphpconnection·issue#362·iamftc/php-five·github刚刚试了一下,有一些试错的东西需要自己跑一遍,循环什么的。在selenium里面的实现:/imfeeds/alpariasphook.php?common=1&workday=2018-09-26&article=php-five-ways-to-generate-a-php-connection&topic=intro-1&from=---tree134e353c4dacf524c0bc111071d0e。 查看全部
php抓取网页连接函数legionaproxy,mysql模式下各项事件触发函数
php抓取网页连接函数legionaproxy,mysql连接函数hibernate函数proxy_transfer,mvc模式下各项事件触发函数。其中legionaproxy是最难用的,这个事件要封装各种方法,不是一般人能搞得定的,要不然他就不是php界的web开发大咖了。
在一些专门的开发框架里面,这种东西是可以嵌套的。比如说以前最流行的drupal就有一套组件可以实现。不过要看具体框架的设计者,有的是直接控制,有的是被block。
mysql方面看一下数据库事务管理;php方面看一下mvc模式中的某个组件或者是某个文件;
其实我觉得吧,问问题的人提的这些问题,建议最好还是自己写代码实现吧。有网站、app出现问题,你不先看看程序怎么跑,而是立马让答题者回答,然后你自己思考,
php撸了一天,
官方的github就有这样的库,
我自己的话是去看一下thefivewaystogenerateaphpconnection·issue#362·iamftc/php-five·github刚刚试了一下,有一些试错的东西需要自己跑一遍,循环什么的。在selenium里面的实现:/imfeeds/alpariasphook.php?common=1&workday=2018-09-26&article=php-five-ways-to-generate-a-php-connection&topic=intro-1&from=---tree134e353c4dacf524c0bc111071d0e。
php抓取网页连接函数(年度爆款文案:pdfkit库的操作流程和安装库 )
网站优化 • 优采云 发表了文章 • 0 个评论 • 96 次浏览 • 2022-04-16 18:29
)
按照上面的操作流程,就可以安装pdfkit库了。对于pdfkit库的使用,常见的三种用法:
上述程序主要完成几个步骤:
首先需要指定wkhtmltopdf.exe文件的路径;
因此,pdfkit库只能将子页面保存为单独的pdf文档,不能通过pdfkit库直接将所有子页面拼接成一个完整的pdf文档。小编使用PyPDF2库中的PdfFileMerger类来实现pdf文档的拼接。程序如下图所示。
程序首先将所有html网页保存为单独的pdf文档,然后通过PdfFileMerger类对象实现pdf文档的拼接。最后,您可以获得所有的pdf内容。最后,我们通过视频展示看一下程序的效果。
另外,该程序不仅可以爬取python3.9的中文文档,还可以爬取其他在线文档,只需要修改程序获取要爬取的网页链接,比如Flask中文文档的爬取,程序只需要按照下图进行修改,Flask的在线文档就可以保存为PDF文档了。
04.总结
学习 Python 实际上是非常有趣和有用的。因为 Python 拥有大量现成的库,可以帮助我们轻松解决工作中的许多琐碎问题。小编对上面的程序稍作修改,很快就帮阿里拿到了教程,保存为pdf发给她,小编和女神的关系就更近了。
其实源码很简单,大家也可以轻点一下(看几千遍还不如自己敲),有不明白的可以找小助手(公众号后台输入:小助手)。
为了方便大家更好的理解,我们会在B站录制一个完整的视频(详情见原文),一步步讲解程序,再提供源码和视频!
推荐阅读:
入门: 最全的零基础学Python的问题 | 零基础学了8个月的Python | 实战项目 |学Python就是这条捷径
干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析 | 从万众期待到口碑扑街!唐探3令人失望 | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |
趣味:弹球游戏 | 九宫格 | 漂亮的花 | 两百行Python《天天酷跑》游戏!
AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影
年度最火副本
点这里,直达菜鸟学PythonB站!! 查看全部
php抓取网页连接函数(年度爆款文案:pdfkit库的操作流程和安装库
)
按照上面的操作流程,就可以安装pdfkit库了。对于pdfkit库的使用,常见的三种用法:

上述程序主要完成几个步骤:
首先需要指定wkhtmltopdf.exe文件的路径;
因此,pdfkit库只能将子页面保存为单独的pdf文档,不能通过pdfkit库直接将所有子页面拼接成一个完整的pdf文档。小编使用PyPDF2库中的PdfFileMerger类来实现pdf文档的拼接。程序如下图所示。

程序首先将所有html网页保存为单独的pdf文档,然后通过PdfFileMerger类对象实现pdf文档的拼接。最后,您可以获得所有的pdf内容。最后,我们通过视频展示看一下程序的效果。
另外,该程序不仅可以爬取python3.9的中文文档,还可以爬取其他在线文档,只需要修改程序获取要爬取的网页链接,比如Flask中文文档的爬取,程序只需要按照下图进行修改,Flask的在线文档就可以保存为PDF文档了。
04.总结
学习 Python 实际上是非常有趣和有用的。因为 Python 拥有大量现成的库,可以帮助我们轻松解决工作中的许多琐碎问题。小编对上面的程序稍作修改,很快就帮阿里拿到了教程,保存为pdf发给她,小编和女神的关系就更近了。
其实源码很简单,大家也可以轻点一下(看几千遍还不如自己敲),有不明白的可以找小助手(公众号后台输入:小助手)。
为了方便大家更好的理解,我们会在B站录制一个完整的视频(详情见原文),一步步讲解程序,再提供源码和视频!
推荐阅读:
入门: 最全的零基础学Python的问题 | 零基础学了8个月的Python | 实战项目 |学Python就是这条捷径
干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析 | 从万众期待到口碑扑街!唐探3令人失望 | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |
趣味:弹球游戏 | 九宫格 | 漂亮的花 | 两百行Python《天天酷跑》游戏!
AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影
年度最火副本
点这里,直达菜鸟学PythonB站!!
php抓取网页连接函数(PHP编码声明与用header或meta实现PHP页面编码的区别)
网站优化 • 优采云 发表了文章 • 0 个评论 • 99 次浏览 • 2022-04-13 08:14
编码格式有两种,一种是php文件本身的编码格式,editplus等编辑器允许你在保存文件时指定文件编码格式;另一种是php输出的文本的编码格式,这个信息只在浏览器上生效,方法是。请注意,必须在任何输出之前调用标头方法。
通常,PHP项目开发工具,如ZendStudio、PHPStorm等,都会设置项目的编码方式,以保证项目中的所有文件使用相同的编码格式。对于 WEB 输出,通常的方式是在 html 文件中指定所需的编码,浏览器根据此设置解释内容。
PHP页面编码声明与PHP页面编码使用header或meta的区别
php header 定义一个php页面为utf编码或GBK编码
php页面是utf编码的header("Content-type: text/html; charset=utf-8");
php 页面将标头编码为 gbk("Content-type: text/html; charset=gb2312");
php页面是big5编码头("Content-type: text/html; charset=big5");
通常上面的代码放在php页面的首页
PHP 页面编码与 header 或 meta 一、页面编码的区别
1. 使用标签设置页面编码
这个标签的作用是声明客户端的浏览器使用什么字符集编码来显示页面,xxx可以是GB2312、GBK、UTF-8(不同于MySQL,MySQL是UTF8)等等。因此,大多数页面这个方法可以用来告诉浏览器在显示这个页面的时候使用什么编码,以免造成编码错误和乱码。也就是说,浏览器总是使用一种编码,我稍后会谈到。
请注意,它属于html信息,它只是一个声明,它的作用是表明服务器已将HTML信息传递给浏览器。
2. header("content-type:text/html; charset=xxx");
这个函数header()的作用是将括号中的信息发送到http头。
如果括号中的内容如文中所述,那么功能与标签基本相同。你可以比较第一个,发现字符相似。但不同的是,如果有这个功能,浏览器会一直使用你需要的xxx编码,永远不会不听话,所以这个功能非常好用。为什么会这样?那我们就不得不说一下HTTPS头和HTML信息的区别了:
https 标头是服务器在使用 HTTP 协议向浏览器发送 HTML 信息之前发送的字符串。
因为meta标签属于html信息,所以header()发送的内容首先到达浏览器。通俗点就是header()的优先级比meta高(不知道能不能这么说)。添加一个php页面同时有header("content-type:text/html; charset=xxx"),浏览器只识别之前的http header而不识别meta。当然这个功能只能在一个php页面中使用。
同样的问题仍然存在,为什么前者绝对有效而后者有时无效?这就是为什么我们接下来要讨论 Apache。
3. 添加默认字符集
在Apache根目录的conf文件夹中,有整个Apache配置文件httpd.conf。
使用文本编辑器打开 httpd.conf。第 708 行(不同版本可能不同)有 AddDefaultCharset xxx,其中 xxx 是编码名称。这行代码的含义:将整个服务器中的网页文件的https头中的字符集设置为你默认的xxx字符集。拥有这一行相当于在每个文件中添加一行 header("content-type:text/html; charset=xxx")。现在我明白为什么meta设置为utf-8,但浏览器总是使用gb2312。
如果网页中有header("content-type:text/html; charset=xxx"),把默认字符集改成你设置的字符集,所以这个函数总是有用的。如果你在AddDefaultCharset xxx前面加了一个“#”,注释掉这句话,页面中没有header(“content-type...”),那么就该meta标签起作用了。
总结:
排序
标头(“内容类型:文本/html;字符集=xxx”)
AddDefaultCharset xxx 查看全部
php抓取网页连接函数(PHP编码声明与用header或meta实现PHP页面编码的区别)
编码格式有两种,一种是php文件本身的编码格式,editplus等编辑器允许你在保存文件时指定文件编码格式;另一种是php输出的文本的编码格式,这个信息只在浏览器上生效,方法是。请注意,必须在任何输出之前调用标头方法。
通常,PHP项目开发工具,如ZendStudio、PHPStorm等,都会设置项目的编码方式,以保证项目中的所有文件使用相同的编码格式。对于 WEB 输出,通常的方式是在 html 文件中指定所需的编码,浏览器根据此设置解释内容。
PHP页面编码声明与PHP页面编码使用header或meta的区别
php header 定义一个php页面为utf编码或GBK编码
php页面是utf编码的header("Content-type: text/html; charset=utf-8");
php 页面将标头编码为 gbk("Content-type: text/html; charset=gb2312");
php页面是big5编码头("Content-type: text/html; charset=big5");
通常上面的代码放在php页面的首页
PHP 页面编码与 header 或 meta 一、页面编码的区别
1. 使用标签设置页面编码
这个标签的作用是声明客户端的浏览器使用什么字符集编码来显示页面,xxx可以是GB2312、GBK、UTF-8(不同于MySQL,MySQL是UTF8)等等。因此,大多数页面这个方法可以用来告诉浏览器在显示这个页面的时候使用什么编码,以免造成编码错误和乱码。也就是说,浏览器总是使用一种编码,我稍后会谈到。
请注意,它属于html信息,它只是一个声明,它的作用是表明服务器已将HTML信息传递给浏览器。
2. header("content-type:text/html; charset=xxx");
这个函数header()的作用是将括号中的信息发送到http头。
如果括号中的内容如文中所述,那么功能与标签基本相同。你可以比较第一个,发现字符相似。但不同的是,如果有这个功能,浏览器会一直使用你需要的xxx编码,永远不会不听话,所以这个功能非常好用。为什么会这样?那我们就不得不说一下HTTPS头和HTML信息的区别了:
https 标头是服务器在使用 HTTP 协议向浏览器发送 HTML 信息之前发送的字符串。
因为meta标签属于html信息,所以header()发送的内容首先到达浏览器。通俗点就是header()的优先级比meta高(不知道能不能这么说)。添加一个php页面同时有header("content-type:text/html; charset=xxx"),浏览器只识别之前的http header而不识别meta。当然这个功能只能在一个php页面中使用。
同样的问题仍然存在,为什么前者绝对有效而后者有时无效?这就是为什么我们接下来要讨论 Apache。
3. 添加默认字符集
在Apache根目录的conf文件夹中,有整个Apache配置文件httpd.conf。
使用文本编辑器打开 httpd.conf。第 708 行(不同版本可能不同)有 AddDefaultCharset xxx,其中 xxx 是编码名称。这行代码的含义:将整个服务器中的网页文件的https头中的字符集设置为你默认的xxx字符集。拥有这一行相当于在每个文件中添加一行 header("content-type:text/html; charset=xxx")。现在我明白为什么meta设置为utf-8,但浏览器总是使用gb2312。
如果网页中有header("content-type:text/html; charset=xxx"),把默认字符集改成你设置的字符集,所以这个函数总是有用的。如果你在AddDefaultCharset xxx前面加了一个“#”,注释掉这句话,页面中没有header(“content-type...”),那么就该meta标签起作用了。
总结:
排序
标头(“内容类型:文本/html;字符集=xxx”)
AddDefaultCharset xxx
php抓取网页连接函数(实现PHP与SQL数据库交互实验演示实验环境(一))
网站优化 • 优采云 发表了文章 • 0 个评论 • 106 次浏览 • 2022-04-11 03:31
内容
1 简介
(1)为了实现网页访问后台数据库的功能,需要与网页代码中对应的数据库进行连接。
(2)PHP 5及以上版本推荐使用以下方法连接MySQL:
(3)两种连接方式对比:
(4)本节通过一个简单的例子来介绍实现PHP与SQL连接的全过程,主要介绍MySQLi的方法。
2 PHP与SQL数据库交互实验演示2.1 实验环境
(1)服务器:本实验基于虚拟机win2008系统的WAMP环境,该环境的配置过程请参考文章"【语言环境】WAMP环境的部署与优化- Win2008R2SP1 作为操作系统”。
(2)客户端:使用浏览器访问和控制。
(3)服务端和客户端在同一个局域网中,在服务端打开phpstudy,确保可以从客户端浏览器访问。
2.2 创建数据库
(1)通过访问phpMyAdmin登录数据库。管理员通过浏览器登录phpMyAdmin(位置在网站默认根目录下的phpMyAdmin文件夹下),输入URL服务器IP/phpMyAdmin在浏览器中输入mysql数据库的登录页面;输入用户名和密码登录(默认用户名和密码都是root),点击执行登录mysql数据库。
(2)数据库页面,登录数据库后可以看到如下界面,左边是默认的4个数据库,点击箭头所指的数据库,操作数据库。
(3)创建一个数据库jrlt。按如下操作,新建一个数据库jrlt,选择类型为“utf8_genrral_ci”,点击创建。创建成功后,在左侧可以看到创建好的数据库。
(4)进入数据库。点击数据库进入,进入后可以看到数据库中没有数据表。
(5)创建数据表users(主要用于存放用户信息id、name、password、photo、money,共5个字段)。创建数据表如下,点击Execute。
(6)编辑数据表。创建数据表的列名(id名密码照片钱),并设置响应的类型和长度,设置id键为主键。(主键需要非空增量),然后单击保存数据表。
(7)可以点击左侧新建的users表,该表的详细结构信息如下图所示。
2.3 插入数据
(1)进入数据表SQL操作界面。点击用户表→点击SQL进入修改表界面→点击要使用的SQL预定义语句。
(2)修改SQL命令。修改命令如下,点击执行。注意符号为英文格式。
INSERT INTO `users`(`name`, `password`,`photo`, `money`) VALUES ("DuZZ",123456,"c:\\photo\\touxiang.png",100);
INSERT INTO `users`( `name`, `password`, `photo`, `money`) VALUES ("GuQQ",654321,"c:\\photo\\touxiang.png",100);
(3)点击左侧的users表可以看到我们输入的用户信息,这里我们输入了DuZZ和GuQQ两个账号,如下图。
2.4 实现PHP与数据库的交互2.4.1 三步实现PHP与数据库的交互
(1)建立连接,用户建立连接时需要提供用户名+密码+地址+数据库名,其中数据库名可以在连接过程中切换。
(2) 执行 SQL 语句。
(3) 断开连接。
2.4.2 程序代码1:简单的三步实现
简单的三步交互,根据连接是否建立成功反馈信息。访问此页面以获取结果
代码中的功能分析如下。更多细节请参考官方手册《MySQL 增强扩展》。
代码执行结果如下:
2.4.3 程序代码2:优化代码1
代码的第一个输出是对象,不方便查看。我们可以使用 mysqli_fetch_assoc() 函数来获取对象中记录的数据作为关联数组。因为mysqli_fetch_assoc()函数一次只能取一条数据,所以是顺序取的。因此,可以将mysqli_fetch_assoc()函数作为条件,当执行成功时,会依次打印得到的关联数组信息。具体代码如下:
上述代码的执行结果如下:
2.4.4 面向对象代码3:将连接方式改为面向对象
代码1和2中面向对象的连接方式与面向过程的方式的主要区别在于连接过程、判断过程和断开过程的区别。数据访问和使用的过程基本相同。
connect_error)
{echo $mysqli->connect_error;}
$sql = "select * from users";
if ($results = mysqli_query($link,$sql)){ //mysqli_query函数执行失败时返回 false,通过mysqli_query() 成功执行SELECT, SHOW, DESCRIBE或 EXPLAIN查询会返回一个mysqli_result 对象,其他查询则返回true。
while ($result = mysqli_fetch_assoc($results)){ //使用mysqli_fetch_assoc函数获取返回对象中的具体数据。
var_dump($result);
echo "";
}
}
else{
die(mysqli_error($link));
}
$link->close();
?>
3 相关函数介绍3.1 数据库连接操作相关函数函数名
新的 mysqli(), mysqli_connect()
连接到指定的数据库
$mysqli->connect_errno, mysqli_connect_errno()
返回最后一次连接调用的错误代码
$mysqli->connect_error, mysqli_connect_error()
返回描述上次连接调用的错误代码的字符串
link->close(), mysqliclose(link->close(), mysqli_close(link->close(), mysqliclose(link)
断开连接,其中 $link 表示已建立的连接。
3.2 运算结果集对象相关函数function
mysqli_fetch_row()
通过索引数组获取记录的数据
mysqli_fetch_assoc()
以关联数组的形式获取记录的数据
mysqli_fetch_array()
获取记录的数据作为索引数组或关联数组
mysqli_fetch_all()
以索引数组或关联数组的形式获取所有记录的数据
mysqli_num_rows()
获取结果中的行数
mysqli_free_result()
释放与结果集关联的内存
3.还有 3 个命令
更多命令请参考官方手册《MySQL 增强扩展》。
4 感应
(1)掌握phpstudy中通过myadmin操作数据库的方法;
(2)使用PHP语句与MySQL交互;
(3)掌握数据库连接和操作结果集对象的常用命令;
(4)学习参考官方手册《MySQL 增强版扩展》。
参考文章
[1]《【PHP基础篇】实现PHP与SQL数据库的连接》 查看全部
php抓取网页连接函数(实现PHP与SQL数据库交互实验演示实验环境(一))
内容
1 简介
(1)为了实现网页访问后台数据库的功能,需要与网页代码中对应的数据库进行连接。
(2)PHP 5及以上版本推荐使用以下方法连接MySQL:
(3)两种连接方式对比:
(4)本节通过一个简单的例子来介绍实现PHP与SQL连接的全过程,主要介绍MySQLi的方法。
2 PHP与SQL数据库交互实验演示2.1 实验环境
(1)服务器:本实验基于虚拟机win2008系统的WAMP环境,该环境的配置过程请参考文章"【语言环境】WAMP环境的部署与优化- Win2008R2SP1 作为操作系统”。
(2)客户端:使用浏览器访问和控制。
(3)服务端和客户端在同一个局域网中,在服务端打开phpstudy,确保可以从客户端浏览器访问。
2.2 创建数据库
(1)通过访问phpMyAdmin登录数据库。管理员通过浏览器登录phpMyAdmin(位置在网站默认根目录下的phpMyAdmin文件夹下),输入URL服务器IP/phpMyAdmin在浏览器中输入mysql数据库的登录页面;输入用户名和密码登录(默认用户名和密码都是root),点击执行登录mysql数据库。


(2)数据库页面,登录数据库后可以看到如下界面,左边是默认的4个数据库,点击箭头所指的数据库,操作数据库。

(3)创建一个数据库jrlt。按如下操作,新建一个数据库jrlt,选择类型为“utf8_genrral_ci”,点击创建。创建成功后,在左侧可以看到创建好的数据库。


(4)进入数据库。点击数据库进入,进入后可以看到数据库中没有数据表。

(5)创建数据表users(主要用于存放用户信息id、name、password、photo、money,共5个字段)。创建数据表如下,点击Execute。

(6)编辑数据表。创建数据表的列名(id名密码照片钱),并设置响应的类型和长度,设置id键为主键。(主键需要非空增量),然后单击保存数据表。

(7)可以点击左侧新建的users表,该表的详细结构信息如下图所示。

2.3 插入数据
(1)进入数据表SQL操作界面。点击用户表→点击SQL进入修改表界面→点击要使用的SQL预定义语句。

(2)修改SQL命令。修改命令如下,点击执行。注意符号为英文格式。
INSERT INTO `users`(`name`, `password`,`photo`, `money`) VALUES ("DuZZ",123456,"c:\\photo\\touxiang.png",100);
INSERT INTO `users`( `name`, `password`, `photo`, `money`) VALUES ("GuQQ",654321,"c:\\photo\\touxiang.png",100);
(3)点击左侧的users表可以看到我们输入的用户信息,这里我们输入了DuZZ和GuQQ两个账号,如下图。

2.4 实现PHP与数据库的交互2.4.1 三步实现PHP与数据库的交互
(1)建立连接,用户建立连接时需要提供用户名+密码+地址+数据库名,其中数据库名可以在连接过程中切换。
(2) 执行 SQL 语句。
(3) 断开连接。
2.4.2 程序代码1:简单的三步实现
简单的三步交互,根据连接是否建立成功反馈信息。访问此页面以获取结果
代码中的功能分析如下。更多细节请参考官方手册《MySQL 增强扩展》。
代码执行结果如下:

2.4.3 程序代码2:优化代码1
代码的第一个输出是对象,不方便查看。我们可以使用 mysqli_fetch_assoc() 函数来获取对象中记录的数据作为关联数组。因为mysqli_fetch_assoc()函数一次只能取一条数据,所以是顺序取的。因此,可以将mysqli_fetch_assoc()函数作为条件,当执行成功时,会依次打印得到的关联数组信息。具体代码如下:
上述代码的执行结果如下:

2.4.4 面向对象代码3:将连接方式改为面向对象
代码1和2中面向对象的连接方式与面向过程的方式的主要区别在于连接过程、判断过程和断开过程的区别。数据访问和使用的过程基本相同。
connect_error)
{echo $mysqli->connect_error;}
$sql = "select * from users";
if ($results = mysqli_query($link,$sql)){ //mysqli_query函数执行失败时返回 false,通过mysqli_query() 成功执行SELECT, SHOW, DESCRIBE或 EXPLAIN查询会返回一个mysqli_result 对象,其他查询则返回true。
while ($result = mysqli_fetch_assoc($results)){ //使用mysqli_fetch_assoc函数获取返回对象中的具体数据。
var_dump($result);
echo "";
}
}
else{
die(mysqli_error($link));
}
$link->close();
?>
3 相关函数介绍3.1 数据库连接操作相关函数函数名
新的 mysqli(), mysqli_connect()
连接到指定的数据库
$mysqli->connect_errno, mysqli_connect_errno()
返回最后一次连接调用的错误代码
$mysqli->connect_error, mysqli_connect_error()
返回描述上次连接调用的错误代码的字符串
link->close(), mysqliclose(link->close(), mysqli_close(link->close(), mysqliclose(link)
断开连接,其中 $link 表示已建立的连接。
3.2 运算结果集对象相关函数function
mysqli_fetch_row()
通过索引数组获取记录的数据
mysqli_fetch_assoc()
以关联数组的形式获取记录的数据
mysqli_fetch_array()
获取记录的数据作为索引数组或关联数组
mysqli_fetch_all()
以索引数组或关联数组的形式获取所有记录的数据
mysqli_num_rows()
获取结果中的行数
mysqli_free_result()
释放与结果集关联的内存
3.还有 3 个命令
更多命令请参考官方手册《MySQL 增强扩展》。
4 感应
(1)掌握phpstudy中通过myadmin操作数据库的方法;
(2)使用PHP语句与MySQL交互;
(3)掌握数据库连接和操作结果集对象的常用命令;
(4)学习参考官方手册《MySQL 增强版扩展》。
参考文章
[1]《【PHP基础篇】实现PHP与SQL数据库的连接》
php抓取网页连接函数(PHP建立CURL请求的基本步骤和数据的工具)
网站优化 • 优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2022-04-11 00:32
CURL 是一个使用 URL 语法传输文件和数据的工具,并且支持多种协议,例如 HTTP、FTP、TELNET 等。最重要的是,PHP 还支持 CURL 库。使用 PHP 的 CURL 库可以轻松高效地抓取网页。你只需要运行一个脚本,然后分析你爬取的网页,然后你就可以通过编程方式获取你想要的数据。无论您是想从链接中获取一些数据,还是获取 XML 文件并将其导入数据库,甚至只是获取网页的内容,CURL 都是一个强大的 PHP 库。
PHP中建立CURL请求的基本步骤
①:初始化
curl_init()
②:设置属性
curl_setopt()。可以设置的 CURL 参数有一长串,可以指定 URL 请求的各种细节。
③:执行并得到结果
curl_exec()
④:松开手柄
curl_close()
CURL 实现 GET 和 POST
①:GET方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
②:POST方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
//设置post数据
$post_data = array(
"username" => "coder",
"password" => "12345"
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
③:如果获取到的数据是json格式,使用json_decode函数将其解释为数组。
$output_array = json_decode($data,true); //如果第二个参数为true,则转换为数组的形式。如果不填充,它将是一个对象的形式
如果你使用 json_decode($data) 来解析,你会得到对象类型的数据。
封装函数
/**
* @param $url 访问的URL
* @param string $post post数据(不填则为GET),数据支持数组,不支持json_encode处理后的json格式
* @param string $cookie 提交的$cookies
* @param int $returnCookie 是否返回$cookies
* @return mixed|string
*/
function curl_request($url, $post = '', $cookie = '', $returnCookie = 0)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url); //发送的地址
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给
curl_setopt($curl, CURLOPT_AUTOREFERER, 1); //重定向时,自动设置header中的Referer:信息
//curl_setopt($curl, CURLOPT_REFERER, "http://XXX"); //构造来路
if ($post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
}
if ($cookie) {
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
}
curl_setopt($curl, CURLOPT_HEADER, $returnCookie);
curl_setopt($curl, CURLOPT_TIMEOUT, 10); //超时时间为10秒
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //要求结果为字符串串且输出到屏幕
$data = curl_exec($curl);
if (curl_errno($curl)) {
return curl_error($curl);
}
curl_close($curl);
if ($returnCookie) {
list($header, $body) = explode("\r\n\r\n", $data, 2);
preg_match_all("/Set\-Cookie:([^;]*);/", $header, $matches);
$info['cookie'] = substr($matches[1][0], 1);
$info['content'] = $body;
return $info;
} else {
return $data;
}
}
建豪发送的函数:
/**
* CURL请求
* @param $url 请求url地址
* @param $method 请求方法 get post
* @param null $postfields post数据数组
* @param array $headers 请求header信息
* @param bool|false $debug 调试开启 默认false
* @return mixed
*/
function httpRequest($url, $method = "GET", $postfields = null, $headers = array(), $debug = false)
{
$method = strtoupper($method);
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
switch ($method) {
case "POST":
curl_setopt($ci, CURLOPT_POST, true);
if (!empty($postfields)) {
$tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields;
curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
}
break;
default:
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
break;
}
$ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
curl_setopt($ci, CURLOPT_URL, $url);
if ($ssl) {
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
}
//curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLINFO_HEADER_OUT, true);
/*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
$response = curl_exec($ci);
$requestinfo = curl_getinfo($ci);
$http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
if ($debug) {
echo "=====post data======\r\n";
var_dump($postfields);
echo "=====info===== \r\n";
print_r($requestinfo);
echo "=====response=====\r\n";
print_r($response);
}
curl_close($ci);
return $response;
//return array($http_code, $response,$requestinfo);
}
转载于: 查看全部
php抓取网页连接函数(PHP建立CURL请求的基本步骤和数据的工具)
CURL 是一个使用 URL 语法传输文件和数据的工具,并且支持多种协议,例如 HTTP、FTP、TELNET 等。最重要的是,PHP 还支持 CURL 库。使用 PHP 的 CURL 库可以轻松高效地抓取网页。你只需要运行一个脚本,然后分析你爬取的网页,然后你就可以通过编程方式获取你想要的数据。无论您是想从链接中获取一些数据,还是获取 XML 文件并将其导入数据库,甚至只是获取网页的内容,CURL 都是一个强大的 PHP 库。
PHP中建立CURL请求的基本步骤
①:初始化
curl_init()
②:设置属性
curl_setopt()。可以设置的 CURL 参数有一长串,可以指定 URL 请求的各种细节。
③:执行并得到结果
curl_exec()
④:松开手柄
curl_close()
CURL 实现 GET 和 POST
①:GET方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
②:POST方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
//设置post数据
$post_data = array(
"username" => "coder",
"password" => "12345"
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
③:如果获取到的数据是json格式,使用json_decode函数将其解释为数组。
$output_array = json_decode($data,true); //如果第二个参数为true,则转换为数组的形式。如果不填充,它将是一个对象的形式
如果你使用 json_decode($data) 来解析,你会得到对象类型的数据。
封装函数
/**
* @param $url 访问的URL
* @param string $post post数据(不填则为GET),数据支持数组,不支持json_encode处理后的json格式
* @param string $cookie 提交的$cookies
* @param int $returnCookie 是否返回$cookies
* @return mixed|string
*/
function curl_request($url, $post = '', $cookie = '', $returnCookie = 0)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url); //发送的地址
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给
curl_setopt($curl, CURLOPT_AUTOREFERER, 1); //重定向时,自动设置header中的Referer:信息
//curl_setopt($curl, CURLOPT_REFERER, "http://XXX"); //构造来路
if ($post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
}
if ($cookie) {
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
}
curl_setopt($curl, CURLOPT_HEADER, $returnCookie);
curl_setopt($curl, CURLOPT_TIMEOUT, 10); //超时时间为10秒
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //要求结果为字符串串且输出到屏幕
$data = curl_exec($curl);
if (curl_errno($curl)) {
return curl_error($curl);
}
curl_close($curl);
if ($returnCookie) {
list($header, $body) = explode("\r\n\r\n", $data, 2);
preg_match_all("/Set\-Cookie:([^;]*);/", $header, $matches);
$info['cookie'] = substr($matches[1][0], 1);
$info['content'] = $body;
return $info;
} else {
return $data;
}
}
建豪发送的函数:
/**
* CURL请求
* @param $url 请求url地址
* @param $method 请求方法 get post
* @param null $postfields post数据数组
* @param array $headers 请求header信息
* @param bool|false $debug 调试开启 默认false
* @return mixed
*/
function httpRequest($url, $method = "GET", $postfields = null, $headers = array(), $debug = false)
{
$method = strtoupper($method);
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
switch ($method) {
case "POST":
curl_setopt($ci, CURLOPT_POST, true);
if (!empty($postfields)) {
$tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields;
curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
}
break;
default:
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
break;
}
$ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
curl_setopt($ci, CURLOPT_URL, $url);
if ($ssl) {
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
}
//curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLINFO_HEADER_OUT, true);
/*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
$response = curl_exec($ci);
$requestinfo = curl_getinfo($ci);
$http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
if ($debug) {
echo "=====post data======\r\n";
var_dump($postfields);
echo "=====info===== \r\n";
print_r($requestinfo);
echo "=====response=====\r\n";
print_r($response);
}
curl_close($ci);
return $response;
//return array($http_code, $response,$requestinfo);
}
转载于:
解决方案:php抓取网页连接函数(1)_parse_url实现跳转
网站优化 • 优采云 发表了文章 • 0 个评论 • 107 次浏览 • 2022-09-22 19:13
php抓取网页连接函数div[1]_parse_url()实现跳转,第一个参数是url,第二个参数是变量名比如抓取notes.php的页面会调用div[1]_parse_url();返回一个dict对象mysql查询脚本f12select*fromtext1;使用select函数不必使用div[1]_parse_url(),而可以使用forloop。
抓取淘宝网页不需要用到select函数,可以用httpget请求数据库。
谢邀,
做个统计工具,模拟用户在淘宝前两页打开。然后分析这些打开的网页,按照事务等级和原始页,计算转化率,跳转次数,跳转间隔。
加了一个select,
原生的select(id,object,*,*)通常效率比较差,不如采用更加通用的形式forloop。比如id大于某个值并且返回时为空,所有的请求都走forloop,因为id这样不能重复。
通过正则提取数据。
匿名用户说的对,
自己写一个抓取脚本,预加载内容,查询时判断值为空,直接进行正则提取就好了。加上正则表达式、json、格式转换等各种功能。 查看全部
解决方案:php抓取网页连接函数(1)_parse_url实现跳转
php抓取网页连接函数div[1]_parse_url()实现跳转,第一个参数是url,第二个参数是变量名比如抓取notes.php的页面会调用div[1]_parse_url();返回一个dict对象mysql查询脚本f12select*fromtext1;使用select函数不必使用div[1]_parse_url(),而可以使用forloop。
抓取淘宝网页不需要用到select函数,可以用httpget请求数据库。

谢邀,
做个统计工具,模拟用户在淘宝前两页打开。然后分析这些打开的网页,按照事务等级和原始页,计算转化率,跳转次数,跳转间隔。
加了一个select,

原生的select(id,object,*,*)通常效率比较差,不如采用更加通用的形式forloop。比如id大于某个值并且返回时为空,所有的请求都走forloop,因为id这样不能重复。
通过正则提取数据。
匿名用户说的对,
自己写一个抓取脚本,预加载内容,查询时判断值为空,直接进行正则提取就好了。加上正则表达式、json、格式转换等各种功能。
phpn实现一个简单网站的爬虫网页连接函数(简称phpn)
网站优化 • 优采云 发表了文章 • 0 个评论 • 125 次浏览 • 2022-09-11 04:08
php抓取网页连接函数(简称phpn)phpn实现对网页数据的响应抓取和处理功能,简单说,phpn主要就是利用了request对象的四个方法来处理网页url地址等等数据。4个方法分别为get,post,delete,head。网站请求数据包post请求分为数据输出到header方法,和数据包输入到header方法。
header方法需要传入headermethod=http_refererrequest_type=xxx或者method=(http_referer)或者request_type=()都可以,这三种都可以用来处理url地址输出到header方法输出的数据可以是字符串,比如text和json串,也可以是pathname,比如ajax请求就会输出相应pathname的值,也可以是其他的一些json输出方法。
其实就是web服务器在看到请求数据包后,对这个数据进行格式化,但是格式化是可以生成php格式化的格式。post请求顾名思义是将数据(类似text或者json)传递给服务器的。类似的还有get请求的post方法,post方法分为trace和content两种,trace直接是返回值,也就是返回响应的地址和响应header的数据内容,content是将响应值输出到目标url,并且传递到method参数中作为响应。
网站访问地址php/homec/shoplandingc/homep/internal/home我们可以这样写php.exe/home访问地址是/home所以我们访问一下/home。接下来我们详细来看看phpn实现一个简单网站的page爬虫抓取网页连接我们最常用的是url,url里面有参数类型可以控制请求数据包输出格式,比如post的post格式格式化过后一般是{requestheader:texttype:json},content格式化过后也是{requestheader:text},也就是text,json格式化后就是{}{post请求数据包参数}。
所以我们写一个简单的抓取网页的page爬虫,我们首先要定义一个请求url:phpn/home通过post把服务器上的url地址传给服务器,定义一个请求数据包,格式化地址后保存在我们自己定义的header里面。可以用请求地址也可以用header,也可以是其他的一些json格式化地址方法header/然后通过get请求把我们通过post保存在header里面的数据解析出来。
我们还可以这样写:phpn/home定义一个处理json的方法,可以定义一个方法用来处理json地址,这样就可以通过json格式化我们的数据了phpn/home/text.js。 查看全部
phpn实现一个简单网站的爬虫网页连接函数(简称phpn)
php抓取网页连接函数(简称phpn)phpn实现对网页数据的响应抓取和处理功能,简单说,phpn主要就是利用了request对象的四个方法来处理网页url地址等等数据。4个方法分别为get,post,delete,head。网站请求数据包post请求分为数据输出到header方法,和数据包输入到header方法。

header方法需要传入headermethod=http_refererrequest_type=xxx或者method=(http_referer)或者request_type=()都可以,这三种都可以用来处理url地址输出到header方法输出的数据可以是字符串,比如text和json串,也可以是pathname,比如ajax请求就会输出相应pathname的值,也可以是其他的一些json输出方法。
其实就是web服务器在看到请求数据包后,对这个数据进行格式化,但是格式化是可以生成php格式化的格式。post请求顾名思义是将数据(类似text或者json)传递给服务器的。类似的还有get请求的post方法,post方法分为trace和content两种,trace直接是返回值,也就是返回响应的地址和响应header的数据内容,content是将响应值输出到目标url,并且传递到method参数中作为响应。

网站访问地址php/homec/shoplandingc/homep/internal/home我们可以这样写php.exe/home访问地址是/home所以我们访问一下/home。接下来我们详细来看看phpn实现一个简单网站的page爬虫抓取网页连接我们最常用的是url,url里面有参数类型可以控制请求数据包输出格式,比如post的post格式格式化过后一般是{requestheader:texttype:json},content格式化过后也是{requestheader:text},也就是text,json格式化后就是{}{post请求数据包参数}。
所以我们写一个简单的抓取网页的page爬虫,我们首先要定义一个请求url:phpn/home通过post把服务器上的url地址传给服务器,定义一个请求数据包,格式化地址后保存在我们自己定义的header里面。可以用请求地址也可以用header,也可以是其他的一些json格式化地址方法header/然后通过get请求把我们通过post保存在header里面的数据解析出来。
我们还可以这样写:phpn/home定义一个处理json的方法,可以定义一个方法用来处理json地址,这样就可以通过json格式化我们的数据了phpn/home/text.js。
web开发实战python系列入门书籍学python前端开发全套书籍中文版
网站优化 • 优采云 发表了文章 • 0 个评论 • 85 次浏览 • 2022-08-20 04:06
php抓取网页连接函数javascript可以实现类似操作,但是网页制作也不复杂嘛,你可以用类似网页翻页函数javascript解析html代码,由此实现分页等功能,html一直都是基础,
这个要分很多情况。另外...也要看你想干嘛了。php的话...慢慢来吧,我基本一个月刷一个页面。静态的话...也得看你做什么了。我有个朋友靠这个吃饭。一个页面要赚1w人民币,那就得1个月或者2个月,得看你水平怎么样了。
一,学习php,毕竟现在快速发展,好上手,收入还比较可观,框架的话很多,你可以看看。可以和同行交流一下,私信我,我可以给你分享我的学习心得。二,学习爬虫。如果你想做h5的seo就不要学php。可以参考我之前的回答。
node.js+npm快速发布爬虫目前我目前的困境是只能在几十个网站爬虫上线工程后,偶尔发现一个有用的例子,大概过了个半年多再去用nodejs+npm发布,上线又成了问题目前只能让爬虫更好用,让它不需要客户端配置,
一个页面用最简单的语言是最高效的,建议你学python或者php。要想有大功率,就先要解决编程的效率问题,看看下面的书列表。web开发-深入浅出mvc,laravel开发实战python系列入门书籍学python前端开发全套书籍中文版。 查看全部
web开发实战python系列入门书籍学python前端开发全套书籍中文版
php抓取网页连接函数javascript可以实现类似操作,但是网页制作也不复杂嘛,你可以用类似网页翻页函数javascript解析html代码,由此实现分页等功能,html一直都是基础,

这个要分很多情况。另外...也要看你想干嘛了。php的话...慢慢来吧,我基本一个月刷一个页面。静态的话...也得看你做什么了。我有个朋友靠这个吃饭。一个页面要赚1w人民币,那就得1个月或者2个月,得看你水平怎么样了。
一,学习php,毕竟现在快速发展,好上手,收入还比较可观,框架的话很多,你可以看看。可以和同行交流一下,私信我,我可以给你分享我的学习心得。二,学习爬虫。如果你想做h5的seo就不要学php。可以参考我之前的回答。

node.js+npm快速发布爬虫目前我目前的困境是只能在几十个网站爬虫上线工程后,偶尔发现一个有用的例子,大概过了个半年多再去用nodejs+npm发布,上线又成了问题目前只能让爬虫更好用,让它不需要客户端配置,
一个页面用最简单的语言是最高效的,建议你学python或者php。要想有大功率,就先要解决编程的效率问题,看看下面的书列表。web开发-深入浅出mvc,laravel开发实战python系列入门书籍学python前端开发全套书籍中文版。
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多
网站优化 • 优采云 发表了文章 • 0 个评论 • 92 次浏览 • 2022-08-11 00:06
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多,此外函数对设计非常友好,并且现代javascript语言比之java、python、.net等较为容易和方便,因此可以尽可能快的得到需要的数据。
1、php到iframe
2、php到cookie,对象存储和事件也可以实现。example1:php打开浏览器,后台开启支付宝,并且post请求,输入你的名字和密码,完成后把这个post请求发送给iframe,修改好密码,登录支付宝后,再把这个post请求返回给php。php也可以抓去订单的信息,但是iframe和cookie比,安全性一般。
例如如果我们抓包的时候是iframe,我们看到浏览器有跳转,是因为在解析页面js脚本,所以无论你怎么做,可能php在解析的时候就隐式加载了一个页面js脚本,所以解析是没有问题的。例如如果你在解析页面js脚本的时候,php在做的js请求的过程中呢?此时php在进行解析页面js脚本,还要处理服务器的响应,要统计响应数据,所以解析就慢了。
还有一种情况,两个php就算做一个js了,和cookie一样,是可以同时抓取到同一个页面的多个线程池的,方法就是如果php抓到同一个页面后,直接解析并返回给js。php抓取网页数据要取决于你的业务逻辑。比如说你想要抓取百度首页后台点击购买的这个链接的一千条,这样的简单举例。并没有扩展性太高,也不适合多台服务器。
所以我们需要合理的配置机器的流量,比如说一台服务器可以跑http的一千个并发,两台机器可以跑一千个并发,一个web服务器可以跑一千个并发。可以这样理解,一千个并发服务器就是一千台服务器。然后运行这些服务器上面运行php,我们就可以抓取包括首页的一千个并发,这个确实比单台机器抓取的慢。当然php也可以做到并发很多,可以帮助我们做idc并发,调整流量到每台机器上,一个站点加上这些机器的服务器就是一万台。
但是对于一千个并发,这样做并不现实。不要操心idc机器的事,你是做快递的,不可能每一单都往idc过去送,就像快递单上面的字很小,不可能每一张都打印成xxx快递。所以那我们需要高配置的php,支持opennginx,支持workerprocessor,速度比php本身快的多。
2、php抓取网页连接函数php拿到本地文件位置有两种方法,一种是用php从网页上读取信息,一种是通过dns抓取,然后拼接字符串。这两种方法一定是比字符串要快很多的。当然,在寻找网页的时候,首先要查找ip,再去找域名。首先定位tomcat路径:dns(原字符串):。然后需要通过抓包命令查看一下dns解析的返回。 查看全部
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多
php抓取网页连接函数php采用对象进行存储和写入通常比单一的字符串快很多,此外函数对设计非常友好,并且现代javascript语言比之java、python、.net等较为容易和方便,因此可以尽可能快的得到需要的数据。
1、php到iframe

2、php到cookie,对象存储和事件也可以实现。example1:php打开浏览器,后台开启支付宝,并且post请求,输入你的名字和密码,完成后把这个post请求发送给iframe,修改好密码,登录支付宝后,再把这个post请求返回给php。php也可以抓去订单的信息,但是iframe和cookie比,安全性一般。
例如如果我们抓包的时候是iframe,我们看到浏览器有跳转,是因为在解析页面js脚本,所以无论你怎么做,可能php在解析的时候就隐式加载了一个页面js脚本,所以解析是没有问题的。例如如果你在解析页面js脚本的时候,php在做的js请求的过程中呢?此时php在进行解析页面js脚本,还要处理服务器的响应,要统计响应数据,所以解析就慢了。
还有一种情况,两个php就算做一个js了,和cookie一样,是可以同时抓取到同一个页面的多个线程池的,方法就是如果php抓到同一个页面后,直接解析并返回给js。php抓取网页数据要取决于你的业务逻辑。比如说你想要抓取百度首页后台点击购买的这个链接的一千条,这样的简单举例。并没有扩展性太高,也不适合多台服务器。

所以我们需要合理的配置机器的流量,比如说一台服务器可以跑http的一千个并发,两台机器可以跑一千个并发,一个web服务器可以跑一千个并发。可以这样理解,一千个并发服务器就是一千台服务器。然后运行这些服务器上面运行php,我们就可以抓取包括首页的一千个并发,这个确实比单台机器抓取的慢。当然php也可以做到并发很多,可以帮助我们做idc并发,调整流量到每台机器上,一个站点加上这些机器的服务器就是一万台。
但是对于一千个并发,这样做并不现实。不要操心idc机器的事,你是做快递的,不可能每一单都往idc过去送,就像快递单上面的字很小,不可能每一张都打印成xxx快递。所以那我们需要高配置的php,支持opennginx,支持workerprocessor,速度比php本身快的多。
2、php抓取网页连接函数php拿到本地文件位置有两种方法,一种是用php从网页上读取信息,一种是通过dns抓取,然后拼接字符串。这两种方法一定是比字符串要快很多的。当然,在寻找网页的时候,首先要查找ip,再去找域名。首先定位tomcat路径:dns(原字符串):。然后需要通过抓包命令查看一下dns解析的返回。
php抓取网页连接函数看php_xpath获取全部元素
网站优化 • 优采云 发表了文章 • 0 个评论 • 153 次浏览 • 2022-08-02 07:03
php抓取网页连接函数看php_xpath获取全部元素functionfav(){return'';}varfavname=$_get['href'];varhomehtml=$_get['href'];vara=preg_encode(""+$_get['href'],'utf-8');vara=preg_encode(""+$_get['href']+">",'utf-8');varp=fav();p。
page=homehtml。encode(""+p);a。src=$_get['href']。string();returna;}。
php的话,你可以看看expansion或者argumenthook,具体的实现可以查阅对应的php代码;html的话,则看看w3chtmldocumentation,
$_get['href']?
具体看官方的文档吧,案例和代码都有。
一个简单点儿的fav,可以百度preg_encode:utf-8其实也不复杂,用通配符的话,估计比较麻烦。如果像我们公司现在自己干点php发布的业务,那后台可能有时候也要做各种高级php的preg_encode,但是相对post/get/put的话,还是非常好办的。前后端分离开来比较麻烦,可以部署下全站的php工程做一个类似xpath_encode的函数,w3c/w3cpreg_encode之类的,方便部署。 查看全部
php抓取网页连接函数看php_xpath获取全部元素
php抓取网页连接函数看php_xpath获取全部元素functionfav(){return'';}varfavname=$_get['href'];varhomehtml=$_get['href'];vara=preg_encode(""+$_get['href'],'utf-8');vara=preg_encode(""+$_get['href']+">",'utf-8');varp=fav();p。

page=homehtml。encode(""+p);a。src=$_get['href']。string();returna;}。
php的话,你可以看看expansion或者argumenthook,具体的实现可以查阅对应的php代码;html的话,则看看w3chtmldocumentation,

$_get['href']?
具体看官方的文档吧,案例和代码都有。
一个简单点儿的fav,可以百度preg_encode:utf-8其实也不复杂,用通配符的话,估计比较麻烦。如果像我们公司现在自己干点php发布的业务,那后台可能有时候也要做各种高级php的preg_encode,但是相对post/get/put的话,还是非常好办的。前后端分离开来比较麻烦,可以部署下全站的php工程做一个类似xpath_encode的函数,w3c/w3cpreg_encode之类的,方便部署。
php抓取网页连接函数:data,可以看出url结构上是匹配的
网站优化 • 优采云 发表了文章 • 0 个评论 • 88 次浏览 • 2022-07-09 21:02
php抓取网页连接函数:data,可以看出url结构上是匹配的,所以这个问题的关键是连接不上是出于什么问题导致的。比如说location?getesturl()可以直接返回/,除非想获取html格式的url,那可以用curl的options参数配置连接参数,参数里有:[scheme='/']。如果想获取/连接里的内容,可以用data.url拿出来。
比如说:url="-xxx-free-javascript/sharing/html/app/helloworld.html";@app(registereditor='jsp')curl(url,'/',cmd=false);location=data.url;不过这样挂在@app就作废了,因为@app不是任何app都可以上,上到@app就会挂掉php爬虫工程师二三事连接到对应的app可以设置@app参数,自定义爬虫名,做一些有特色的页面效果:打开html时需要flag=0,选择爬取方式js控制的页面效果:通过以上的抓取title,我们可以不断对headers做手脚,比如下面的部分:我们可以通过爬虫的post到目标页面,再post到收起来的websocket会话:if(!success&&no_timeout||endif){//definewebsocketsessionvarsess=session()varrequest=newrequest(streamname='https_post',success='success',end='end',fail='fail',success='error',end='end',success='fail',end='end',fail='fail',end='fail',success='fail',end='end',success='fail',end='end',fail='fail',end='fail',fail='fail',end='fail',success='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='f。 查看全部
php抓取网页连接函数:data,可以看出url结构上是匹配的

php抓取网页连接函数:data,可以看出url结构上是匹配的,所以这个问题的关键是连接不上是出于什么问题导致的。比如说location?getesturl()可以直接返回/,除非想获取html格式的url,那可以用curl的options参数配置连接参数,参数里有:[scheme='/']。如果想获取/连接里的内容,可以用data.url拿出来。

比如说:url="-xxx-free-javascript/sharing/html/app/helloworld.html";@app(registereditor='jsp')curl(url,'/',cmd=false);location=data.url;不过这样挂在@app就作废了,因为@app不是任何app都可以上,上到@app就会挂掉php爬虫工程师二三事连接到对应的app可以设置@app参数,自定义爬虫名,做一些有特色的页面效果:打开html时需要flag=0,选择爬取方式js控制的页面效果:通过以上的抓取title,我们可以不断对headers做手脚,比如下面的部分:我们可以通过爬虫的post到目标页面,再post到收起来的websocket会话:if(!success&&no_timeout||endif){//definewebsocketsessionvarsess=session()varrequest=newrequest(streamname='https_post',success='success',end='end',fail='fail',success='error',end='end',success='fail',end='end',fail='fail',end='fail',success='fail',end='end',success='fail',end='end',fail='fail',end='fail',fail='fail',end='fail',success='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',end='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='fail',fail='fail',end='f。
php抓取网页连接函数arraydeliever要连接java的native数据库吗
网站优化 • 优采云 发表了文章 • 0 个评论 • 92 次浏览 • 2022-07-02 09:05
php抓取网页连接函数是post连接sqldml连接。而数据库连接函数是select连接。post连接函数是post方法,post是类名,post是方法名,post是参数,是动态数据库连接函数是dml方法,dml是类名,dml是方法名,dml是参数,是静态数据库连接函数是select方法,select是方法名,select是参数,是java反射数据库连接函数是extjava反射数据库连接函数是extjava反射。
是java反射数据库连接函数arraydeliever要连接java的native数据库吗?可以用反射java动态数据库,将post的数据(如查询表名)转换为java的静态数据库连接。不想用c++写arraydeliever连接的话,jdbc(javadriverbroker)本身内置的arraydeliever方法或用jdbcmsgen包定义的arraydeliever:arraydeliever(auto,java.util.*,jarjdbc);//java.util包的arraydeliever方法.socket(socket,connectionfactory,jdbc);//jdbc包的socket方法我自己先看过下面这个网站的,你看下能不能启用jdbc:jdbc安装教程:jdbc教程。
反射是gc算法,要求数据库连接必须一次性作数据库连接,而post是对应于执行方法的http方法,且两个方法可以有重叠, 查看全部
php抓取网页连接函数arraydeliever要连接java的native数据库吗

php抓取网页连接函数是post连接sqldml连接。而数据库连接函数是select连接。post连接函数是post方法,post是类名,post是方法名,post是参数,是动态数据库连接函数是dml方法,dml是类名,dml是方法名,dml是参数,是静态数据库连接函数是select方法,select是方法名,select是参数,是java反射数据库连接函数是extjava反射数据库连接函数是extjava反射。

是java反射数据库连接函数arraydeliever要连接java的native数据库吗?可以用反射java动态数据库,将post的数据(如查询表名)转换为java的静态数据库连接。不想用c++写arraydeliever连接的话,jdbc(javadriverbroker)本身内置的arraydeliever方法或用jdbcmsgen包定义的arraydeliever:arraydeliever(auto,java.util.*,jarjdbc);//java.util包的arraydeliever方法.socket(socket,connectionfactory,jdbc);//jdbc包的socket方法我自己先看过下面这个网站的,你看下能不能启用jdbc:jdbc安装教程:jdbc教程。
反射是gc算法,要求数据库连接必须一次性作数据库连接,而post是对应于执行方法的http方法,且两个方法可以有重叠,
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 86 次浏览 • 2022-06-25 12:32
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
【技术分享】梨子带你刷burpsuite靶场系列之高级漏洞篇
网站优化 • 优采云 发表了文章 • 0 个评论 • 107 次浏览 • 2022-06-20 17:27
高级漏洞篇介绍
相对于服务器端漏洞篇和客户端漏洞篇,高级漏洞篇需要更深入的知识以及更复杂的利用手段,该篇也是梨子的全程学习记录,力求把漏洞原理及利用等讲的通俗易懂。
Web缓存投毒专题
什么是Web缓存投毒?
首先了解一下什么是Web缓存,Web缓存就是服务器会先将之前没见过的请求对应的响应缓存下来,然后当有认为是相同请求的时候直接将缓存发给用户,这样可以减轻服务器的负荷。但是服务器端在识别的时候是根据特征来的,如果两个请求的特征相同即会认为是相同的请求,此时如果攻击者首先触发服务器缓存附有恶意payload的响应,当其他用户发送相同请求时即会接收到这个恶意的响应。从影响范围来看,一旦成功缓存被投毒的响应,会影响到大量的用户,比起以往某些只能针对某个用户发起的攻击,危害大很多很多。
Web缓存的流程是什么样的?
其实前面梨子已经讲了差不多了,这一小节主要是补充讲解。其实这个Web缓存不是永久的,它只保存固定的一段时间,过了这个时间以后就会重新生成缓存,所以这类攻击还是有一定难度的。那么服务器端怎么识别等效的请求呢,我们接下来介绍一下缓存键的概念。
缓存键
缓存键就是服务器端用来识别等效请求的一系列特征的统称。一般缓存键包括请求行和Host头。服务器端只识别设置为缓存键的特征是否相同,这也就导致了Web缓存投毒漏洞的产生。
如何构造Web缓存投毒攻击?
一般构造Web缓存投毒攻击需要以下几个步骤
识别并确认不会被缓存的输入
我们想要构造Web缓存投毒就需要我们的输入能够反馈在响应中,但是如果我们的输入被设置为缓存键,那就不可能有用户发出等效请求,所以我们需要不断调试直到找到我们的输入既不会是缓存键又可以被反馈在被缓存的响应中。这样才能保证被投毒的响应缓存被投放到受害者那里,burp推荐了一款插件Param Miner来辅助我们寻找这样的不会被缓存的字段。
Param Miner
这款插件会自动去检测不会被缓存的字段使用方法很简单,只需要右键选择Guess headers即可。并且为了不给真实用户造成困扰,可以开启cache buster(缓存粉碎机)。
从服务器诱发被投毒的响应
我们确认了不会被缓存的输入以后,我们就要看服务端是如何处理这个输入的,如果可以动态反馈到响应中,就是我们能够发动Web缓存投毒的关键。
得到被缓存的响应
我们的输入可以被反馈到响应中还不够,还得能够生成缓存,这样才可以真正地将恶意payload落地。所以我们为此还是要不断调试才能成功找到生成投毒缓存的操作。
基于缓存设计缺陷的Web缓存投毒攻击
使用Web缓存投毒发动XSS攻击
因为XSS攻击也是有一部分是输入被反馈在响应中,所以Web缓存投毒当然也可以用来发动XSS攻击。我们关注这样一对请求和响应
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: innocent-website.co.uk<br />HTTP/1.1 200 OKCache-Control: public
我们观察到X-Forwarded-Host指定的URL会代替Host的值被反馈在响应中,并且X-Forwarded-Host是不会被缓存的字段,但是Host和请求行是缓存键。所以所有Host为的用户请求/en?region=uk都会接收到被投毒的响应,像这样
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: a.">alert(1)"<br />HTTP/1.1 200 OKCache-Control: publicalert(1)"/cms/social.png" />
当然了,alert只是弹窗验证而已,攻击者可以构造更复杂的XSS payload获取更多的东西。
使用Web缓存投毒发动不安全的资源导入
有的应用程序会导入Host指定服务器的某个资源,比如JS资源,但是如果我们像上面一样通过X-Forwarded-Host代替Host进行导入,则可能导入同名但是内容为恶意payload的JS资源,例如
GET / HTTP/1.1Host: innocent-website.comX-Forwarded-Host: evil-user.netUser-Agent: Mozilla/5.0 Firefox/57.0<br />HTTP/1.1 200 OK
配套靶场:使用不被缓存头的Web缓存投毒
首先我们随便设置一个查询参数,然后随便设置一个值,只是为了测试Web缓存
从上图来看,现在是没有产生Web缓存的,然后我们插入到X-Forwarded-Host字段会替换掉本应是Host字段的值,这样就相当于resources/js/tracking.js这个文件是可以伪造的,然后我们再go一下看一下缓存之后是什么样子的
好,我们现在看到Web缓存已经生成了,这样,所有被识别为发出等效请求的用户都会接收到这个响应,就会访问到”中毒”后的网页,模拟结束,现在我们到Exploit Server把payload写入到这个同名文件下
然后随便找一个靶场里面的帖子进去,抓包改包,把Exploit Server的域名插到X-Forwarded-Host字段下,然后重放几次让Web缓存生成
然后在浏览器访问这个页面,就能实现弹窗了,如果没生效就再缓存一次就行
使用不被缓存Cookie的Web缓存投毒
像上面一样,我们观察这样的请求
GET /blog/post.php?mobile=1 HTTP/1.1Host: innocent-website.comUser-Agent: Mozilla/5.0 Firefox/57.0Cookie: language=pl;Connection: close
我们看到应用程序通过Cookie中的language的值来调整网站的语言,当该请求生成响应后,等效请求的用户收到的就是波兰语(pl)的页面了。当然了,这种攻击方式比较少,因为很容易因为影响到正常用户被发现。
配套靶场:使用不被缓存Cookie的Web缓存投毒
首先我们观察一下请求与响应的关系
我们可以看到Cookie中的fehost字段被自动拼接到响应中的script节点下,那么我们可以修改这个字段的值实现XSS攻击
我们可以看到现在Web缓存已经生成了,并且我们修改后的字段值也是直接拼接到响应里,为了防止直接插XSS语句不生效,我们在前后多加了一般的script标签分别把前面后面的script标签闭合掉,从而成功发动XSS
使用多重头发动Web缓存投毒攻击
如果我们想要发动更高级的攻击可以操纵多个请求头来实现,现在我们考虑这样的一个情况,就是应用程序默认是使用HTTPS协议传输的,但是如果我们使用HTTP协议访问会自动触发一个指向Host的重定向,但是我们可以通过X-Forwarded-Host代替Host的值重定向到恶意域,像这样
GET /random HTTP/1.1Host: innocent-site.comX-Forwarded-Proto: http<br />HTTP/1.1 301 moved permanentlyLocation: https://innocent-site.com/random
配套靶场:使用多重头的Web缓存投毒
首先我们还是把HTTP History中的首页的那个包发到Repeater里面,随便加一个X-Forwarded-Host,Go一下,发现并没有什么变化,而且/resources/js/tracking.js变成了相对路径,那么我们再观察一下是相对于谁呢?那么我们就想办法让页面重定向,这就需要X-Forwarded-Scheme字段了,这个字段作用和X-Forwarded-Proto的效果是一样的,如果请求方法是这个方法指定的则会重定向到用HTTPS请求主机名
我们发现当同时有X-Forwarded-Host和X-Forwarded-Scheme两个字段的时候,就会有组合效果,因为请求方法为HTTP,则会重定向HTTPS流去请求主机名,然后这时候我们已经将主机名转为我们指定的主机名,这样它的相对路径就可以生效了,我们先去Exploit Server构造Payload
然后我们在Repeater里面抓包改包,让服务器产生Web缓存
我们可以看到重定向成功了,会跳转到Exploit Server下的/resources/js/tracking.js
利用暴露大量信息的响应
有时,网站会泄露大量有关自身及其行为的信息,从而使自己更容易遭受Web缓存投毒攻击。### Cache-control指令 有的时候响应会暴露缓存有效期等敏感信息,例如
HTTP/1.1 200 OKVia: 1.1 varnish-v4Age: 174Cache-Control: public, max-age=1800
有效期可以帮助我们去计算时间从而达到某种恶意目的。### Vary头 Vary头指定了一些可以被视为缓存键的字段列表,常见的如User-Agent头,应用程序通过其可以仅向指定用户群投放响应,也可以利用这个特点向特定用户群发动Web缓存投毒攻击。
配套靶场:使用未知请求头的定向Web缓存投毒
找到隐藏字段X-Host,然后像之前那样生成Web缓存,直接放生成缓存的结果
然后我们发现与之前的不同是User-Agent也是缓存键,所以我们还要去留言板钓他们的User-Agent
进到Exploit Server查收钓到的User-Agent,然后再结合刚才的那个生成新的Web缓存
使用Web缓存投毒发动基于DOM的漏洞攻击
不仅可以通过Web缓存投毒导入恶意JS文件,还可以导入恶意的JSON字符串,例如
{"someProperty" : ""}
如果这条payload被传递到恶意的sink就可能触发基于DOM的漏洞,如果想要让网站加载恶意JSON就需要CORS授权允许跨站,例如
HTTP/1.1 200 OKContent-Type: application/jsonAccess-Control-Allow-Origin: *<br />{ "malicious json" : "malicious json"}
配套靶场:在严格可缓存性标准下使用Web缓存投毒发动基于DOM的漏洞攻击
首先我们还是,默认使用Param Miner扫到可伪造字段X-Forwarded-Host。然后我们把首页放到Repeater里看一下有没有什么新东西
这些应该都是json的东西,大概的行为就是以data.host下的/resources/json/geolocate.json为参数执行initGeoLocate函数,然后我们再追踪一下这个geolocate.js和geolocate.json
从图上来看,geolocate.js会把j.country变量直接拼接进来,所以我们看一下这个country变量在哪,应该是在geolocate.json文件里面
那么我们就需要在Exploit Server中伪造属于我们的geolocate.json文件,只是country的值换成xss语句
好的,然后我们就可以生成Web缓存了
刷新首页成功加载我们自己的geolocate.json
注:扫到字段以后要把Param Miner的cachebuster都关了,不然永远也不会生成缓存
Web缓存投毒链
如果将我们学过的几种漏洞与Web缓存投毒结合起来,可以发动更高级的攻击。下面我们直接通过一道靶场来深入理解。
Web缓存投毒链
我们识别到两个可用来投毒的字段:X-Forwarded-Host、X-Original-URL,然后分析一下/resources/js/translations.js这个文件,看一下initTranslations()这个函数是怎么运作的
首先我们来看一下提取现在网页的语言的函数
大概是这么一个流程,然后是翻译函数,因为只翻译首页,所以所谓的翻译就是一对一的替换,然后我们留意一下红框部分,就是说除了英语其他的都会执行翻译,也就是说除了英语以外的我们都能用来插入DOM-XSS语句,考虑到乱码问题,我们选择en-gb这个,虽然也是英语,但是因为代码里严格匹配en,所以这个也是可以用来插入的,而且也不用考虑乱码的问题,现在我们去Exploit Server写入DOM-XSS语句进去
然后我们需要抓取/?localized=1这个包,因为你选择翻译的时候会请求这个页面,然后修改X-Forwarded-Host字段为Exploit Server的,并产生Web缓存
但是这是翻译页面,我们要怎样才能让用户首先进到的就是我们的翻译页面呢,那就是在首页加入一个X-Original-URL字段指向翻译页面,这样访问首页的人就都会被重定向到这个页面了
因为需要生成两个Web缓存并且需要在靶场访问的时候同时在生效中,所以需要多试几次
基于缓存实现缺陷的Web缓存投毒攻击
缓存键缺陷
传统的攻击经常通过将payload注入查询字符串实现,但是请求行是缓存键,这就导致不可能会有用户发出等效请求,也就接收不到投毒响应缓存,但是如果有CDN的话,他们会将缓存键内容进行某些处理之后再存入缓存,例如
尤其是前两条,即使我们注入payload到查询字符串或参数中,用户也可能收到被投毒的响应缓存。
识别合适的缓存断言
首先我们要识别合适的缓存断言以测试缓存的过程。我们需要知道我们接收到的响应是来自缓存还是服务器,比如从以下地方可以得到反馈
有的时候应用程序会使用第三方的缓存组件,此时可以通过查阅相关文档的方式得知缓存的过程。例如,基于Akamai的网站可能支持Pragma:akamai-x-get-cache-key,它可以在响应标头中显示缓存键。
GET /?param=1 HTTP/1.1Host: innocent-website.comPragma: akamai-x-get-cache-key<br />HTTP/1.1 200 OKX-Cache-Key: innocent-website.com/?param=1
探测缓存键的处理过程
我们应该还要观察缓存是否对缓存键有其他的处理。比如剔除Host中的端口号等。下面我们来关注这样一对请求和响应。它会动态将Host值拼接到Location中
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com/enCache-Status: miss
然后我们在Host中随便加入一个端口,观察一下响应
GET / HTTP/1.1Host: vulnerable-website.com:1337<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: miss
我们再去掉端口号重新发送请求
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: hit
发现已经生成缓存,但是缓存是我们加了端口号发出时的响应版本。这就说明端口号是不会加入缓存键中的。
识别可利用的漏洞
讲了这么多,其实Web缓存投毒的危害程度完全取决于其能够用来发动的攻击,常见的有像反射型XSS、开放重定向等客户端漏洞。并且Web缓存投毒不需要用户点击任何链接,可能在发出正常的请求时也会接收到被投毒的响应缓存。
缓存键缺陷的利用
不被缓存的端口号
前面我们介绍过,缓存键有时候可能不会只会缓存域名或主机名而不缓存端口号。所以我们可以利用这个特点发动如DDOS(向任意端口号发出大量请求)、XSS(在端口号位置插入payload)等攻击。
不被缓存的查询字符串
探测不被缓存的查询字符串
有的应用程序并不会在响应中告知是否产生缓存。而且如果查询字符串不是缓存键的话,即使不同的查询字符串也会得到相同的响应缓存。那么我们可以在其他请求头中下手,例如加在Accept-Encoding字段中
Accept-Encoding: gzip, deflate, cachebusterAccept: */*, text/cachebusterCookie: cachebuster=1Origin: https://cachebuster.vulnerable-website.com
其实也可以在Param Miner开启动态的缓存粉碎机(cachebuster)。还有一种办法就是修改不同的路径,但是仍然可以的到相同的响应缓存。例如
Apache: GET //Nginx: GET /%2FPHP: GET /index.php/xyz.NET GET /(A(xyz)/
利用不被缓存的查询字符串
查询字符串不会被缓存可能会扩大XSS攻击面,因为附有XSS payload的查询字符串的请求在缓存看来与普通请求无异。但是普通用户可能就会接收到被投毒的响应缓存。
配套靶场:通过不被缓存的查询字符串的Web缓存响应
这一关考察就是常规的web缓存投毒,这一关并没有对查询字符串进行绑定,所以同一个路径下即识别为等效页面即投放缓存,那么我们直接在URL里面投毒
响应中的Age字段可以告诉我们它的缓存已经生效多长时间了,这一关设定的是35秒后重新缓存,可以作为我们投毒的一个信号标,于是我们看一下这时候访问根目录的响应是怎样的
我们得到的就是投毒后的缓存,这时访问首页的人都会接收到投毒的缓存
不被缓存的查询参数
有的时候缓存仅将某几个查询参数排除在缓存键中,例如utm_content 等utm参数。但是这种攻击方式会因为参数不太会被专门处理而没有那么大的危害。但是如果有功能点可以处理整个URL则可能会有转机。
配套靶场:通过不被缓存的查询参数的Web缓存响应
这一关考点是它只有某些查询参数不会作为缓存键,我们扫到utm_content这个查询参数不是缓存键,所以我们可以用这个参数实现Web缓存投毒
等到重新生成Web缓存的时候,成功通关
缓存参数隐藏
有的时候因为解析差异,可以将某些本来是缓存键的查询参数隐藏到非缓存键中。例如查询字符串中通过与(&)区分各个参数,通过问号(?)识别查询参数的开始。但是如果有多个问号,就会以第一个问号作为查询参数的开始,例如
GET /?example=123?excluded_param=bad-stuff-here
这时,excluded_param就不会被作为缓存键处理。
利用参数解析差异
有的应用程序有一些特殊的查询参数解析规则,比如Ruby on Rails框架将与(&)和分号 (;)作为参数分隔符。但是如果缓存不支持这样解析参数,就会造成差异。例如
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
如上所示,keyed_param就会被视为缓存键,而excluded_param不会,而且只会被缓存解析成两个参数
keyed_param=abcexcluded_param=123;keyed_param=bad-stuff-here
而被Ruby on Rails解析成三个参数
keyed_param=abcexcluded_param=123keyed_param=bad-stuff-here
此时keyed_param的值就会被覆盖为bad-stuff-here,也会得到由这个值生成的响应,但是在缓存看来只要该参数值为abc的都会被视为该响应的等效请求。利用这种特点可以发动JSONP攻击,它会将回调函数名附在查询参数中,例如
GET /jsonp?callback=innocentFunction
我们可以利用上面的特点替换成我们指定的函数名。
配套靶场:参数差异
这一关主要是考察缓存与后台对URL参数的处理方式不同导致的Web缓存投毒,通过扫描得知utm_content是可伪装的缓存键,后台将(&)和(;)识别为分界符,这样就可以利用这种差异覆盖回调参数的值为我们指定的函数
这样访问首页的人就也会接收到我们的投毒缓存了
利用支持fat GET请求
有的时候请求方法并不是缓存键,这时候如果支持fat GET请求方法就也可以用来进行Web缓存投毒。fat GET请求很特殊,拥有URL查询参数和正文参数,而只有请求行是缓存键,而且应用程序会从正文参数获取参数值,例如我们构造这样的请求
GET /?param=innocent HTTP/1.1…param=bad-stuff-here 查看全部
【技术分享】梨子带你刷burpsuite靶场系列之高级漏洞篇
高级漏洞篇介绍
相对于服务器端漏洞篇和客户端漏洞篇,高级漏洞篇需要更深入的知识以及更复杂的利用手段,该篇也是梨子的全程学习记录,力求把漏洞原理及利用等讲的通俗易懂。
Web缓存投毒专题
什么是Web缓存投毒?
首先了解一下什么是Web缓存,Web缓存就是服务器会先将之前没见过的请求对应的响应缓存下来,然后当有认为是相同请求的时候直接将缓存发给用户,这样可以减轻服务器的负荷。但是服务器端在识别的时候是根据特征来的,如果两个请求的特征相同即会认为是相同的请求,此时如果攻击者首先触发服务器缓存附有恶意payload的响应,当其他用户发送相同请求时即会接收到这个恶意的响应。从影响范围来看,一旦成功缓存被投毒的响应,会影响到大量的用户,比起以往某些只能针对某个用户发起的攻击,危害大很多很多。
Web缓存的流程是什么样的?
其实前面梨子已经讲了差不多了,这一小节主要是补充讲解。其实这个Web缓存不是永久的,它只保存固定的一段时间,过了这个时间以后就会重新生成缓存,所以这类攻击还是有一定难度的。那么服务器端怎么识别等效的请求呢,我们接下来介绍一下缓存键的概念。
缓存键
缓存键就是服务器端用来识别等效请求的一系列特征的统称。一般缓存键包括请求行和Host头。服务器端只识别设置为缓存键的特征是否相同,这也就导致了Web缓存投毒漏洞的产生。
如何构造Web缓存投毒攻击?
一般构造Web缓存投毒攻击需要以下几个步骤
识别并确认不会被缓存的输入
我们想要构造Web缓存投毒就需要我们的输入能够反馈在响应中,但是如果我们的输入被设置为缓存键,那就不可能有用户发出等效请求,所以我们需要不断调试直到找到我们的输入既不会是缓存键又可以被反馈在被缓存的响应中。这样才能保证被投毒的响应缓存被投放到受害者那里,burp推荐了一款插件Param Miner来辅助我们寻找这样的不会被缓存的字段。
Param Miner
这款插件会自动去检测不会被缓存的字段使用方法很简单,只需要右键选择Guess headers即可。并且为了不给真实用户造成困扰,可以开启cache buster(缓存粉碎机)。
从服务器诱发被投毒的响应
我们确认了不会被缓存的输入以后,我们就要看服务端是如何处理这个输入的,如果可以动态反馈到响应中,就是我们能够发动Web缓存投毒的关键。
得到被缓存的响应
我们的输入可以被反馈到响应中还不够,还得能够生成缓存,这样才可以真正地将恶意payload落地。所以我们为此还是要不断调试才能成功找到生成投毒缓存的操作。
基于缓存设计缺陷的Web缓存投毒攻击
使用Web缓存投毒发动XSS攻击
因为XSS攻击也是有一部分是输入被反馈在响应中,所以Web缓存投毒当然也可以用来发动XSS攻击。我们关注这样一对请求和响应
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: innocent-website.co.uk<br />HTTP/1.1 200 OKCache-Control: public
我们观察到X-Forwarded-Host指定的URL会代替Host的值被反馈在响应中,并且X-Forwarded-Host是不会被缓存的字段,但是Host和请求行是缓存键。所以所有Host为的用户请求/en?region=uk都会接收到被投毒的响应,像这样
GET /en?region=uk HTTP/1.1Host: innocent-website.comX-Forwarded-Host: a.">alert(1)"<br />HTTP/1.1 200 OKCache-Control: publicalert(1)"/cms/social.png" />
当然了,alert只是弹窗验证而已,攻击者可以构造更复杂的XSS payload获取更多的东西。
使用Web缓存投毒发动不安全的资源导入
有的应用程序会导入Host指定服务器的某个资源,比如JS资源,但是如果我们像上面一样通过X-Forwarded-Host代替Host进行导入,则可能导入同名但是内容为恶意payload的JS资源,例如
GET / HTTP/1.1Host: innocent-website.comX-Forwarded-Host: evil-user.netUser-Agent: Mozilla/5.0 Firefox/57.0<br />HTTP/1.1 200 OK
配套靶场:使用不被缓存头的Web缓存投毒
首先我们随便设置一个查询参数,然后随便设置一个值,只是为了测试Web缓存
从上图来看,现在是没有产生Web缓存的,然后我们插入到X-Forwarded-Host字段会替换掉本应是Host字段的值,这样就相当于resources/js/tracking.js这个文件是可以伪造的,然后我们再go一下看一下缓存之后是什么样子的
好,我们现在看到Web缓存已经生成了,这样,所有被识别为发出等效请求的用户都会接收到这个响应,就会访问到”中毒”后的网页,模拟结束,现在我们到Exploit Server把payload写入到这个同名文件下
然后随便找一个靶场里面的帖子进去,抓包改包,把Exploit Server的域名插到X-Forwarded-Host字段下,然后重放几次让Web缓存生成
然后在浏览器访问这个页面,就能实现弹窗了,如果没生效就再缓存一次就行
使用不被缓存Cookie的Web缓存投毒
像上面一样,我们观察这样的请求
GET /blog/post.php?mobile=1 HTTP/1.1Host: innocent-website.comUser-Agent: Mozilla/5.0 Firefox/57.0Cookie: language=pl;Connection: close
我们看到应用程序通过Cookie中的language的值来调整网站的语言,当该请求生成响应后,等效请求的用户收到的就是波兰语(pl)的页面了。当然了,这种攻击方式比较少,因为很容易因为影响到正常用户被发现。
配套靶场:使用不被缓存Cookie的Web缓存投毒
首先我们观察一下请求与响应的关系
我们可以看到Cookie中的fehost字段被自动拼接到响应中的script节点下,那么我们可以修改这个字段的值实现XSS攻击
我们可以看到现在Web缓存已经生成了,并且我们修改后的字段值也是直接拼接到响应里,为了防止直接插XSS语句不生效,我们在前后多加了一般的script标签分别把前面后面的script标签闭合掉,从而成功发动XSS
使用多重头发动Web缓存投毒攻击
如果我们想要发动更高级的攻击可以操纵多个请求头来实现,现在我们考虑这样的一个情况,就是应用程序默认是使用HTTPS协议传输的,但是如果我们使用HTTP协议访问会自动触发一个指向Host的重定向,但是我们可以通过X-Forwarded-Host代替Host的值重定向到恶意域,像这样
GET /random HTTP/1.1Host: innocent-site.comX-Forwarded-Proto: http<br />HTTP/1.1 301 moved permanentlyLocation: https://innocent-site.com/random
配套靶场:使用多重头的Web缓存投毒
首先我们还是把HTTP History中的首页的那个包发到Repeater里面,随便加一个X-Forwarded-Host,Go一下,发现并没有什么变化,而且/resources/js/tracking.js变成了相对路径,那么我们再观察一下是相对于谁呢?那么我们就想办法让页面重定向,这就需要X-Forwarded-Scheme字段了,这个字段作用和X-Forwarded-Proto的效果是一样的,如果请求方法是这个方法指定的则会重定向到用HTTPS请求主机名
我们发现当同时有X-Forwarded-Host和X-Forwarded-Scheme两个字段的时候,就会有组合效果,因为请求方法为HTTP,则会重定向HTTPS流去请求主机名,然后这时候我们已经将主机名转为我们指定的主机名,这样它的相对路径就可以生效了,我们先去Exploit Server构造Payload
然后我们在Repeater里面抓包改包,让服务器产生Web缓存
我们可以看到重定向成功了,会跳转到Exploit Server下的/resources/js/tracking.js
利用暴露大量信息的响应
有时,网站会泄露大量有关自身及其行为的信息,从而使自己更容易遭受Web缓存投毒攻击。### Cache-control指令 有的时候响应会暴露缓存有效期等敏感信息,例如
HTTP/1.1 200 OKVia: 1.1 varnish-v4Age: 174Cache-Control: public, max-age=1800
有效期可以帮助我们去计算时间从而达到某种恶意目的。### Vary头 Vary头指定了一些可以被视为缓存键的字段列表,常见的如User-Agent头,应用程序通过其可以仅向指定用户群投放响应,也可以利用这个特点向特定用户群发动Web缓存投毒攻击。
配套靶场:使用未知请求头的定向Web缓存投毒
找到隐藏字段X-Host,然后像之前那样生成Web缓存,直接放生成缓存的结果
然后我们发现与之前的不同是User-Agent也是缓存键,所以我们还要去留言板钓他们的User-Agent
进到Exploit Server查收钓到的User-Agent,然后再结合刚才的那个生成新的Web缓存
使用Web缓存投毒发动基于DOM的漏洞攻击
不仅可以通过Web缓存投毒导入恶意JS文件,还可以导入恶意的JSON字符串,例如
{"someProperty" : ""}
如果这条payload被传递到恶意的sink就可能触发基于DOM的漏洞,如果想要让网站加载恶意JSON就需要CORS授权允许跨站,例如
HTTP/1.1 200 OKContent-Type: application/jsonAccess-Control-Allow-Origin: *<br />{ "malicious json" : "malicious json"}
配套靶场:在严格可缓存性标准下使用Web缓存投毒发动基于DOM的漏洞攻击
首先我们还是,默认使用Param Miner扫到可伪造字段X-Forwarded-Host。然后我们把首页放到Repeater里看一下有没有什么新东西
这些应该都是json的东西,大概的行为就是以data.host下的/resources/json/geolocate.json为参数执行initGeoLocate函数,然后我们再追踪一下这个geolocate.js和geolocate.json
从图上来看,geolocate.js会把j.country变量直接拼接进来,所以我们看一下这个country变量在哪,应该是在geolocate.json文件里面
那么我们就需要在Exploit Server中伪造属于我们的geolocate.json文件,只是country的值换成xss语句
好的,然后我们就可以生成Web缓存了
刷新首页成功加载我们自己的geolocate.json
注:扫到字段以后要把Param Miner的cachebuster都关了,不然永远也不会生成缓存
Web缓存投毒链
如果将我们学过的几种漏洞与Web缓存投毒结合起来,可以发动更高级的攻击。下面我们直接通过一道靶场来深入理解。
Web缓存投毒链
我们识别到两个可用来投毒的字段:X-Forwarded-Host、X-Original-URL,然后分析一下/resources/js/translations.js这个文件,看一下initTranslations()这个函数是怎么运作的
首先我们来看一下提取现在网页的语言的函数
大概是这么一个流程,然后是翻译函数,因为只翻译首页,所以所谓的翻译就是一对一的替换,然后我们留意一下红框部分,就是说除了英语其他的都会执行翻译,也就是说除了英语以外的我们都能用来插入DOM-XSS语句,考虑到乱码问题,我们选择en-gb这个,虽然也是英语,但是因为代码里严格匹配en,所以这个也是可以用来插入的,而且也不用考虑乱码的问题,现在我们去Exploit Server写入DOM-XSS语句进去
然后我们需要抓取/?localized=1这个包,因为你选择翻译的时候会请求这个页面,然后修改X-Forwarded-Host字段为Exploit Server的,并产生Web缓存
但是这是翻译页面,我们要怎样才能让用户首先进到的就是我们的翻译页面呢,那就是在首页加入一个X-Original-URL字段指向翻译页面,这样访问首页的人就都会被重定向到这个页面了
因为需要生成两个Web缓存并且需要在靶场访问的时候同时在生效中,所以需要多试几次
基于缓存实现缺陷的Web缓存投毒攻击
缓存键缺陷
传统的攻击经常通过将payload注入查询字符串实现,但是请求行是缓存键,这就导致不可能会有用户发出等效请求,也就接收不到投毒响应缓存,但是如果有CDN的话,他们会将缓存键内容进行某些处理之后再存入缓存,例如
尤其是前两条,即使我们注入payload到查询字符串或参数中,用户也可能收到被投毒的响应缓存。
识别合适的缓存断言
首先我们要识别合适的缓存断言以测试缓存的过程。我们需要知道我们接收到的响应是来自缓存还是服务器,比如从以下地方可以得到反馈
有的时候应用程序会使用第三方的缓存组件,此时可以通过查阅相关文档的方式得知缓存的过程。例如,基于Akamai的网站可能支持Pragma:akamai-x-get-cache-key,它可以在响应标头中显示缓存键。
GET /?param=1 HTTP/1.1Host: innocent-website.comPragma: akamai-x-get-cache-key<br />HTTP/1.1 200 OKX-Cache-Key: innocent-website.com/?param=1
探测缓存键的处理过程
我们应该还要观察缓存是否对缓存键有其他的处理。比如剔除Host中的端口号等。下面我们来关注这样一对请求和响应。它会动态将Host值拼接到Location中
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com/enCache-Status: miss
然后我们在Host中随便加入一个端口,观察一下响应
GET / HTTP/1.1Host: vulnerable-website.com:1337<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: miss
我们再去掉端口号重新发送请求
GET / HTTP/1.1Host: vulnerable-website.com<br />HTTP/1.1 302 Moved PermanentlyLocation: https://vulnerable-website.com ... atus: hit
发现已经生成缓存,但是缓存是我们加了端口号发出时的响应版本。这就说明端口号是不会加入缓存键中的。
识别可利用的漏洞
讲了这么多,其实Web缓存投毒的危害程度完全取决于其能够用来发动的攻击,常见的有像反射型XSS、开放重定向等客户端漏洞。并且Web缓存投毒不需要用户点击任何链接,可能在发出正常的请求时也会接收到被投毒的响应缓存。
缓存键缺陷的利用
不被缓存的端口号
前面我们介绍过,缓存键有时候可能不会只会缓存域名或主机名而不缓存端口号。所以我们可以利用这个特点发动如DDOS(向任意端口号发出大量请求)、XSS(在端口号位置插入payload)等攻击。
不被缓存的查询字符串
探测不被缓存的查询字符串
有的应用程序并不会在响应中告知是否产生缓存。而且如果查询字符串不是缓存键的话,即使不同的查询字符串也会得到相同的响应缓存。那么我们可以在其他请求头中下手,例如加在Accept-Encoding字段中
Accept-Encoding: gzip, deflate, cachebusterAccept: */*, text/cachebusterCookie: cachebuster=1Origin: https://cachebuster.vulnerable-website.com
其实也可以在Param Miner开启动态的缓存粉碎机(cachebuster)。还有一种办法就是修改不同的路径,但是仍然可以的到相同的响应缓存。例如
Apache: GET //Nginx: GET /%2FPHP: GET /index.php/xyz.NET GET /(A(xyz)/
利用不被缓存的查询字符串
查询字符串不会被缓存可能会扩大XSS攻击面,因为附有XSS payload的查询字符串的请求在缓存看来与普通请求无异。但是普通用户可能就会接收到被投毒的响应缓存。
配套靶场:通过不被缓存的查询字符串的Web缓存响应
这一关考察就是常规的web缓存投毒,这一关并没有对查询字符串进行绑定,所以同一个路径下即识别为等效页面即投放缓存,那么我们直接在URL里面投毒
响应中的Age字段可以告诉我们它的缓存已经生效多长时间了,这一关设定的是35秒后重新缓存,可以作为我们投毒的一个信号标,于是我们看一下这时候访问根目录的响应是怎样的
我们得到的就是投毒后的缓存,这时访问首页的人都会接收到投毒的缓存
不被缓存的查询参数
有的时候缓存仅将某几个查询参数排除在缓存键中,例如utm_content 等utm参数。但是这种攻击方式会因为参数不太会被专门处理而没有那么大的危害。但是如果有功能点可以处理整个URL则可能会有转机。
配套靶场:通过不被缓存的查询参数的Web缓存响应
这一关考点是它只有某些查询参数不会作为缓存键,我们扫到utm_content这个查询参数不是缓存键,所以我们可以用这个参数实现Web缓存投毒
等到重新生成Web缓存的时候,成功通关
缓存参数隐藏
有的时候因为解析差异,可以将某些本来是缓存键的查询参数隐藏到非缓存键中。例如查询字符串中通过与(&)区分各个参数,通过问号(?)识别查询参数的开始。但是如果有多个问号,就会以第一个问号作为查询参数的开始,例如
GET /?example=123?excluded_param=bad-stuff-here
这时,excluded_param就不会被作为缓存键处理。
利用参数解析差异
有的应用程序有一些特殊的查询参数解析规则,比如Ruby on Rails框架将与(&)和分号 (;)作为参数分隔符。但是如果缓存不支持这样解析参数,就会造成差异。例如
GET /?keyed_param=abc&excluded_param=123;keyed_param=bad-stuff-here
如上所示,keyed_param就会被视为缓存键,而excluded_param不会,而且只会被缓存解析成两个参数
keyed_param=abcexcluded_param=123;keyed_param=bad-stuff-here
而被Ruby on Rails解析成三个参数
keyed_param=abcexcluded_param=123keyed_param=bad-stuff-here
此时keyed_param的值就会被覆盖为bad-stuff-here,也会得到由这个值生成的响应,但是在缓存看来只要该参数值为abc的都会被视为该响应的等效请求。利用这种特点可以发动JSONP攻击,它会将回调函数名附在查询参数中,例如
GET /jsonp?callback=innocentFunction
我们可以利用上面的特点替换成我们指定的函数名。
配套靶场:参数差异
这一关主要是考察缓存与后台对URL参数的处理方式不同导致的Web缓存投毒,通过扫描得知utm_content是可伪装的缓存键,后台将(&)和(;)识别为分界符,这样就可以利用这种差异覆盖回调参数的值为我们指定的函数
这样访问首页的人就也会接收到我们的投毒缓存了
利用支持fat GET请求
有的时候请求方法并不是缓存键,这时候如果支持fat GET请求方法就也可以用来进行Web缓存投毒。fat GET请求很特殊,拥有URL查询参数和正文参数,而只有请求行是缓存键,而且应用程序会从正文参数获取参数值,例如我们构造这样的请求
GET /?param=innocent HTTP/1.1…param=bad-stuff-here
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 97 次浏览 • 2022-06-13 16:08
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 97 次浏览 • 2022-05-28 07:03
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(

)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 158 次浏览 • 2022-05-25 13:19
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(

)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
网站优化 • 优采云 发表了文章 • 0 个评论 • 101 次浏览 • 2022-05-25 03:29
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(
)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS 查看全部
php抓取网页连接函数 CMS真的安全吗?洞鉴PHPCMS
什么是PHPCMS
说到PHPCMS,就不得不先提一提CMS。CMS全称“Content Management System”,意为“内容管理系统”,内容管理系统是一种位于WEB 前端(Web 服务器)和后端办公系统或流程(内容创作、编辑)之间的软件系统。内容的创作人员、编辑人员、发布人员使用内容管理系统来提交、修改、审批、发布内容。
这里指的“内容”可能包括文件、表格、图片、数据库中的数据甚至视频等一切你想要发布到Internet、Intranet以及Extranet网站的信息。内容管理还可选地提供内容抓取工具,将第三方信息来源,比如将文本文件、HTML网页、Web服务、关系数据库等的内容自动抓取,并经分析处理后放到自身的内容库中。
随着个性化的发展,内容管理还辅助WEB前端将内容以个性化的方式提供给内容使用者,即提供个性化的门户框架,以基于WEB技术将内容更好地推送到用户的浏览器端。
PHPCMS由国内80后知名创业者钟胜辉于2005年创办,是国内知名内容管理系统,它是一款基于 PHP 技术和 AJAX 技术的企业级网站内容管理系统,旨在帮助用户解决日益复杂与重要的 Web 内容的创建、维护、发布和应用。
PHPCMS的创立赶上了CMS蓬勃发展的时期,而且它将模块化开发方式做为功能开发形式,框架易于功能扩展,代码维护,优秀的二次开发能力,可满足所有网站的应用需求,所以迅速占领了国内的内容管理系统领域的大片领土。
从2005年PHPCMS第一版创立,到2007年推出PHPCMS 2007版本,再到2008年年底推出了PHPCMS 2008版本,四年三大版本均赢得了市场好评,最终在2010年盛大在线收购PHPCMS后,PHPCMS推出了主线产品PHPCMS V9版本,到2017年5月推出了最新的V9.6.3版本,PHPCMS仍为现在国内领先的内容管理系统,是各大站长建站的首选。
根据全网数据统计,使用PHPCMS的网站多达4万多个,其中大部分集中在国内,共有约3万余个,占使用量的75%以上,同时,PHPCMS在教育行业约有上千个网站使用,在政府部门的网站中,也有上百个网站使用,其他则用于商业用途,具体分布如下图:
从图中可以看出,PHPCMS的使用范围较广,其中在政府部门中不乏市县级政府这样的客户;教育行业内,也不乏国内大学的校级、院系官网使用PHPCMS作为其发布信息的官方主页;商业用途中,PHPCMS也备受中小型企业的青睐。
PHPCMS的版本更新迭代速度以及功能齐全等特性也为其快速扩张提供发展基础。但是,PHPCMS安全吗?
PHPCMS安全吗?
根据千里目实验室漏洞库的统计,PHPCMS从创办到如今近十三年,产品三大版本的漏洞总量高达100余个,其中核心版本PHPCMS2008系列、PHPCMS V9系列高危漏洞近40个,在40个漏洞之中,全网公布的高可利用性漏洞个数也高达两位数,其中尤其PHPCMS V9最多,所以,PHPCMS的安全性一直备受广大用户的质疑。
经过对PHPCMS高危漏洞的分析整理以及统计,PHPCMS的高危漏洞类型主要有以下五类:SQL注入类、命令执行类、任意文件上传类、任意文件下载类、任意文件读取类。其中SQL注入问题是最为严重的一类,不管数量还是威胁性都是其中最高的,5类漏洞的数量具体分布占比如下图。接下来按照这些漏洞中威胁性最高的五类漏洞来介绍PHPCMS的经典漏洞,以及漏洞间的共性和漏洞成因。
2.1 SQL注入漏洞
PHPCMS框架层面分析
PHPCMS的核心版本中,PHPCMS v9的SQL注入问题比PHPCMS 2008要严重很多,这是为什么?
首先从PHPCMS的框架上分析了这个问题的原因。PHPCMS2008中对GPC变量的处理, 是在
include/common.inc.php中使用$db->escape($_GET)对输入变量进行转义,实质上是调用了
mysql_real_escape_string,PHP手册上对这个函数的描述:
string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )
#本函数将 unescaped_string中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于 mysql_query()
也就是说PHPCMS 2008中使用mysql_real_escape_string对输入变量进行处理,是可以达到防注入效果的,PHPCMS2008中这样的处理方式是比较安全的,但是也有其不合理的一面,原因在于:入口中对所有变量进行mysql_real_escape_string处理,显然并不是所有变量需要入库,这样处理必须影响性能(连接mysql->发送变量至mysql server ->mysql server转义->返回给PHP程序)。
而PHPCMS V9在入口文件phpcms/libs/classes/param.class.php中采用以下方式:
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
new_addslashes其实就是调用了PHP内置的addslashes对GPC变量添加转义斜线,这就相当于php.ini中打开了magic_quotes_gpc选项,PHP手册中对magic_quotes_gpc的处理有以下描述:
Warning
This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.
从PHP 5.4.0开始,已经不支持magic_quotes_gpc了,这是为什么?
(1)性能:由于并不是每一段被转义的数据都要插入数据库的,如果所有进入 PHP 的数据都被转义的话,那么会对程序的执行效率产生一定的影响。
(2)方便性:由于不是所有数据都需要转义,在不需要转义的地方看到转义的数据就很烦。比如说通过表单发送邮件,结果看到一大堆的 \'。
(3)安全问题:事实上addslashes或开启magic_quotes_gpc并不能完全杜绝SQL注入。因为addslashes对有些特殊字符后跟上'(单引号)并不会加把这个'变成/',如:0xbf27的字就不会,所以縗' OR 1 limit 1/* 这个就存在。
针对这个问题,PDO扩展而生,它主要解决两个问题:批量查询时使用prepare提升查询性能,使用参数化查询从根本上杜绝SQL注入,但是PHPCMS V9的phpcms\libs\classes\param.class.php依旧使用以下代码,所这就是造成PHPCMS中SQL注入如此之多的根本原因。:
private $route_config = '';
public function __construct() {
if(!get_magic_quotes_gpc()) {
$_POST = new_addslashes($_POST);
$_GET = new_addslashes($_GET);
$_REQUEST = new_addslashes($_REQUEST);
$_COOKIE = new_addslashes($_COOKIE);
PHPCMS漏洞层面分析
从具体的漏洞来看,PHPCMS代码中出现的SQL注入点主要有三种情况:
第一种情况,即外部可控参数未进行任何过滤,这种问题是因为系统设计人员代码设计的不严谨导致。其中最经典的PHPCMS v9 前台用户登录处SQL注入漏洞产生即是因为这个原因:用户在登录页面输入用户名与密码,在\phpcms\modules\member\index.php的login方法中,username使用的is_username进行了过滤而password没有做任何处理。
$username = isset($_POST['username']) && is_username($_POST['username']) ?
trim($_POST['username']) : showmessage(L('username_empty'), HTTP_REFERER);
$password = isset($_POST['password']) && trim($_POST['password']) ?
trim($_POST['password']) : showmessage(L('password_empty'), HTTP_REFERER);
$cookietime = intval($_POST['cookietime']);
$synloginstr = ''; //同步登陆js代码
所以,Password就成为了一个注入点。输入的username以及password被传到phpsso模块中进行认证,而phpsso模块并没有解析过滤用户名与密码就直接进行认证,认证后直接将信息返回到登录页面的login方法中。
第二种情况也是SQL注入中的经典情形,即代码中处理外部输入数据的时候使用了urldecode()函数。比如单引号被 urlencode 两次以后是 %2527,然后 POST。PHP 内部在生成全局变量 $_POST 的时候会先 urldecode,得到 %27,然后 PHP 会检查 Magic Quotes 的设置,但是无论是否开启 Magic Quotes,%27 都不会被 addslashes,因为这时根本没有单引号。
但是这时如果你在 PHP 代码中画蛇添足的加上 urldecode,%27就变成单引号了,这样就成功绕过了,这种情况出现的根源在于之前分析的PHPCMS使用magic_quotes_gpc这种方式来处理外部数据。
PHPCMS V9 WAP SQL注入漏洞即是因为这种情况:
function comment_list() {
$WAP = $this->wap;
$TYPE = $this->types;
$comment = pc_base::load_app_class('comment','comment');
pc_base::load_app_func('global','comment');
$typeid = intval($_GET['typeid']);
$GLOBALS['siteid'] = max($this->siteid,1);
$commentid = isset($_GET['commentid']) && trim(urldecode($_GET['commentid'])) ?
trim(urldecode($_GET['commentid'])) : exit('参数错误');
list($modules, $contentid, $siteid) = decode_commentid($commentid);
list($module, $catid) = explode('_', $modules);
$comment_setting_db = pc_base::load_model('comment_setting_model');
$setting = $comment_setting_db->get_one(array('siteid'=>$this->siteid));
这段代码中在处理外部可控参数commentid的时候,使用了urldecode()函数,最终可以采用%2527的方式来进行过滤绕过。
PHPCMS db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id)); //id传入sql查询语句
在函数中,代码通过GET获取'a_k'值,并调用sys_auth函数进行解密,然后使用parse_str()来解析解密后的$a_k参数,将字符串解析到变量中,并同时解码。在parse_str()解析的过程中,就将之前构造好的SQL注入paylaod赋值到各个参数,并且绕过了所有限制。
这个漏洞利用还有另一个关口,就是需要将payload使用sys_auth加密,同样,PHPCMS一个任意文件下载漏洞也是这个原理:构造payload—>sys_auth加密—>带入到phpcms\modules\content\
down.php文件init函数中—>进行解码并parse_str()解析,payload通过这种方式可以绕过所有过滤关口,最终成功触发。
从之上的分析可知,所有的SQL注入漏洞其根源都在magic_quotes_gpc方法上。但是除了框架之外,如此多的SQL注入问题就在于程序员对待代码逻辑的不严谨,缺少对外部可控参数的过滤或者说是缺少完全的过滤验证过程,PHP是很灵活的语言,其代码的逻辑严谨程度即代表着它的安全性高低!
命令执行漏洞
命令执行是一类威胁性非常大的漏洞类型,大多数“一锅端”(能够getshell,控制全站)漏洞都是命令执行漏洞。由之前的漏洞数量分布可知,命令执行问题的数量仅次于SQL注入,但是它的严重程度可能更甚于SQL注入漏洞。
PHPCMS框架中的命令执行漏洞大部分都是由于一个函数,即global.func.php中的string2array()函数,函数代码如下:
/**
* 将字符串转换为数组
*
* @param string $data 字符串
* @return array 返回数组格式,如果,data为空,则返回空数组
*/
function string2array($data) {
$data = trim($data);
if($data == '') return array();
if(strpos($data, 'array')===0){
@eval("\$array = $data;");
}else{
if(strpos($data, '{\\')===0) $data = stripslashes($data);
$array=json_decode($data,true);
if(strtolower(CHARSET)=='gbk'){
$array = mult_iconv("UTF-8", "GBK//IGNORE", $array);
}
}
return $array;
}
该函数中使用了eval函数,这就代表所有调用这个函数的地方,如果存在过滤不严的情况,都会出现严重的命令执行漏洞。当然,我们不用怀疑程序员写BUG的能力,PHPCMS中多个命令执行漏洞都是因为外部参数未进行严格过滤,就直接带进了string2array()中进行处理,所以最终触发了漏洞。
其中最经典的一个漏洞PHPCMS 2008任意代码执行漏洞,该漏洞利用门槛之低让人大跌眼镜,漏洞的出处就在yp/web/include/common.inc.php的menu变量:
$menu = string2array($menu);
$siteurl = $m_s_url[1] = $M['url'].'web/?'.$userid;
$introduceurl = $m_s_url[2] = $siteurl.'/category-introduce.html';
$newsurl = $m_s_url[3] = $siteurl.'/category-news.html';
$product = $m_s_url[4] = $siteurl.'/category-product.html';
$buyurl = $m_s_url[5] = $siteurl.'/category-buy.html';
$joburl = $m_s_url[6] = $siteurl.'/category-job.html';
$joburl = $m_s_url[7] = $siteurl.'/category-certificate.html';
$guestbookurl = $m_s_url[8] = $siteurl.'/category-guestbook.html';
$contacturl = $m_s_url[9] = $siteurl.'/category-contact.html';
Menu参数是一个外部可控参数,该参数被被传进后台之后未经过任何处理以及限制,导致在string2array()函数中直接被eval函数执行,导致远程代码执行漏洞。
而这个函数从PHPCMS 2008到PHPCMS V9一直都存在,而该系统程序员也只是在之后每一个调用此函数的地方都先对传入参数进行过滤或者限制,但是任何过滤限制都有被绕过的可能,之后的事实证明确实如此,比如之后再V9版本出现的PHPCMS V9 phpcms\
modules\dbsource\data.php出现的远程命令执行漏洞,PHPCMSV9 /phpcms/modules/vote/
index.php代码执行漏洞等均是因为这个原因。
所以,防止PHP程序出现代码执行漏洞有一条金科玉律:永远不要在代码中使用eval。
因为,很少有人能够掌控住这个函数,一旦控制不住,可能就会出现一个“一锅端”的命令执行漏洞。
另外PHPCMS中出现的一个PHPCMS /phpsso_server/phpcms/modules/phpsso/index.php代码执行漏洞,它的思想很特别、也很典型。该漏洞中存在的一个猛兽理论值得很多程序员以及安全研究人员来认真研究的。“猛兽来了,我们应该将其绝杀在门外,但是有些人非得把它放进屋内,才杀之,你们难道不知道猛兽的嘴里可能叼了一个炸药包吗? 砰!!!结果全都玩完了…”。下面用具体代码来解释这个理论。
其中:
猛兽放进室内:copy($_GET['src'],$_GET['dst']);
这条猛兽不安全,杀之:unlink($_GET['dst']);
炸药包:$_GET['dst'] 此炸药包的作用是生成恶意文件。
这段代码中存在缺陷的地方就是将“猛兽”放进市内的地方:
copy($_GET['src'],$_GET['dst']);
可将任意文件copy成恶意文件,如木马,后来发现这个文件不安全,然后使用unlink将之删除...,但是,有种可能就是木马在删除之前,生成了新的木马文件,结果可想而知。
PHPCMS V9
/phpsso_server/phpcms/modules/phpsso/index.php任意命令执行就是这个原理。首先上传头像,头像的格式被压缩为zip格式文件,然后文件到了/phpsso_server/phpcms/modules/phpsso/index.php的uploadavatar()函数,首先被解压,然后判断为非jpg文件后删除。攻击者利用这个漏洞的过程中是将webshell保存为jpg中,构造后缀名突破jpg的限制,在文件被上传解压后,webshell执行在上层目录生成一个新的webshell文件,由此触发漏洞。
命令执行漏洞的威胁性极大,在任何代码审计中,只要代码中出现了eval,assert等这类代码执行函数后,都有可能出现代码执行漏洞。并且猛兽理论也应该在被关注,这样的逻辑在很多代码中都会被用到,其解决的方法即应该在第一步的时候就处理风险,不能将“猛兽放进屋来”。
任意文件上传漏洞、任意文件下载、任意文件读取漏洞
这三类漏洞是关于文件操作的漏洞,具有很多的共性。PHPCMS中的任意文件上传漏洞主要是因为文件上传过程中,文件类型的限制不严或者因为存在过滤限制绕过,导致可以上传一些可执行文件,最终GetShell。该类漏洞与代码执行很相似,最根本就是为了执行上传文件中的恶意代码。
其中PHPCMS v9 注册页面任意文件上传漏洞和PHPCMS v9.6.1 后缀名提取导致任意文件上传漏洞都是一种采用构造后缀名绕过限制过滤的漏洞。第一个漏洞中的正则要求输入满足src/href=url.(gif|jpg|jpeg|bmp|png),则构造的(

)符合这一格式(这也就是为什么后面要加.jpg的原因)。之后通过一系列处理,将.jpg去掉,那么最后文件的后缀就变成了.php成为了可执行文件。
而第二个漏洞中则因strtolower(trim
(substr(strrchr($filename, '.'), 1, 10)));处理被绕过,攻击这可以使用/1.thumb_.Php.JPG%20%20
%20%20%20%20%20Php来绕过过滤,最终由于apache的文件名解析特性(可以去掉空格)导致上传文件里php代码最终被解析执行。所以该类漏洞中,都是因为对文件名后缀缺少更严谨的处理,导致上传可执行文件最终导致getshell。
PHPCMS
php抓取网页连接函数 面试PHP一般会考查你哪些内容呢?不妨看看
网站优化 • 优采云 发表了文章 • 0 个评论 • 75 次浏览 • 2022-05-21 18:52
状态码 401表示未授权;<br />header(“HTTP/1.0 404 Not Found”);<br />
扩展
8、把John新增到users阵列?
$users[] = ‘john’; <br />array_push($users,‘john’);<br />
9 、在PHP中error_reporting这个函数有什么作用?
error_reporting()设置PHP的报错级别并返回当前级别。
10、如何修改SESSION的生存时间.
方法1:将php.ini中的session.gc_maxlifetime设置为9999 重启apache<br /><br />方法2:<br />$savePath = "./session_save_dir/";<br />$lifeTime = 小时 * 秒;<br />session_save_path($savePath);<br />session_set_cookie_params($lifeTime);<br />session_start();<br /><br />方法3:<br />setcookie() and session_set_cookie_params($lifeTime);<br />
11、有一个网页地址,比如PHP自学中心的主页:,如何得到它的内容?
方法1(对于PHP5及更高版本):<br />$readcontents = fopen("http://www.startphp.cn/index.html", "rb");<br />$contents = stream_get_contents($readcontents);<br />fclose($readcontents);<br />echo $contents;<br /><br />方法2:<br />echo file_get_contents("http://www.startphp.cn/index.html");<br />
12、写一个函数,尽可能高效的,从一个标准url里取出文件的扩展名
例如:需要取出php或.php
方法1:<br />function getExt($url){<br /> $arr = parse_url($url);<br /> $file = basename($arr['path']);<br /> $ext = explode(".",$file);<br /> return $ext[1];<br />}<br /><br />方法2:<br />function getExt($url) {<br /> $url = basename($url);<br /> $pos1 = strpos($url,".");<br /> $pos2 = strpos($url,"?");<br /><br /> if(strstr($url,"?")){<br /> Return substr($url,$pos1 + 1,$pos2 – $pos1 – 1);<br /><br /> } else {<br /> return substr($url,$pos1);<br /> }<br />}<br />
13、使用五种以上方式获取一个文件的扩展名
要求:dir/upload.image.jpg,找出.jpg或者jpg,必须使用PHP自带的处理函数进行处理,方法不能明显重复,可以封装成函数get_ext1($file_name),get_ext2($file_name)
方法1:<br />function get_ext1($file_name){<br /> return strrchr($file_name, '.');<br />}<br />方法2:<br />function get_ext2($file_name){<br /> return substr($file_name,strrpos($file_name, '.'));<br />}<br />方法3:<br />function get_ext3($file_name){<br /> return array_pop(explode('.', $file_name));<br />}<br />方法4:<br />function get_ext4($file_name){<br /> $p = pathinfo($file_name);<br /> return $p['extension'];<br /><br />}<br />方法5:<br />function get_ext5($file_name){<br /> return strrev(substr(strrev($file_name), 0,strpos(strrev($file_name), '.')));<br />}<br />
14、PHP中的null与false,‘’与0有什么区别?
<p> 查看全部
php抓取网页连接函数 面试PHP一般会考查你哪些内容呢?不妨看看
状态码 401表示未授权;<br />header(“HTTP/1.0 404 Not Found”);<br />
扩展
8、把John新增到users阵列?
$users[] = ‘john’; <br />array_push($users,‘john’);<br />
9 、在PHP中error_reporting这个函数有什么作用?
error_reporting()设置PHP的报错级别并返回当前级别。
10、如何修改SESSION的生存时间.
方法1:将php.ini中的session.gc_maxlifetime设置为9999 重启apache<br /><br />方法2:<br />$savePath = "./session_save_dir/";<br />$lifeTime = 小时 * 秒;<br />session_save_path($savePath);<br />session_set_cookie_params($lifeTime);<br />session_start();<br /><br />方法3:<br />setcookie() and session_set_cookie_params($lifeTime);<br />
11、有一个网页地址,比如PHP自学中心的主页:,如何得到它的内容?
方法1(对于PHP5及更高版本):<br />$readcontents = fopen("http://www.startphp.cn/index.html", "rb");<br />$contents = stream_get_contents($readcontents);<br />fclose($readcontents);<br />echo $contents;<br /><br />方法2:<br />echo file_get_contents("http://www.startphp.cn/index.html");<br />
12、写一个函数,尽可能高效的,从一个标准url里取出文件的扩展名
例如:需要取出php或.php
方法1:<br />function getExt($url){<br /> $arr = parse_url($url);<br /> $file = basename($arr['path']);<br /> $ext = explode(".",$file);<br /> return $ext[1];<br />}<br /><br />方法2:<br />function getExt($url) {<br /> $url = basename($url);<br /> $pos1 = strpos($url,".");<br /> $pos2 = strpos($url,"?");<br /><br /> if(strstr($url,"?")){<br /> Return substr($url,$pos1 + 1,$pos2 – $pos1 – 1);<br /><br /> } else {<br /> return substr($url,$pos1);<br /> }<br />}<br />
13、使用五种以上方式获取一个文件的扩展名
要求:dir/upload.image.jpg,找出.jpg或者jpg,必须使用PHP自带的处理函数进行处理,方法不能明显重复,可以封装成函数get_ext1($file_name),get_ext2($file_name)
方法1:<br />function get_ext1($file_name){<br /> return strrchr($file_name, '.');<br />}<br />方法2:<br />function get_ext2($file_name){<br /> return substr($file_name,strrpos($file_name, '.'));<br />}<br />方法3:<br />function get_ext3($file_name){<br /> return array_pop(explode('.', $file_name));<br />}<br />方法4:<br />function get_ext4($file_name){<br /> $p = pathinfo($file_name);<br /> return $p['extension'];<br /><br />}<br />方法5:<br />function get_ext5($file_name){<br /> return strrev(substr(strrev($file_name), 0,strpos(strrev($file_name), '.')));<br />}<br />
14、PHP中的null与false,‘’与0有什么区别?
<p>
php抓取网页连接函数download_urls,传递server_id进去判断下
网站优化 • 优采云 发表了文章 • 0 个评论 • 109 次浏览 • 2022-05-10 07:02
php抓取网页连接函数download_urls,传递server_id进去判断下server_id是否在爬虫本地常驻,如果不常驻会建立临时线程下载连接。类似动态页面时会实时更新信息的做法。具体可以参考phptagcloud。php对apache的支持也很好。本人没有用过apache,只是使用过tengine,我猜apache在爬虫大部分任务上好用的多。
不要用php,这次应该是你配置问题。如果php的echo$url+:(但是php+symbian环境中的这个其实写错了。其实应该是),就可以抓取到浏览器上的加载链接,因为它返回的就是url标签下的标签。因为相比使用js,你的浏览器下载过程是不会立即完成的,而是类似exfat或formdata文件,使用stock文件。
实测为不可以,你要安装一个apache的插件apache-nginx,在你的php安装的目录下。这是我下的图apache插件安装完毕后,那个叫simpleurlhandler,下载地址:ocean'sblog,貌似现在都免费了apache-nginx安装完毕之后,你的php就可以抓取了,但是只能一个人用,并且要全局配置,为什么呢?因为这个simpleurlhandler是可选的,也就是你可以抓取网页的任何部分。
那么在大部分的小网站上(http请求量很小的除外)php可以实现你的目的。it'snotareadabilitytest.(译者注:语感很重要)所以,你的php环境真的很牛很牛,那么就安全,看不懂也没关系,反正你不懂php语法。 查看全部
php抓取网页连接函数download_urls,传递server_id进去判断下
php抓取网页连接函数download_urls,传递server_id进去判断下server_id是否在爬虫本地常驻,如果不常驻会建立临时线程下载连接。类似动态页面时会实时更新信息的做法。具体可以参考phptagcloud。php对apache的支持也很好。本人没有用过apache,只是使用过tengine,我猜apache在爬虫大部分任务上好用的多。
不要用php,这次应该是你配置问题。如果php的echo$url+:(但是php+symbian环境中的这个其实写错了。其实应该是),就可以抓取到浏览器上的加载链接,因为它返回的就是url标签下的标签。因为相比使用js,你的浏览器下载过程是不会立即完成的,而是类似exfat或formdata文件,使用stock文件。
实测为不可以,你要安装一个apache的插件apache-nginx,在你的php安装的目录下。这是我下的图apache插件安装完毕后,那个叫simpleurlhandler,下载地址:ocean'sblog,貌似现在都免费了apache-nginx安装完毕之后,你的php就可以抓取了,但是只能一个人用,并且要全局配置,为什么呢?因为这个simpleurlhandler是可选的,也就是你可以抓取网页的任何部分。
那么在大部分的小网站上(http请求量很小的除外)php可以实现你的目的。it'snotareadabilitytest.(译者注:语感很重要)所以,你的php环境真的很牛很牛,那么就安全,看不懂也没关系,反正你不懂php语法。
php抓取网页连接函数legionaproxy,mysql模式下各项事件触发函数
网站优化 • 优采云 发表了文章 • 0 个评论 • 86 次浏览 • 2022-05-07 22:01
php抓取网页连接函数legionaproxy,mysql连接函数hibernate函数proxy_transfer,mvc模式下各项事件触发函数。其中legionaproxy是最难用的,这个事件要封装各种方法,不是一般人能搞得定的,要不然他就不是php界的web开发大咖了。
在一些专门的开发框架里面,这种东西是可以嵌套的。比如说以前最流行的drupal就有一套组件可以实现。不过要看具体框架的设计者,有的是直接控制,有的是被block。
mysql方面看一下数据库事务管理;php方面看一下mvc模式中的某个组件或者是某个文件;
其实我觉得吧,问问题的人提的这些问题,建议最好还是自己写代码实现吧。有网站、app出现问题,你不先看看程序怎么跑,而是立马让答题者回答,然后你自己思考,
php撸了一天,
官方的github就有这样的库,
我自己的话是去看一下thefivewaystogenerateaphpconnection·issue#362·iamftc/php-five·github刚刚试了一下,有一些试错的东西需要自己跑一遍,循环什么的。在selenium里面的实现:/imfeeds/alpariasphook.php?common=1&workday=2018-09-26&article=php-five-ways-to-generate-a-php-connection&topic=intro-1&from=---tree134e353c4dacf524c0bc111071d0e。 查看全部
php抓取网页连接函数legionaproxy,mysql模式下各项事件触发函数
php抓取网页连接函数legionaproxy,mysql连接函数hibernate函数proxy_transfer,mvc模式下各项事件触发函数。其中legionaproxy是最难用的,这个事件要封装各种方法,不是一般人能搞得定的,要不然他就不是php界的web开发大咖了。
在一些专门的开发框架里面,这种东西是可以嵌套的。比如说以前最流行的drupal就有一套组件可以实现。不过要看具体框架的设计者,有的是直接控制,有的是被block。
mysql方面看一下数据库事务管理;php方面看一下mvc模式中的某个组件或者是某个文件;
其实我觉得吧,问问题的人提的这些问题,建议最好还是自己写代码实现吧。有网站、app出现问题,你不先看看程序怎么跑,而是立马让答题者回答,然后你自己思考,
php撸了一天,
官方的github就有这样的库,
我自己的话是去看一下thefivewaystogenerateaphpconnection·issue#362·iamftc/php-five·github刚刚试了一下,有一些试错的东西需要自己跑一遍,循环什么的。在selenium里面的实现:/imfeeds/alpariasphook.php?common=1&workday=2018-09-26&article=php-five-ways-to-generate-a-php-connection&topic=intro-1&from=---tree134e353c4dacf524c0bc111071d0e。
php抓取网页连接函数(年度爆款文案:pdfkit库的操作流程和安装库 )
网站优化 • 优采云 发表了文章 • 0 个评论 • 96 次浏览 • 2022-04-16 18:29
)
按照上面的操作流程,就可以安装pdfkit库了。对于pdfkit库的使用,常见的三种用法:
上述程序主要完成几个步骤:
首先需要指定wkhtmltopdf.exe文件的路径;
因此,pdfkit库只能将子页面保存为单独的pdf文档,不能通过pdfkit库直接将所有子页面拼接成一个完整的pdf文档。小编使用PyPDF2库中的PdfFileMerger类来实现pdf文档的拼接。程序如下图所示。
程序首先将所有html网页保存为单独的pdf文档,然后通过PdfFileMerger类对象实现pdf文档的拼接。最后,您可以获得所有的pdf内容。最后,我们通过视频展示看一下程序的效果。
另外,该程序不仅可以爬取python3.9的中文文档,还可以爬取其他在线文档,只需要修改程序获取要爬取的网页链接,比如Flask中文文档的爬取,程序只需要按照下图进行修改,Flask的在线文档就可以保存为PDF文档了。
04.总结
学习 Python 实际上是非常有趣和有用的。因为 Python 拥有大量现成的库,可以帮助我们轻松解决工作中的许多琐碎问题。小编对上面的程序稍作修改,很快就帮阿里拿到了教程,保存为pdf发给她,小编和女神的关系就更近了。
其实源码很简单,大家也可以轻点一下(看几千遍还不如自己敲),有不明白的可以找小助手(公众号后台输入:小助手)。
为了方便大家更好的理解,我们会在B站录制一个完整的视频(详情见原文),一步步讲解程序,再提供源码和视频!
推荐阅读:
入门: 最全的零基础学Python的问题 | 零基础学了8个月的Python | 实战项目 |学Python就是这条捷径
干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析 | 从万众期待到口碑扑街!唐探3令人失望 | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |
趣味:弹球游戏 | 九宫格 | 漂亮的花 | 两百行Python《天天酷跑》游戏!
AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影
年度最火副本
点这里,直达菜鸟学PythonB站!! 查看全部
php抓取网页连接函数(年度爆款文案:pdfkit库的操作流程和安装库
)
按照上面的操作流程,就可以安装pdfkit库了。对于pdfkit库的使用,常见的三种用法:

上述程序主要完成几个步骤:
首先需要指定wkhtmltopdf.exe文件的路径;
因此,pdfkit库只能将子页面保存为单独的pdf文档,不能通过pdfkit库直接将所有子页面拼接成一个完整的pdf文档。小编使用PyPDF2库中的PdfFileMerger类来实现pdf文档的拼接。程序如下图所示。

程序首先将所有html网页保存为单独的pdf文档,然后通过PdfFileMerger类对象实现pdf文档的拼接。最后,您可以获得所有的pdf内容。最后,我们通过视频展示看一下程序的效果。
另外,该程序不仅可以爬取python3.9的中文文档,还可以爬取其他在线文档,只需要修改程序获取要爬取的网页链接,比如Flask中文文档的爬取,程序只需要按照下图进行修改,Flask的在线文档就可以保存为PDF文档了。
04.总结
学习 Python 实际上是非常有趣和有用的。因为 Python 拥有大量现成的库,可以帮助我们轻松解决工作中的许多琐碎问题。小编对上面的程序稍作修改,很快就帮阿里拿到了教程,保存为pdf发给她,小编和女神的关系就更近了。
其实源码很简单,大家也可以轻点一下(看几千遍还不如自己敲),有不明白的可以找小助手(公众号后台输入:小助手)。
为了方便大家更好的理解,我们会在B站录制一个完整的视频(详情见原文),一步步讲解程序,再提供源码和视频!
推荐阅读:
入门: 最全的零基础学Python的问题 | 零基础学了8个月的Python | 实战项目 |学Python就是这条捷径
干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析 | 从万众期待到口碑扑街!唐探3令人失望 | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |
趣味:弹球游戏 | 九宫格 | 漂亮的花 | 两百行Python《天天酷跑》游戏!
AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影
年度最火副本
点这里,直达菜鸟学PythonB站!!
php抓取网页连接函数(PHP编码声明与用header或meta实现PHP页面编码的区别)
网站优化 • 优采云 发表了文章 • 0 个评论 • 99 次浏览 • 2022-04-13 08:14
编码格式有两种,一种是php文件本身的编码格式,editplus等编辑器允许你在保存文件时指定文件编码格式;另一种是php输出的文本的编码格式,这个信息只在浏览器上生效,方法是。请注意,必须在任何输出之前调用标头方法。
通常,PHP项目开发工具,如ZendStudio、PHPStorm等,都会设置项目的编码方式,以保证项目中的所有文件使用相同的编码格式。对于 WEB 输出,通常的方式是在 html 文件中指定所需的编码,浏览器根据此设置解释内容。
PHP页面编码声明与PHP页面编码使用header或meta的区别
php header 定义一个php页面为utf编码或GBK编码
php页面是utf编码的header("Content-type: text/html; charset=utf-8");
php 页面将标头编码为 gbk("Content-type: text/html; charset=gb2312");
php页面是big5编码头("Content-type: text/html; charset=big5");
通常上面的代码放在php页面的首页
PHP 页面编码与 header 或 meta 一、页面编码的区别
1. 使用标签设置页面编码
这个标签的作用是声明客户端的浏览器使用什么字符集编码来显示页面,xxx可以是GB2312、GBK、UTF-8(不同于MySQL,MySQL是UTF8)等等。因此,大多数页面这个方法可以用来告诉浏览器在显示这个页面的时候使用什么编码,以免造成编码错误和乱码。也就是说,浏览器总是使用一种编码,我稍后会谈到。
请注意,它属于html信息,它只是一个声明,它的作用是表明服务器已将HTML信息传递给浏览器。
2. header("content-type:text/html; charset=xxx");
这个函数header()的作用是将括号中的信息发送到http头。
如果括号中的内容如文中所述,那么功能与标签基本相同。你可以比较第一个,发现字符相似。但不同的是,如果有这个功能,浏览器会一直使用你需要的xxx编码,永远不会不听话,所以这个功能非常好用。为什么会这样?那我们就不得不说一下HTTPS头和HTML信息的区别了:
https 标头是服务器在使用 HTTP 协议向浏览器发送 HTML 信息之前发送的字符串。
因为meta标签属于html信息,所以header()发送的内容首先到达浏览器。通俗点就是header()的优先级比meta高(不知道能不能这么说)。添加一个php页面同时有header("content-type:text/html; charset=xxx"),浏览器只识别之前的http header而不识别meta。当然这个功能只能在一个php页面中使用。
同样的问题仍然存在,为什么前者绝对有效而后者有时无效?这就是为什么我们接下来要讨论 Apache。
3. 添加默认字符集
在Apache根目录的conf文件夹中,有整个Apache配置文件httpd.conf。
使用文本编辑器打开 httpd.conf。第 708 行(不同版本可能不同)有 AddDefaultCharset xxx,其中 xxx 是编码名称。这行代码的含义:将整个服务器中的网页文件的https头中的字符集设置为你默认的xxx字符集。拥有这一行相当于在每个文件中添加一行 header("content-type:text/html; charset=xxx")。现在我明白为什么meta设置为utf-8,但浏览器总是使用gb2312。
如果网页中有header("content-type:text/html; charset=xxx"),把默认字符集改成你设置的字符集,所以这个函数总是有用的。如果你在AddDefaultCharset xxx前面加了一个“#”,注释掉这句话,页面中没有header(“content-type...”),那么就该meta标签起作用了。
总结:
排序
标头(“内容类型:文本/html;字符集=xxx”)
AddDefaultCharset xxx 查看全部
php抓取网页连接函数(PHP编码声明与用header或meta实现PHP页面编码的区别)
编码格式有两种,一种是php文件本身的编码格式,editplus等编辑器允许你在保存文件时指定文件编码格式;另一种是php输出的文本的编码格式,这个信息只在浏览器上生效,方法是。请注意,必须在任何输出之前调用标头方法。
通常,PHP项目开发工具,如ZendStudio、PHPStorm等,都会设置项目的编码方式,以保证项目中的所有文件使用相同的编码格式。对于 WEB 输出,通常的方式是在 html 文件中指定所需的编码,浏览器根据此设置解释内容。
PHP页面编码声明与PHP页面编码使用header或meta的区别
php header 定义一个php页面为utf编码或GBK编码
php页面是utf编码的header("Content-type: text/html; charset=utf-8");
php 页面将标头编码为 gbk("Content-type: text/html; charset=gb2312");
php页面是big5编码头("Content-type: text/html; charset=big5");
通常上面的代码放在php页面的首页
PHP 页面编码与 header 或 meta 一、页面编码的区别
1. 使用标签设置页面编码
这个标签的作用是声明客户端的浏览器使用什么字符集编码来显示页面,xxx可以是GB2312、GBK、UTF-8(不同于MySQL,MySQL是UTF8)等等。因此,大多数页面这个方法可以用来告诉浏览器在显示这个页面的时候使用什么编码,以免造成编码错误和乱码。也就是说,浏览器总是使用一种编码,我稍后会谈到。
请注意,它属于html信息,它只是一个声明,它的作用是表明服务器已将HTML信息传递给浏览器。
2. header("content-type:text/html; charset=xxx");
这个函数header()的作用是将括号中的信息发送到http头。
如果括号中的内容如文中所述,那么功能与标签基本相同。你可以比较第一个,发现字符相似。但不同的是,如果有这个功能,浏览器会一直使用你需要的xxx编码,永远不会不听话,所以这个功能非常好用。为什么会这样?那我们就不得不说一下HTTPS头和HTML信息的区别了:
https 标头是服务器在使用 HTTP 协议向浏览器发送 HTML 信息之前发送的字符串。
因为meta标签属于html信息,所以header()发送的内容首先到达浏览器。通俗点就是header()的优先级比meta高(不知道能不能这么说)。添加一个php页面同时有header("content-type:text/html; charset=xxx"),浏览器只识别之前的http header而不识别meta。当然这个功能只能在一个php页面中使用。
同样的问题仍然存在,为什么前者绝对有效而后者有时无效?这就是为什么我们接下来要讨论 Apache。
3. 添加默认字符集
在Apache根目录的conf文件夹中,有整个Apache配置文件httpd.conf。
使用文本编辑器打开 httpd.conf。第 708 行(不同版本可能不同)有 AddDefaultCharset xxx,其中 xxx 是编码名称。这行代码的含义:将整个服务器中的网页文件的https头中的字符集设置为你默认的xxx字符集。拥有这一行相当于在每个文件中添加一行 header("content-type:text/html; charset=xxx")。现在我明白为什么meta设置为utf-8,但浏览器总是使用gb2312。
如果网页中有header("content-type:text/html; charset=xxx"),把默认字符集改成你设置的字符集,所以这个函数总是有用的。如果你在AddDefaultCharset xxx前面加了一个“#”,注释掉这句话,页面中没有header(“content-type...”),那么就该meta标签起作用了。
总结:
排序
标头(“内容类型:文本/html;字符集=xxx”)
AddDefaultCharset xxx
php抓取网页连接函数(实现PHP与SQL数据库交互实验演示实验环境(一))
网站优化 • 优采云 发表了文章 • 0 个评论 • 106 次浏览 • 2022-04-11 03:31
内容
1 简介
(1)为了实现网页访问后台数据库的功能,需要与网页代码中对应的数据库进行连接。
(2)PHP 5及以上版本推荐使用以下方法连接MySQL:
(3)两种连接方式对比:
(4)本节通过一个简单的例子来介绍实现PHP与SQL连接的全过程,主要介绍MySQLi的方法。
2 PHP与SQL数据库交互实验演示2.1 实验环境
(1)服务器:本实验基于虚拟机win2008系统的WAMP环境,该环境的配置过程请参考文章"【语言环境】WAMP环境的部署与优化- Win2008R2SP1 作为操作系统”。
(2)客户端:使用浏览器访问和控制。
(3)服务端和客户端在同一个局域网中,在服务端打开phpstudy,确保可以从客户端浏览器访问。
2.2 创建数据库
(1)通过访问phpMyAdmin登录数据库。管理员通过浏览器登录phpMyAdmin(位置在网站默认根目录下的phpMyAdmin文件夹下),输入URL服务器IP/phpMyAdmin在浏览器中输入mysql数据库的登录页面;输入用户名和密码登录(默认用户名和密码都是root),点击执行登录mysql数据库。
(2)数据库页面,登录数据库后可以看到如下界面,左边是默认的4个数据库,点击箭头所指的数据库,操作数据库。
(3)创建一个数据库jrlt。按如下操作,新建一个数据库jrlt,选择类型为“utf8_genrral_ci”,点击创建。创建成功后,在左侧可以看到创建好的数据库。
(4)进入数据库。点击数据库进入,进入后可以看到数据库中没有数据表。
(5)创建数据表users(主要用于存放用户信息id、name、password、photo、money,共5个字段)。创建数据表如下,点击Execute。
(6)编辑数据表。创建数据表的列名(id名密码照片钱),并设置响应的类型和长度,设置id键为主键。(主键需要非空增量),然后单击保存数据表。
(7)可以点击左侧新建的users表,该表的详细结构信息如下图所示。
2.3 插入数据
(1)进入数据表SQL操作界面。点击用户表→点击SQL进入修改表界面→点击要使用的SQL预定义语句。
(2)修改SQL命令。修改命令如下,点击执行。注意符号为英文格式。
INSERT INTO `users`(`name`, `password`,`photo`, `money`) VALUES ("DuZZ",123456,"c:\\photo\\touxiang.png",100);
INSERT INTO `users`( `name`, `password`, `photo`, `money`) VALUES ("GuQQ",654321,"c:\\photo\\touxiang.png",100);
(3)点击左侧的users表可以看到我们输入的用户信息,这里我们输入了DuZZ和GuQQ两个账号,如下图。
2.4 实现PHP与数据库的交互2.4.1 三步实现PHP与数据库的交互
(1)建立连接,用户建立连接时需要提供用户名+密码+地址+数据库名,其中数据库名可以在连接过程中切换。
(2) 执行 SQL 语句。
(3) 断开连接。
2.4.2 程序代码1:简单的三步实现
简单的三步交互,根据连接是否建立成功反馈信息。访问此页面以获取结果
代码中的功能分析如下。更多细节请参考官方手册《MySQL 增强扩展》。
代码执行结果如下:
2.4.3 程序代码2:优化代码1
代码的第一个输出是对象,不方便查看。我们可以使用 mysqli_fetch_assoc() 函数来获取对象中记录的数据作为关联数组。因为mysqli_fetch_assoc()函数一次只能取一条数据,所以是顺序取的。因此,可以将mysqli_fetch_assoc()函数作为条件,当执行成功时,会依次打印得到的关联数组信息。具体代码如下:
上述代码的执行结果如下:
2.4.4 面向对象代码3:将连接方式改为面向对象
代码1和2中面向对象的连接方式与面向过程的方式的主要区别在于连接过程、判断过程和断开过程的区别。数据访问和使用的过程基本相同。
connect_error)
{echo $mysqli->connect_error;}
$sql = "select * from users";
if ($results = mysqli_query($link,$sql)){ //mysqli_query函数执行失败时返回 false,通过mysqli_query() 成功执行SELECT, SHOW, DESCRIBE或 EXPLAIN查询会返回一个mysqli_result 对象,其他查询则返回true。
while ($result = mysqli_fetch_assoc($results)){ //使用mysqli_fetch_assoc函数获取返回对象中的具体数据。
var_dump($result);
echo "";
}
}
else{
die(mysqli_error($link));
}
$link->close();
?>
3 相关函数介绍3.1 数据库连接操作相关函数函数名
新的 mysqli(), mysqli_connect()
连接到指定的数据库
$mysqli->connect_errno, mysqli_connect_errno()
返回最后一次连接调用的错误代码
$mysqli->connect_error, mysqli_connect_error()
返回描述上次连接调用的错误代码的字符串
link->close(), mysqliclose(link->close(), mysqli_close(link->close(), mysqliclose(link)
断开连接,其中 $link 表示已建立的连接。
3.2 运算结果集对象相关函数function
mysqli_fetch_row()
通过索引数组获取记录的数据
mysqli_fetch_assoc()
以关联数组的形式获取记录的数据
mysqli_fetch_array()
获取记录的数据作为索引数组或关联数组
mysqli_fetch_all()
以索引数组或关联数组的形式获取所有记录的数据
mysqli_num_rows()
获取结果中的行数
mysqli_free_result()
释放与结果集关联的内存
3.还有 3 个命令
更多命令请参考官方手册《MySQL 增强扩展》。
4 感应
(1)掌握phpstudy中通过myadmin操作数据库的方法;
(2)使用PHP语句与MySQL交互;
(3)掌握数据库连接和操作结果集对象的常用命令;
(4)学习参考官方手册《MySQL 增强版扩展》。
参考文章
[1]《【PHP基础篇】实现PHP与SQL数据库的连接》 查看全部
php抓取网页连接函数(实现PHP与SQL数据库交互实验演示实验环境(一))
内容
1 简介
(1)为了实现网页访问后台数据库的功能,需要与网页代码中对应的数据库进行连接。
(2)PHP 5及以上版本推荐使用以下方法连接MySQL:
(3)两种连接方式对比:
(4)本节通过一个简单的例子来介绍实现PHP与SQL连接的全过程,主要介绍MySQLi的方法。
2 PHP与SQL数据库交互实验演示2.1 实验环境
(1)服务器:本实验基于虚拟机win2008系统的WAMP环境,该环境的配置过程请参考文章"【语言环境】WAMP环境的部署与优化- Win2008R2SP1 作为操作系统”。
(2)客户端:使用浏览器访问和控制。
(3)服务端和客户端在同一个局域网中,在服务端打开phpstudy,确保可以从客户端浏览器访问。
2.2 创建数据库
(1)通过访问phpMyAdmin登录数据库。管理员通过浏览器登录phpMyAdmin(位置在网站默认根目录下的phpMyAdmin文件夹下),输入URL服务器IP/phpMyAdmin在浏览器中输入mysql数据库的登录页面;输入用户名和密码登录(默认用户名和密码都是root),点击执行登录mysql数据库。


(2)数据库页面,登录数据库后可以看到如下界面,左边是默认的4个数据库,点击箭头所指的数据库,操作数据库。

(3)创建一个数据库jrlt。按如下操作,新建一个数据库jrlt,选择类型为“utf8_genrral_ci”,点击创建。创建成功后,在左侧可以看到创建好的数据库。


(4)进入数据库。点击数据库进入,进入后可以看到数据库中没有数据表。

(5)创建数据表users(主要用于存放用户信息id、name、password、photo、money,共5个字段)。创建数据表如下,点击Execute。

(6)编辑数据表。创建数据表的列名(id名密码照片钱),并设置响应的类型和长度,设置id键为主键。(主键需要非空增量),然后单击保存数据表。

(7)可以点击左侧新建的users表,该表的详细结构信息如下图所示。

2.3 插入数据
(1)进入数据表SQL操作界面。点击用户表→点击SQL进入修改表界面→点击要使用的SQL预定义语句。

(2)修改SQL命令。修改命令如下,点击执行。注意符号为英文格式。
INSERT INTO `users`(`name`, `password`,`photo`, `money`) VALUES ("DuZZ",123456,"c:\\photo\\touxiang.png",100);
INSERT INTO `users`( `name`, `password`, `photo`, `money`) VALUES ("GuQQ",654321,"c:\\photo\\touxiang.png",100);
(3)点击左侧的users表可以看到我们输入的用户信息,这里我们输入了DuZZ和GuQQ两个账号,如下图。

2.4 实现PHP与数据库的交互2.4.1 三步实现PHP与数据库的交互
(1)建立连接,用户建立连接时需要提供用户名+密码+地址+数据库名,其中数据库名可以在连接过程中切换。
(2) 执行 SQL 语句。
(3) 断开连接。
2.4.2 程序代码1:简单的三步实现
简单的三步交互,根据连接是否建立成功反馈信息。访问此页面以获取结果
代码中的功能分析如下。更多细节请参考官方手册《MySQL 增强扩展》。
代码执行结果如下:

2.4.3 程序代码2:优化代码1
代码的第一个输出是对象,不方便查看。我们可以使用 mysqli_fetch_assoc() 函数来获取对象中记录的数据作为关联数组。因为mysqli_fetch_assoc()函数一次只能取一条数据,所以是顺序取的。因此,可以将mysqli_fetch_assoc()函数作为条件,当执行成功时,会依次打印得到的关联数组信息。具体代码如下:
上述代码的执行结果如下:

2.4.4 面向对象代码3:将连接方式改为面向对象
代码1和2中面向对象的连接方式与面向过程的方式的主要区别在于连接过程、判断过程和断开过程的区别。数据访问和使用的过程基本相同。
connect_error)
{echo $mysqli->connect_error;}
$sql = "select * from users";
if ($results = mysqli_query($link,$sql)){ //mysqli_query函数执行失败时返回 false,通过mysqli_query() 成功执行SELECT, SHOW, DESCRIBE或 EXPLAIN查询会返回一个mysqli_result 对象,其他查询则返回true。
while ($result = mysqli_fetch_assoc($results)){ //使用mysqli_fetch_assoc函数获取返回对象中的具体数据。
var_dump($result);
echo "";
}
}
else{
die(mysqli_error($link));
}
$link->close();
?>
3 相关函数介绍3.1 数据库连接操作相关函数函数名
新的 mysqli(), mysqli_connect()
连接到指定的数据库
$mysqli->connect_errno, mysqli_connect_errno()
返回最后一次连接调用的错误代码
$mysqli->connect_error, mysqli_connect_error()
返回描述上次连接调用的错误代码的字符串
link->close(), mysqliclose(link->close(), mysqli_close(link->close(), mysqliclose(link)
断开连接,其中 $link 表示已建立的连接。
3.2 运算结果集对象相关函数function
mysqli_fetch_row()
通过索引数组获取记录的数据
mysqli_fetch_assoc()
以关联数组的形式获取记录的数据
mysqli_fetch_array()
获取记录的数据作为索引数组或关联数组
mysqli_fetch_all()
以索引数组或关联数组的形式获取所有记录的数据
mysqli_num_rows()
获取结果中的行数
mysqli_free_result()
释放与结果集关联的内存
3.还有 3 个命令
更多命令请参考官方手册《MySQL 增强扩展》。
4 感应
(1)掌握phpstudy中通过myadmin操作数据库的方法;
(2)使用PHP语句与MySQL交互;
(3)掌握数据库连接和操作结果集对象的常用命令;
(4)学习参考官方手册《MySQL 增强版扩展》。
参考文章
[1]《【PHP基础篇】实现PHP与SQL数据库的连接》
php抓取网页连接函数(PHP建立CURL请求的基本步骤和数据的工具)
网站优化 • 优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2022-04-11 00:32
CURL 是一个使用 URL 语法传输文件和数据的工具,并且支持多种协议,例如 HTTP、FTP、TELNET 等。最重要的是,PHP 还支持 CURL 库。使用 PHP 的 CURL 库可以轻松高效地抓取网页。你只需要运行一个脚本,然后分析你爬取的网页,然后你就可以通过编程方式获取你想要的数据。无论您是想从链接中获取一些数据,还是获取 XML 文件并将其导入数据库,甚至只是获取网页的内容,CURL 都是一个强大的 PHP 库。
PHP中建立CURL请求的基本步骤
①:初始化
curl_init()
②:设置属性
curl_setopt()。可以设置的 CURL 参数有一长串,可以指定 URL 请求的各种细节。
③:执行并得到结果
curl_exec()
④:松开手柄
curl_close()
CURL 实现 GET 和 POST
①:GET方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
②:POST方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
//设置post数据
$post_data = array(
"username" => "coder",
"password" => "12345"
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
③:如果获取到的数据是json格式,使用json_decode函数将其解释为数组。
$output_array = json_decode($data,true); //如果第二个参数为true,则转换为数组的形式。如果不填充,它将是一个对象的形式
如果你使用 json_decode($data) 来解析,你会得到对象类型的数据。
封装函数
/**
* @param $url 访问的URL
* @param string $post post数据(不填则为GET),数据支持数组,不支持json_encode处理后的json格式
* @param string $cookie 提交的$cookies
* @param int $returnCookie 是否返回$cookies
* @return mixed|string
*/
function curl_request($url, $post = '', $cookie = '', $returnCookie = 0)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url); //发送的地址
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给
curl_setopt($curl, CURLOPT_AUTOREFERER, 1); //重定向时,自动设置header中的Referer:信息
//curl_setopt($curl, CURLOPT_REFERER, "http://XXX"); //构造来路
if ($post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
}
if ($cookie) {
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
}
curl_setopt($curl, CURLOPT_HEADER, $returnCookie);
curl_setopt($curl, CURLOPT_TIMEOUT, 10); //超时时间为10秒
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //要求结果为字符串串且输出到屏幕
$data = curl_exec($curl);
if (curl_errno($curl)) {
return curl_error($curl);
}
curl_close($curl);
if ($returnCookie) {
list($header, $body) = explode("\r\n\r\n", $data, 2);
preg_match_all("/Set\-Cookie:([^;]*);/", $header, $matches);
$info['cookie'] = substr($matches[1][0], 1);
$info['content'] = $body;
return $info;
} else {
return $data;
}
}
建豪发送的函数:
/**
* CURL请求
* @param $url 请求url地址
* @param $method 请求方法 get post
* @param null $postfields post数据数组
* @param array $headers 请求header信息
* @param bool|false $debug 调试开启 默认false
* @return mixed
*/
function httpRequest($url, $method = "GET", $postfields = null, $headers = array(), $debug = false)
{
$method = strtoupper($method);
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
switch ($method) {
case "POST":
curl_setopt($ci, CURLOPT_POST, true);
if (!empty($postfields)) {
$tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields;
curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
}
break;
default:
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
break;
}
$ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
curl_setopt($ci, CURLOPT_URL, $url);
if ($ssl) {
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
}
//curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLINFO_HEADER_OUT, true);
/*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
$response = curl_exec($ci);
$requestinfo = curl_getinfo($ci);
$http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
if ($debug) {
echo "=====post data======\r\n";
var_dump($postfields);
echo "=====info===== \r\n";
print_r($requestinfo);
echo "=====response=====\r\n";
print_r($response);
}
curl_close($ci);
return $response;
//return array($http_code, $response,$requestinfo);
}
转载于: 查看全部
php抓取网页连接函数(PHP建立CURL请求的基本步骤和数据的工具)
CURL 是一个使用 URL 语法传输文件和数据的工具,并且支持多种协议,例如 HTTP、FTP、TELNET 等。最重要的是,PHP 还支持 CURL 库。使用 PHP 的 CURL 库可以轻松高效地抓取网页。你只需要运行一个脚本,然后分析你爬取的网页,然后你就可以通过编程方式获取你想要的数据。无论您是想从链接中获取一些数据,还是获取 XML 文件并将其导入数据库,甚至只是获取网页的内容,CURL 都是一个强大的 PHP 库。
PHP中建立CURL请求的基本步骤
①:初始化
curl_init()
②:设置属性
curl_setopt()。可以设置的 CURL 参数有一长串,可以指定 URL 请求的各种细节。
③:执行并得到结果
curl_exec()
④:松开手柄
curl_close()
CURL 实现 GET 和 POST
①:GET方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
②:POST方法实现
//初始化
$curl = curl_init();
//设置抓取的url
curl_setopt($curl, CURLOPT_URL, 'http://www.baidu.com');
//设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_HEADER, 1);
//设置获取的信息以文件流的形式返回,而不是直接输出。
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
//设置post方式提交
curl_setopt($curl, CURLOPT_POST, 1);
//设置post数据
$post_data = array(
"username" => "coder",
"password" => "12345"
);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
//执行命令
$data = curl_exec($curl);
//关闭URL请求
curl_close($curl);
//显示获得的数据
print_r($data);
③:如果获取到的数据是json格式,使用json_decode函数将其解释为数组。
$output_array = json_decode($data,true); //如果第二个参数为true,则转换为数组的形式。如果不填充,它将是一个对象的形式
如果你使用 json_decode($data) 来解析,你会得到对象类型的数据。
封装函数
/**
* @param $url 访问的URL
* @param string $post post数据(不填则为GET),数据支持数组,不支持json_encode处理后的json格式
* @param string $cookie 提交的$cookies
* @param int $returnCookie 是否返回$cookies
* @return mixed|string
*/
function curl_request($url, $post = '', $cookie = '', $returnCookie = 0)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url); //发送的地址
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); //启用时会将服务器服务器返回的"Location: "放在header中递归的返回给
curl_setopt($curl, CURLOPT_AUTOREFERER, 1); //重定向时,自动设置header中的Referer:信息
//curl_setopt($curl, CURLOPT_REFERER, "http://XXX"); //构造来路
if ($post) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
}
if ($cookie) {
curl_setopt($curl, CURLOPT_COOKIE, $cookie);
}
curl_setopt($curl, CURLOPT_HEADER, $returnCookie);
curl_setopt($curl, CURLOPT_TIMEOUT, 10); //超时时间为10秒
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //要求结果为字符串串且输出到屏幕
$data = curl_exec($curl);
if (curl_errno($curl)) {
return curl_error($curl);
}
curl_close($curl);
if ($returnCookie) {
list($header, $body) = explode("\r\n\r\n", $data, 2);
preg_match_all("/Set\-Cookie:([^;]*);/", $header, $matches);
$info['cookie'] = substr($matches[1][0], 1);
$info['content'] = $body;
return $info;
} else {
return $data;
}
}
建豪发送的函数:
/**
* CURL请求
* @param $url 请求url地址
* @param $method 请求方法 get post
* @param null $postfields post数据数组
* @param array $headers 请求header信息
* @param bool|false $debug 调试开启 默认false
* @return mixed
*/
function httpRequest($url, $method = "GET", $postfields = null, $headers = array(), $debug = false)
{
$method = strtoupper($method);
$ci = curl_init();
/* Curl settings */
curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间,如果设置为0,则无限等待 */
curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
switch ($method) {
case "POST":
curl_setopt($ci, CURLOPT_POST, true);
if (!empty($postfields)) {
$tmpdatastr = is_array($postfields) ? http_build_query($postfields) : $postfields;
curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
}
break;
default:
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
break;
}
$ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
curl_setopt($ci, CURLOPT_URL, $url);
if ($ssl) {
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
}
//curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量,这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLINFO_HEADER_OUT, true);
/*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
$response = curl_exec($ci);
$requestinfo = curl_getinfo($ci);
$http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
if ($debug) {
echo "=====post data======\r\n";
var_dump($postfields);
echo "=====info===== \r\n";
print_r($requestinfo);
echo "=====response=====\r\n";
print_r($response);
}
curl_close($ci);
return $response;
//return array($http_code, $response,$requestinfo);
}
转载于: