M0nk3y's Blog

新秀企业网站系统代码审计学习(复现)

Word count: 2.1kReading time: 9 min
2020/09/12 Share

[TOC]

参考资料:

代码审计常见的三种方法(PHP篇)

PHP代码审计初次尝试之新秀企业网站系统

审计思路通过脑图大概总结如下:

image-20200912095322738

了解系统

CMS名称:新秀企业网站系统PHP版

image-20200912095625466

看着界面就知道是用于企业打广告(x)和发布信息,招聘等功能的系统。且存在前台用户登录和后台管理系统。存在搜索功能。

防护策略

IP登录限制 - 猜测伪造IP注入

限制了前台、后台的登录次数限制、注册限制、可能会影响到后面SQL注入漏洞的测试。

image-20200912100248513

可能出现的漏洞:伪造IP进行注入攻击

数据库监控,在注册的地方看看ip 是否被带入了数据库。

image-20200912102325325

1
select * from php_shisiusafe where saf_ip = '127.0.0.1'  and saf_action = 'register'

全局定位到获取用户iP的代码部分:

phpstorm 搜索 获取IP,即可。

include/function.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//获取客户端IP
function get_ip()
{
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'),'unknown'))
{
$ip = getenv('HTTP_CLIENT_IP');
}elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),'unknown')){
$ip = getenv('HTTP_X_FORWARDED_FOR');
}elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'),'unknown')){
$ip = getenv('REMOTE_ADDR');
}elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'],'unknown')){
$ip = $_SERVER['REMOTE_ADDR'];
}else{
$ip = '0.0.0.0';
}
if(!is_numeric(str_replace('.','',$ip)))
{
$ip = '0.0.0.0';
}
return $ip;
}

可以发现,当ip 除去. 后,如果不是纯数字,那么就设置为 0.0.0.0 。因此通过伪造IP进行注入是行不通了。

XSS 过滤

前台存在留言功能。

image-20200912103732317

提交后,我们登录后台管理员进行查看留言内容。 发现并没有执行js代码。

index/module/info_main.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function add_message()
{
safe('message');
global $global,$smarty,$lang;
$mes_email = post('email');
$mes_type = post('type');
$mes_title = post('title');
$mes_text = post('text');
$mes_show = post('show');
if($mes_email == '' || $mes_type == '' || $mes_title == '' || $mes_text == '')
{
$info_text = $lang['submit_error_info'];
}else{
$mes_add_time = time();
if($mes_show != '2')
{
$mes_show = '0';
}
$obj = new message();
$obj->set_value('mes_user_id',$global['user_id']);
$obj->set_value('mes_type',$mes_type);
$obj->set_value('mes_email',$mes_email);
$obj->set_value('mes_title',$mes_title);
$obj->set_value('mes_text',$mes_text);
$obj->set_value('mes_add_time',$mes_add_time);
$obj->set_value('mes_show',$mes_show);
$obj->set_value('mes_lang',S_LANG);
$obj->add();
if(intval(get_varia('sentmail')))
{
$email_title = '您的网站有了新的留言';
$email_text = "[$mes_type] $mes_title <br /> $mes_text";
call_send_email($email_title,$email_text,$global['user_id'],$mes_email);
}
$info_text = $lang['submit_message'];
}
$smarty->assign('info_text',$info_text);
$smarty->assign('link_text',$lang['go_back']);
$smarty->assign('link_href',url(array('channel'=>'message')));
}

我们的输入是被传入了post 函数进行执行,跟进该函数。

1
2
3
4
5
//获取post
function post($val,$filter = 'strict')
{
return $filter(isset($_POST[$val])?$_POST[$val]:'');
}

通过了strict 条件的过滤函数,找到这个的定义处。

include/function.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//严格过滤字符串中的危险符号
function strict($str)
{
if(S_MAGIC_QUOTES_GPC)
{
$str = stripslashes($str);
}
$str = str_replace('<','&#60;',$str);
$str = str_replace('>','&#62;',$str);
$str = str_replace('?','&#63;',$str);
$str = str_replace('%','&#37;',$str);
$str = str_replace(chr(39),'&#39;',$str);
$str = str_replace(chr(34),'&#34;',$str);
$str = str_replace(chr(13).chr(10),'<br />',$str);
return $str;
}

可以看到html 的闭合标签被转义了,所以没法XSS

CSRF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function edit_pwd()
{
safe('edit_pwd');
global $global,$smarty,$lang;
$old_pwd = post('old_pwd');
$new_pwd = post('new_pwd');
$re_pwd = post('re_pwd');
if(strlen($old_pwd) < 6 || strlen($old_pwd) > 15 || strlen($new_pwd) < 6 || strlen($new_pwd) > 15 || $new_pwd != $re_pwd)
{
$info_text = $lang['submit_error_info'];
}else{
$use_password = md5($old_pwd);
$obj = new users();
$obj->set_where('use_id = '.$global['user_id']);
$obj->set_where("use_password = '$use_password'");
if($obj->get_count() > 0)
{
$use_password = md5($new_pwd);
$obj->set_value('use_password',$use_password);
$obj->set_where('');
$obj->set_where('use_id = '.$global['user_id']);
$obj->edit();
$info_text = $lang['over'];
}else{
$info_text = $lang['old_pwd_error'];
}
}
$smarty->assign('info_text',$info_text);
$smarty->assign('link_text',$lang['go_back']);
$smarty->assign('link_href',url(array('entrance'=>$global['entrance'],'channel'=>'user','mod'=>'profile')));
}

CSRF 修改用户密码,需要旧密码,行不通。

可控变量过滤

session 过滤

