├── .DS_Store ├── 74cms ├── 74CMS RCE漏洞分析.md └── 74cms_Home_Setup_v6.0.4.zip ├── README.md ├── appcms ├── appcms 代码审计.md └── appcms_2.0.zip └── bluecms ├── bluecms v1.6 代码审计.md └── bluecms_src.zip /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KpLi0rn/Code-audit/b59ea51f853e16fe53df175b94ce69de45a6aca1/.DS_Store -------------------------------------------------------------------------------- /74cms/74CMS RCE漏洞分析.md: -------------------------------------------------------------------------------- 1 | ## 74CMS RCE漏洞分析 2 | 3 | ### 0x00 前言 4 | 5 | 之前在逛p牛知识星球的时候看到了panda师傅发的这个74cms文件包含RCE,然后又是最近的所以就跟进一波,从中学习学习 6 | 7 | 复现环境以及源码下载链接: 8 | 9 | win10 、phpstudy 10 | 11 | 源码下载链接:http://data.wjlshare.xyz/project-source-code/74cms_Home_Setup_v6.0.4.zip 12 | 13 | ### 0x01 前置知识 14 | 15 | 74cms是基于ThinkPHP 3.2.3框架的基础上二次开发的,所以我们在审计74cms之前我们需要先对ThinkPHP框架做一个熟悉,ThinkPHP的开发手册写的非常好,我放到我的服务器上了下载链接如下 16 | 17 | 下载链接:http://data.wjlshare.xyz/project-source-code/ThinkPHP3.2.3dev.pdf 18 | 19 | ThinkPHP是一个单入口文件进行项目的部署和访问,所有功能的实现都是通过这个唯一的入口文件。 20 | 21 | ![image-20210109180758031](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109180758031.png) 22 | 23 | 大致项目结构如下: 24 | 25 | ![image-20210109181339812](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109181339812.png) 26 | 27 | ThinkPHP中主要采用的是多层MVC架构:Views(视图)、Model(模块)、Controller(控制器) 28 | 29 | 30 | 31 | Model: 主要负责封装和实现应用的具体功能等 32 | 33 | Views:主要负责视图的展现 34 | 35 | Controller:主要负责请求拦截妆发,加载配置文件,调配对应的模块来处理对应的请求等 36 | 37 | ThinkPHP框架自带了一个应用目录结构,并且带了一个默认的应用入口文件,方便部署和测试,默认的应 38 | 用目录是Application 39 | 40 | ![image-20210109182639678](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109182639678.png) 41 | 42 | ThinkPHP项目在初始化的时候会自动的生成几个默认的模块 43 | 44 | ![image-20210109183238527](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109183238527.png) 45 | 46 | Common 公共模块是不能直接访问的 47 | 48 | 其中每个模块之间都是独立的,所以每个模块都可以自由的设置和添加 (也就是之前说的多层MVC架构) 49 | 50 | ![image-20210109185006152](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109185006152.png) 51 | 52 | ThinkPHP下访问是通过利用入口文件访问对应模块下对应控制器的对应操作方法 53 | 54 | ThinkPHP下访问默认页面就会通过如下路由进行访问 55 | 56 | `http://localhost/index.php?m=Home&c=Index&a=index` 57 | 58 | m : Home模块 59 | 60 | c :Index控制器类 61 | 62 | a :index操作 63 | 64 | ![image-20210109185735953](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109185735953.png) 65 | 66 | 效果如下: 67 | 68 | ![image-20210109190440864](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109190440864.png) 69 | 70 | 我们这里直接来看74cms这样更加形象一些 71 | 72 | 73 | 74 | ![image-20210109190633733](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109190633733.png) 75 | 76 | 上面这个路由对应访问的就是 77 | 78 | Admin模块下的Index控制类中的login操作 79 | 80 | ![image-20210109190807254](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109190807254.png) 81 | 82 | 这边只能简单的介绍一下ThinkPHP的一些信息,具体的话师傅们可以去看开发者手册 83 | 84 | ### 0x02 漏洞复现 85 | 86 | #### 文件包含日志实现RCE 87 | 88 | 利用报错将shell写入日志 89 | 90 | ![image-20210108163807087](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210108163807087.png) 91 | 92 | 由于基于thinkPHP 二次开发,所以日志目录就是 ThinkPHP的日志目录 93 | 94 | `/data/Runtime/Logs/Home/xx_xx_xx.log` 95 | 96 | ![image-20210108163828660](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210108163828660.png) 97 | 98 | 这里如果直接是 `` 是不会有内容回显的,为什么呢?我们在下面的代码分析中会提到 99 | 100 | ### 0x03 代码分析 101 | 102 | 我们来看74cms官网修复的地方 103 | 104 | ![image-20210109210212287](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109210212287.png) 105 | 106 | 漏洞代码如下: 107 | 108 | ![image-20210109210303204](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109210303204.png) 109 | 110 | 可以发现官方修复手法在之前的基础上增加了一个判断 111 | 112 | 113 | 114 | 我们从漏洞点开始分析: 115 | 116 | 文件位置:`/Application/Common/Controller/BaseController.class.php` 大约 168行左右 117 | 118 | 发现`assign_resume_tpl` 函数需要传入两个参数,文件包含存在的位置在 `$tpl` 参数,所以我们直接跟进 fetch方法 119 | 120 | ![image-20210109210425387](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109210425387.png) 121 | 122 | 文件位置 : `/ThinkPHP/Library/Think/Controller.class.php` , 之前传入的 `$tpl` 传到了这里的第一个参数的位置,`$content` `$prefix` 都为空,继续跟进 123 | 124 | ![image-20210109210945718](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109210945718.png) 125 | 126 | 文件位置: `/ThinkPHP/Library/Think/View.class.php` 127 | 128 | 由于`$content` 参数为空,所以进入if判断语句,如果文件存在就进入 `parseTemplate` ,此时的 `$templateFile` 的数值为我们之前传入的文件地址 例 :` ./data/upload/resume_img/2101/08/5ff814074d7af.png`,由于文件调用 `parseTemplate` 进行解析 129 | 130 | ```php 131 | $templateFile = $this->parseTemplate($templateFile); 132 | ``` 133 | 134 | ![image-20210109211312929](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109211312929.png) 135 | 136 | 跟进到我们的 `parseTemplate` 方法,在同一个文件 137 | 138 | 这个函数会进行一个判断传入目标路径是否是文件,如果是的话则直接进行一个返回,如果不是文件的话就会利用ThinkPHP的C函数读取配置文件中的`TMPL_TEMPLATE_SUFFIX` 进行一个拼接 139 | 140 | 默认拼接后缀为 `.html` 141 | 142 | ![image-20210109211909708](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109211909708.png) 143 | 144 | 这里我们传入的文件路径是文件所以直接返回路径,并不会进行拼接 145 | 146 | ![image-20210109211724567](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109211724567.png) 147 | 148 | 149 | 150 | 继续返回到之前的fetch函数 151 | 152 | 继续往下看发现有一个if判断语句会将`php`和 `TMPL_ENGINE_TYPE` 进行一个比较 ,通过查看发现74cms默认的值为THINK 153 | 154 | ![image-20210109212510764](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109212510764.png) 155 | 156 | 所以我们进入到else语句中,将数值传入了数组,然后再带入到了 `Hook::listen` 方法中 157 | 158 | 即监听 `view_parse` 标签位,利用数组传入多个参数 159 | 160 | ![image-20210109212358136](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109212358136.png) 161 | 162 | ps: 这里来解释一下上面的那个问题 为什么 普通的 `` 没有回显,而是需要在后面添加 `ob_flush();` 163 | 164 | 我们这里看到第118行,`ob_start();` 这个代码的意思是打开缓冲区,所以我们phpinfo信息会存储在缓冲区,所以才没有回显,那么我们想要有回显的话,只需要执行ob_flush冲刷出(送出)输出缓冲区中的内容,打印到浏览器页面上即可 165 | 166 | 参考链接:https://xz.aliyun.com/t/8596#toc-5 167 | 168 | 这里的标签涉及ThinkPHP框架的Behavior行为,我们可以把标签理解为钩子函数,即程序运行到这个标签的时候就会被拦截下来执行相关的行为 169 | 170 | ![image-20210109215920344](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109215920344.png) 171 | 172 | 继续跟进文件 173 | 174 | 文件位置:`/ThinkPHP/Library/Think/Hook.class.php` 大约80行左右的位置 175 | 176 | 当系统执行到了 `view_parse` 标签位的时候,ThinkPHP会找到`Hook::listen()` 方法,查找 `$tags` 中绑定 `view_parse` 标签的方法,然后利用foreach进行遍历并且执行 `Hook:exec` 方法 177 | 178 | ![image-20210109220103078](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109220103078.png) 179 | 180 | 跟进 `exec` 方法 约119行左右 181 | 182 | 发现会做一个判断检查行为名称,如果包含 `Behavior` 字符串,那么入口就必须是run方法 183 | 184 | ![image-20210109221447689](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109221447689.png) 185 | 186 | 全局搜索 `view_parse` 找到配置文件 187 | 188 | 文件位置:`/ThinkPHP/Mode/common.php` 大约61行左右 189 | 190 | ![image-20210109221714998](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109221714998.png) 191 | 192 | 从上面的配置文件中可以发现,`view_parse` 执行了 `Behavior\ParseTemplateBehavior` 类,所以入口就是`run` 方法,我们跟踪过去进行查看 193 | 194 | 文件位置 : `/ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php` 大约17行左右 195 | 196 | 上文我们已经知道我们的模板引擎就是Think所以我们进入第一个if 判断,如果我们是第一次进行模板解析的话会进入else语句,然后执行了`fetch()` 方法 197 | 198 | ![image-20210109222218884](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109222218884.png) 199 | 200 | 继续跟踪 201 | 202 | 文件位置:`/ThinkPHP/Library/Think/Template.class.php` 大约75行左右 203 | 204 | 发现在77行左右调用了 `loadTemplate` 方法 205 | 206 | ![image-20210109222601472](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109222601472.png) 207 | 208 | 继续跟进我们的方法在同文件下大约89行左右 209 | 210 | 会进行一个if判断如果我们是文件的话就会读取我们的内容 存放到 `$tmplContent` 中,由于默认 `LAYOUT_ON` 是false 所以我们直接进入else语句 211 | 212 | ![image-20210109222750913](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109222750913.png) 213 | 214 | 继续跟踪我们的`compiler` 函数 215 | 216 | 同名文件下 大约129行左右 217 | 218 | 发现在解析过程中,并没有对文件内容进行过滤,而是直接进行了拼接,然后返回了我们的内容 219 | 220 | ![image-20210109223013875](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223013875.png) 221 | 222 | 回到我们之前的 `loadTemplate`函数 223 | 224 | 将我们之前返回的内容进行一个缓存处理 225 | 226 | ![image-20210109223413736](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223413736.png) 227 | 228 | 全局搜索 `cache_suffix` 229 | 230 | ![image-20210109223551025](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223551025.png) 231 | 232 | ![image-20210109223513777](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223513777.png) 233 | 234 | 即如下位置 235 | 236 | ![image-20210109223629502](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223629502.png) 237 | 238 | 最后返回我们的缓存文件名给fetch函数 239 | 240 | ![image-20210109223804266](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223804266.png) 241 | 242 | 如下图: 243 | 244 | ![image-20210109223844377](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223844377.png) 245 | 246 | 进入了 `Storage::load($templateCacheFile,$this->tVar,null,'tpl');` 方法 247 | 248 | 我们跟进该方法 249 | 250 | 文件位置:`/ThinkPHP/Library/Think/Storage/Driver/File.class.php` 大约76行左右 251 | 252 | 发现包含了这个文件,从而导致RCE 253 | 254 | ![image-20210109223954100](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210109223954100.png) 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | ### 0x04 参考链接 263 | 264 | https://xz.aliyun.com/t/8596#toc-5 265 | 266 | https://xz.aliyun.com/t/8520 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /74cms/74cms_Home_Setup_v6.0.4.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KpLi0rn/Code-audit/b59ea51f853e16fe53df175b94ce69de45a6aca1/74cms/74cms_Home_Setup_v6.0.4.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code-audit 2 | 3 | 用来记录自己代码审计的学习笔记 4 | 5 | 包含源码和笔记,如有版权问题请联系我 -------------------------------------------------------------------------------- /appcms/appcms 代码审计.md: -------------------------------------------------------------------------------- 1 | ## Appcms 代码审计 2 | 3 | ### 0x01 任意文件包含(前台RCE) 4 | 5 | 文件位置: `index.php` 大约202行左右 6 | 7 | ![image-20210113215845480](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113215845480.png) 8 | 9 | 发现在文件202行进行了路径的拼接,且`$tpl` 参数用户可控 10 | 11 | ![image-20210113215958368](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113215958368.png) 12 | 13 | 并没有对路径进行过滤,导致之间将文件包含到我们`/template/default/` 文件夹下 14 | 15 | 这里不过我们需要清楚一点,就是我们只是部分路径可控,比如我们上传了一个 `xxxxx.jpg` 那么在包含的过程中代码会拼接 `.php` 就会导致模版找不到的情况出现 16 | 17 | ![image-20210113224041607](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224041607.png) 18 | 19 | 所以如果想要包含我们的shell的话,我们需要利用php低版本的阶段来截断后面的 `.php` ,php版本小于 5.3.4 我这里的版本是`phpstudy的php 5.2.17` 20 | 21 | 接下来我们可以有两种办法 22 | 23 | 1. 包含中间件的日志(这套cms好像没有自己的日志) 24 | 2. 找前台可上传的点,上传我们的文件然后利用文件包含进行引入 25 | 26 | 我这里尝试了第二种,因为在实战过程中第一种的路径又需要信息搜集不是很稳,如果我们能有cms的前台上传点的话那么就很稳了 27 | 28 | 这里我翻了翻源码发现在 `upload` 文件夹下有两个文件 29 | 30 | ![image-20210113224502897](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224502897.png) 31 | 32 | 直接访问第一个文件会发现有权限校验 33 | 34 | ![image-20210113224539482](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224539482.png) 35 | 36 | 访问第二个文件发现是一个上传样式 37 | 38 | ![image-20210113224611260](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224611260.png) 39 | 40 | 图片上传之后发现没反应,f12查看源码,发现会将请求发送到 `/upload/upload_file.php?params=&v=dBJhuBrKulYUJ8wNxlliKrclH` 接口 41 | 42 | ![image-20210113224657660](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224657660.png) 43 | 44 | 这不直接本地梭哈一下 45 | 46 | 本地构造上传文件,点击上传,美滋滋文件上传成功了 47 | 48 | ```html 49 |
50 | 51 | 52 |
53 | ``` 54 | 55 | 同时在url中返回了重新命名的文件名 56 | 57 | ![image-20210113224827056](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224827056.png) 58 | 59 | url解码一下 60 | 61 | ![image-20210113224839149](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224839149.png) 62 | 63 | 然后回到我们的index文件,直接包含+截断 64 | 65 | ![image-20210113224902297](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224902297.png) 66 | 67 | 美滋滋啊 真不戳 68 | 69 | ![image-20210113224930727](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113224930727.png) 70 | 71 | 嘿嘿既然发现了接口未授权我们就去看一下好了 72 | 73 | 文件 `/upload/upload_file.php` 74 | 75 | 发现这里源码是根据get和v参数接受到的参数进行鉴权的,看来是大意了呀,还是得从session中进行鉴权 76 | 77 | ![image-20210113230705171](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113230705171.png) 78 | 79 | 那么再去看 `upload_form.php` 为啥会显示get和v参数的值 80 | 81 | 发现这里35行直接获取了,其实主要是这个文件没有做好权限的校验,导致我们能获取get和v从而上传文件 82 | 83 | ![image-20210113230857303](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210113230857303.png) 84 | 85 | ### 0x02 部分SSRF+文件读取 86 | 87 | 文件位置:`/pic.php` 88 | 89 | 读取非php文件源码,利用SSRF探测内网开放情况,访问默认内网页面 90 | 91 | 这尼玛就离谱,搞了半天 92 | 93 | 可以看到这里作者还是做了一些过滤的 94 | 95 | 首先会对我们传入的参数进行一个base64的解码 96 | 97 | ![image-20210114220541433](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210114220541433.png) 98 | 99 | 然后代码中会先用 `.` 对传入的url进行一个拆分,然后会取最后一个 `.`后的判断是否在数组中 100 | 101 | 这种情况我们还是比较好绕过的可以利用如下 102 | 103 | `?q=1.jpg` 104 | 105 | 接下来判断里面有没有php,如果有那么就不行 106 | 107 | 这里真的绕不过去,看网上有师傅是利用url编码绕过的,但是我本地不行,后面重新测试了一下感觉确实是不行 108 | 109 | 在我们浏览器发送请求的时候 服务器会对我们URL中的参数进行一次url解码,但是这里传输的是base64 110 | 111 | 后端只会接收到base64然后进行解码,这中间是不会再进行一次url解码的 112 | 113 | 不过根据目前的情况还是有一些姿势能读取部分文件,ssrf,和host头注入 114 | 115 | 1. 部分文件读取 116 | 117 | 这里读取的是`index.php` 因为访问目录的时候默认访问的是 index文件,所以我们可以构造 118 | 119 | `http://localhost/?q=1.jpg` 进行读取主页的源码,或配置文件等 120 | 121 | ![image-20210115180021700](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115180021700.png) 122 | 123 | 2. 部分ssrf 124 | 125 | 同样由于php的限制我们只能访问 index这样的页面 126 | 127 | `http://localhost/?q=1.jpg` 128 | 129 | 130 | 131 | 发现在第17行的时候,host头可控,正常情况下可以利用%0a%0d 进行CRLF攻击 132 | 133 | ![image-20210115143113953](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115143113953.png) 134 | 135 | 但是`header` 函数自4.4之后就增加了对host头攻击的保护 ,所以这里是不会存在CRLF注入等,这里感谢丁师傅的提示 136 | 137 | ![image-20210115143250308](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115143250308.png) 138 | 139 | ### 0x03 SQL注入 ? 140 | 141 | 文件位置 `comment.php` 142 | 143 | 个人感觉不存在,理由如下 144 | 145 | 首先 `$page['post'] ` 接受的是post接受到的数据,按照如下格式存储在`page['post'] ` 中 146 | 147 | ```php 148 | 'ip'=>'xxxx','host'=>'localhost' 149 | ``` 150 | 151 | ![image-20210115203340451](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115203340451.png) 152 | 153 | 继续往下看 ,发现对传入对参数都进行了html实体化处理 154 | 155 | 这样我们无法通过闭合前面的特殊符号来使得我们的sql语句逃逸出来从而执行,继续往下看,因为此时我们还不知道sql语句的结构 156 | 157 | ![image-20210115203710894](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115203710894.png) 158 | 159 | 发现在80行我们的评论进入了`filter_words` 函数 160 | 161 | 跟进一下发现只是对敏感词的一个过滤 162 | 163 | ![image-20210115204014646](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115204014646.png) 164 | 165 | 继续往下看发现调用了 `single_insert` 来执行我们的语句 166 | 167 | ![image-20210115204104870](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115204104870.png) 168 | 169 | 我们跟进去看一下sql语句的结构 170 | 171 | 发现我们传入的 `$fields` 会进行一个循环处理,然后对我们的数值进行一个 `' '` 的添加,key是不会添加 `'`但是key我们并没有办法控制,我们只能控制 `value` ,在html实体转义之后我们输入的引号不会闭合之前的结构,所以我个人觉得这里是不存在SQL注入的,同样的ip也没做过滤但是我们仍然无法跳脱出引号。有可能我这个版本是漏洞修复后的吧 172 | 173 | ![image-20210115204200001](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115204200001.png) 174 | 175 | ### 0x04 反射XSS 176 | 177 | 文件位置 : `callback.php` 178 | 179 | 没有进行过滤2333 拿下! 180 | 181 | ![image-20210115212959547](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115212959547.png) 182 | 183 | 简单构造一下即可 184 | 185 | ![image-20210115213029775](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115213029775.png) 186 | 187 | ![image-20210115213043490](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210115213043490.png) 188 | 189 | ### 0x05 模版注入(后台RCE) 190 | 191 | 文件位置:`/admin/template.php` 192 | 193 | `$page['post']['content']` 可控,所以我们可以在编辑过程中插入我们的恶意代码 194 | 195 | ![image-20210117095932192](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210117095932192.png) 196 | 197 | ![image-20210117102316003](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210117102316003.png) 198 | 199 | -------------------------------------------------------------------------------- /appcms/appcms_2.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KpLi0rn/Code-audit/b59ea51f853e16fe53df175b94ce69de45a6aca1/appcms/appcms_2.0.zip -------------------------------------------------------------------------------- /bluecms/bluecms v1.6 代码审计.md: -------------------------------------------------------------------------------- 1 | ## BlueCMS V1.6 代码审计 2 | 3 | ### 0x00 前言 4 | 5 | 之前因为种种原因一直没有开始php的审计(其实就是因为自己懒),现在趁着寒假的时间来搞一波,这个cms是之前先知上看到了,也看到很多人都用这个cms开头所以也来审计一下 6 | 7 | ### 0x01 思路 8 | 9 | 由于本人也是审计小白所以有的地方有误还请多多提出。 10 | 11 | 由于cms的文件、文件夹都会很多,所以我会事先简单的看一下各个文件夹下的php大致功能,然后我再依次文件看下来,不过一般都是从入口文件开始查看 12 | 13 | 项目结构如下: 14 | 15 | ![image-20210105135817608](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105135817608.png) 16 | 17 | 这里主要的代码都在uploads下所以我们直接看uploads下即可 18 | 19 | 这里参考:https://xz.aliyun.com/t/7992?spm=5176.12901015.0.i12901015.5277525cY8DiJc 20 | 21 | ``` 22 | ├── admin 后台管理目录 23 | ├── install 网站的安装目录 24 | ├── api 接口文件目录 25 | ├── data 系统处理数据相关目录 26 | ├── include 用来包含的全局文件 27 | └── template 模板 28 | ``` 29 | 30 | ### 0x02 漏洞挖掘 31 | 32 | 在安装过程中查看install文件夹发现配置文件 data/confg.php 33 | 34 | ![image-20210105141342155](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105141342155.png) 35 | 36 | 在配置文件中发现项目采用的编码格式为 GBK2312 ,看到这个编码后面在看SQL语句过程中就可以多留心一下了,因为可以通过 %df 来进行宽字节注入来绕过php的转义 37 | 38 | ![image-20210105141436156](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105141436156.png) 39 | 40 | 在对框架以及一些配置文件熟悉了之后就可以开始正式审计了,我这里直接从第一个文件开始看 41 | 42 | ### 0x03 ad_js.php 43 | 44 | ![image-20210105141805829](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105141805829.png) 45 | 46 | 在第二行我们发现该文件引入了/include/common.inc.php文件,我们追踪过去看看。 47 | 48 | 在开头部分我们可以看到这里定义了三个参数,定义了文件的根目录,上传目录以及数据处理相关目录 49 | 50 | ![image-20210105142024209](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105142024209.png) 51 | 52 | 下面发现引入了一些功能 53 | 54 | ![image-20210105142148089](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105142148089.png) 55 | 56 | 这个结构其实就是对所有传入的参数都进行了转义处理,会对传入的 " 和 ' 进行转义处理 57 | 58 | ![image-20210105142225661](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105142225661.png) 59 | 60 | 所以引入这个文件其实就是引入了一些基本设置,过滤规则等功能 61 | 62 | #### UNION SQL注入漏洞 63 | 64 | 文件位置 `/uploads/ad_js.php` 65 | 66 | $_GET获取到的数值并没有进行处理,直接拼接到了后面的SQL语句并进行了执行 67 | 68 | ![image-20210105143212554](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105143212554.png) 69 | 70 | 71 | 72 | 利用如下: 73 | 74 | 回显点为7 75 | 76 | ![image-20210105145239089](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105145239089.png) 77 | 78 | `/php/bluecms/uploads/ad_js.php?ad_id=-1+union+all+select+1,2,3,4,5,6,database()` 79 | 80 | ![image-20210105145257567](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105145257567.png) 81 | 82 | 83 | 84 | #### 反射型XSS漏洞 85 | 86 | 文件位置 `/uploads/ad_js.php` 87 | 88 | 由于引入的common.inc.php 文件中,为显示ERROR报错信息,从而导致直接将我们输入的信息进行了输出 89 | 90 | ```php 91 | error_reporting(E_ERROR); 92 | ``` 93 | 94 | ![image-20210105145816591](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105145816591.png) 95 | 96 | ```php 97 | $ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : ''; 98 | ``` 99 | 100 | ![image-20210105150110505](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105150110505.png) 101 | 102 | ### 0x04 comment.php 103 | 104 | #### INSERT SQL注入漏洞 105 | 106 | 问题出在getip() 函数上 107 | 108 | ![image-20210105151004488](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105151004488.png) 109 | 110 | 发现获取ip过程中没有对数值进行过滤 111 | 112 | ![image-20210105151102641](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105151102641.png) 113 | 114 | 所以只要是带有getip() 函数的地方就会存在SQL注入 115 | 116 | 根据代码进行构造 117 | 118 | ![image-20210105151351166](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105151351166.png) 119 | 120 | 构造如下payload 121 | 122 | `888' and if(ascii(substr(database(),1,1)=98),sleep(5),1),'1')#` 123 | 124 | ![image-20210105151907298](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105151907298.png) 125 | 126 | ### 0x05 guest_book.php 127 | 128 | #### INSERT SQL注入漏洞 129 | 130 | 同样的这里问题出在onlineip上 131 | 132 | ![image-20210105152651154](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105152651154.png) 133 | 134 | $online_ip 其实就是getup() 所以实际上出问题的还是getip() 135 | 136 | ![image-20210105152708558](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105152708558.png) 137 | 138 | 还是同样的配方hhhh 139 | 140 | `888' and if(ascii(substr(database(),1,1)=98),sleep(5),1),'1')#` 141 | 142 | ![image-20210105153019665](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105153019665.png) 143 | 144 | 145 | 146 | ### 0x06 publish.php 147 | 148 | #### INSERT SQL注入漏洞 149 | 150 | 原理还是因为getip()没有做好过滤导致 151 | 152 | ![image-20210105171622921](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105171622921.png) 153 | 154 | payload如下: 155 | 156 | `888' and if(ascii(substr(database(),1,1)=98),sleep(5),1) or '1'='1` 157 | 158 | ![image-20210105170706246](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105170706246.png) 159 | 160 | #### 反射型XSS漏洞 161 | 162 | 原因和之前一样,因为将错误回显了出来,所以导致可解析payload从而导致xss (不过这里位置是X-Forward-For 所以漏洞危害非常小) 163 | 164 | 注意要加入引号或其他的使其报错 165 | 166 | ![image-20210105164705643](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105164705643.png) 167 | 168 | 169 | 170 | #### 任意文件删除漏洞 171 | 172 | 由于id参数没有进行路径限定,同时又直接和项目路径进行了拼接,从而导致任意文件删除 173 | 174 | ![image-20210105171800678](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105171800678.png) 175 | 176 | 只需构造如下格式即可删除文件 177 | 178 | `http://192.168.1.9:8888/php/bluecms/uploads/publish.php?act=del_pic&id=q.txt` 179 | 180 | 执行之后q.txt文件便会被删除 181 | 182 | ### 0x07 user.php 183 | 184 | #### 任意URL跳转(比较鸡肋) 185 | 186 | 第16行,我们传入的参数可控 187 | 188 | ![image-20210105213312129](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105213312129.png) 189 | 190 | 然后再进行一次base64解码 191 | 192 | ![image-20210105213413497](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105213413497.png) 193 | 194 | 最后来到我们的漏洞点,直接传入了我们可以控制的$from 195 | 196 | ![image-20210105213432647](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105213432647.png) 197 | 198 | burp抓包将请求包中的from进行修改 199 | 200 | ![image-20210105213732294](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105213732294.png) 201 | 202 | 释放之后则会跳转到百度 203 | 204 | ![image-20210105213947612](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105213947612.png) 205 | 206 | #### 存储XSS漏洞 207 | 208 | 我们来看到第268行,发现将我们的数值传入到了filter_data中 209 | 210 | ![image-20210105175909698](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105175909698.png) 211 | 212 | 跟踪 filter_data函数,我们可以发现这里过滤没有做好,我们有很多中方式可以绕过~ 213 | 214 | ![image-20210105180001958](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105180001958.png) 215 | 216 | 直接在文章概要中插入我们的xss payload即可 217 | 218 | ![image-20210105181144207](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105181144207.png) 219 | 220 | 可以发现存储xss已经写进去了 221 | 222 | ![image-20210105181258942](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105181258942.png) 223 | 224 | #### 任意文件删除漏洞 225 | 226 | 同样是因为传入参数可控导致,这里直接通过post接受lit_pic参数的数值并且进行了一个拼接,如果文件存在则删除 227 | 228 | ![image-20210105182000228](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105182000228.png) 229 | 230 | 构造如下payload即可 231 | 232 | post内容 `post_id=1&link_man=aaa&link_phone=13888&link_email=123@123&link_qq=1234&link_address=cccc&lit_pic=./1.txt&title=aaa` 233 | 234 | ![image-20210105194602932](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105194602932.png) 235 | 236 | 237 | 238 | #### 任意文件包含漏洞 239 | 240 | 我们来看到这个代码,发现include中部分路径是我们可以控制的,那么在特定版本下我们可以实现任意文件包含 241 | 242 | 在php 5.4版本下我们可以截断后面的 /index.php 来实现任意文件包含 243 | 244 | ![image-20210105194811739](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210105194811739.png) 245 | 246 | 只需要如下即可进行截断 247 | 248 | ``` 249 | pay=..%2F..%2Frobots.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... 250 | ``` 251 | 252 | 可以通过上传图片马进行包含从而getshell 253 | 254 | #### 宽字节 SQL注入 255 | 256 | 该文件在头部引入了 common.inc.php 文件 257 | 258 | ```php 259 | require_once dirname(__FILE__) . '/include/common.inc.php'; 260 | ``` 261 | 262 | 前面分析过 common.inc.php 引入会对传入的参数都进行一个转义但是由于编码是gbk所以我们可以通过宽字节注入来实现 %df 263 | 264 | 在代码的第862行开始利用$_GET获取前端的user_name参数的数值 265 | 266 | ![image-20210106114131426](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106114131426.png) 267 | 268 | 来看具体功能点,根据源码分析,在判断用户名是否存在的时候会进行两次SQL查询,会通过这个接口进行判断 269 | 270 | ![image-20210106114328779](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106114328779.png) 271 | 272 | ![image-20210106190006183](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106190006183.png) 273 | 274 | ![image-20210106094717188](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106094717188.png) 275 | 276 | 当我们输入%df' 的时候会发现报错 277 | 278 | 利用宽字节来闭合前面的 \' 279 | 280 | ![image-20210106114441577](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106114441577.png) 281 | 282 | payload如下 发现了时延 283 | 284 | `1%df' and (select 1 from(select case when(ascii(substr(database(),1,1))=98) then sleep(5) else (1) end)x) -- -` 285 | 286 | 因为这里在代码中是会执行两次sql查询语句的所以这里sleep的时间是双倍的 287 | 288 | ![image-20210106114740286](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106114740286.png) 289 | 290 | 291 | 292 | #### 存储型XSS漏洞 293 | 294 | 136行处起的代码对传入的参数都没有进行严格的过滤 295 | 296 | ![image-20210106173022943](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106173022943.png) 297 | 298 | 这里选取邮箱来举例,可插入类似 `123@123.` 的payload 299 | 300 | 只需在注册的时候将邮箱改成上述payload即可 301 | 302 | ![image-20210106173255432](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106173255432.png) 303 | 304 | #### INSERT SQL注入 305 | 306 | 还是一样的问题由于没有对email那块的参数进行过滤导致,可以通过构造进行数据的插入 307 | 308 | ![image-20210106192931224](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106192931224.png) 309 | 310 | 利用burp进行抓包,修改email ,通过测试发现如果有 123@123.x前端会导致注入失败,所以利用burp绕过前端的邮箱验证 311 | 312 | payload如下 ,进行多行的插入 313 | 314 | `%df',1,1),(88,1223,md5(123456),(select database()),1,1) #` 315 | 316 | ![image-20210106193152356](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106193152356.png) 317 | 318 | 利用账户名 1223 密码 123456 进行登陆,发现database已经注入出来了 319 | 320 | ![image-20210106193244956](https://gitee.com/KpLi0rn/BlogImag/raw/master/img/image-20210106193244956.png) 321 | 322 | 323 | 324 | 325 | 326 | ### 0x08 总结 327 | 328 | 这个毕竟是十年前的cms了 漏洞自然非常多,第一次审cms感觉收获还是比较多的,以前总看着代码很多就退缩了,但是其实耐心看还是能捋清楚的。 329 | 330 | ps:其中还存在很多这样的漏洞,其实就是开发者以为转义就万事解决了,但是由于编码设置失败的问题导致可以利用宽字节进行注入,其他还有很多问题,比如注册可使用同一个邮箱导致覆盖万能密码这种等等 -------------------------------------------------------------------------------- /bluecms/bluecms_src.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KpLi0rn/Code-audit/b59ea51f853e16fe53df175b94ce69de45a6aca1/bluecms/bluecms_src.zip --------------------------------------------------------------------------------