前言
红名谷线下半决赛遇到的一个PHP论坛,据说这个论坛有几个很精彩的漏洞,这次来玩一玩。
因为找不到什么文章以及漏洞适用环境不符,所以基本上的思路是利用任意文件删除来删除install.lock,然后再重装论坛配置密码,到后台使用命令执行漏洞。
配置环境
首先到GitHub下载源码,我用的是(https://github.com/mektap/Discuz-3.4-Uyghurche)。

任意文件删除漏洞
先在首页注册一个账号,然后到设置页面源码里面找到formhash保存起来,一会还要用到。

对保存这个按钮事件触发的请求包抓包,把数据包中的出生地改成要删除文件的相对路径。

可以看到页面出生地的地方回显成刚刚修改的相对路径。

构造一个payload来上传文件,这里的formhash记得改成一开始从前端获取到的那个。

这里贴一下payload的源码。
<body>
<form action="http://[网站域名]/home.php?mod=spacecp&profilesubmit=1&formhash=[刚刚得到的formhash]" method="post" enctype="multipart/form-data">
<input type="file" name="birthprovince" />
<input type="submit" value="upload" />
</form>
</body>
接着,在本地表单上传一张喜欢的照片。(不喜欢的其实也行)

可以看到data目录下的install.lock已经被删除啦!

来看一下在源码里面到底发生了什么。首先来到source/include/spacecp/spacecp_profile.php,这里有个submitcheck('profilesubit')需要绕过。

进来submitcheck()函数之后会进入getgpc()函数,这里需要通过get对profilesubmit传参,var才能获取到其它的get参数。 这就解释了为什么上传文件那个payload中的url带有profilesubmit参数。

接着,回调submitcheck()函数,可以看到要满足if的条件才能返回一个TRUE。 可以看到这里对formhash进行了校验,所以在刚刚上传文件那个payload中的url带上从前端获取的formhash。

这个地方的unlink是补丁前的利用点,补丁添加了formtype的判断, 导致这个unlink不可以利用。不过,下面还有一个unlink可以利用。

上传文件后会进入这个if语句中,先是if判断是否上传失败,成功的话往if里面走。

接着,这里对更新前的个人资料做unlink处理,这就解释了为什么要在个人设置中找到一个可控的出生地(birthprovine), 并且把它赋值为要删除文件的相对路径。所以,这里删除的就是我们一开始在burpsuite中构造的出生地(birthprovince)。

然后在这里开始对数据库内容进行update处理,构造update的sql语句。

执行update的sql语句,将个人资料出生地变为照片的相对路径。

重装getshell漏洞
访问/install/index.php,进入重装页面,将数据表前缀改为x');phpinfo();('。不过这里不知道数据库的账号密码,实战中可以尝试下root或者123456这类弱密码。 实在不行的话,牺牲一下自己的vps,远程连接自己的数据库(毕竟拿shell血赚不亏)。
不过这里有一个前提条件需要注意,目标网站需要没登录过后台。因为登录后台后会把install/index.php删除导致不能重装, 所以拿到站,最好先访问/install/index.php,看看能不能通过重装getshell再决定要不要打payload。

访问config/config_ucenter.php就可以执行插入的payload了。

审计一下源码,可以看到进入了203行的elseif分支。

接着,进入install_uc_server()函数。

调用save_uc_config()函数保存配置。

可以看到这里没有任何的过滤就直接写入php配置文件,所以只要闭合')即可代码注入。

后台SQL注入
注入点在后台的UCenter应用id处。

构造报错注入payload,页面成功回显。

来到后台设置的代码段。

可以看到这里对输入的id进行了addslashes()转义处理。

这里读取配置文件,对配置缓存进行了更新。 而问题在于此处读取时没有再次转义,转义符号在读取的时候已被解析,造成了二次注入。

note_exists()函数通过result_first()函数将配置内容select出来。

从result_first()函数执行的sql语句,也可以看到转义符号已经被解析,构造了SQL注入的payload。

There Is Nothing Below