1
2
3
4
5
6
7
8
9
10
//获取session
function get_session($name,$filter = 'strict')
{
if(S_SESSION)
{
return $filter(isset($_SESSION[$name])?$_SESSION[$name]:'');
}else{
return $filter(isset($_COOKIE[$name])?$_COOKIE[$name]:'');
}
}
1
2
3
4
5
6
7
8
9
10
//设置cookie
function set_cookie($name,$value,$filter = 'strict',$expire = 0)
{
if($expire == 0)
{
setcookie($name,$filter($value));
}else{
setcookie($name,$filter($value),$expire);
}
}

admin 登录

admin/module/info_main.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function admin_login()
{
safe('admin_login');
global $smarty,$lang;
$username = substr(post('username'),0,30);
$password = substr(post('password'),0,30);
if($username == '' || $password == '')
{
unset_session('admin_username');
unset_session('admin_password');
$info_text = '对不起,用户名和密码不能为空';
$link_text = '返回重新登录';
}else{
……

user 登录

index/module/info_main.php

1
2
3
4
5
6
7
8
9
10
11
12
13
function user_login()
{
safe('user_login');
global $global,$smarty,$lang;
$info_text = post('info_text');
$link_text = post('link_text');
$link_href = post('link_href');
$username = post('username');
$password = post('password');

if(strlen($username) > 30){$username = substr($username,30);}
if(strlen($password) > 30){$password = substr($password,30);}
if($username == '' or $password == '')

可以看到,基本都经过了带有过滤的函数处理。所以像,SQL注入和XSS 这种需要构造特殊符号的漏洞几乎很难了

漏洞分析

基于功能点去测,先高危后低危。后台一般防御较弱,从后台突破较容易。而后台存在的功能有:图片、文件、模板管理、删除、留言审核,等其他功能。

前台搜索框SQL 注入

开启MySQL 监控,然后再搜索框进行搜索。

image-20200912161945551

如图,123,被带入SQL语句进行查询,单引号闭合。将关键字在整个文件夹中搜索:

image-20200912162117271

这里先rawurldecode 解码,然后带入拼接进入查询。

然后下断点进行分析:

image-20200912165411168

直接上sqlmap。

image-20200912155851842

后台任意文件删除漏洞

/admin/deal.php

image-20200912144227587

此处采用了白名单的形式,只能删除 指定的三个目录下的文件。但是忽略了可以用../来绕过。

1
2
3
4
if(substr($path,0,strlen($dir[$i])) == $dir[$i])
{
$flag = true;
}

substr 从第$path的第一个字母开始往后判断,截取path前半部分长度和白名单是否相等,即是否是白名单里的那几个目录,是,然后unlink删除掉。

成功删除文件时,返回1。

这里我遇到了一个问题,就是这个域名是通过MAMP修改的本地HOSTS文件解析的,然后找到了一篇文章,https://www.jianshu.com/p/3018b2697bb0,简单设置一下就可以了。

image-20200912152353172

image-20200912152108244

漏洞演示

后台编辑语言文件设置GetShell

成功编辑后,回显 编辑语言包成功。那么在整个文件中搜索即可定位到代码。
**/admin/module/file/deal.php**

1
2
3
4
5
6
7
8
9
10
function edit_lang()
{
global $smarty,$lang;
$path = post('path');
$lang_text = post('lang_text','no_filter');
file_put_contents($path,$lang_text);
$smarty->assign('info_text','编辑语言包成功');
$smarty->assign('link_text','返回上一页');
$smarty->assign('link_href',url(array('channel'=>'file','mod'=>'lang_edit','path'=>rawurlencode($path))));
}

在该函数中,经过post() 函数过滤,但是对于$lang_text 的过滤规则是no_filter ,跟进查看一下该规则。

/include/function.php,104行。

1
2
3
4
5
6
7
8
9
//不过滤
function no_filter($str)
{
if(S_MAGIC_QUOTES_GPC)
{
$str = stripslashes($str); // 删除反斜杠
}
return $str;
}

这里没有过滤 $lang_text 就通过file_put_contents 写入文件,那么这里就可以写WebShell。

同样的,下断点来调试分析。

step into ,慢慢点,可以看到这里没有任何过滤,写入webshell

image-20200912173700069

image-20200912173822681

$path 可控的,这里只需要把$path 改为一个.php 后缀的就可以GetShell 了。

image-20200912174519203

image-20200912174743837

审计总结

基本上是照搬照抄别人的思路来搞的,总比躺着玩手机强,也遇到了一些问题,比如Burp Suite 设置 域名和ip 绑定,phpstorm 调试分析,总之,能学到东西就行。学到了思路,下一步就是复现thinkphp 的历史漏洞,和thinkcmf 或者其他php框架的历史漏洞,学习完之后就去找cms进行实战!(其实这个已经搁置了快半年了。

一个不能孜孜不倦,始终处于新知识、新技术学习状态下的安全爱好者,必然会被超越和取代。

Author:m0nk3y

原文链接:https://hack-for.fun/844d1b07.html

发表日期:September 12th 2020, 9:19:38 am

更新日期:September 12th 2020, 6:49:37 pm

版权声明:原创文章转载时请注明出处

CATALOG
  1. 1. 了解系统
  2. 2. 防护策略
    1. 2.1. IP登录限制 - 猜测伪造IP注入
    2. 2.2. XSS 过滤
    3. 2.3. CSRF
    4. 2.4. 可控变量过滤
      1. 2.4.1. session 过滤
      2. 2.4.2. cookie 过滤
      3. 2.4.3. admin 登录
      4. 2.4.4. user 登录
  3. 3. 漏洞分析
    1. 3.1. 前台搜索框SQL 注入
    2. 3.2. 后台任意文件删除漏洞
    3. 3.3. 后台编辑语言文件设置GetShell
  4. 4. 审计总结